slint-ui/slint · error
a setter belongs to a field
Error message
a setter belongs to a field
What it means
In the slint_sc (safety-critical) generator, codegen for a property distinguishes stored properties (which read a struct field) from binding-kind properties (which evaluate a binding). The unwrap of p.field asserts the invariant that any property reaching the Stored arm has an backing field — i.e. 'a setter belongs to a field'. The panic fires when a property is marked stored/settable but the lowering pass failed to assign it a field, indicating an internal inconsistency between the property analysis and the generator.
Solutions
- Fix the pass that builds the `Property` so every setter is paired with a `field`
- Assert the setter/field pairing at Property construction time to fail earlier
- If you hit it as an end user, reduce the .slint input and file a bug with the reproduction
Example fix
// before
let setter = p.setter.as_ref().map(|setter| {
let field = p.field.as_ref().expect("a setter belongs to a field");
// after
let setter = match (&p.setter, &p.field) {
(Some(setter), Some(field)) => Some(/* emit setter using field */),
(Some(_), None) => return Err(anyhow!("property {} has a setter but no field", p.name)),
_ => None,
}; Defensive patterns
Strategy: validation
Validate before calling
debug_assert!(p.setter.is_none() || p.field.is_some(), "setter requires field");
Prevention
- When constructing generator Properties, always allocate the field before the setter
- Add construction-time asserts so violations surface at the pass, not at codegen
When it happens
Trigger: A pass (e.g. default_geometry or the SC lowering) produced a `Property` with `setter: Some(_)` but `field: None`. This is a compiler-internal bug, not user code.
Common situations: Developers working on the compiler who add setter generation in a pass without allocating the backing field, or changing `Property` construction in llr lowering.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- element without a size
- an identifier
- Components written in Rust can not get embedded yet.
- default value requested for unknown struct field
- invalid parent reference
AI-assisted analysis of slint-ui/slint@bb937076de (2026-09-16).
Data as JSON: /api/errors/f4a0ca69fe4fee0c.
Report an issue: GitHub.
Appendix: source
Thrown at internal/compiler/generator/slint_sc.rs:440
// A non-`Copy` field (a struct) is cloned on read.
let read =
|field| if p.copy { quote!(self.#field) } else { quote!(self.#field.clone()) };
let body = match &p.kind {
PropertyKind::Stored(_) => read(p.field.as_ref().unwrap()),
// A settable bound property falls back to the binding until
// set; a non-settable one always evaluates it.
PropertyKind::Binding(default) if p.setter.is_some() => {
let read = read(p.field.as_ref().unwrap());
// A block body: LLVM records no coverage for a closure
// whose body is a bare literal.
quote!(#read.unwrap_or_else(|| { #default }))
}
PropertyKind::Binding(default) => quote!(#default),
};
quote!(pub fn #getter(&self) -> #ty { #body })
});
let setter = p.setter.as_ref().map(|setter| {
let field = p.field.as_ref().expect("a setter belongs to a field");
let assign = match p.kind {
PropertyKind::Binding(_) => quote!(self.#field = Some(value);),
PropertyKind::Stored(_) => quote!(self.#field = value;),
};
quote!(pub fn #setter(&mut self, value: #ty) { #assign })
});
quote!(#getter #setter)
});
output.extend(quote! {
/// The callbacks the component declares, which the application implements.
pub trait #callbacks_trait {
#(#trait_methods)*
}
#struct_decl
impl #name {
pub fn new(size: slint_sc::Size) -> Self {
#new_bodyView on GitHub (pinned to bb937076de)