{"record":{"id":"8cf48e1f0b727b44","repo":"slint-ui/slint","slug":"model-two-way-bindings-only-exist-on-sub-components","errorCode":null,"errorMessage":"model two-way bindings only exist on sub-components","messagePattern":"model two-way bindings only exist on sub-components","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/compiler/generator/cpp.rs","lineNumber":675,"sourceCode":"\n/// Emit a `link_two_way_to_model_data` call wiring `p1` to a row of the\n/// model described by `info`, optionally through a struct `field_access`.\nfn generate_model_two_way_binding(\n    ctx: &EvaluationContext,\n    info: &llr::ResolvedModelTwoWayBinding,\n    p1: &str,\n    field_access: &[SmolStr],\n) -> String {\n    let body_sc = &ctx.compilation_unit.sub_components[info.body_sub_component];\n    let data_prop_name = field_name(&body_sc.properties[info.data_prop].name);\n    let index_prop_name = field_name(&body_sc.properties[info.index_prop].name);\n    let repeater_index = usize::from(info.repeater_index);\n\n    // Determine the C++ class name of `self` so we can cast back from\n    // the type-erased VRc obtained by locking the weak pointer.\n    let self_type = ident(\n        &ctx.current_sub_component()\n            .expect(\"model two-way bindings only exist on sub-components\")\n            .name,\n    );\n\n    // Walk the parent chain in a single expression so the intermediate\n    // `lock().value()` temporaries live until we assign to `body_rc`.\n    let (body_setup, body) = if info.parent_level == 0 {\n        (String::new(), \"self\")\n    } else {\n        let chain: String = (0..info.parent_level).map(|_| \"->parent.lock().value()\").collect();\n        (format!(\"auto body_rc = self{chain}; \"), \"body_rc\")\n    };\n\n    let (getter_expr, ty) = lower_field_access_chain(\n        format!(\"{body}->{data_prop_name}.get()\"),\n        info.data_prop_ty,\n        field_access,\n    );\n    let (setter_lvalue, _) =","sourceCodeStart":657,"sourceCodeEnd":693,"githubUrl":"https://github.com/slint-ui/slint/blob/3a7e700487dc74d404cad0726378053ab763d4e0/internal/compiler/generator/cpp.rs#L657-L693","documentation":"This panic fires in the C++ code generator while emitting a model two-way binding (`generate_model_two_way_binding`). The generator needs the name of the current sub-component to compute the C++ class name for casting back from a type-erased VRc, but `ctx.current_sub_component()` returned None. Model two-way bindings are only lowered when attached to a sub-component (repeater), so hitting this means the code generator encountered the binding outside that context.","triggerScenarios":"A model two-way binding expression is generated for a context whose current entity is a global or the root without a sub-component frame on the EvaluationContext stack — i.e. a compiler-internal scheduling/pass-ordering bug, not something a .slint author writes directly.","commonSituations":"Slint compiler development: adding a new lowering pass or generator path that routes model two-way binding expressions through `compile_expression` without setting up a sub-component context; regressions after refactoring EvaluationContext creation.","solutions":["Check how the binding reached this code path; model two-way bindings must be emitted from `generate_sub_component` where the context has a current sub-component","Ensure the EvaluationContext was built with `new_sub_component` and the correct sub-component index before compiling the binding","If the binding legitimately belongs elsewhere, handle it explicitly instead of relying on the expect","File/report a compiler bug with the .slint input that reproduces it"],"exampleFix":"// before\nlet self_type = ident(&ctx.current_sub_component().expect(\"...\").name);\n// after\nlet Some(sub) = ctx.current_sub_component() else {\n    panic!(\"model two-way binding generated outside sub-component context: {:?}\", info.repeater_index);\n};\nlet self_type = ident(&sub.name);","handlingStrategy":"validation","validationCode":"debug_assert!(\n    ctx.current_sub_component().is_some(),\n    \"model two-way binding must be generated inside a sub-component context\"\n);","typeGuard":"let sub_component = match ctx.current_sub_component() {\n    Some(sub) => sub,\n    None => panic!(\"model two-way binding generated outside sub-component context\"),\n};","tryCatchPattern":null,"preventionTips":["Only emit model two-way bindings from generate_sub_component","Always build EvaluationContext with new_sub_component for binding compilation","Add a debug assertion in the binding compilation entry point","Write a regression test per generator path that compiles model two-way bindings"],"tags":["compiler","cpp-codegen","internal-invariant","panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"3a7e700487dc74d404cad0726378053ab763d4e0","analyzedAt":"2026-09-16T01:37:20.251Z","contentChangedAt":"2026-09-16T01:37:20.251Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}