Skip to main content

i_slint_compiler/llr/
expression.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 super::{
5    GlobalIdx, GridLayoutRepeatedElement, LayoutRepeatedElement, LocalMemberIndex,
6    LocalMemberReference, MemberReference, RepeatedElementIdx, SubComponentIdx,
7    SubComponentInstanceIdx,
8};
9use crate::expression_tree::{BuiltinFunction, MinMaxOp, OperatorClass};
10use crate::langtype::{Keys, Type};
11use crate::layout::Orientation;
12use itertools::Either;
13use smol_str::SmolStr;
14use std::collections::BTreeMap;
15use std::sync::Arc;
16
17#[derive(Debug, Clone)]
18pub enum ArrayOutput {
19    Slice,
20    Model,
21    Vector,
22}
23
24pub use crate::expression_tree::MouseCursorInner;
25
26/// One cell of a generated flexbox measure callback: how to re-measure it at a
27/// taffy-assigned width. See [`Expression::SolveFlexboxLayoutWithMeasure`].
28#[derive(Debug, Clone)]
29#[allow(clippy::large_enum_variant)]
30pub enum FlexboxMeasureCell {
31    /// A static height-for-width cell, with its vertical `LayoutInfo`-typed
32    /// expression reading the `measure_known_w` local as its width constraint.
33    Static { v_info: Expression },
34    /// A repeater: its instances are only known at run time, so the generated
35    /// callback queries the instance directly.
36    Repeated(LayoutRepeatedElement),
37    /// A cell whose height does not depend on its width: the sizes
38    /// pre-resolved from the cell arrays are already correct, so no measure
39    /// arm is generated.
40    Fixed,
41}
42
43/// One cell of a box layout's cross-axis measure pass. See
44/// [`Expression::BoxLayoutInfoOrthoWithMeasure`].
45#[derive(Debug, Clone)]
46#[allow(clippy::large_enum_variant)]
47pub enum BoxMeasureCell {
48    /// A static cell: its vertical `LayoutInfo` expression. A height-for-width
49    /// cell reads the `measure_known_w` local as its width constraint; any
50    /// other cell just doesn't read it.
51    Static { info: Expression },
52    /// A repeater: its instances are only known at run time, so the generated
53    /// code queries each instance's `layout_item_info_at_cross_width` at its
54    /// solved width.
55    Repeated(LayoutRepeatedElement),
56}
57
58#[derive(Debug, Clone)]
59pub enum Expression {
60    /// A string literal. The .0 is the content of the string, without the quotes
61    StringLiteral(SmolStr),
62    /// Number
63    NumberLiteral(f64),
64    /// Bool
65    BoolLiteral(bool),
66
67    // Keys
68    KeysLiteral(Keys),
69
70    /// Reference to a property (which can also be a callback) or an element (property name is empty then).
71    PropertyReference(MemberReference),
72
73    /// Reference the parameter at the given index of the current function.
74    FunctionParameterReference {
75        index: usize,
76        //ty: Type,
77    },
78
79    /// Should be directly within a CodeBlock expression, and store the value of the expression in a local variable
80    StoreLocalVariable {
81        name: SmolStr,
82        value: Box<Expression>,
83    },
84
85    /// a reference to the local variable with the given name. The type system should ensure that a variable has been stored
86    /// with this name and this type before in one of the statement of an enclosing codeblock
87    ReadLocalVariable {
88        name: SmolStr,
89        ty: Type,
90    },
91
92    /// Access to a field of the given name within a struct.
93    StructFieldAccess {
94        /// This expression should have [`Type::Struct`] type
95        base: Box<Expression>,
96        name: SmolStr,
97    },
98
99    /// Access to a index within an array.
100    ArrayIndex {
101        /// This expression should have [`Type::Array`] type
102        array: Box<Expression>,
103        index: Box<Expression>,
104    },
105
106    /// Cast an expression to the given type
107    Cast {
108        from: Box<Expression>,
109        to: Type,
110    },
111
112    /// a code block with different expression
113    CodeBlock(Vec<Expression>),
114
115    /// A function call
116    BuiltinFunctionCall {
117        function: BuiltinFunction,
118        arguments: Vec<Expression>,
119        /// The location of the call in the .slint source, for run-time diagnostics
120        source_location: Option<crate::diagnostics::SourceLocation>,
121    },
122    CallBackCall {
123        callback: MemberReference,
124        arguments: Vec<Expression>,
125    },
126    FunctionCall {
127        function: MemberReference,
128        arguments: Vec<Expression>,
129    },
130    ItemMemberFunctionCall {
131        function: MemberReference,
132    },
133
134    /// A BuiltinFunctionCall, but the function is not yet in the `BuiltinFunction` enum
135    /// TODO: merge in BuiltinFunctionCall
136    ExtraBuiltinFunctionCall {
137        return_ty: Type,
138        function: String,
139        arguments: Vec<Expression>,
140    },
141
142    /// An assignment of a value to a property
143    PropertyAssignment {
144        property: MemberReference,
145        value: Box<Expression>,
146    },
147    /// an assignment of a value to the model data
148    ModelDataAssignment {
149        // how deep in the parent hierarchy we go
150        level: usize,
151        value: Box<Expression>,
152    },
153    /// An assignment done with the `foo[idx] = ...`
154    ArrayIndexAssignment {
155        array: Box<Expression>,
156        index: Box<Expression>,
157        value: Box<Expression>,
158    },
159    /// An assignment to a mutable slice element: `slice[idx] = value`
160    /// Unlike ArrayIndexAssignment, this writes directly to the slice without model semantics
161    SliceIndexAssignment {
162        /// Name of the slice variable (e.g., "result")
163        slice_name: SmolStr,
164        index: usize,
165        value: Box<Expression>,
166    },
167
168    BinaryExpression {
169        lhs: Box<Expression>,
170        rhs: Box<Expression>,
171        /// '+', '-', '/', '*', '=', '!', '<', '>', '≤', '≥', '&', '|'
172        op: char,
173    },
174
175    UnaryOp {
176        sub: Box<Expression>,
177        /// '+', '-', '!'
178        op: char,
179    },
180
181    ImageReference {
182        resource_ref: crate::expression_tree::ImageReference,
183        nine_slice: Option<[u16; 4]>,
184    },
185
186    Condition {
187        condition: Box<Expression>,
188        true_expr: Box<Expression>,
189        false_expr: Box<Expression>,
190    },
191
192    Array {
193        element_ty: Type,
194        values: Vec<Expression>,
195        /// Choose what will be generated: a slice, a model, or a vector
196        output: ArrayOutput,
197    },
198    Struct {
199        ty: Arc<crate::langtype::Struct>,
200        values: BTreeMap<SmolStr, Expression>,
201    },
202
203    EasingCurve(crate::expression_tree::EasingCurve),
204
205    MouseCursor(MouseCursorInner<Expression>),
206
207    LinearGradient {
208        angle: Box<Expression>,
209        /// First expression in the tuple is a color, second expression is the stop position
210        stops: Vec<(Expression, Expression)>,
211    },
212
213    RadialGradient {
214        /// Explicit gradient center in the element's local coordinate space (`at <x> <y>`).
215        /// `None` means use the element's bbox centre.
216        center: Option<(Box<Expression>, Box<Expression>)>,
217        /// Explicit radius in the element's local coordinate space (`circle <radius>`).
218        /// `None` means use the element's bbox half-diagonal.
219        radius: Option<Box<Expression>>,
220        /// First expression in the tuple is a color, second expression is the stop position
221        stops: Vec<(Expression, Expression)>,
222    },
223
224    ConicGradient {
225        /// The starting angle (rotation) of the gradient, corresponding to CSS `from <angle>`
226        from_angle: Box<Expression>,
227        /// Explicit gradient center in the element's local coordinate space (`at <x> <y>`).
228        /// `None` means use the element's bbox centre.
229        center: Option<(Box<Expression>, Box<Expression>)>,
230        /// First expression in the tuple is a color, second expression is the stop position (normalized angle 0-1)
231        stops: Vec<(Expression, Expression)>,
232    },
233
234    EnumerationValue(crate::langtype::EnumerationValue),
235
236    /// Standard cache access (box layouts and static grid cells).
237    /// See LayoutCacheAccess in expression_tree.rs
238    LayoutCacheAccess {
239        layout_cache_prop: MemberReference,
240        index: usize,
241        repeater_index: Option<Box<Expression>>,
242        entries_per_item: usize,
243    },
244    /// Two-level indirection cache access for grid layouts with repeaters.
245    /// See GridRepeaterCacheAccess in expression_tree.rs
246    GridRepeaterCacheAccess {
247        layout_cache_prop: MemberReference,
248        index: usize,
249        repeater_index: Box<Expression>,
250        stride: Box<Expression>,
251        child_offset: usize,
252        inner_repeater_index: Option<Box<Expression>>,
253        entries_per_item: usize,
254    },
255    /// Will call the sub_expression, with the cells variable set to the
256    /// array of LayoutItemInfo from the elements
257    WithLayoutItemInfo {
258        /// The local variable (as read with [`Self::ReadLocalVariable`]) that contains the cells
259        cells_variable: String,
260        /// The name for the local variable that contains the repeater indices
261        repeater_indices_var_name: Option<SmolStr>,
262        /// The name for the local variable that contains the repeater steps
263        repeater_steps_var_name: Option<SmolStr>,
264        /// Either an expression of type LayoutItemInfo, or information about the repeater
265        elements: Vec<Either<Expression, LayoutRepeatedElement>>,
266        orientation: Orientation,
267        /// Content width of a vertical box layout on its main-axis pass:
268        /// passed to each repeated cell's `layout_item_info_at_cross_width` so
269        /// a height-for-width instance wraps to the real width instead of its
270        /// preferred width. `None` on a horizontal layout's main pass, on the
271        /// cross-axis pass, and for grids. Only the plain column-repeater code
272        /// path forwards it — box layout repeaters are always step-1 column
273        /// repeaters (no `row_child_templates`); the generators assert this.
274        repeated_cross_size: Option<Box<Expression>>,
275        sub_expression: Box<Expression>,
276    },
277    /// Will call the sub_expression, with two cells variables (horizontal and vertical)
278    /// set to the arrays of LayoutItemInfo from the elements for FlexboxLayout
279    WithFlexboxLayoutItemInfo {
280        /// The local variable for horizontal cells
281        cells_h_variable: String,
282        /// The local variable for vertical cells
283        cells_v_variable: String,
284        /// The local variable for the per-item flex properties. `None` when the
285        /// sub-expression does not read them (e.g. `flexbox_layout_unwrapped_main`):
286        /// the flex-props expressions are then not evaluated, so the binding does
287        /// not depend on a static cell's flex properties. (A repeated cell still
288        /// computes its props inside the bundled item-info call, whose constraint
289        /// half is needed either way.)
290        flex_props_variable: Option<String>,
291        /// The name for the local variable that contains the repeater indices
292        repeater_indices_var_name: Option<SmolStr>,
293        /// Either an expression triple of type (LayoutItemInfo, LayoutItemInfo,
294        /// FlexItemProps), or information about the repeater
295        elements: Vec<Either<(Expression, Expression, Expression), LayoutRepeatedElement>>,
296        /// Container (cross-axis) width for a column flex: passed to each
297        /// repeated cell's `flexbox_layout_item_info_at_cross_width` so a
298        /// height-for-width instance wraps to the real width instead of its
299        /// preferred width. `None` for a row flex (no cross-width to forward).
300        repeated_cross_width: Option<Box<Expression>>,
301        sub_expression: Box<Expression>,
302    },
303    /// Calls `solve_flexbox_layout_with_measure` with a generated measure
304    /// callback so the height of height-for-width cells is recomputed at the
305    /// width taffy actually assigns (rather than the cell's preferred width).
306    /// `data` is the `FlexboxLayoutData`. For each static height-for-width
307    /// cell, `measure_cells[i]` carries its vertical `LayoutInfo`-typed
308    /// expression, which reads `ReadLocalVariable("measure_known_w")` (a
309    /// `Float32`) as its width constraint. A repeater cell is measured by
310    /// calling `flexbox_layout_item_info_at_cross_width` on the instance taffy
311    /// asks for; the callback maps taffy's flat cell index to it with a
312    /// runtime cursor, since a repeater expands to a runtime number of cells.
313    SolveFlexboxLayoutWithMeasure {
314        /// The `FlexboxLayoutData` (built inline with the cell arrays, so its
315        /// temporaries live for the duration of the solve call).
316        data: Box<Expression>,
317        repeater_indices: Box<Expression>,
318        measure_cells: Vec<FlexboxMeasureCell>,
319    },
320    /// Vertical info of a horizontal box layout at a known width: solves the
321    /// main axis at that width, then folds the cells' vertical infos with
322    /// `box_layout_info_ortho`, measuring each height-for-width cell at its
323    /// solved width — the box layout counterpart of
324    /// [`Self::FlexboxLayoutInfoCrossAxisWithMeasure`].
325    BoxLayoutInfoOrthoWithMeasure {
326        /// The `BoxLayoutData` for the main-axis solve; its `size` is the
327        /// known width of the info being computed.
328        solve_data: Box<Expression>,
329        /// The vertical `Padding` for the fold.
330        padding_ortho: Box<Expression>,
331        measure_cells: Vec<BoxMeasureCell>,
332    },
333    /// Calls `flexbox_layout_info_cross_axis_with_measure` with the same
334    /// generated measure callback as [`Self::SolveFlexboxLayoutWithMeasure`],
335    /// so height-for-width cells are measured at the main-axis size taffy
336    /// assigns them rather than at the container size the cells in `arguments`
337    /// were pre-measured at.
338    FlexboxLayoutInfoCrossAxisWithMeasure {
339        /// The arguments of `flexbox_layout_info_cross_axis` (without the
340        /// trailing measure callback).
341        arguments: Vec<Expression>,
342        measure_cells: Vec<FlexboxMeasureCell>,
343    },
344    /// Will call the sub_expression, with the cells variable set to the
345    /// array of GridLayoutInputData from the elements
346    WithGridInputData {
347        /// The local variable (as read with [`Self::ReadLocalVariable`]) that contains the cells
348        cells_variable: String,
349        /// The name for the local variable that contains the repeater indices
350        repeater_indices_var_name: SmolStr,
351        /// The name for the local variable that contains the repeater steps
352        repeater_steps_var_name: SmolStr,
353        /// Either an expression of type GridLayoutInputData, or information about the repeated element
354        elements: Vec<Either<Expression, GridLayoutRepeatedElement>>,
355        sub_expression: Box<Expression>,
356    },
357
358    MinMax {
359        ty: Type,
360        op: MinMaxOp,
361        lhs: Box<Expression>,
362        rhs: Box<Expression>,
363    },
364
365    EmptyComponentFactory,
366
367    EmptyDataTransfer,
368
369    /// A reference to bundled translated string
370    TranslationReference {
371        /// An expression of type array of strings
372        format_args: Box<Expression>,
373        string_index: usize,
374        /// The `n` value to use for the plural form if it is a plural form
375        plural: Option<Box<Expression>>,
376    },
377
378    Closure {
379        arg_name: SmolStr,
380        expression: Box<Expression>,
381    },
382
383    /// Wraps a binding so the live-preview can observe or override its value.
384    /// Only present when the `debug_hooks` compiler option is enabled.
385    DebugHook {
386        expression: Box<Expression>,
387        id: SmolStr,
388    },
389}
390
391/// The type of a binary expression with the given operator:
392/// comparison and logic operators produce a bool,
393/// while the arithmetic operators keep the type of the left operand
394pub fn binary_expression_ty(op: char, lhs_ty: impl FnOnce() -> Type) -> Type {
395    if crate::expression_tree::operator_class(op) != OperatorClass::ArithmeticOp {
396        Type::Bool
397    } else {
398        lhs_ty()
399    }
400}
401
402impl Expression {
403    pub fn default_value_for_type(ty: &Type) -> Option<Self> {
404        Some(match ty {
405            Type::Invalid
406            | Type::Callback { .. }
407            | Type::Function { .. }
408            | Type::Void
409            | Type::InferredProperty
410            | Type::InferredCallback
411            | Type::ElementReference
412            | Type::LayoutCache
413            | Type::ArrayOfU16
414            | Type::Closure => return None,
415            Type::Float32
416            | Type::Duration
417            | Type::Int32
418            | Type::Angle
419            | Type::PhysicalLength
420            | Type::LogicalLength
421            | Type::Rem
422            | Type::UnitProduct(_) => Expression::NumberLiteral(0.),
423            Type::Percent => Expression::NumberLiteral(1.),
424            Type::String => Expression::StringLiteral(SmolStr::default()),
425            Type::Color => {
426                Expression::Cast { from: Box::new(Expression::NumberLiteral(0.)), to: ty.clone() }
427            }
428            Type::Image => Expression::ImageReference {
429                resource_ref: crate::expression_tree::ImageReference::None,
430                nine_slice: None,
431            },
432            Type::Bool => Expression::BoolLiteral(false),
433            Type::Model => return None,
434            Type::PathData => return None,
435            Type::Array(element_ty) => Expression::Array {
436                element_ty: (**element_ty).clone(),
437                values: Vec::new(),
438                output: ArrayOutput::Model,
439            },
440            Type::Struct(s) => Expression::Struct {
441                ty: s.clone(),
442                values: s
443                    .fields
444                    .iter()
445                    .map(|(k, v)| {
446                        let value = match s.field_defaults.get(k) {
447                            Some(default_value) => {
448                                super::lower_expression::lower_constant_expression(default_value)
449                            }
450                            None => Expression::default_value_for_type(v)?,
451                        };
452                        Some((k.clone(), value))
453                    })
454                    .collect::<Option<_>>()?,
455            },
456            Type::Easing => Expression::EasingCurve(crate::expression_tree::EasingCurve::default()),
457            Type::MouseCursor => {
458                let e = crate::typeregister::BUILTIN.enums.BuiltInMouseCursor.clone();
459                Expression::MouseCursor(MouseCursorInner::BuiltIn(Box::new(
460                    Expression::EnumerationValue(e.default_value()),
461                )))
462            }
463            Type::Brush => Expression::Cast {
464                from: Box::new(Expression::default_value_for_type(&Type::Color)?),
465                to: Type::Brush,
466            },
467            Type::Enumeration(enumeration) => {
468                Expression::EnumerationValue(enumeration.clone().default_value())
469            }
470            Type::Keys => Expression::KeysLiteral(Keys::default()),
471            Type::DataTransfer => Expression::EmptyDataTransfer,
472            Type::ComponentFactory => Expression::EmptyComponentFactory,
473            Type::StyledText => Expression::BuiltinFunctionCall {
474                source_location: None,
475                function: BuiltinFunction::StringToStyledText,
476                arguments: vec![Expression::StringLiteral(SmolStr::default())],
477            },
478        })
479    }
480
481    pub fn ty(&self, ctx: &dyn TypeResolutionContext) -> Type {
482        match self {
483            Self::StringLiteral(_) => Type::String,
484            Self::NumberLiteral(_) => Type::Float32,
485            Self::BoolLiteral(_) => Type::Bool,
486            Self::PropertyReference(prop) => ctx.property_ty(prop).clone(),
487            Self::FunctionParameterReference { index } => ctx.arg_type(*index).clone(),
488            Self::StoreLocalVariable { .. } => Type::Void,
489            Self::ReadLocalVariable { ty, .. } => ty.clone(),
490            Self::StructFieldAccess { base, name } => match base.ty(ctx) {
491                Type::Struct(s) => s.fields[name].clone(),
492                _ => unreachable!(),
493            },
494            Self::ArrayIndex { array, .. } => match array.ty(ctx) {
495                Type::Array(ty) => (*ty).clone(),
496                _ => unreachable!(),
497            },
498            Self::Cast { to, .. } => to.clone(),
499            Self::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty(ctx)),
500            Self::BuiltinFunctionCall { function, .. } => function.ty().return_type.clone(),
501            Self::CallBackCall { callback, .. } => match ctx.property_ty(callback) {
502                Type::Callback(callback) => callback.return_type.clone(),
503                _ => Type::Invalid,
504            },
505            Self::FunctionCall { function, .. } => ctx.property_ty(function).clone(),
506            Self::ItemMemberFunctionCall { function } => match ctx.property_ty(function) {
507                Type::Function(function) => function.return_type.clone(),
508                _ => Type::Invalid,
509            },
510            Self::ExtraBuiltinFunctionCall { return_ty, .. } => return_ty.clone(),
511            Self::PropertyAssignment { .. } => Type::Void,
512            Self::ModelDataAssignment { .. } => Type::Void,
513            Self::ArrayIndexAssignment { .. } => Type::Void,
514            Self::SliceIndexAssignment { .. } => Type::Void,
515            Self::BinaryExpression { lhs, rhs: _, op } => binary_expression_ty(*op, || lhs.ty(ctx)),
516            Self::UnaryOp { sub, .. } => sub.ty(ctx),
517            Self::ImageReference { .. } => Type::Image,
518            Self::Condition { false_expr, .. } => false_expr.ty(ctx),
519            Self::Array { element_ty, .. } => Type::Array(element_ty.clone().into()),
520            Self::Struct { ty, .. } => ty.clone().into(),
521            Self::EasingCurve(_) => Type::Easing,
522            Self::MouseCursor(_) => Type::MouseCursor,
523            Self::LinearGradient { .. } => Type::Brush,
524            Self::RadialGradient { .. } => Type::Brush,
525            Self::ConicGradient { .. } => Type::Brush,
526            Self::EnumerationValue(e) => Type::Enumeration(e.enumeration.clone()),
527            Self::KeysLiteral(_) => Type::Keys,
528            Self::LayoutCacheAccess { .. } => Type::LogicalLength,
529            Self::GridRepeaterCacheAccess { .. } => Type::LogicalLength,
530            Self::WithLayoutItemInfo { sub_expression, .. } => sub_expression.ty(ctx),
531            Self::WithFlexboxLayoutItemInfo { sub_expression, .. } => sub_expression.ty(ctx),
532            Self::SolveFlexboxLayoutWithMeasure { .. } => Type::LayoutCache,
533            Self::BoxLayoutInfoOrthoWithMeasure { .. } => {
534                crate::typeregister::layout_info_type().into()
535            }
536            Self::FlexboxLayoutInfoCrossAxisWithMeasure { .. } => {
537                crate::typeregister::layout_info_type().into()
538            }
539            Self::WithGridInputData { sub_expression, .. } => sub_expression.ty(ctx),
540            Self::MinMax { ty, .. } => ty.clone(),
541            Self::EmptyComponentFactory => Type::ComponentFactory,
542            Self::EmptyDataTransfer => Type::DataTransfer,
543            Self::TranslationReference { .. } => Type::String,
544            Self::Closure { .. } => Type::Closure,
545            Self::DebugHook { expression, .. } => expression.ty(ctx),
546        }
547    }
548}
549
550macro_rules! visit_impl {
551    ($self:ident, $visitor:ident, $as_ref:ident, $iter:ident, $values:ident) => {
552        match $self {
553            Expression::StringLiteral(_) => {}
554            Expression::NumberLiteral(_) => {}
555            Expression::BoolLiteral(_) => {}
556            Expression::PropertyReference(_) => {}
557            Expression::FunctionParameterReference { .. } => {}
558            Expression::StoreLocalVariable { value, .. } => $visitor(value),
559            Expression::ReadLocalVariable { .. } => {}
560            Expression::StructFieldAccess { base, .. } => $visitor(base),
561            Expression::ArrayIndex { array, index } => {
562                $visitor(array);
563                $visitor(index);
564            }
565            Expression::Cast { from, .. } => $visitor(from),
566            Expression::CodeBlock(b) => b.$iter().for_each($visitor),
567            Expression::BuiltinFunctionCall { arguments, .. }
568            | Expression::CallBackCall { arguments, .. }
569            | Expression::FunctionCall { arguments, .. } => arguments.$iter().for_each($visitor),
570            Expression::ItemMemberFunctionCall { function: _ } => {}
571            Expression::ExtraBuiltinFunctionCall { arguments, .. } => {
572                arguments.$iter().for_each($visitor)
573            }
574            Expression::PropertyAssignment { value, .. } => $visitor(value),
575            Expression::ModelDataAssignment { value, .. } => $visitor(value),
576            Expression::ArrayIndexAssignment { array, index, value } => {
577                $visitor(array);
578                $visitor(index);
579                $visitor(value);
580            }
581            Expression::SliceIndexAssignment { value, .. } => {
582                $visitor(value);
583            }
584            Expression::BinaryExpression { lhs, rhs, .. } => {
585                $visitor(lhs);
586                $visitor(rhs);
587            }
588            Expression::UnaryOp { sub, .. } => {
589                $visitor(sub);
590            }
591            Expression::ImageReference { .. } => {}
592            Expression::Condition { condition, true_expr, false_expr } => {
593                $visitor(condition);
594                $visitor(true_expr);
595                $visitor(false_expr);
596            }
597            Expression::Array { values, .. } => values.$iter().for_each($visitor),
598            Expression::Struct { values, .. } => values.$values().for_each($visitor),
599            Expression::EasingCurve(_) => {}
600            Expression::MouseCursor(cursor) => match cursor {
601                MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
602                    $visitor(image);
603                    $visitor(hotspot_x);
604                    $visitor(hotspot_y);
605                }
606                MouseCursorInner::BuiltIn(e) => {
607                    $visitor(e);
608                }
609            },
610            Expression::LinearGradient { angle, stops } => {
611                $visitor(angle);
612                for (a, b) in stops {
613                    $visitor(a);
614                    $visitor(b);
615                }
616            }
617            Expression::RadialGradient { center, radius, stops } => {
618                if let Some((cx, cy)) = center {
619                    $visitor(cx);
620                    $visitor(cy);
621                }
622                if let Some(r) = radius {
623                    $visitor(r);
624                }
625                for (a, b) in stops {
626                    $visitor(a);
627                    $visitor(b);
628                }
629            }
630            Expression::ConicGradient { from_angle, center, stops } => {
631                $visitor(from_angle);
632                if let Some((cx, cy)) = center {
633                    $visitor(cx);
634                    $visitor(cy);
635                }
636                for (a, b) in stops {
637                    $visitor(a);
638                    $visitor(b);
639                }
640            }
641            Expression::EnumerationValue(_) => {}
642            Expression::KeysLiteral(_) => {}
643            Expression::LayoutCacheAccess { repeater_index, .. } => {
644                if let Some(repeater_index) = repeater_index {
645                    $visitor(repeater_index);
646                }
647            }
648            Expression::GridRepeaterCacheAccess {
649                repeater_index,
650                stride,
651                inner_repeater_index,
652                ..
653            } => {
654                $visitor(repeater_index);
655                $visitor(stride);
656                if let Some(inner_repeater_index) = inner_repeater_index {
657                    $visitor(inner_repeater_index);
658                }
659            }
660            Expression::WithLayoutItemInfo {
661                elements,
662                repeated_cross_size,
663                sub_expression,
664                ..
665            } => {
666                $visitor(sub_expression);
667                if let Some(s) = repeated_cross_size {
668                    $visitor(s);
669                }
670                elements.$iter().for_each(|x| match x.$as_ref() {
671                    Either::Left(e) => $visitor(e),
672                    Either::Right(r) => {
673                        if let Some(w) = r.cross_width.$as_ref() {
674                            $visitor(w);
675                        }
676                    }
677                });
678            }
679            Expression::WithFlexboxLayoutItemInfo {
680                elements,
681                repeated_cross_width,
682                sub_expression,
683                ..
684            } => {
685                $visitor(sub_expression);
686                if let Some(w) = repeated_cross_width {
687                    $visitor(w);
688                }
689                elements.$iter().for_each(|x| match x.$as_ref() {
690                    Either::Left((h, v, f)) => {
691                        $visitor(h);
692                        $visitor(v);
693                        // Visited even when `flex_props_variable` is `None` and the
694                        // generators skip `f`: this only over-counts property use,
695                        // and the layout's solve binding reads the same properties.
696                        $visitor(f);
697                    }
698                    Either::Right(r) => {
699                        if let Some(w) = r.cross_width.$as_ref() {
700                            $visitor(w);
701                        }
702                    }
703                });
704            }
705            Expression::SolveFlexboxLayoutWithMeasure { data, repeater_indices, measure_cells } => {
706                $visitor(data);
707                $visitor(repeater_indices);
708                measure_cells.$iter().for_each(|x| match x {
709                    FlexboxMeasureCell::Static { v_info } => $visitor(v_info),
710                    FlexboxMeasureCell::Repeated(r) => {
711                        if let Some(w) = r.cross_width.$as_ref() {
712                            $visitor(w);
713                        }
714                    }
715                    FlexboxMeasureCell::Fixed => {}
716                });
717            }
718            Expression::BoxLayoutInfoOrthoWithMeasure {
719                solve_data,
720                padding_ortho,
721                measure_cells,
722            } => {
723                $visitor(solve_data);
724                $visitor(padding_ortho);
725                measure_cells.$iter().for_each(|x| match x {
726                    BoxMeasureCell::Static { info } => $visitor(info),
727                    BoxMeasureCell::Repeated(r) => {
728                        if let Some(w) = r.cross_width.$as_ref() {
729                            $visitor(w);
730                        }
731                    }
732                });
733            }
734            Expression::FlexboxLayoutInfoCrossAxisWithMeasure { arguments, measure_cells } => {
735                arguments.$iter().for_each(&mut $visitor);
736                measure_cells.$iter().for_each(|x| {
737                    if let FlexboxMeasureCell::Static { v_info } = x {
738                        $visitor(v_info);
739                    }
740                });
741            }
742            Expression::WithGridInputData { elements, sub_expression, .. } => {
743                $visitor(sub_expression);
744                elements.$iter().filter_map(|x| x.$as_ref().left()).for_each($visitor);
745            }
746            Expression::MinMax { ty: _, op: _, lhs, rhs } => {
747                $visitor(lhs);
748                $visitor(rhs);
749            }
750            Expression::EmptyComponentFactory => {}
751            Expression::EmptyDataTransfer => {}
752            Expression::TranslationReference { format_args, plural, string_index: _ } => {
753                $visitor(format_args);
754                if let Some(plural) = plural {
755                    $visitor(plural);
756                }
757            }
758            Expression::Closure { expression, .. } => {
759                $visitor(expression);
760            }
761            Expression::DebugHook { expression, id: _ } => $visitor(expression),
762        }
763    };
764}
765
766impl Expression {
767    /// Call the visitor for each sub-expression (not recursive)
768    pub fn visit(&self, mut visitor: impl FnMut(&Self)) {
769        visit_impl!(self, visitor, as_ref, iter, values)
770    }
771
772    /// Call the visitor for each sub-expression (not recursive)
773    pub fn visit_mut(&mut self, mut visitor: impl FnMut(&mut Self)) {
774        visit_impl!(self, visitor, as_mut, iter_mut, values_mut)
775    }
776
777    /// Visit itself and each sub expression recursively
778    pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
779        visitor(self);
780        self.visit(|e| e.visit_recursive(visitor));
781    }
782
783    /// Visit itself and each sub expression recursively
784    pub fn visit_recursive_mut(&mut self, visitor: &mut dyn FnMut(&mut Self)) {
785        visitor(self);
786        self.visit_mut(|e| e.visit_recursive_mut(visitor));
787    }
788
789    pub fn visit_property_references(
790        &self,
791        ctx: &EvaluationContext,
792        visitor: &mut dyn FnMut(&MemberReference, &EvaluationContext),
793    ) {
794        self.visit_recursive(&mut |expr| {
795            let p = match expr {
796                Expression::PropertyReference(p) => p,
797                Expression::CallBackCall { callback, .. } => callback,
798                // The `function` of a call is also a member reference. `property_info`
799                // returns nothing for it, so callers that only care about properties
800                // ignore it, while callers that track function use can act on it.
801                Expression::FunctionCall { function, .. } => function,
802                Expression::PropertyAssignment { property, .. } => {
803                    if let Some((a, map)) = &ctx.property_info(property).animation {
804                        let ctx2 = map.map_context(ctx);
805                        a.visit_property_references(&ctx2, visitor);
806                    }
807                    property
808                }
809                // FIXME  (should be fine anyway because we mark these as not optimizable)
810                Expression::ModelDataAssignment { .. } => return,
811                Expression::LayoutCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
812                Expression::GridRepeaterCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
813                _ => return,
814            };
815            visitor(p, ctx)
816        });
817    }
818}
819
820pub trait TypeResolutionContext {
821    /// The type of the property.
822    ///
823    /// For reference to function, this is the return type
824    fn property_ty(&self, _: &MemberReference) -> &Type;
825
826    // The type of the specified argument when evaluating a callback
827    fn arg_type(&self, _index: usize) -> &Type {
828        unimplemented!()
829    }
830}
831
832/// The parent context of the current context when the current context is repeated
833#[derive(Clone, Copy)]
834pub struct ParentScope<'a> {
835    /// The parent sub component
836    pub sub_component: SubComponentIdx,
837    /// Index of the repeater within the ctx.current_sub_component
838    pub repeater_index: Option<RepeatedElementIdx>,
839    /// A further parent context when the parent context is itself in a repeater
840    pub parent: Option<&'a ParentScope<'a>>,
841}
842
843impl<'a> ParentScope<'a> {
844    pub fn new<T>(
845        ctx: &'a EvaluationContext<'a, T>,
846        repeater_index: Option<RepeatedElementIdx>,
847    ) -> Self {
848        let EvaluationScope::SubComponent(sub_component, parent) = ctx.current_scope else {
849            unreachable!()
850        };
851        Self { sub_component, repeater_index, parent }
852    }
853}
854
855#[derive(Clone, Copy)]
856pub enum EvaluationScope<'a> {
857    /// The evaluation context is in a sub component, optionally with information about the repeater parent
858    SubComponent(SubComponentIdx, Option<&'a ParentScope<'a>>),
859    /// The evaluation context is in a global
860    Global(GlobalIdx),
861    /// The evaluation context is a constant expression that cannot reference any
862    /// properties or elements, such as the default value of a struct field
863    Const,
864}
865
866#[derive(Clone)]
867pub struct EvaluationContext<'a, T = ()> {
868    pub compilation_unit: &'a super::CompilationUnit,
869    pub current_scope: EvaluationScope<'a>,
870    pub generator_state: T,
871
872    /// The callback argument types
873    pub argument_types: &'a [Type],
874}
875
876impl<'a, T> EvaluationContext<'a, T> {
877    pub fn new_sub_component(
878        compilation_unit: &'a super::CompilationUnit,
879        sub_component: SubComponentIdx,
880        generator_state: T,
881        parent: Option<&'a ParentScope<'a>>,
882    ) -> Self {
883        Self {
884            compilation_unit,
885            current_scope: EvaluationScope::SubComponent(sub_component, parent),
886            generator_state,
887            argument_types: &[],
888        }
889    }
890
891    pub fn new_global(
892        compilation_unit: &'a super::CompilationUnit,
893        global: GlobalIdx,
894        generator_state: T,
895    ) -> Self {
896        Self {
897            compilation_unit,
898            current_scope: EvaluationScope::Global(global),
899            generator_state,
900            argument_types: &[],
901        }
902    }
903
904    /// A context for compiling a constant expression that cannot reference any
905    /// properties or elements, such as the default value of a struct field
906    /// (see [`crate::langtype::Struct::field_defaults`])
907    pub fn new_const(compilation_unit: &'a super::CompilationUnit, generator_state: T) -> Self {
908        Self {
909            compilation_unit,
910            current_scope: EvaluationScope::Const,
911            generator_state,
912            argument_types: &[],
913        }
914    }
915
916    pub(crate) fn property_info<'b>(&'b self, prop: &MemberReference) -> PropertyInfoResult<'b> {
917        fn match_in_sub_component<'b>(
918            cu: &'b super::CompilationUnit,
919            sc: &'b super::SubComponent,
920            prop: &LocalMemberReference,
921            map: ContextMap,
922        ) -> PropertyInfoResult<'b> {
923            let use_count_and_ty = || {
924                let mut sc = sc;
925                for i in &prop.sub_component_path {
926                    sc = &cu.sub_components[sc.sub_components[*i].ty];
927                }
928                match &prop.reference {
929                    LocalMemberIndex::Property(property_index) => {
930                        sc.properties.get(*property_index).map(|x| (&x.use_count, &x.ty))
931                    }
932                    LocalMemberIndex::Callback(callback_index) => {
933                        sc.callbacks.get(*callback_index).map(|x| (&x.use_count, &x.ty))
934                    }
935                    _ => None,
936                }
937            };
938
939            let animation = sc.animations.get(prop).map(|a| (a, map.clone()));
940            let analysis = sc.prop_analysis.get(&prop.clone().into());
941            if let Some(a) = &analysis
942                && let Some(init) = a.property_init
943            {
944                let u = use_count_and_ty();
945                return PropertyInfoResult {
946                    analysis: Some(&a.analysis),
947                    binding: Some((&sc.property_init[init].1, map)),
948                    animation,
949                    ty: u.map_or(Type::Invalid, |x| x.1.clone()),
950                    use_count: u.map(|x| x.0),
951                };
952            }
953            let mut r = if let &[idx, ref rest @ ..] = prop.sub_component_path.as_slice() {
954                let prop2 = LocalMemberReference {
955                    sub_component_path: rest.to_vec(),
956                    reference: prop.reference.clone(),
957                };
958                match_in_sub_component(
959                    cu,
960                    &cu.sub_components[sc.sub_components[idx].ty],
961                    &prop2,
962                    map.deeper_in_sub_component(idx),
963                )
964            } else {
965                let u = use_count_and_ty();
966                PropertyInfoResult {
967                    ty: u.map_or(Type::Invalid, |x| x.1.clone()),
968                    use_count: u.map(|x| x.0),
969                    ..Default::default()
970                }
971            };
972
973            if animation.is_some() {
974                r.animation = animation
975            };
976            if let Some(a) = analysis {
977                r.analysis = Some(&a.analysis);
978            }
979            r
980        }
981
982        fn in_global<'a>(
983            g: &'a super::GlobalComponent,
984            r: &'_ LocalMemberIndex,
985            map: ContextMap,
986        ) -> PropertyInfoResult<'a> {
987            let binding = g.init_values.get(r).map(|b| (b, map));
988            match r {
989                LocalMemberIndex::Property(index) => {
990                    let property_decl = &g.properties[*index];
991                    PropertyInfoResult {
992                        analysis: Some(&g.prop_analysis[*index]),
993                        binding,
994                        animation: None,
995                        ty: property_decl.ty.clone(),
996                        use_count: Some(&property_decl.use_count),
997                    }
998                }
999                LocalMemberIndex::Callback(index) => {
1000                    let callback_decl = &g.callbacks[*index];
1001                    PropertyInfoResult {
1002                        analysis: None,
1003                        binding,
1004                        animation: None,
1005                        ty: callback_decl.ty.clone(),
1006                        use_count: Some(&callback_decl.use_count),
1007                    }
1008                }
1009                _ => PropertyInfoResult::default(),
1010            }
1011        }
1012
1013        match prop {
1014            MemberReference::Relative { parent_level, local_reference } => {
1015                match self.current_scope {
1016                    EvaluationScope::Global(g) => {
1017                        let g = &self.compilation_unit.globals[g];
1018                        in_global(g, &local_reference.reference, ContextMap::Identity)
1019                    }
1020                    EvaluationScope::SubComponent(mut sc, mut parent) => {
1021                        for _ in 0..*parent_level {
1022                            // The parent chain is severed for function bodies (see
1023                            // `for_each_expression`); the reference is then not
1024                            // resolvable, like `function_info` also reports.
1025                            let Some(p) = parent else {
1026                                return PropertyInfoResult::default();
1027                            };
1028                            sc = p.sub_component;
1029                            parent = p.parent;
1030                        }
1031                        match_in_sub_component(
1032                            self.compilation_unit,
1033                            &self.compilation_unit.sub_components[sc],
1034                            local_reference,
1035                            ContextMap::from_parent_level(*parent_level),
1036                        )
1037                    }
1038                    EvaluationScope::Const => {
1039                        panic!("property reference in a constant expression")
1040                    }
1041                }
1042            }
1043            MemberReference::Global { global_index, member } => {
1044                let g = &self.compilation_unit.globals[*global_index];
1045                in_global(g, member, ContextMap::InGlobal(*global_index))
1046            }
1047        }
1048    }
1049
1050    /// Resolve a reference to a user function, returning the function and the
1051    /// [`ContextMap`] to evaluate its body in the current context.
1052    pub(crate) fn function_info<'b>(
1053        &'b self,
1054        reference: &MemberReference,
1055    ) -> Option<(&'b super::Function, ContextMap)> {
1056        let cu = self.compilation_unit;
1057        match reference {
1058            MemberReference::Relative { parent_level, local_reference } => {
1059                // Cheap check before walking the scope: most references are not functions.
1060                let LocalMemberIndex::Function(idx) = local_reference.reference else {
1061                    return None;
1062                };
1063                let mut scope = self.current_scope;
1064                for _ in 0..*parent_level {
1065                    let EvaluationScope::SubComponent(_, Some(p)) = scope else { return None };
1066                    scope = EvaluationScope::SubComponent(p.sub_component, p.parent);
1067                }
1068                let EvaluationScope::SubComponent(mut sc, _) = scope else { return None };
1069                for i in &local_reference.sub_component_path {
1070                    sc = cu.sub_components[sc].sub_components[*i].ty;
1071                }
1072                Some((
1073                    cu.sub_components[sc].functions.get(idx)?,
1074                    ContextMap::from_parent_level(*parent_level)
1075                        .deeper_by_path(&local_reference.sub_component_path),
1076                ))
1077            }
1078            MemberReference::Global { global_index, member } => {
1079                let LocalMemberIndex::Function(idx) = member else { return None };
1080                Some((
1081                    cu.globals[*global_index].functions.get(*idx)?,
1082                    ContextMap::InGlobal(*global_index),
1083                ))
1084            }
1085        }
1086    }
1087
1088    /// Resolve the component that a [`MemberReference::Relative`]
1089    /// with the given `parent_level` and `sub_component_path` refers to,
1090    /// and call `f` with a [`ParentScope`] for it,
1091    /// suitable as the parent scope of e.g. a popup declared there.
1092    /// Continuation style because the parent chain of a descended sub-component
1093    /// borrows from the stack.
1094    pub fn with_reference_scope<R>(
1095        &self,
1096        parent_level: usize,
1097        sub_component_path: &[SubComponentInstanceIdx],
1098        f: impl FnOnce(ParentScope<'_>) -> R,
1099    ) -> R {
1100        fn descend<R>(
1101            cu: &super::CompilationUnit,
1102            sc: SubComponentIdx,
1103            parent: Option<&ParentScope<'_>>,
1104            path: &[SubComponentInstanceIdx],
1105            f: impl FnOnce(ParentScope<'_>) -> R,
1106        ) -> R {
1107            if let [first, rest @ ..] = path {
1108                let ps = ParentScope { sub_component: sc, repeater_index: None, parent };
1109                let child = cu.sub_components[sc].sub_components[*first].ty;
1110                descend(cu, child, Some(&ps), rest, f)
1111            } else {
1112                f(ParentScope { sub_component: sc, repeater_index: None, parent })
1113            }
1114        }
1115        let EvaluationScope::SubComponent(mut sc, mut parent) = self.current_scope else {
1116            panic!("not in a sub-component scope")
1117        };
1118        for _ in 0..parent_level {
1119            let p = parent.expect("invalid parent reference");
1120            sc = p.sub_component;
1121            parent = p.parent;
1122        }
1123        descend(self.compilation_unit, sc, parent, sub_component_path, f)
1124    }
1125
1126    pub fn current_sub_component(&self) -> Option<&super::SubComponent> {
1127        let EvaluationScope::SubComponent(i, _) = self.current_scope else { return None };
1128        self.compilation_unit.sub_components.get(i)
1129    }
1130
1131    pub fn current_global(&self) -> Option<&super::GlobalComponent> {
1132        let EvaluationScope::Global(i) = self.current_scope else { return None };
1133        self.compilation_unit.globals.get(i)
1134    }
1135
1136    pub fn parent_sub_component_idx(&self, parent: usize) -> Option<SubComponentIdx> {
1137        let EvaluationScope::SubComponent(mut sc, mut par) = self.current_scope else {
1138            return None;
1139        };
1140        for _ in 0..parent {
1141            let p = par?;
1142            sc = p.sub_component;
1143            par = p.parent;
1144        }
1145        Some(sc)
1146    }
1147
1148    pub fn relative_property_ty(
1149        &self,
1150        local_reference: &LocalMemberReference,
1151        parent_level: usize,
1152    ) -> &Type {
1153        if let Some(g) = self.current_global() {
1154            return match &local_reference.reference {
1155                LocalMemberIndex::Property(property_idx) => &g.properties[*property_idx].ty,
1156                LocalMemberIndex::Function(function_idx) => &g.functions[*function_idx].ret_ty,
1157                LocalMemberIndex::Callback(callback_idx) => &g.callbacks[*callback_idx].ty,
1158                LocalMemberIndex::Native { .. } | LocalMemberIndex::Timer(_) => unreachable!(),
1159            };
1160        }
1161
1162        let mut sc = &self.compilation_unit.sub_components
1163            [self.parent_sub_component_idx(parent_level).unwrap()];
1164        for i in &local_reference.sub_component_path {
1165            sc = &self.compilation_unit.sub_components[sc.sub_components[*i].ty];
1166        }
1167        match &local_reference.reference {
1168            LocalMemberIndex::Property(property_index) => &sc.properties[*property_index].ty,
1169            LocalMemberIndex::Function(function_index) => &sc.functions[*function_index].ret_ty,
1170            LocalMemberIndex::Callback(callback_index) => &sc.callbacks[*callback_index].ty,
1171            LocalMemberIndex::Timer(_) => unreachable!("a timer reference has no type"),
1172            LocalMemberIndex::Native { item_index, prop_name, .. } => {
1173                if prop_name == "elements" {
1174                    // The `Path::elements` property is not in the NativeClass
1175                    return &Type::PathData;
1176                }
1177                let item = &sc.items[*item_index];
1178                item.ty.lookup_property(prop_name).unwrap_or_else(|| {
1179                    panic!("Failed to lookup property {prop_name} for {}", item.name)
1180                })
1181            }
1182        }
1183    }
1184}
1185
1186impl<T> TypeResolutionContext for EvaluationContext<'_, T> {
1187    fn property_ty(&self, prop: &MemberReference) -> &Type {
1188        match prop {
1189            MemberReference::Relative { parent_level, local_reference } => {
1190                self.relative_property_ty(local_reference, *parent_level)
1191            }
1192            MemberReference::Global { global_index, member } => {
1193                let g = &self.compilation_unit.globals[*global_index];
1194                match member {
1195                    LocalMemberIndex::Property(property_idx) => &g.properties[*property_idx].ty,
1196                    LocalMemberIndex::Function(function_idx) => &g.functions[*function_idx].ret_ty,
1197                    LocalMemberIndex::Callback(callback_idx) => &g.callbacks[*callback_idx].ty,
1198                    LocalMemberIndex::Native { .. } | LocalMemberIndex::Timer(_) => unreachable!(),
1199                }
1200            }
1201        }
1202    }
1203
1204    fn arg_type(&self, index: usize) -> &Type {
1205        &self.argument_types[index]
1206    }
1207}
1208
1209#[derive(Default, Debug)]
1210pub(crate) struct PropertyInfoResult<'a> {
1211    pub analysis: Option<&'a crate::object_tree::PropertyAnalysis>,
1212    pub binding: Option<(&'a super::BindingExpression, ContextMap)>,
1213    pub animation: Option<(&'a Expression, ContextMap)>,
1214    pub ty: Type,
1215    pub use_count: Option<&'a std::cell::Cell<usize>>,
1216}
1217
1218/// Maps between two evaluation context.
1219/// This allows to go from the current subcomponents context, to the context
1220/// relative to the binding we want to inline
1221#[derive(Debug, Clone)]
1222pub(crate) enum ContextMap {
1223    Identity,
1224    InSubElement { path: Vec<SubComponentInstanceIdx>, parent: usize },
1225    InGlobal(GlobalIdx),
1226}
1227
1228impl ContextMap {
1229    fn from_parent_level(parent_level: usize) -> Self {
1230        if parent_level == 0 {
1231            ContextMap::Identity
1232        } else {
1233            ContextMap::InSubElement { parent: parent_level, path: Vec::new() }
1234        }
1235    }
1236
1237    fn deeper_in_sub_component(self, sub: SubComponentInstanceIdx) -> Self {
1238        match self {
1239            ContextMap::Identity => ContextMap::InSubElement { parent: 0, path: vec![sub] },
1240            ContextMap::InSubElement { mut path, parent } => {
1241                path.push(sub);
1242                ContextMap::InSubElement { path, parent }
1243            }
1244            ContextMap::InGlobal(_) => panic!(),
1245        }
1246    }
1247
1248    fn deeper_by_path(self, path: &[SubComponentInstanceIdx]) -> Self {
1249        path.iter().fold(self, |m, sub| m.deeper_in_sub_component(*sub))
1250    }
1251
1252    pub fn map_property_reference(&self, p: &MemberReference) -> MemberReference {
1253        match self {
1254            ContextMap::Identity => p.clone(),
1255            ContextMap::InSubElement { path, parent } => match p {
1256                MemberReference::Relative { parent_level, local_reference } => {
1257                    MemberReference::Relative {
1258                        parent_level: *parent_level + *parent,
1259                        local_reference: LocalMemberReference {
1260                            sub_component_path: path
1261                                .iter()
1262                                .chain(local_reference.sub_component_path.iter())
1263                                .copied()
1264                                .collect(),
1265                            reference: local_reference.reference.clone(),
1266                        },
1267                    }
1268                }
1269                MemberReference::Global { .. } => p.clone(),
1270            },
1271            ContextMap::InGlobal(global_index) => match p {
1272                MemberReference::Relative { parent_level, local_reference } => {
1273                    assert!(local_reference.sub_component_path.is_empty());
1274                    assert_eq!(*parent_level, 0);
1275                    MemberReference::Global {
1276                        global_index: *global_index,
1277                        member: local_reference.reference.clone(),
1278                    }
1279                }
1280                g @ MemberReference::Global { .. } => g.clone(),
1281            },
1282        }
1283    }
1284
1285    pub fn map_expression(&self, e: &mut Expression) {
1286        match e {
1287            Expression::PropertyReference(p)
1288            | Expression::CallBackCall { callback: p, .. }
1289            | Expression::FunctionCall { function: p, .. }
1290            | Expression::ItemMemberFunctionCall { function: p, .. }
1291            | Expression::PropertyAssignment { property: p, .. }
1292            | Expression::LayoutCacheAccess { layout_cache_prop: p, .. }
1293            | Expression::GridRepeaterCacheAccess { layout_cache_prop: p, .. } => {
1294                *p = self.map_property_reference(p);
1295            }
1296            _ => (),
1297        }
1298        e.visit_mut(|e| self.map_expression(e))
1299    }
1300
1301    pub fn map_context<'a>(&self, ctx: &EvaluationContext<'a>) -> EvaluationContext<'a> {
1302        match self {
1303            ContextMap::Identity => ctx.clone(),
1304            ContextMap::InSubElement { path, parent } => {
1305                let mut sc = ctx.parent_sub_component_idx(*parent).unwrap();
1306                for i in path {
1307                    sc = ctx.compilation_unit.sub_components[sc].sub_components[*i].ty;
1308                }
1309                EvaluationContext::new_sub_component(ctx.compilation_unit, sc, (), None)
1310            }
1311            ContextMap::InGlobal(g) => EvaluationContext::new_global(ctx.compilation_unit, *g, ()),
1312        }
1313    }
1314}