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
4//! Passes that resolve the type of two way bindings.
5//!
6//! Before this pass, two way binding that did not specified the type have Type::Void
7//! type and their bindings are still a Expression::Uncompiled,
8//! this pass will attempt to assign a type to these based on the type of property they alias.
9
10use crate::diagnostics::BuildDiagnostics;
11use crate::expression_tree::Expression;
12use crate::langtype::Type;
13use crate::lookup::LookupCtx;
14use crate::object_tree::{Document, ElementRc};
15use crate::parser::syntax_nodes;
16use crate::typeregister::TypeRegister;
17use std::rc::Rc;
18
19#[derive(Clone)]
20struct ComponentScope(Vec<ElementRc>);
21
22pub fn resolve_aliases(doc: &Document, diag: &mut BuildDiagnostics) {
23 for component in doc.inner_components.iter() {
24 let scope = ComponentScope(vec![]);
25 crate::object_tree::recurse_elem_no_borrow(
26 &component.root_element,
27 &scope,
28 &mut |elem, scope| {
29 let mut new_scope = scope.clone();
30 new_scope.0.push(elem.clone());
31
32 let mut need_resolving = vec![];
33 for (prop, decl) in elem.borrow().property_declarations.iter() {
34 if matches!(decl.property_type, Type::InferredProperty | Type::InferredCallback)
35 {
36 need_resolving.push(prop.clone());
37 }
38 }
39 // make it deterministic
40 need_resolving.sort();
41 for n in need_resolving {
42 resolve_alias(elem, &n, &new_scope, &doc.local_registry, diag);
43 }
44 new_scope
45 },
46 );
47 }
48}
49
50fn resolve_alias(
51 elem: &ElementRc,
52 prop: &str,
53 scope: &ComponentScope,
54 type_register: &TypeRegister,
55 diag: &mut BuildDiagnostics,
56) {
57 let mut borrow_mut = elem.borrow_mut();
58 let old_type = match borrow_mut.property_declarations.get_mut(prop) {
59 Some(decl) => {
60 if !matches!(decl.property_type, Type::InferredCallback | Type::InferredProperty) {
61 // already processed;
62 return;
63 };
64 // mark the type as invalid now so that we catch recursion
65 std::mem::replace(&mut decl.property_type, Type::Invalid)
66 }
67 None => {
68 // Unresolved callback from a base component?
69 debug_assert!(matches!(
70 borrow_mut.lookup_property(prop).property_type,
71 Type::InferredCallback | Type::InferredProperty
72 ));
73 // It is still unresolved because there is an error in that component
74 assert!(diag.has_errors());
75 return;
76 }
77 };
78 drop(borrow_mut);
79
80 let borrow = elem.borrow();
81 let Some(binding) = borrow.bindings.get(prop) else {
82 assert!(diag.has_errors());
83 return;
84 };
85 let nr = match super::ignore_debug_hooks(&binding.borrow().expression) {
86 Expression::Uncompiled(node) => {
87 let Some(node) = syntax_nodes::TwoWayBinding::new(node.clone()) else {
88 assert!(
89 diag.has_errors(),
90 "The parser only avoid missing types for two way bindings"
91 );
92 return;
93 };
94 let mut lookup_ctx = LookupCtx::empty_context(type_register, diag);
95 lookup_ctx.property_name = Some(prop);
96 lookup_ctx.property_type = old_type.clone();
97 lookup_ctx.component_scope = &scope.0;
98 crate::passes::resolving::resolve_two_way_binding(node, &mut lookup_ctx)
99 }
100 _ => panic!("There should be a Uncompiled expression at this point."),
101 };
102 drop(borrow);
103
104 let mut ty = Type::Invalid;
105 if let Some(nr) = &nr {
106 let element = nr.element();
107 let same_element = Rc::ptr_eq(&element, elem);
108 if same_element && nr.name() == prop {
109 diag.push_error(
110 "Cannot alias to itself".to_string(),
111 &elem.borrow().property_declarations[prop].type_node(),
112 );
113 return;
114 }
115 ty = nr.ty();
116 if matches!(ty, Type::InferredCallback | Type::InferredProperty) {
117 if same_element {
118 resolve_alias(&element, nr.name(), scope, type_register, diag)
119 } else {
120 resolve_alias(&element, nr.name(), &recompute_scope(&element), type_register, diag)
121 };
122 ty = nr.ty();
123 }
124 }
125
126 if old_type == Type::InferredProperty {
127 if !ty.is_property_type() {
128 diag.push_error(
129 format!("Could not infer type of property '{prop}'"),
130 &elem.borrow().property_declarations[prop].type_node(),
131 );
132 } else {
133 elem.borrow_mut().property_declarations.get_mut(prop).unwrap().property_type = ty;
134 }
135 } else if old_type == Type::InferredCallback {
136 if !matches!(ty, Type::Callback { .. }) {
137 if nr.is_some() && ty == Type::Invalid {
138 debug_assert!(diag.has_errors());
139 } else {
140 diag.push_error(
141 format!("Binding to callback '{prop}' must bind to another callback"),
142 &elem.borrow().property_declarations[prop].type_node(),
143 );
144 }
145 } else {
146 let nr = nr.unwrap();
147 let is_global = nr.element().borrow().base_type == crate::langtype::ElementType::Global;
148 let purity = nr.element().borrow().lookup_property(nr.name()).declared_pure;
149 let mut elem = elem.borrow_mut();
150 let decl = elem.property_declarations.get_mut(prop).unwrap();
151 if decl.pure.unwrap_or(false) != purity.unwrap_or(false) {
152 diag.push_error(
153 format!("Purity of callbacks '{prop}' and '{nr:?}' doesn't match"),
154 &decl.type_node(),
155 );
156 }
157 if is_global {
158 diag.push_warning("Aliases to global callback are deprecated. Export the global to access the global callback directly from native code".into(), &decl.node);
159 }
160 decl.property_type = ty;
161 }
162 }
163}
164
165/// Recompute the scope of element
166///
167/// (since there is no parent mapping, we need to recursively search for the element)
168fn recompute_scope(element: &ElementRc) -> ComponentScope {
169 fn recurse(
170 base: &ElementRc,
171 needle: &ElementRc,
172 scope: &mut Vec<ElementRc>,
173 ) -> std::ops::ControlFlow<()> {
174 scope.push(base.clone());
175 if Rc::ptr_eq(this:base, other:needle) {
176 return std::ops::ControlFlow::Break(());
177 }
178 for child: &Rc> in &base.borrow().children {
179 if recurse(base:child, needle, scope).is_break() {
180 return std::ops::ControlFlow::Break(());
181 }
182 }
183 scope.pop();
184 std::ops::ControlFlow::Continue(())
185 }
186
187 let root: Rc> = element.borrow().enclosing_component.upgrade().unwrap().root_element.clone();
188 let mut scope: Vec>> = Vec::new();
189 let _ = recurse(&root, needle:element, &mut scope);
190 ComponentScope(scope)
191}
192