Repository: slint-ui/slint (language: rust)

Issue:
Conditional element causes runtime panic when used as breakpoint
### Bug Description

I tried to add a 'breakpoint' for a mobile screen inside a layout tree. I slimed down the example, but interstingly the panic only occures when the condition is inside a component.

Try to use the wrappers in the code below. Each wrapper causes a panic.

I also tried plastering `width: 100%`s, `VerticalLayout`s and `HorizontalLayout`s everywhere. Nothing helped.

### Reproducible Code (if applicable)

```slint
component Wrapper {
    width: 100%;
    height: 100%;

    Rectangle {
        VerticalLayout {
            if root.width > 200px: Rectangle { }
        }
    }
}

component WrapperInherited inherits Rectangle {
    VerticalLayout {
        if root.width > 200px: Rectangle { }
    }
}

export component Test inherits Window {

    VerticalLayout {
        HorizontalLayout {

            // Wrapper { }
            
            // WrapperInherited { }
           
            Rectangle {
                VerticalLayout {
                    if root.width > 200px: Rectangle { }
                }
            }
        }
    }
}
```


### Environment Details

- Slint Version: 1.8.0
- Platform/OS:
- Programming Language:
- Backend/Renderer:


### Product Impact

This bug blocks us from building a mobile friendly version (lower prio)


Discussion / hints:
The panic is

```
thread 'main' panicked at /Users/simon/src/slint/internal/core/properties.rs:523:9:
Recursion detected
```

That means that there's a circular dependency. For example:

At least in case of the direct use (without wrapper), `root.width` refers to the `Window`'s width. And that in turn doesn't depend directly on the constraints of the layout.
But shouldn't the wrapper with `width: 100%` just take the parents width and do not depend on the layout?

A workaround is to make the whole layout conditional:

```
component WrapperInherited inherits Rectangle {
    if root.width > 200px: VerticalLayout {
        if root.width > 200px: Rectangle { }
    }
}
```

I am not sure if this only works because of #407.

Or is there another way to achieve the behaviour I want? In a nested layout tree, I want to use a vertical layout on small screens, and a horizontal on larger ones.
Another workaround is to wrap the `WrapperInherited` inside a rectangle:

```
component WrapperInherited inherits Rectangle {
    VerticalLayout {
        if root.width > 200px: Rectangle { }
    }
}

export component Test inherits Window {

    VerticalLayout {
        HorizontalLayout {
            Rectangle {
                WrapperInherited { }
            }
        }
    }
}

```

I am not sure why it works, and also not if this is desired behaviour or if it will change. This does not work with the non-inherited Wrapper.

Fix patch:
diff --git a/internal/compiler/passes/binding_analysis.rs b/internal/compiler/passes/binding_analysis.rs
index 783e710a241..b742db13404 100644
--- a/internal/compiler/passes/binding_analysis.rs
+++ b/internal/compiler/passes/binding_analysis.rs
@@ -474,7 +474,7 @@ fn visit_layout_items_dependencies<'a>(
 ) {
     for it in items {
         let mut element = it.element.clone();
-        if element.borrow().repeated.is_some() {
+        if element.borrow().repeated.as_ref().map(|r| recurse_expression(&r.model, vis)).is_some() {
             element = it.element.borrow().base_type.as_component().root_element.clone();
         }
 


Test patch:
diff --git a/internal/compiler/tests/syntax/analysis/binding_loop_layout_if.slint b/internal/compiler/tests/syntax/analysis/binding_loop_layout_if.slint
new file mode 100644
index 00000000000..74403aba907
--- /dev/null
+++ b/internal/compiler/tests/syntax/analysis/binding_loop_layout_if.slint
@@ -0,0 +1,37 @@
+// Copyright © SixtyFPS GmbH <info@slint.dev>
+// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
+
+component Wrapper {
+    width: 100%;
+    height: 100%;
+
+    Rectangle {
+//  ^error{The binding for the property 'layoutinfo-h' is part of a binding loop}
+        VerticalLayout {
+//      ^error{The binding for the property 'layoutinfo-h' is part of a binding loop}
+            if root.width > 200px: Rectangle { }
+        }
+    }
+}
+
+component WrapperInherited inherits Rectangle {
+//                                  ^error{The binding for the property 'layoutinfo-h' is part of a binding loop}
+    VerticalLayout {
+//  ^error{The binding for the property 'layoutinfo-h' is part of a binding loop}
+        if root.width > 200px: Rectangle { }
+    }
+}
+
+export component Test inherits Window {
+
+    VerticalLayout {
+        HorizontalLayout {
+//      ^error{The binding for the property 'width' is part of a binding loop}
+//      ^^error{The binding for the property 'width' is part of a binding loop}
+//      ^^^error{The binding for the property 'layout-cache' is part of a binding loop}
+            WrapperInherited { }
+            Wrapper { }
+//          ^error{The binding for the property 'layoutinfo-h' is part of a binding loop}
+        }
+    }
+}
\ No newline at end of file