slint icon indicating copy to clipboard operation
slint copied to clipboard

Panic when accessing properties of the parent and the parent was deleted.

Open ogoffart opened this issue 2 years ago • 4 comments

Update

The original bug was fixed for Rust and C++, but not yet for the interpreter.

When this bug is fixed, we can remove this line

https://github.com/slint-ui/slint/blob/bf4ab9a01466c5523a988aa622899d99726794c7/tests/cases/models/delete_from_clicked.slint#L62-L63

And this line

https://github.com/slint-ui/slint/blob/bf4ab9a01466c5523a988aa622899d99726794c7/tests/driver/rust/build.rs#L30-L31

Another candidate testcase is in this comment: https://github.com/slint-ui/slint/issues/4699#issuecomment-1983295178

Bug report

Imagine this code:

component Foo {
     // connecte in native code to something that remove item from the model
     callback clicked(int);
     in property <[string]> model;
     for string[idx] in model: abcd := Rectangle {
          property <int> foo: idx;
          // This create a new repeated component with a parent
          if true : efgh := Button {
              clicked => {
                  // native code will remove element from the model that will destroy the repeated component `abcd`
                  root.clicked(idx);  
                  //  accessing `foo` in the parent will cause panic in rust or UB (crash) in C++
                  debug(foo);
              }
     }
  }

To reproduce this, edit the test delete_from_clicked.slint to uncomment the commented line:
https://github.com/slint-ui/slint/blob/f546d22861af3199b801c77185b59b2a52906c3d/tests/cases/models/delete_from_clicked.slint#L39 Ideally this test should work with only the line 39 commented, and the line 37 removed (so asign the value only after the clicked handler)

ogoffart avatar Sep 14 '23 08:09 ogoffart

The problem is that ItemRc keeps the item and its enclosing ItemTree alive. but the ItemTree's parent is a weak and is destroyed, so we unwrap the parent.upgrade().unwrap() in the generated binding https://github.com/slint-ui/slint/blob/5bf2c7192b6264947e5e80e7e85f831f4ea3f852/internal/compiler/generator/rust.rs#L1726 (and other places)

How to fix?

Option 1

Make ItemRc keep all the parent ItemTree alive. In otder to do so without introducing cycle, ItemRc could have a Vec<ItemTreeRc> instead of a simple ItemTreeRc. But that could be quite expensive. ItemRc could also call increment_strong_count manually on all the parent. and decrement_strong_count when Drop'ed

Option 2

Make the property binding failable and use ? or early return when something goes wrong. So instead of unwrapping the parent, we just check it and fail the binding if there is an error. The problem is what happen to the value of a binding that errors out? Default value? Previous value? Poisoned value? Stay dirty? Do error buble up to all dependent properties?

Option 3

When accessing the property of a deleted parent, just map_or_default instead of unwrap. So we get the default propery instead. Getting the default value is probably wrong behavior though. Especially if the binding/callback has side effect.

ogoffart avatar Oct 21 '23 12:10 ogoffart

The bug was fixed for rust in #5813 , but the bug remains in C++ and in the interpreter

ogoffart avatar Oct 10 '25 13:10 ogoffart

Sorry, that got closed by accident.

hunger avatar Oct 14 '25 07:10 hunger

C++ part was done in https://github.com/slint-ui/slint/pull/9863

ogoffart avatar Dec 05 '25 10:12 ogoffart