mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
WIP: pure qualifier for callback and functions
This commit is contained in:
parent
8a09043e63
commit
1cbd61145e
34 changed files with 366 additions and 93 deletions
|
@ -335,8 +335,8 @@ fn recurse_expression(expr: &Expression, vis: &mut impl FnMut(&PropertyPath)) {
|
|||
expr.visit(|sub| recurse_expression(sub, vis));
|
||||
match expr {
|
||||
Expression::PropertyReference(r)
|
||||
| Expression::CallbackReference(r)
|
||||
| Expression::FunctionReference(r) => vis(&r.clone().into()),
|
||||
| Expression::CallbackReference(r, _)
|
||||
| Expression::FunctionReference(r, _) => vis(&r.clone().into()),
|
||||
Expression::LayoutCacheAccess { layout_cache_prop, .. } => {
|
||||
vis(&layout_cache_prop.clone().into())
|
||||
}
|
||||
|
|
|
@ -497,6 +497,7 @@ fn lower_dialog_layout(
|
|||
"clicked",
|
||||
)),
|
||||
visibility: PropertyVisibility::InOut,
|
||||
pure: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -215,9 +215,9 @@ fn expression_for_property(element: &ElementRc, name: &str) -> ExpressionForProp
|
|||
// Check that the expresison is valid in the new scope
|
||||
let mut has_invalid = false;
|
||||
e.expression.visit_recursive(&mut |ex| match ex {
|
||||
Expression::CallbackReference(nr)
|
||||
Expression::CallbackReference(nr, _)
|
||||
| Expression::PropertyReference(nr)
|
||||
| Expression::FunctionReference(nr) => {
|
||||
| Expression::FunctionReference(nr, _) => {
|
||||
let e = nr.element();
|
||||
if !Rc::ptr_eq(&e, &element)
|
||||
&& Weak::ptr_eq(
|
||||
|
|
94
internal/compiler/passes/purity_check.rs
Normal file
94
internal/compiler/passes/purity_check.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
use crate::diagnostics::BuildDiagnostics;
|
||||
use crate::expression_tree::Expression;
|
||||
|
||||
/// Check that pure expression only call pure functions
|
||||
pub fn purity_check(doc: &crate::object_tree::Document, diag: &mut BuildDiagnostics) {
|
||||
for component in &doc.inner_components {
|
||||
crate::object_tree::recurse_elem_including_sub_components_no_borrow(
|
||||
component,
|
||||
&(),
|
||||
&mut |elem, &()| {
|
||||
crate::object_tree::visit_element_expressions(elem, |expr, name, _| {
|
||||
if let Some(name) = name {
|
||||
let lookup = elem.borrow().lookup_property(name);
|
||||
if lookup.declared_pure.unwrap_or(false)
|
||||
|| lookup.property_type.is_property_type()
|
||||
{
|
||||
ensure_pure(expr, Some(diag));
|
||||
}
|
||||
} else {
|
||||
// model expression must be pure
|
||||
ensure_pure(expr, Some(diag));
|
||||
};
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_pure(expr: &Expression, mut diag: Option<&mut BuildDiagnostics>) -> bool {
|
||||
let mut r = true;
|
||||
expr.visit_recursive(&mut |e| match e {
|
||||
Expression::CallbackReference(nr, node) => {
|
||||
if !nr.element().borrow().lookup_property(nr.name()).declared_pure.unwrap_or(false) {
|
||||
if let Some(diag) = diag.as_deref_mut() {
|
||||
diag.push_error(format!("Cannot call impure callback '{}'", nr.name()), node);
|
||||
}
|
||||
r = false;
|
||||
}
|
||||
}
|
||||
Expression::FunctionReference(nr, node) => {
|
||||
match nr.element().borrow().lookup_property(nr.name()).declared_pure {
|
||||
Some(true) => return,
|
||||
Some(false) => {
|
||||
if let Some(diag) = diag.as_deref_mut() {
|
||||
diag.push_error(
|
||||
format!("Cannot call impure function '{}'", nr.name(),),
|
||||
node,
|
||||
);
|
||||
}
|
||||
r = false;
|
||||
}
|
||||
None => {
|
||||
if !ensure_pure(
|
||||
&nr.element()
|
||||
.borrow()
|
||||
.bindings
|
||||
.get(nr.name())
|
||||
.expect("private function must be local and defined")
|
||||
.borrow()
|
||||
.expression,
|
||||
None,
|
||||
) {
|
||||
if let Some(diag) = diag.as_deref_mut() {
|
||||
diag.push_error(
|
||||
format!("Cannot call impure function '{}'", nr.name(),),
|
||||
node,
|
||||
);
|
||||
}
|
||||
r = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::BuiltinFunctionReference(func, node) => {
|
||||
if !func.is_pure() {
|
||||
if let Some(diag) = diag.as_deref_mut() {
|
||||
diag.push_error("Cannot call impure function".into(), node);
|
||||
}
|
||||
r = false;
|
||||
}
|
||||
}
|
||||
Expression::SelfAssignment { node, .. } => {
|
||||
if let Some(diag) = diag.as_deref_mut() {
|
||||
diag.push_error("Cannot assign in a pure context".into(), node);
|
||||
}
|
||||
r = false;
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
r
|
||||
}
|
|
@ -746,6 +746,7 @@ impl Expression {
|
|||
lhs: Box::new(lhs),
|
||||
rhs: Box::new(rhs.maybe_convert_to(expected_ty, &rhs_n, ctx.diag)),
|
||||
op,
|
||||
node: Some(NodeOrToken::Node(node.into())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1063,7 +1064,10 @@ fn continue_lookup_within_element(
|
|||
if let Some(x) = it.next() {
|
||||
ctx.diag.push_error("Cannot access fields of callback".into(), &x)
|
||||
}
|
||||
Expression::CallbackReference(NamedReference::new(elem, &lookup_result.resolved_name))
|
||||
Expression::CallbackReference(
|
||||
NamedReference::new(elem, &lookup_result.resolved_name),
|
||||
Some(NodeOrToken::Token(second)),
|
||||
)
|
||||
} else if matches!(lookup_result.property_type, Type::Function { .. }) {
|
||||
if !lookup_result.is_local_to_component
|
||||
&& lookup_result.property_visibility == PropertyVisibility::Private
|
||||
|
@ -1083,7 +1087,10 @@ fn continue_lookup_within_element(
|
|||
.into(),
|
||||
}
|
||||
} else {
|
||||
Expression::FunctionReference(NamedReference::new(elem, &lookup_result.resolved_name))
|
||||
Expression::FunctionReference(
|
||||
NamedReference::new(elem, &lookup_result.resolved_name),
|
||||
Some(NodeOrToken::Token(second)),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let mut err = |extra: &str| {
|
||||
|
@ -1295,7 +1302,7 @@ pub fn resolve_two_way_binding(
|
|||
}
|
||||
Some(n)
|
||||
}
|
||||
Expression::CallbackReference(n) => {
|
||||
Expression::CallbackReference(n, _) => {
|
||||
if ctx.property_type != Type::InferredCallback && ty != ctx.property_type {
|
||||
ctx.diag.push_error("Cannot bind to a callback".into(), &node);
|
||||
None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue