Skip to main content

i_slint_compiler/
langtype.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 std::borrow::Cow;
5use std::collections::{BTreeMap, HashMap};
6use std::fmt::Display;
7use std::rc::Rc;
8use std::sync::Arc;
9
10use itertools::Itertools;
11
12use smol_str::SmolStr;
13
14use crate::diagnostics::SourceLocation;
15use crate::expression_tree::{BuiltinFunction, Expression, Unit};
16use crate::object_tree::{Component, DEFAULT_SLOT_NAME, PropertyVisibility};
17use crate::parser::SyntaxNode;
18use crate::typeregister::TypeRegister;
19
20#[derive(Debug, Clone, Default)]
21pub enum Type {
22    /// Correspond to an uninitialized type, or an error
23    #[default]
24    Invalid,
25    /// The type of an expression that return nothing
26    Void,
27    /// The type of a property two way binding whose type was not yet inferred
28    InferredProperty,
29    /// The type of a callback alias whose type was not yet inferred
30    InferredCallback,
31
32    Callback(Arc<Function>),
33    Function(Arc<Function>),
34
35    ComponentFactory,
36
37    // Other property types:
38    Float32,
39    Int32,
40    String,
41    Color,
42    Duration,
43    PhysicalLength,
44    LogicalLength,
45    Rem,
46    Angle,
47    Percent,
48    Image,
49    Bool,
50    /// Fake type that can represent anything that can be converted into a model.
51    Model,
52    PathData, // Either a vector of path elements or a two vectors of events and coordinates
53    Easing,
54    Brush,
55    /// This is usually a model
56    Array(Arc<Type>),
57    Struct(Arc<Struct>),
58    Enumeration(Arc<Enumeration>),
59    Keys,
60    /// `data-transfer` - a special type that handles reading a value from the system with
61    /// some set of available MIME types.
62    DataTransfer,
63
64    /// A type made up of the product of several "unit" types.
65    /// The first parameter is the unit, and the second parameter is the power.
66    /// The vector should be sorted by 1) the power, 2) the unit.
67    UnitProduct(Vec<(Unit, i8)>),
68
69    ElementReference,
70
71    /// This is a `SharedArray<f32>`
72    LayoutCache,
73    /// This is used by GridLayoutOrganizedData
74    ArrayOfU16,
75
76    StyledText,
77    MouseCursor,
78    Closure,
79}
80
81impl core::cmp::PartialEq for Type {
82    fn eq(&self, other: &Self) -> bool {
83        match self {
84            Type::Invalid => matches!(other, Type::Invalid),
85            Type::Void => matches!(other, Type::Void),
86            Type::InferredProperty => matches!(other, Type::InferredProperty),
87            Type::InferredCallback => matches!(other, Type::InferredCallback),
88            Type::Callback(lhs) => {
89                matches!(other, Type::Callback(rhs) if lhs == rhs)
90            }
91            Type::Function(lhs) => {
92                matches!(other, Type::Function(rhs) if lhs == rhs)
93            }
94            Type::ComponentFactory => matches!(other, Type::ComponentFactory),
95            Type::Float32 => matches!(other, Type::Float32),
96            Type::Int32 => matches!(other, Type::Int32),
97            Type::String => matches!(other, Type::String),
98            Type::Color => matches!(other, Type::Color),
99            Type::Duration => matches!(other, Type::Duration),
100            Type::Angle => matches!(other, Type::Angle),
101            Type::PhysicalLength => matches!(other, Type::PhysicalLength),
102            Type::LogicalLength => matches!(other, Type::LogicalLength),
103            Type::Rem => matches!(other, Type::Rem),
104            Type::Percent => matches!(other, Type::Percent),
105            Type::Image => matches!(other, Type::Image),
106            Type::Bool => matches!(other, Type::Bool),
107            Type::Model => matches!(other, Type::Model),
108            Type::PathData => matches!(other, Type::PathData),
109            Type::Easing => matches!(other, Type::Easing),
110            Type::MouseCursor => matches!(other, Type::MouseCursor),
111            Type::Brush => matches!(other, Type::Brush),
112            Type::Array(a) => matches!(other, Type::Array(b) if a == b),
113            Type::Struct(lhs) => {
114                matches!(other, Type::Struct(rhs) if lhs.fields == rhs.fields && lhs.name == rhs.name)
115            }
116            Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
117            Type::Keys => matches!(other, Type::Keys),
118            Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
119            Type::ElementReference => matches!(other, Type::ElementReference),
120            Type::LayoutCache => matches!(other, Type::LayoutCache),
121            Type::ArrayOfU16 => matches!(other, Type::ArrayOfU16),
122            Type::StyledText => matches!(other, Type::StyledText),
123            Type::DataTransfer => matches!(other, Type::DataTransfer),
124            Type::Closure => matches!(other, Type::Closure),
125        }
126    }
127}
128
129impl Display for Type {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        match self {
132            Type::Invalid => write!(f, "<error>"),
133            Type::Void => write!(f, "void"),
134            Type::InferredProperty => write!(f, "?"),
135            Type::InferredCallback => write!(f, "callback"),
136            Type::Callback(callback) => {
137                write!(f, "callback{}", callback)
138            }
139            Type::ComponentFactory => write!(f, "component-factory"),
140            Type::Function(function) => {
141                write!(f, "function{}", function)
142            }
143            Type::Float32 => write!(f, "float"),
144            Type::Int32 => write!(f, "int"),
145            Type::String => write!(f, "string"),
146            Type::Duration => write!(f, "duration"),
147            Type::Angle => write!(f, "angle"),
148            Type::PhysicalLength => write!(f, "physical-length"),
149            Type::LogicalLength => write!(f, "length"),
150            Type::Rem => write!(f, "relative-font-size"),
151            Type::Percent => write!(f, "percent"),
152            Type::Color => write!(f, "color"),
153            Type::Image => write!(f, "image"),
154            Type::Bool => write!(f, "bool"),
155            Type::Model => write!(f, "model"),
156            Type::Array(t) => write!(f, "[{t}]"),
157            Type::Struct(t) => write!(f, "{t}"),
158            Type::PathData => write!(f, "pathdata"),
159            Type::Easing => write!(f, "easing"),
160            Type::MouseCursor => write!(f, "MouseCursor"),
161            Type::Brush => write!(f, "brush"),
162            Type::Enumeration(enumeration) => write!(f, "{}", enumeration.name),
163            Type::Keys => write!(f, "keys"),
164            Type::DataTransfer => write!(f, "data-transfer"),
165            Type::UnitProduct(vec) => {
166                const POWERS: &[char] = &['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
167                let mut x = vec.iter().map(|(unit, power)| {
168                    if *power == 1 {
169                        return unit.to_string();
170                    }
171                    let mut res = format!("{}{}", unit, if *power < 0 { "⁻" } else { "" });
172                    let value = power.abs().to_string();
173                    for x in value.as_bytes() {
174                        res.push(POWERS[(x - b'0') as usize]);
175                    }
176
177                    res
178                });
179                write!(f, "({})", x.join("×"))
180            }
181            Type::ElementReference => write!(f, "element ref"),
182            Type::LayoutCache => write!(f, "layout cache"),
183            Type::ArrayOfU16 => write!(f, "[u16]"),
184            Type::StyledText => write!(f, "styled-text"),
185            Type::Closure => write!(f, "closure"),
186        }
187    }
188}
189
190impl From<Arc<Struct>> for Type {
191    fn from(value: Arc<Struct>) -> Self {
192        Self::Struct(value)
193    }
194}
195
196impl Type {
197    /// Whether the type is part of the Slint SC subset.
198    /// Callable without the `slint-sc` feature, so shared call sites need no `cfg`.
199    pub fn is_slint_sc(&self) -> bool {
200        #[cfg(feature = "slint-sc")]
201        return match self {
202            Self::Int32 | Self::LogicalLength | Self::Color | Self::Bool | Self::Image => true,
203            // A user-declared enum.
204            Self::Enumeration(en) => en.node.is_some(),
205            // A user-declared struct. Its field types were validated where the
206            // struct was declared, so they need no re-check here.
207            Self::Struct(s) => matches!(&s.name, StructName::User { .. }),
208            _ => false,
209        };
210        #[cfg(not(feature = "slint-sc"))]
211        false
212    }
213
214    /// valid type for properties
215    pub fn is_property_type(&self) -> bool {
216        matches!(
217            self,
218            Self::Float32
219                | Self::Int32
220                | Self::String
221                | Self::Color
222                | Self::ComponentFactory
223                | Self::Duration
224                | Self::Angle
225                | Self::PhysicalLength
226                | Self::LogicalLength
227                | Self::Rem
228                | Self::Percent
229                | Self::Image
230                | Self::Bool
231                | Self::Easing
232                | Self::MouseCursor
233                | Self::Enumeration(_)
234                | Self::Keys
235                | Self::DataTransfer
236                | Self::ElementReference
237                | Self::Struct { .. }
238                | Self::Array(_)
239                | Self::Brush
240                | Self::InferredProperty
241                | Self::StyledText
242        )
243    }
244
245    pub fn ok_for_public_api(&self) -> bool {
246        !matches!(self, Self::Easing)
247    }
248
249    /// Assume it is an enumeration, panic if it isn't
250    pub fn as_enum(&self) -> &Arc<Enumeration> {
251        match self {
252            Type::Enumeration(e) => e,
253            _ => panic!("should be an enumeration, bug in compiler pass"),
254        }
255    }
256
257    /// Return true if the type can be converted to the other type
258    pub fn can_convert(&self, other: &Self) -> bool {
259        let can_convert_struct = |a: &BTreeMap<SmolStr, Type>, b: &BTreeMap<SmolStr, Type>| {
260            // the struct `b` has property that the struct `a` doesn't
261            let mut has_more_property = false;
262            for (k, v) in b {
263                match a.get(k) {
264                    Some(t) if !t.can_convert(v) => return false,
265                    None => has_more_property = true,
266                    _ => (),
267                }
268            }
269            if has_more_property {
270                // we should reject the conversion if `a` has property that `b` doesn't have
271                if a.keys().any(|k| !b.contains_key(k)) {
272                    return false;
273                }
274            }
275            true
276        };
277        match (self, other) {
278            (a, b) if a == b => true,
279            (_, Type::Invalid)
280            | (_, Type::Void)
281            | (Type::Float32, Type::Int32)
282            | (Type::Float32, Type::String)
283            | (Type::Int32, Type::Float32)
284            | (Type::Int32, Type::String)
285            | (Type::Float32, Type::Model)
286            | (Type::Int32, Type::Model)
287            | (Type::PhysicalLength, Type::LogicalLength)
288            | (Type::LogicalLength, Type::PhysicalLength)
289            | (Type::Rem, Type::LogicalLength)
290            | (Type::Rem, Type::PhysicalLength)
291            | (Type::LogicalLength, Type::Rem)
292            | (Type::PhysicalLength, Type::Rem)
293            | (Type::Percent, Type::Float32)
294            | (Type::Brush, Type::Color)
295            | (Type::Color, Type::Brush) => true,
296            (Type::Array(a), Type::Model) if a.is_property_type() => true,
297            (Type::Struct(a), Type::Struct(b)) => can_convert_struct(&a.fields, &b.fields),
298            (Type::UnitProduct(u), o) => match o.as_unit_product() {
299                Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
300                None => false,
301            },
302            (o, Type::UnitProduct(u)) => match o.as_unit_product() {
303                Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
304                None => false,
305            },
306            _ => false,
307        }
308    }
309
310    /// If this is a number type which should be used with an unit, this returns the default unit
311    /// otherwise, returns None
312    pub fn default_unit(&self) -> Option<Unit> {
313        match self {
314            Type::Duration => Some(Unit::Ms),
315            Type::PhysicalLength => Some(Unit::Phx),
316            Type::LogicalLength => Some(Unit::Px),
317            Type::Rem => Some(Unit::Rem),
318            // Unit::Percent is special that it does not combine with other units like
319            Type::Percent => None,
320            Type::Angle => Some(Unit::Deg),
321            Type::Invalid => None,
322            Type::Void => None,
323            Type::InferredProperty | Type::InferredCallback => None,
324            Type::Callback { .. } => None,
325            Type::ComponentFactory => None,
326            Type::Function { .. } => None,
327            Type::Float32 => None,
328            Type::Int32 => None,
329            Type::String => None,
330            Type::Color => None,
331            Type::Image => None,
332            Type::Bool => None,
333            Type::Model => None,
334            Type::PathData => None,
335            Type::Easing => None,
336            Type::MouseCursor => None,
337            Type::Brush => None,
338            Type::Array(_) => None,
339            Type::Struct { .. } => None,
340            Type::Enumeration(_) => None,
341            Type::Keys => None,
342            Type::DataTransfer => None,
343            Type::UnitProduct(_) => None,
344            Type::ElementReference => None,
345            Type::LayoutCache => None,
346            Type::ArrayOfU16 => None,
347            Type::StyledText => None,
348            Type::Closure => None,
349        }
350    }
351
352    /// Return a unit product vector even for single scalar
353    pub fn as_unit_product(&self) -> Option<Vec<(Unit, i8)>> {
354        match self {
355            Type::UnitProduct(u) => Some(u.clone()),
356            Type::Float32 | Type::Int32 => Some(Vec::new()),
357            Type::Percent => Some(Vec::new()),
358            _ => self.default_unit().map(|u| vec![(u, 1)]),
359        }
360    }
361}
362
363#[derive(Debug, Clone)]
364pub enum BuiltinPropertyDefault {
365    None,
366    Expr(ConstantExpression),
367    /// The property is computed per element by this function, which takes the element.
368    ElementFunction(BuiltinFunction),
369    /// A value only the runtime knows, which this function returns. It takes no argument, so
370    /// unlike [`Self::ElementFunction`] the property still belongs to the native item.
371    RuntimeValue(BuiltinFunction),
372    /// The property is actually not a property but a builtin function
373    BuiltinFunction(BuiltinFunction),
374}
375
376impl BuiltinPropertyDefault {
377    /// The default of a property that doesn't need an element to express, for the callers that
378    /// have no `ElementRc` at hand.
379    pub fn expr_without_element(&self) -> Option<Expression> {
380        match self {
381            BuiltinPropertyDefault::None => None,
382            BuiltinPropertyDefault::Expr(constant) => Some(constant.to_expression()),
383            BuiltinPropertyDefault::RuntimeValue(function) => Some(Expression::FunctionCall {
384                function: function.clone().into(),
385                arguments: Vec::new(),
386                source_location: None,
387            }),
388            // Neither is a default this caller can express: ElementFunction needs the element,
389            // and a function is not a property in the first place
390            BuiltinPropertyDefault::ElementFunction(..)
391            | BuiltinPropertyDefault::BuiltinFunction(..) => None,
392        }
393    }
394
395    pub fn expr(&self, elem: &crate::object_tree::ElementRc) -> Option<Expression> {
396        match self {
397            BuiltinPropertyDefault::ElementFunction(function) => Some(Expression::FunctionCall {
398                function: function.clone().into(),
399                arguments: vec![Expression::ElementReference(Rc::downgrade(elem))],
400                source_location: None,
401            }),
402            other => other.expr_without_element(),
403        }
404    }
405}
406
407/// Information about properties in NativeClass
408#[derive(Debug, Clone)]
409pub struct BuiltinPropertyInfo {
410    /// The property type
411    pub ty: Type,
412    /// When != None, this is the initial value that we will have to set if no other binding were specified
413    pub default_value: BuiltinPropertyDefault,
414    pub property_visibility: PropertyVisibility,
415    /// Raw `///` doc comment from the builtin element declaration, if any.
416    pub docs: Option<String>,
417    /// Whether the property is part of the Slint SC subset
418    /// (`@sc` modifier in the builtin element declaration).
419    pub slint_sc: bool,
420    /// True when a component may declare a member of the same name, shadowing this one
421    /// (`@shadowable` attribute in the builtin element declaration).
422    /// Members added to a builtin element after its initial release should be marked
423    /// shadowable so that older code that already declares the name keeps compiling —
424    /// unless a compiler pass accesses the member by name, in which case shadowing
425    /// would generate wrong code and the member must not be marked.
426    pub shadowable: bool,
427    /// For a function or callback: whether it is pure.
428    /// A member implemented natively has no body the compiler could inspect, so this comes from
429    /// the `pure` qualifier of the builtin element declaration, or from [`BuiltinFunction::is_pure`] when the
430    /// declaration names one.
431    pub pure: bool,
432}
433
434impl BuiltinPropertyInfo {
435    pub fn new(ty: Type) -> Self {
436        Self {
437            ty,
438            default_value: BuiltinPropertyDefault::None,
439            property_visibility: PropertyVisibility::InOut,
440            docs: None,
441            shadowable: false,
442            pure: false,
443            slint_sc: false,
444        }
445    }
446
447    pub fn is_native_output(&self) -> bool {
448        matches!(self.property_visibility, PropertyVisibility::InOut | PropertyVisibility::Output)
449    }
450
451    /// The `pure` declaration of a function or callback, `None` for a property.
452    pub fn declared_pure(&self) -> Option<bool> {
453        matches!(self.ty, Type::Function(_) | Type::Callback(_)).then_some(self.pure)
454    }
455}
456
457impl From<BuiltinFunction> for BuiltinPropertyInfo {
458    fn from(function: BuiltinFunction) -> Self {
459        Self {
460            ty: Type::Function(function.ty()),
461            property_visibility: PropertyVisibility::Public,
462            docs: None,
463            shadowable: false,
464            pure: function.is_pure(),
465            default_value: BuiltinPropertyDefault::BuiltinFunction(function),
466            slint_sc: false,
467        }
468    }
469}
470
471/// The base of an element
472#[derive(Clone, Debug, derive_more::From, Default)]
473pub enum ElementType {
474    /// The element is based of a component
475    Component(Rc<Component>),
476    /// The element is a builtin element
477    Builtin(Rc<BuiltinElement>),
478    /// The native type was resolved by the resolve_native_class pass.
479    Native(Arc<NativeClass>),
480    /// The base element couldn't be looked up
481    #[default]
482    Error,
483    /// This should be the base type of the root element of a global component
484    Global,
485    /// This should be the base type of the root element of an interface
486    Interface,
487}
488
489impl PartialEq for ElementType {
490    fn eq(&self, other: &Self) -> bool {
491        match (self, other) {
492            (Self::Component(a), Self::Component(b)) => Rc::ptr_eq(a, b),
493            (Self::Builtin(a), Self::Builtin(b)) => Rc::ptr_eq(a, b),
494            (Self::Native(a), Self::Native(b)) => Arc::ptr_eq(a, b),
495            (Self::Error, Self::Error)
496            | (Self::Global, Self::Global)
497            | (Self::Interface, Self::Interface) => true,
498            _ => false,
499        }
500    }
501}
502
503impl ElementType {
504    /// Resolve a name written in `.slint` source.
505    /// Resolve `name` in the given [`PropertyLookupMode`]. See
506    /// [`crate::object_tree::Element::lookup_property`]. Only a component can have shadowed members,
507    /// so the other bases ignore the mode.
508    pub fn lookup_property<'a>(
509        &self,
510        name: &'a str,
511        mode: PropertyLookupMode,
512    ) -> PropertyLookupResult<'a> {
513        match self {
514            Self::Component(c) => c.root_element.borrow().lookup_property(name, mode),
515            Self::Builtin(b) => {
516                let resolved_name =
517                    if let Some(alias_name) = b.native_class.lookup_alias(name.as_ref()) {
518                        Cow::Owned(alias_name.to_string())
519                    } else {
520                        Cow::Borrowed(name)
521                    };
522                match b.properties.get(resolved_name.as_ref()) {
523                    None => {
524                        if b.is_non_item_type || b.is_global {
525                            PropertyLookupResult::invalid(resolved_name)
526                        } else {
527                            crate::typeregister::reserved_property(resolved_name)
528                        }
529                    }
530                    Some(p) => PropertyLookupResult {
531                        resolved_name,
532                        property_type: p.ty.clone(),
533                        property_visibility: p.property_visibility,
534                        declared_pure: p.declared_pure(),
535                        is_local_to_component: false,
536                        is_in_direct_base: false,
537                        is_shadowable: p.shadowable,
538                        builtin_function: match &p.default_value {
539                            BuiltinPropertyDefault::BuiltinFunction(f) => Some(f.clone()),
540                            _ => None,
541                        },
542                        is_slint_sc: p.slint_sc,
543                        internal_name: None,
544                        deprecated: None,
545                    },
546                }
547            }
548            Self::Native(n) => {
549                let resolved_name = if let Some(alias_name) = n.lookup_alias(name.as_ref()) {
550                    Cow::Owned(alias_name.to_string())
551                } else {
552                    Cow::Borrowed(name)
553                };
554                let info = n.lookup_property_info(resolved_name.as_ref());
555                PropertyLookupResult {
556                    resolved_name,
557                    property_type: info.map(|p| p.ty.clone()).unwrap_or_default(),
558                    property_visibility: PropertyVisibility::InOut,
559                    declared_pure: info.and_then(|p| p.declared_pure()),
560                    is_local_to_component: false,
561                    is_in_direct_base: false,
562                    is_shadowable: false,
563                    builtin_function: None,
564                    is_slint_sc: false,
565                    internal_name: None,
566                    deprecated: None,
567                }
568            }
569            _ => PropertyLookupResult::invalid(Cow::Borrowed(name)),
570        }
571    }
572
573    /// Return the node declaring `name` in this type or one of its bases, if there is one.
574    pub fn property_declaration_node(&self, name: &str) -> Option<SyntaxNode> {
575        match self {
576            Self::Component(c) => c.root_element.borrow().property_declaration_node(name),
577            _ => None,
578        }
579    }
580
581    /// List of sub properties valid for the auto completion
582    pub fn property_list(&self) -> Vec<(SmolStr, Type)> {
583        match self {
584            Self::Component(c) => {
585                let root = c.root_element.borrow();
586                let mut r = root.base_type.property_list();
587                // A visible shadowing declaration replaces the inherited entry of the same name.
588                if !root.shadowing_members.is_empty() {
589                    let hidden: std::collections::HashSet<_> =
590                        root.visible_shadowing_members().collect();
591                    r.retain(|(name, _)| !hidden.contains(name));
592                }
593                r.extend(
594                    root.property_declarations
595                        .iter()
596                        .filter(|(_, d)| d.visibility != PropertyVisibility::Private)
597                        .map(|(k, d)| (d.declared_name(k).clone(), d.property_type.clone())),
598                );
599                r
600            }
601            Self::Builtin(b) => {
602                b.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
603            }
604            Self::Native(n) => {
605                n.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
606            }
607            _ => Vec::new(),
608        }
609    }
610
611    /// This function looks at the element and checks whether it can have Elements of type `name` as children.
612    /// In addition to what `accepts_child_element` does, this method also probes the type of `name`.
613    /// It returns an Error if that is not possible or an `ElementType` if it is.
614    pub fn lookup_type_for_child_element(
615        &self,
616        name: &str,
617        tr: &TypeRegister,
618    ) -> Result<ElementType, String> {
619        match self {
620            Self::Component(component) => {
621                let base_type = match component.child_insertion_points.borrow().get(DEFAULT_SLOT_NAME) {
622                    Some(insert_in) => insert_in.parent.borrow().base_type.clone(),
623                    None => {
624                        let base_type = component.root_element.borrow().base_type.clone();
625                        if base_type == tr.empty_type() {
626                            let element = tr.lookup_element(name)?;
627                            if matches!(&element, ElementType::Builtin(b) if b.can_be_declared_without_children_slot) {
628                                return Ok(element);
629                            }
630                            return Err(format!("'{}' cannot have children. Only components with @children can have children", component.id));
631                        }
632                        base_type
633                    }
634                };
635                base_type.lookup_type_for_child_element(name, tr)
636            }
637            Self::Builtin(builtin) => {
638                let looked_up = tr.lookup_element(name);
639                if let Ok(ElementType::Builtin(b)) = &looked_up
640                    && b.can_be_declared_without_children_slot
641                {
642                    return Ok(ElementType::Builtin(b.clone()));
643                }
644                if builtin.disallow_global_types_as_child_elements {
645                    if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
646                        return Ok(child_type.clone().into());
647                    } else if builtin.additional_accept_self && name == builtin.native_class.class_name {
648                        return Ok(builtin.clone().into());
649                    }
650                    let mut valid_children: Vec<_> =
651                        builtin.additional_accepted_child_types.keys().cloned().collect();
652                    if builtin.additional_accept_self {
653                        valid_children.push(builtin.native_class.class_name.clone());
654                    }
655                    valid_children.sort();
656
657                    let err = if valid_children.is_empty() {
658                        // No whitelist to suggest from; prefer "Unknown element" for typos.
659                        looked_up?;
660                        format!("{} cannot have children elements", builtin.native_class.class_name,)
661                    } else {
662                        format!(
663                            "{} is not allowed within {}. Only {} are valid children",
664                            name,
665                            builtin.native_class.class_name,
666                            valid_children.join(" ")
667                        )
668                    };
669                    return Err(err);
670                }
671                let err = match looked_up {
672                    Err(e) => e,
673                    Ok(t) => {
674                        if !tr.expose_internal_types
675                            && matches!(&t, Self::Builtin(e) if e.is_internal)
676                        {
677                            format!("Unknown element '{name}'. (The type exists as an internal type, but cannot be accessed in this scope)")
678                        } else {
679                            return Ok(t);
680                        }
681                    }
682                };
683                if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
684                    return Ok(child_type.clone().into());
685                } else if builtin.additional_accept_self && name == builtin.native_class.class_name {
686                    return Ok(builtin.clone().into());
687                }
688                match tr.lookup(name) {
689                    Type::Invalid => Err(err),
690                    ty => Err(format!("'{ty}' cannot be used as an element")),
691                }
692            }
693            _ => tr.lookup_element(name).and_then(|t| {
694                if !tr.expose_internal_types && matches!(&t, Self::Builtin(e) if e.is_internal) {
695                    Err(format!("Unknown element '{name}'. (The type exists as an internal type, but cannot be accessed in this scope)"))
696                } else {
697                    Ok(t)
698                }
699            })
700        }
701    }
702
703    /// Assume this is a builtin type, panic if it isn't
704    pub fn as_builtin(&self) -> &BuiltinElement {
705        match self {
706            Self::Builtin(b) => b,
707            Self::Component(_) => panic!("This should not happen because of inlining"),
708            _ => panic!("invalid type"),
709        }
710    }
711
712    /// Assume this is a builtin type, panic if it isn't
713    pub fn as_native(&self) -> &NativeClass {
714        match self {
715            Self::Native(b) => b,
716            Self::Component(_) => {
717                panic!("This should not happen because of native class resolution")
718            }
719            _ => panic!("invalid type"),
720        }
721    }
722
723    /// Assume it is a Component, panic if it isn't
724    pub fn as_component(&self) -> &Rc<Component> {
725        match self {
726            Self::Component(c) => c,
727            _ => panic!("should be a component because of the repeater_component pass"),
728        }
729    }
730
731    /// Returns the Slint type name if applicable (for example `Rectangle` or `MyButton` when `component MyButton {}` is used as `MyButton` element)
732    pub fn type_name(&self) -> Option<&str> {
733        match self {
734            ElementType::Component(component) => Some(&component.id),
735            ElementType::Builtin(b) => Some(&b.name),
736            ElementType::Native(_) => None, // Too late, caller should call this function before the native class lowering
737            ElementType::Error => None,
738            ElementType::Global => None,
739            ElementType::Interface => None,
740        }
741    }
742}
743
744impl Display for ElementType {
745    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
746        match self {
747            Self::Component(c) => c.id.fmt(f),
748            Self::Builtin(b) => b.name.fmt(f),
749            Self::Native(b) => b.class_name.fmt(f),
750            Self::Error => write!(f, "<error>"),
751            Self::Global => Ok(()),
752            Self::Interface => Ok(()),
753        }
754    }
755}
756
757macro_rules! define_builtin_struct_enum {
758    ($(
759        $(#[$attr:meta])*
760        $vis:vis struct $Name:ident {
761            $( $(#[$field_attr:meta])* $field:ident : $field_type:ty $(= $field_default:expr)?, )*
762        }
763    )*) => {
764        #[derive(Debug, Clone, PartialEq, strum::EnumString, strum::IntoStaticStr)]
765        pub enum BuiltinStruct {
766            // Generated from for_each_builtin_structs
767            $($Name,)*
768
769            // Public structs not in the macro (registered in typeregister.rs)
770            Color,
771            LogicalPosition,
772            LogicalSize,
773
774            // Path element types, set via the `builtin_struct` flag of the
775            // builtin element declaration and read through NativeClass.builtin_struct
776            PathMoveTo,
777            PathLineTo,
778            PathArcTo,
779            PathCubicTo,
780            PathQuadraticTo,
781            PathClose,
782            PathElement,
783
784            // Compiler-internal structs (no slint_name, not exposed to .slint)
785
786            // Internal coordinate struct for compiled SVG path data (x/y as Float32,
787            // unlike LogicalPosition which uses LogicalLength)
788            Point,
789            // Return type of ArraySize (width/height as Int32,
790            // unlike LogicalSize which uses LogicalLength)
791            Size,
792            StateInfo,
793            PropertyAnimation,
794            GridLayoutData,
795            GridLayoutInputData,
796            BoxLayoutData,
797            BoxLayoutOrthoData,
798            FlexboxLayoutData,
799            LayoutItemInfo,
800            FlexboxLayoutItemInfo,
801            FlexItemProps,
802            Padding,
803            LayoutInfo,
804        }
805
806        impl BuiltinStruct {
807            pub fn is_public(&self) -> bool {
808                match self {
809                    // Macro-defined structs: derived from the `pub` visibility keyword
810                    $(Self::$Name => stringify!($vis) == "pub",)*
811                    // Non-macro public structs
812                    Self::Color | Self::LogicalPosition | Self::LogicalSize => true,
813                    _ => false,
814                }
815            }
816
817            /// The name of this struct in the Slint language, or None if it is
818            /// purely internal and not visible in .slint files.
819            pub fn slint_name(&self) -> Option<SmolStr> {
820                match self {
821                    // Macro-defined structs all have a slint name matching their Rust name
822                    $(Self::$Name => {
823                        Some(SmolStr::new_static(stringify!($Name)))
824                    })*
825                    // Non-macro structs with custom slint names
826                    Self::Color => Some(SmolStr::new_static("color")),
827                    Self::LogicalPosition => Some(SmolStr::new_static("Point")),
828                    Self::LogicalSize => Some(SmolStr::new_static("Size")),
829                    _ => None,
830                }
831            }
832        }
833    };
834}
835i_slint_common::for_each_builtin_structs!(define_builtin_struct_enum);
836
837impl BuiltinStruct {
838    pub fn is_layout_data(&self) -> bool {
839        matches!(
840            self,
841            Self::GridLayoutInputData
842                | Self::GridLayoutData
843                | Self::BoxLayoutData
844                | Self::BoxLayoutOrthoData
845                | Self::FlexboxLayoutData
846        )
847    }
848}
849
850#[derive(Debug, Clone, Default)]
851pub struct NativeClass {
852    pub parent: Option<Arc<NativeClass>>,
853    pub class_name: SmolStr,
854    pub cpp_vtable_getter: String,
855    pub properties: BTreeMap<SmolStr, BuiltinPropertyInfo>,
856    pub deprecated_aliases: HashMap<SmolStr, SmolStr>,
857    /// Type override if class_name is not equal to the name to be used in the
858    /// target language API.
859    pub builtin_struct: Option<BuiltinStruct>,
860}
861
862impl NativeClass {
863    pub fn new(class_name: &str) -> Self {
864        let cpp_vtable_getter = format!("SLINT_GET_ITEM_VTABLE({class_name}VTable)");
865        Self {
866            class_name: class_name.into(),
867            cpp_vtable_getter,
868            properties: Default::default(),
869            ..Default::default()
870        }
871    }
872
873    pub fn new_with_properties(
874        class_name: &str,
875        properties: impl IntoIterator<Item = (SmolStr, BuiltinPropertyInfo)>,
876    ) -> Self {
877        let mut class = Self::new(class_name);
878        class.properties = properties.into_iter().collect();
879        class
880    }
881
882    pub fn property_count(&self) -> usize {
883        self.properties.len() + self.parent.clone().map(|p| p.property_count()).unwrap_or_default()
884    }
885
886    pub fn lookup_property(&self, name: &str) -> Option<&Type> {
887        self.lookup_property_info(name).map(|info| &info.ty)
888    }
889
890    /// The declaration of `name` on this class or the closest parent that has it.
891    pub fn lookup_property_info(&self, name: &str) -> Option<&BuiltinPropertyInfo> {
892        self.properties
893            .get(name)
894            .or_else(|| self.parent.as_ref().and_then(|parent| parent.lookup_property_info(name)))
895    }
896
897    pub fn lookup_alias(&self, name: &str) -> Option<&str> {
898        if let Some(alias_target) = self.deprecated_aliases.get(name) {
899            Some(alias_target)
900        } else if self.properties.contains_key(name) {
901            None
902        } else if let Some(parent_class) = &self.parent {
903            parent_class.lookup_alias(name)
904        } else {
905            None
906        }
907    }
908}
909
910#[derive(Debug, Clone, Copy, PartialEq, Default)]
911pub enum DefaultSizeBinding {
912    /// There should not be a default binding for the size
913    #[default]
914    None,
915    /// The size should default to `width:100%; height:100%`
916    ExpandsToParentGeometry,
917    /// The size should default to the item's implicit size
918    ImplicitSize,
919}
920
921/// One entry in the documentation of a builtin element, in declaration order.
922#[derive(Debug, Clone)]
923pub enum ElementDocEntry {
924    /// Free-form documentation text (from `///` or `//!` comments).
925    Text(String),
926    /// Reference to a property, callback, or function by name.
927    Member(SmolStr),
928}
929
930#[derive(Debug, Clone, Default)]
931pub struct BuiltinElement {
932    pub name: SmolStr,
933    pub native_class: Arc<NativeClass>,
934    pub properties: BTreeMap<SmolStr, BuiltinPropertyInfo>,
935    /// Additional builtin element that can be accepted as child of this element
936    /// (example `Tab` in `TabWidget`, `Row` in `GridLayout` and the path elements in `Path`)
937    pub additional_accepted_child_types: BTreeMap<SmolStr, Rc<BuiltinElement>>,
938    /// `Self` is conceptually in `additional_accepted_child_types` (which it can't otherwise that'd make a Rc loop)
939    pub additional_accept_self: bool,
940    pub disallow_global_types_as_child_elements: bool,
941    /// Non-item type do not have reserved properties (x/width/rowspan/...) added to them  (eg: PropertyAnimation)
942    pub is_non_item_type: bool,
943    pub accepts_focus: bool,
944    pub is_global: bool,
945    pub default_size_binding: DefaultSizeBinding,
946    /// When true this is an internal type not shown in the auto-completion
947    pub is_internal: bool,
948    /// Documentation sections of the builtin element declaration, preserving source order.
949    /// `Text` entries come from `///` (element-level) and `//!` (section) comments;
950    /// `Member` entries reference a property, callback, or function by name.
951    pub docs: Vec<ElementDocEntry>,
952    /// When true this builtin can be declared as a child even if the parent element
953    /// does not expose an explicit @children insertion slot.
954    pub can_be_declared_without_children_slot: bool,
955    /// When true this element is part of the Slint SC (safety-critical) subset.
956    pub slint_sc: bool,
957}
958
959/// How [`crate::object_tree::Element::lookup_property`] resolves a name.
960#[derive(Copy, Clone, PartialEq, Debug)]
961pub enum PropertyLookupMode {
962    /// A source name resolved from within the declaring component: a private shadow is visible.
963    ComponentLocal,
964    /// A source name resolved from outside the component: a private shadow is invisible, so the name
965    /// resolves to the member it shadows.
966    FromOutside,
967    /// A storage key, as a `NamedReference` carries: no shadow resolution.
968    InternalName,
969}
970
971#[derive(PartialEq, Debug)]
972pub struct PropertyLookupResult<'a> {
973    pub resolved_name: std::borrow::Cow<'a, str>,
974    pub property_type: Type,
975    pub property_visibility: PropertyVisibility,
976    pub declared_pure: Option<bool>,
977    /// True if the property is part of the current component (for visibility purposes)
978    pub is_local_to_component: bool,
979    /// True if the property in the direct base of the component (for protected visibility purposes)
980    pub is_in_direct_base: bool,
981    /// True if a local declaration may shadow this member: it is marked `@shadowable`.
982    pub is_shadowable: bool,
983
984    /// Set when the lookup went through a shadow: the member is declared under this
985    /// name in `Element::property_declarations`, `bindings`, etc, while `resolved_name`
986    /// keeps the name as it is written in the source. Only ever set by
987    /// [`crate::object_tree::Element::lookup_property`].
988    pub internal_name: Option<SmolStr>,
989
990    /// If the property is a builtin function
991    pub builtin_function: Option<BuiltinFunction>,
992
993    /// Whether the property is part of the Slint SC subset (`@sc` in the builtin element
994    /// declaration).
995    pub is_slint_sc: bool,
996
997    /// Some if the property was declared with `@deprecated`: the hint message shown after
998    /// "The property 'xxx' has been deprecated." in the warning.
999    /// (Only set for properties declared in a component; builtin aliases use `resolved_name` instead.)
1000    pub deprecated: Option<SmolStr>,
1001}
1002
1003impl<'a> PropertyLookupResult<'a> {
1004    pub fn is_valid(&self) -> bool {
1005        self.property_type != Type::Invalid
1006    }
1007
1008    /// Can this property be used in an assignment
1009    pub fn is_valid_for_assignment(&self) -> bool {
1010        !matches!(
1011            (self.property_visibility, self.is_local_to_component),
1012            (PropertyVisibility::Private, false)
1013                | (PropertyVisibility::Input, true)
1014                | (PropertyVisibility::Output, false)
1015        )
1016    }
1017
1018    /// Report the property as outside the Slint SC subset, unless it's one the subset has.
1019    /// A name that resolves to nothing is left to the diagnostic that says so.
1020    #[cfg(feature = "slint-sc")]
1021    pub fn check_slint_sc(
1022        &self,
1023        name: &dyn Display,
1024        source: &dyn crate::diagnostics::Spanned,
1025        diag: &mut crate::diagnostics::BuildDiagnostics,
1026    ) {
1027        if self.is_valid() && !self.is_slint_sc {
1028            diag.slint_sc_error(&format!("The property '{name}' is"), source);
1029        }
1030    }
1031
1032    /// The name the member is stored under in `Element::property_declarations`, `bindings`,
1033    /// `change_callbacks` and `property_analysis`, and the name a `NamedReference` to it carries.
1034    pub fn internal_or_resolved_name(&self) -> SmolStr {
1035        self.internal_name.clone().unwrap_or_else(|| self.resolved_name.as_ref().into())
1036    }
1037
1038    pub fn invalid(resolved_name: Cow<'a, str>) -> Self {
1039        Self {
1040            resolved_name,
1041            property_type: Type::Invalid,
1042            property_visibility: PropertyVisibility::Private,
1043            declared_pure: None,
1044            is_local_to_component: false,
1045            is_in_direct_base: false,
1046            is_shadowable: false,
1047            builtin_function: None,
1048            is_slint_sc: false,
1049            internal_name: None,
1050            deprecated: None,
1051        }
1052    }
1053}
1054
1055#[derive(Debug, Clone, PartialEq)]
1056pub struct Function {
1057    pub return_type: Type,
1058    pub args: Vec<Type>,
1059    /// The optional names of the arguments (empty string means not set).
1060    /// The names are not technically part of the type, but it is good to have them available for auto-completion
1061    pub arg_names: Vec<SmolStr>,
1062}
1063
1064impl Display for Function {
1065    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1066        write!(formatter, "(")?;
1067        for (i, arg) in self.args.iter().enumerate() {
1068            if i > 0 {
1069                write!(formatter, ", ")?;
1070            }
1071            write!(formatter, "{arg}")?;
1072        }
1073        let return_type = if self.return_type == Type::Void {
1074            String::new()
1075        } else {
1076            format!(" -> {}", self.return_type)
1077        };
1078        write!(formatter, "){return_type}")
1079    }
1080}
1081
1082#[derive(Debug, Clone)]
1083pub enum StructName {
1084    /// Anonymous structs
1085    None,
1086    /// When declared in .slint as  `struct Foo { }`, then the name is "Foo"
1087    User {
1088        name: SmolStr,
1089        /// Where the declaration was written (for the language server).
1090        node: SourceLocation,
1091        /// The raw text of each `@rust-attr(...)` on the declaration, captured
1092        /// at build time so the Rust generator does not need the syntax tree.
1093        rust_attributes: Vec<SmolStr>,
1094        /// The field names in declaration order. The C++ generator emits struct
1095        /// members in this order (positional aggregate initialization relies on
1096        /// it), which `fields` — a sorted map — does not preserve.
1097        field_order: Vec<SmolStr>,
1098    },
1099    Builtin(BuiltinStruct),
1100}
1101
1102impl PartialEq for StructName {
1103    fn eq(&self, other: &Self) -> bool {
1104        match (self, other) {
1105            (Self::User { name: l_user_name, .. }, Self::User { name: r_user_name, .. }) => {
1106                l_user_name == r_user_name
1107            }
1108            (Self::Builtin(l0), Self::Builtin(r0)) => l0 == r0,
1109            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
1110        }
1111    }
1112}
1113
1114impl StructName {
1115    pub fn slint_name(&self) -> Option<SmolStr> {
1116        match self {
1117            StructName::None => None,
1118            StructName::User { name, .. } => Some(name.clone()),
1119            StructName::Builtin(builtin) => builtin.slint_name(),
1120        }
1121    }
1122
1123    pub fn is_none(&self) -> bool {
1124        matches!(self, Self::None)
1125    }
1126
1127    pub fn is_some(&self) -> bool {
1128        !matches!(self, Self::None)
1129    }
1130
1131    pub fn or(self, other: Self) -> Self {
1132        match self {
1133            Self::None => other,
1134            this => this,
1135        }
1136    }
1137}
1138
1139impl From<BuiltinStruct> for StructName {
1140    fn from(value: BuiltinStruct) -> Self {
1141        Self::Builtin(value)
1142    }
1143}
1144
1145#[derive(Debug, Clone)]
1146pub struct Struct {
1147    pub fields: BTreeMap<SmolStr, Type>,
1148    /// Default values for the fields.
1149    /// The expressions are resolved, converted to the field type, and constant-folded.
1150    /// Like the syntax node in `name`, this is ignored by `Type`'s equality comparison.
1151    pub field_defaults: BTreeMap<SmolStr, ConstantExpression>,
1152    pub name: StructName,
1153}
1154
1155impl Struct {
1156    /// Create a struct without declared field default values.
1157    pub fn new(fields: BTreeMap<SmolStr, Type>, name: impl Into<StructName>) -> Self {
1158        Self { fields, field_defaults: Default::default(), name: name.into() }
1159    }
1160
1161    /// Where a user-declared struct was written (for the language server).
1162    pub fn node(&self) -> Option<&SourceLocation> {
1163        match &self.name {
1164            StructName::User { node, .. } => Some(node),
1165            _ => None,
1166        }
1167    }
1168
1169    /// The raw text of each `@rust-attr(...)` on a user-declared struct.
1170    pub fn rust_attributes(&self) -> &[SmolStr] {
1171        match &self.name {
1172            StructName::User { rust_attributes, .. } => rust_attributes,
1173            _ => &[],
1174        }
1175    }
1176
1177    /// The field names in declaration order for a user-declared struct.
1178    pub fn field_order(&self) -> &[SmolStr] {
1179        match &self.name {
1180            StructName::User { field_order, .. } => field_order,
1181            _ => &[],
1182        }
1183    }
1184
1185    /// The default value for the given field: the user-declared default if there is one,
1186    /// otherwise the default value for the field's type.
1187    pub fn default_value_for_field(&self, name: &SmolStr) -> Expression {
1188        self.field_defaults.get(name).map(ConstantExpression::to_expression).unwrap_or_else(|| {
1189            Expression::default_value_for_type(
1190                self.fields.get(name).expect("default value requested for unknown struct field"),
1191            )
1192        })
1193    }
1194}
1195
1196/// A constant expression, used for the default values of struct fields
1197/// (see [`Struct::field_defaults`]).
1198///
1199/// This is deliberately neither [`Expression`] nor an llr expression:
1200/// unlike those, it cannot reference any properties, elements, or syntax nodes,
1201/// so a [`Struct`] carrying one can safely outlive the object tree.
1202/// The variants are the subset that every consumer can materialize.
1203/// Keep the matches over this type exhaustive,
1204/// so that adding a variant is a compile error in each consumer:
1205/// the conversion to an expression tree ([`Self::to_expression`]),
1206/// the lowering for the code generators (`lower_constant_expression` in the llr module),
1207/// and the interpreter's evaluator (`eval_constant_expression` there).
1208#[derive(Debug, Clone)]
1209pub enum ConstantExpression {
1210    StringLiteral(SmolStr),
1211    /// A number and its unit, in normalized form
1212    NumberLiteral(f64, Unit),
1213    BoolLiteral(bool),
1214    EnumerationValue(EnumerationValue),
1215    Cast {
1216        from: Box<ConstantExpression>,
1217        to: Type,
1218    },
1219    UnaryOp {
1220        sub: Box<ConstantExpression>,
1221        op: char,
1222    },
1223    Struct {
1224        ty: Arc<Struct>,
1225        values: BTreeMap<SmolStr, ConstantExpression>,
1226    },
1227    Array {
1228        element_ty: Type,
1229        values: Vec<ConstantExpression>,
1230    },
1231}
1232
1233impl ConstantExpression {
1234    /// Create a constant expression from a resolved, converted, and constant-folded
1235    /// expression, or `None` if the expression is not in the constant subset.
1236    pub fn from_expression(expression: &Expression) -> Option<Self> {
1237        Some(match expression {
1238            Expression::StringLiteral(s) => Self::StringLiteral(s.clone()),
1239            Expression::NumberLiteral(n, unit) => Self::NumberLiteral(*n, *unit),
1240            Expression::BoolLiteral(b) => Self::BoolLiteral(*b),
1241            Expression::EnumerationValue(e) => Self::EnumerationValue(e.clone()),
1242            Expression::Cast { from, to } => {
1243                // Converting a number to a string depends on the locale's decimal separator.
1244                // The constant propagation folds the cases that render the same in every
1245                // locale into a string literal, so a cast that's still here isn't constant
1246                // (see `Expression::is_constant`).
1247                if *to == Type::String {
1248                    return None;
1249                }
1250                Self::Cast { from: Box::new(Self::from_expression(from)?), to: to.clone() }
1251            }
1252            Expression::UnaryOp { sub, op } => {
1253                Self::UnaryOp { sub: Box::new(Self::from_expression(sub)?), op: *op }
1254            }
1255            Expression::Struct { ty, values } => Self::Struct {
1256                ty: ty.clone(),
1257                values: values
1258                    .iter()
1259                    .map(|(k, v)| Some((k.clone(), Self::from_expression(v)?)))
1260                    .collect::<Option<_>>()?,
1261            },
1262            Expression::Array { element_ty, values } => Self::Array {
1263                element_ty: element_ty.clone(),
1264                values: values.iter().map(Self::from_expression).collect::<Option<_>>()?,
1265            },
1266            _ => return None,
1267        })
1268    }
1269
1270    /// The expression tree form, for splicing the constant into bindings at compile time
1271    pub fn to_expression(&self) -> Expression {
1272        match self {
1273            Self::StringLiteral(s) => Expression::StringLiteral(s.clone()),
1274            Self::NumberLiteral(n, unit) => Expression::NumberLiteral(*n, *unit),
1275            Self::BoolLiteral(b) => Expression::BoolLiteral(*b),
1276            Self::EnumerationValue(e) => Expression::EnumerationValue(e.clone()),
1277            Self::Cast { from, to } => {
1278                Expression::Cast { from: Box::new(from.to_expression()), to: to.clone() }
1279            }
1280            Self::UnaryOp { sub, op } => {
1281                Expression::UnaryOp { sub: Box::new(sub.to_expression()), op: *op }
1282            }
1283            Self::Struct { ty, values } => Expression::Struct {
1284                ty: ty.clone(),
1285                values: values.iter().map(|(k, v)| (k.clone(), v.to_expression())).collect(),
1286            },
1287            Self::Array { element_ty, values } => Expression::Array {
1288                element_ty: element_ty.clone(),
1289                values: values.iter().map(Self::to_expression).collect(),
1290            },
1291        }
1292    }
1293}
1294
1295impl Display for Struct {
1296    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1297        if let Some(name) = &self.name.slint_name() {
1298            write!(f, "{name}")
1299        } else {
1300            write!(f, "{{ ")?;
1301            for (k, v) in &self.fields {
1302                write!(f, "{k}: {v},")?;
1303            }
1304            write!(f, "}}")
1305        }
1306    }
1307}
1308
1309/// Call `visitor` for every user-declared (non-builtin) struct or enum reachable from `ty`,
1310/// recursing through struct fields, arrays, and callback/function signatures.
1311pub(crate) fn visit_declared_types(ty: &Type, visitor: &mut impl FnMut(&SmolStr, &Type)) {
1312    match ty {
1313        Type::Struct(s) => {
1314            if let StructName::User { name, .. } = &s.name {
1315                visitor(name, ty);
1316            }
1317            for sub_ty in s.fields.values() {
1318                visit_declared_types(sub_ty, visitor);
1319            }
1320        }
1321        Type::Array(x) => visit_declared_types(x, visitor),
1322        Type::Function(function) | Type::Callback(function) => {
1323            visit_declared_types(&function.return_type, visitor);
1324            for a in &function.args {
1325                visit_declared_types(a, visitor);
1326            }
1327        }
1328        Type::Enumeration(en) if en.node.is_some() => visitor(&en.name, ty),
1329        _ => {}
1330    }
1331}
1332
1333#[derive(Debug, Clone)]
1334pub struct Enumeration {
1335    pub name: SmolStr,
1336    pub values: Vec<SmolStr>,
1337    pub default_value: usize, // index in values
1338    // For non-builtins enums, this is where the declaration was written.
1339    pub node: Option<SourceLocation>,
1340    /// The raw text of each `@rust-attr(...)` on the declaration, captured at
1341    /// build time so the Rust generator does not need the syntax tree.
1342    pub rust_attributes: Vec<SmolStr>,
1343}
1344
1345impl PartialEq for Enumeration {
1346    fn eq(&self, other: &Self) -> bool {
1347        self.name.eq(&other.name)
1348    }
1349}
1350
1351impl Enumeration {
1352    pub fn default_value(self: Arc<Self>) -> EnumerationValue {
1353        EnumerationValue { value: self.default_value, enumeration: self.clone() }
1354    }
1355
1356    pub fn try_value_from_string(self: Arc<Self>, value: &str) -> Option<EnumerationValue> {
1357        self.values.iter().enumerate().find_map(|(idx, name)| {
1358            if name == value {
1359                Some(EnumerationValue { value: idx, enumeration: self.clone() })
1360            } else {
1361                None
1362            }
1363        })
1364    }
1365}
1366
1367#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
1368pub struct KeyboardModifiers {
1369    pub alt: bool,
1370    pub control: bool,
1371    pub meta: bool,
1372    pub shift: bool,
1373}
1374
1375#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
1376pub struct Keys {
1377    pub key: SmolStr,
1378    pub modifiers: KeyboardModifiers,
1379    pub ignore_shift: bool,
1380    pub ignore_alt: bool,
1381}
1382
1383impl std::fmt::Display for Keys {
1384    // Make sure to keep this in sync with the implementation in core/input.rs
1385    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1386        if self.key.is_empty() {
1387            write!(f, "")
1388        } else {
1389            let alt = self
1390                .ignore_alt
1391                .then_some("Alt?+")
1392                .or(self.modifiers.alt.then_some("Alt+"))
1393                .unwrap_or_default();
1394            let ctrl = if self.modifiers.control { "Control+" } else { "" };
1395            let meta = if self.modifiers.meta { "Meta+" } else { "" };
1396            let shift = self
1397                .ignore_shift
1398                .then_some("Shift?+")
1399                .or(self.modifiers.shift.then_some("Shift+"))
1400                .unwrap_or_default();
1401            let keycode: String = self
1402                .key
1403                .chars()
1404                .flat_map(|character| {
1405                    let mut escaped = vec![];
1406                    if character.is_control() {
1407                        escaped.extend(character.escape_unicode());
1408                    } else {
1409                        escaped.push(character);
1410                    }
1411                    escaped
1412                })
1413                .collect();
1414            write!(f, "{meta}{ctrl}{alt}{shift}\"{keycode}\"")
1415        }
1416    }
1417}
1418
1419#[derive(Clone, Debug)]
1420pub struct EnumerationValue {
1421    pub value: usize, // index in enumeration.values
1422    pub enumeration: Arc<Enumeration>,
1423}
1424
1425impl PartialEq for EnumerationValue {
1426    fn eq(&self, other: &Self) -> bool {
1427        Arc::ptr_eq(&self.enumeration, &other.enumeration) && self.value == other.value
1428    }
1429}
1430
1431impl std::fmt::Display for EnumerationValue {
1432    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1433        self.enumeration.values[self.value].fmt(f)
1434    }
1435}
1436
1437impl EnumerationValue {
1438    pub fn to_pascal_case(&self) -> String {
1439        crate::generator::to_pascal_case(&self.enumeration.values[self.value])
1440    }
1441}
1442
1443#[derive(Debug, PartialEq)]
1444pub struct LengthConversionPowers {
1445    pub rem_to_px_power: i8,
1446    pub px_to_phx_power: i8,
1447}
1448
1449/// If the `Type::UnitProduct(a)` can be converted to `Type::UnitProduct(b)` by multiplying
1450/// by the scale factor, return that scale factor, otherwise, return None
1451pub fn unit_product_length_conversion(
1452    a: &[(Unit, i8)],
1453    b: &[(Unit, i8)],
1454) -> Option<LengthConversionPowers> {
1455    // e.g. float to int conversion, no units
1456    if a.is_empty() && b.is_empty() {
1457        return Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: 0 });
1458    }
1459
1460    let mut units = [0i8; 16];
1461    for (u, count) in a {
1462        units[*u as usize] += count;
1463    }
1464    for (u, count) in b {
1465        units[*u as usize] -= count;
1466    }
1467
1468    if units[Unit::Px as usize] + units[Unit::Phx as usize] + units[Unit::Rem as usize] != 0 {
1469        return None;
1470    }
1471
1472    if units[Unit::Rem as usize] != 0
1473        && units[Unit::Phx as usize] == -units[Unit::Rem as usize]
1474        && units[Unit::Px as usize] == 0
1475    {
1476        units[Unit::Px as usize] = -units[Unit::Rem as usize];
1477        units[Unit::Phx as usize] = -units[Unit::Rem as usize];
1478    }
1479
1480    let result = LengthConversionPowers {
1481        rem_to_px_power: if units[Unit::Rem as usize] != 0 { units[Unit::Px as usize] } else { 0 },
1482        px_to_phx_power: if units[Unit::Px as usize] != 0 { units[Unit::Phx as usize] } else { 0 },
1483    };
1484
1485    units[Unit::Px as usize] = 0;
1486    units[Unit::Phx as usize] = 0;
1487    units[Unit::Rem as usize] = 0;
1488    units.into_iter().all(|x| x == 0).then_some(result)
1489}
1490
1491#[test]
1492fn unit_product_length_conversion_test() {
1493    use Option::None;
1494    use Unit::*;
1495    assert_eq!(
1496        unit_product_length_conversion(&[], &[]),
1497        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: 0 })
1498    );
1499    assert_eq!(
1500        unit_product_length_conversion(&[(Px, 1)], &[(Phx, 1)]),
1501        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -1 })
1502    );
1503    assert_eq!(
1504        unit_product_length_conversion(&[(Phx, -2)], &[(Px, -2)]),
1505        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -2 })
1506    );
1507    assert_eq!(
1508        unit_product_length_conversion(&[(Px, 1), (Phx, -2)], &[(Phx, -1)]),
1509        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -1 })
1510    );
1511    assert_eq!(
1512        unit_product_length_conversion(
1513            &[(Deg, 3), (Phx, 2), (Ms, -1)],
1514            &[(Phx, 4), (Deg, 3), (Ms, -1), (Px, -2)]
1515        ),
1516        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -2 })
1517    );
1518    assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
1519    assert_eq!(unit_product_length_conversion(&[(Deg, 1), (Phx, -2)], &[(Px, -2)]), None);
1520    assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
1521
1522    assert_eq!(
1523        unit_product_length_conversion(&[(Rem, 1)], &[(Px, 1)]),
1524        Some(LengthConversionPowers { rem_to_px_power: -1, px_to_phx_power: 0 })
1525    );
1526    assert_eq!(
1527        unit_product_length_conversion(&[(Rem, 1)], &[(Phx, 1)]),
1528        Some(LengthConversionPowers { rem_to_px_power: -1, px_to_phx_power: -1 })
1529    );
1530    assert_eq!(
1531        unit_product_length_conversion(&[(Rem, 2)], &[(Phx, 2)]),
1532        Some(LengthConversionPowers { rem_to_px_power: -2, px_to_phx_power: -2 })
1533    );
1534}