slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
16.93k stars 566 forks source link

Spurious property dependency cycle and `BorrowMutError` panic in compiler #1689

Closed tay64 closed 1 year ago

tay64 commented 1 year ago

Another example of a design that works in slint-viewer but crashes the compiler. I was unable to isolate the problem properly. I made several attempts to recreate (what I thought to be) the essential parts of the structure but the error didn't appear, so I ended up copying my app UI and paring it down. Sorry about that.

This error appears when using the attached .slint file as is (let's call it Error A):

thread 'main' panicked at 'already borrowed: BorrowMutError', [...]\i-slint-compiler-0.3.0\passes\binding_analysis.rs:209:13

backtrace ``` $ RUST_BACKTRACE=1 cargo build Compiling slint-bug-binding-analysis v0.1.0 (D:\0\slint-bug-binding-analysis) error: failed to run custom build command for `slint-bug-binding-analysis v0.1.0 (D:\0\slint-bug-binding-analysis)` Caused by: process didn't exit successfully: `D:\0\slint-bug-binding-analysis\target\debug\build\slint-bug-binding-analysis-4234411efd23be05\build-script-build` (exit code: 101) --- stderr thread 'main' panicked at 'already borrowed: BorrowMutError', C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:209:13 stack backtrace: 0: rust_begin_unwind at /rustc/4493a0f4724c0bae1436242d76cccc9c0a287b80/library\std\src/panicking.rs:584:5 1: core::panicking::panic_fmt at /rustc/4493a0f4724c0bae1436242d76cccc9c0a287b80/library\core\src/panicking.rs:142:14 2: core::result::unwrap_failed at /rustc/4493a0f4724c0bae1436242d76cccc9c0a287b80/library\core\src/result.rs:1814:5 3: core::result::Result::expect at /rustc/4493a0f4724c0bae1436242d76cccc9c0a287b80\library\core\src/result.rs:1064:23 4: core::cell::RefCell::borrow_mut at /rustc/4493a0f4724c0bae1436242d76cccc9c0a287b80\library\core\src/cell.rs:956:9 5: i_slint_compiler::passes::binding_analysis::analyse_binding at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:209:5 6: i_slint_compiler::passes::binding_analysis::process_property at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:291:13 7: i_slint_compiler::passes::binding_analysis::analyse_binding::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:222:13 8: i_slint_compiler::passes::binding_analysis::visit_layout_items_dependencies at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:381:13 9: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:329:21 10: i_slint_compiler::passes::binding_analysis::analyse_binding at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:231:5 11: i_slint_compiler::passes::binding_analysis::process_property at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:291:13 12: i_slint_compiler::passes::binding_analysis::analyse_binding::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:222:13 13: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:318:68 14: i_slint_compiler::passes::binding_analysis::analyse_binding at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:231:5 15: i_slint_compiler::passes::binding_analysis::process_property at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:291:13 16: i_slint_compiler::passes::binding_analysis::analyse_binding::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:222:13 17: i_slint_compiler::passes::binding_analysis::visit_layout_items_dependencies at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:381:13 18: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:329:21 19: i_slint_compiler::passes::binding_analysis::analyse_binding at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:231:5 20: i_slint_compiler::passes::binding_analysis::process_property at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:291:13 21: i_slint_compiler::passes::binding_analysis::analyse_binding::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:222:13 22: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:317:80 23: i_slint_compiler::passes::binding_analysis::recurse_expression::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:315:22 24: i_slint_compiler::expression_tree::Expression::visit at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\expression_tree.rs:656:59 25: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:315:5 26: i_slint_compiler::passes::binding_analysis::analyse_binding at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:231:5 27: i_slint_compiler::passes::binding_analysis::process_property at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:291:13 28: i_slint_compiler::passes::binding_analysis::analyse_binding::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:222:13 29: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:317:80 30: i_slint_compiler::passes::binding_analysis::recurse_expression::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:315:22 31: i_slint_compiler::expression_tree::Expression::visit at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\expression_tree.rs:682:17 32: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:315:5 33: i_slint_compiler::passes::binding_analysis::analyse_binding at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:231:5 34: i_slint_compiler::passes::binding_analysis::process_property at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:291:13 35: i_slint_compiler::passes::binding_analysis::analyse_binding::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:222:13 36: i_slint_compiler::passes::binding_analysis::visit_layout_items_dependencies at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:381:13 37: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:329:21 38: i_slint_compiler::passes::binding_analysis::analyse_binding at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:231:5 39: i_slint_compiler::passes::binding_analysis::process_property at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:291:13 40: i_slint_compiler::passes::binding_analysis::analyse_binding::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:222:13 41: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:317:80 42: i_slint_compiler::passes::binding_analysis::recurse_expression::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:315:22 43: i_slint_compiler::expression_tree::Expression::visit at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\expression_tree.rs:683:17 44: i_slint_compiler::passes::binding_analysis::recurse_expression at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:315:5 45: i_slint_compiler::passes::binding_analysis::analyse_binding at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:231:5 46: i_slint_compiler::passes::binding_analysis::analyze_element at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:142:9 47: i_slint_compiler::passes::binding_analysis::perform_binding_analysis::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:128:21 48: i_slint_compiler::object_tree::recurse_elem_including_sub_components_no_borrow::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\object_tree.rs:1501:9 49: i_slint_compiler::object_tree::recurse_elem_no_borrow at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\object_tree.rs:1475:17 50: i_slint_compiler::object_tree::recurse_elem_including_sub_components_no_borrow at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\object_tree.rs:1488:5 51: i_slint_compiler::passes::binding_analysis::perform_binding_analysis at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:125:5 52: i_slint_compiler::passes::binding_analysis::binding_analysis at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes\binding_analysis.rs:36:5 53: i_slint_compiler::passes::run_passes::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\passes.rs:188:5 54: as core::future::future::Future>::poll at /rustc/4493a0f4724c0bae1436242d76cccc9c0a287b80\library\core\src\future/mod.rs:91:19 55: i_slint_compiler::compile_syntax_node::{{closure}} at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\i-slint-compiler-0.3.0\lib.rs:175:82 56: as core::future::future::Future>::poll at /rustc/4493a0f4724c0bae1436242d76cccc9c0a287b80\library\core\src\future/mod.rs:91:19 57: spin_on::spin_on at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\spin_on-0.1.1\src\lib.rs:78:38 58: slint_build::compile_with_config at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\slint-build-0.3.0\lib.rs:313:9 59: slint_build::compile at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\slint-build-0.3.0\lib.rs:270:5 60: build_script_build::main at .\build.rs:2:5 61: core::ops::function::FnOnce::call_once at /rustc/4493a0f4724c0bae1436242d76cccc9c0a287b80\library\core\src\ops/function.rs:248:5 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. ```

If the second button is deleted from AppWindow (as shown below), the error changes to this one (Error B): thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: CompileError(["The binding for the property 'layout-cache' is part of a binding loop" ...snipped, details under spoiler... ])', build.rs:2:48

binding loop diag messages ``` error: The binding for the property 'layout-cache' is part of a binding loop --> D:\0\slint-bug-binding-analysis\ui/appwindow.slint:96:2 | 96 | HorizontalBox { | ^ error: The binding for the property 'width' is part of a binding loop --> D:\0\slint-bug-binding-analysis\ui/appwindow.slint:96:2 | 96 | HorizontalBox { | ^ error: The binding for the property 'layoutinfo-h' is part of a binding loop --> D:\0\slint-bug-binding-analysis\ui/appwindow.slint:43:9 | 43 | hl := HorizontalLayout { | ^ error: The binding for the property 'min-width' is part of a binding loop --> D:\0\slint-bug-binding-analysis\ui/appwindow.slint:43:9 | 43 | hl := HorizontalLayout { | ^ error: The binding for the property 'min-width' is part of a binding loop --> D:\0\slint-bug-binding-analysis\ui/appwindow.slint:27:12 | 27 | min-width: hl.min-width + shadow-depth; | ^ error: The binding for the property 'preferred-width' is part of a binding loop --> D:\0\slint-bug-binding-analysis\ui/appwindow.slint:43:9 | 43 | hl := HorizontalLayout { | ^ error: The binding for the property 'preferred-width' is part of a binding loop --> D:\0\slint-bug-binding-analysis\ui/appwindow.slint:28:18 | 28 | preferred-width: hl.preferred-width + shadow-depth; | ^ ```

even though there is no binding loop, as far as I can see.

Notable details

(NB: the Button here is the custom button)

AppWindow := Window {
    HorizontalBox {
        startstop := Button {}
        if(false): setup := Button {}
    }
}
export Button := FocusScope {
    min-width:  hl.min-width + shadow-depth;
    preferred-width:  hl.preferred-width + shadow-depth;
    shadow := Rectangle {
        body := Rectangle {
            hl := HorizontalLayout {
                if( icon.width > 0 ): Image {
                    width   : icon-size;
                }
            }
        }
    }
}

My current workaround is to compute min-width and preferred-width manually:

    min-width: txt.min-width + inner-extra-width;
    preferred-width: txt.preferred-width + inner-extra-width;

    property<length> inner-extra-width: shadow-depth
        + padding-left + padding-right
        + inner-spacing
        + (icon.width > 0 ? icon-size : 0)
        ;

    property<length> inner-spacing: icon.width > 0 && text != "" ? 4px : 0;

This works, but it feels fragile, because I do not understand the cause of the problem.

Steps to reproduce:

  1. cargo generate --git https://github.com/slint-ui/slint-rust-template
  2. In the generated example, replace ui/appwindow.slint with the attached file.
  3. cargo build

Reproduced with slint-ui 0.3.0 from crates.io.

appwindow.slint.txt

ogoffart commented 1 year ago

Thank you for your bug report.

This appear to be a duplicate of https://github.com/slint-ui/slint/issues/1659 which was fixed recently in the master branch with PR #1661

The fix will be released with the next release.