{"id":"361b1133e656d1f7","repo":"rust-lang/rust","slug":"cannot-derive-on-union-361b11","errorCode":null,"errorMessage":"cannot derive on union","messagePattern":"cannot derive on union","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_macros/src/type_visitable.rs","lineNumber":8,"sourceCode":"use quote::quote;\nuse syn::parse_quote;\n\npub(super) fn type_visitable_derive(\n    mut s: synstructure::Structure<'_>,\n) -> proc_macro2::TokenStream {\n    if let syn::Data::Union(_) = s.ast().data {\n        panic!(\"cannot derive on union\")\n    }\n\n    // ignore fields with #[type_visitable(ignore)]\n    s.filter(|bi| {\n        let mut ignored = false;\n\n        bi.ast().attrs.iter().for_each(|attr| {\n            if !attr.path().is_ident(\"type_visitable\") {\n                return;\n            }\n            let _ = attr.parse_nested_meta(|nested| {\n                if nested.path.is_ident(\"ignore\") {\n                    ignored = true;\n                }\n                Ok(())\n            });\n        });\n","sourceCodeStart":1,"sourceCodeEnd":26,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_macros/src/type_visitable.rs#L1-L26","documentation":"`type_visitable_derive` generates a `TypeVisitable::visit_with` impl that walks every field with a visitor. Visiting a union's overlapping fields is unsound and order-dependent, so the guard at type_visitable.rs:7 panics on `syn::Data::Union` before emitting any visiting code.","triggerScenarios":"Annotating a `union` with `#[derive(TypeVisitable)]` (the rustc_middle trait used by visitors/traversal over types); the derive reaches the union check and panics during expansion.","commonSituations":"Applying the standard `#[derive(TypeVisitable, TypeFoldable)]` pair (almost always present together on rustc type-system types) to a union. Refactoring a struct into a union without pruning derives. Copying derives from a sibling type definition.","solutions":["Remove `#[derive(TypeVisitable)]` from the union (and the matching `TypeFoldable` derive, since the two are co-dependent).","Re-model the data as an `enum` and keep the derive on the enum.","Hand-write `TypeVisitable` to visit only a single explicitly-tagged active field rather than the raw union storage."],"exampleFix":"// before\n#[derive(TypeVisitable)]\nunion U {\n    a: Ty<'tcx>,\n    b: Region<'tcx>,\n}\n\n// after\n#[derive(TypeVisitable)]\nenum U {\n    A(Ty<'tcx>),\n    B(Region<'tcx>),\n}","handlingStrategy":"validation","validationCode":"// `type_visitable.rs` derive also rejects unions, for the same reason as\n// [214]: visiting a union's inactive field is UB.\nfn assert_not_union(item: &syn::Item) -> Result<(), String> {\n    if let syn::Item::Union(u) = item {\n        return Err(format!(\n            \"union `{}` cannot derive TypeVisitable; implement it manually\",\n            u.ident));\n    }\n    Ok(())\n}","typeGuard":"fn type_visitable_derivable(item: &syn::Item) -> bool {\n    matches!(item, syn::Item::Struct(_) | syn::Item::Enum(_))\n}","tryCatchPattern":null,"preventionTips":["TypeVisitable and TypeFoldable usually go together; check both before annotating a union.","Manual impls for union-backed types must only touch the active field, documented with a SAFETY note."],"tags":["rustc-macros","derive","type-visitable","union"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}