rust-lang/rust · error

cannot derive on union

Error message

cannot derive on union

What it means

`type_foldable_derive` generates `TypeFoldable`/`try_fold_with`/`fold_with` impls by reconstructing each variant with folded fields. A union has overlapping fields and no constructor that selects one, so there is no way to produce a folded copy; the guard at type_foldable.rs:5 panics on `syn::Data::Union` before generating any folding code.

Source

Thrown at compiler/rustc_macros/src/type_foldable.rs:6

use quote::{ToTokens, quote};
use syn::parse_quote;

pub(super) fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
    if let syn::Data::Union(_) = s.ast().data {
        panic!("cannot derive on union")
    }

    if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
        s.add_impl_generic(parse_quote! { 'tcx });
    }

    s.add_bounds(synstructure::AddBounds::Generics);
    s.bind_with(|_| synstructure::BindStyle::Move);
    let try_body_fold = s.each_variant(|vi| {
        let bindings = vi.bindings();
        vi.construct(|_, index| {
            let bind = &bindings[index];

            // retain value of fields with #[type_foldable(identity)]
            if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
                bind.to_token_stream()
            } else {
                quote! {

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Remove `#[derive(TypeFoldable)]` from the union.
  2. Convert the union into an `enum` so each alternative is a constructible variant, then keep the derive.
  3. If folding is genuinely required, implement `TypeFoldable` manually by tracking the active field via an external tag and folding only that field.

Example fix

// before
#[derive(TypeFoldable)]
union U {
    a: Ty<'tcx>,
    b: Region<'tcx>,
}

// after
#[derive(TypeFoldable)]
enum U {
    A(Ty<'tcx>),
    B(Region<'tcx>),
}
Defensive patterns

Strategy: validation

Validate before calling

// `type_foldable.rs` derive refuses unions. TypeFoldable needs to traverse
// every field, which is undefined for a union's inactive members.
fn assert_derive_safe(item: &syn::Item) -> Result<(), String> {
    if let syn::Item::Union(u) = item {
        return Err(format!(
            "union `{}` cannot derive TypeFoldable; provide a manual impl \
             that folds the active field", u.ident));
    }
    Ok(())
}

Type guard

fn type_foldable_derivable(item: &syn::Item) -> bool {
    matches!(item, syn::Item::Struct(_) | syn::Item::Enum(_))
}

Prevention

When it happens

Trigger: Annotating a `union` with `#[derive(TypeFoldable)]` (the rustc_middle trait used to rewrite/fold types and regions during queries); macro expansion matches the union arm and panics.

Common situations: Adding `#[derive(TypeFoldable, TypeVisitable)]` boilerplate (common on rustc_middle `Ty`/`Region` types) to a union by accident during a refactor. Changing a struct used inside the type system into a union. Bulk-applying a derive template to many types including a union.

Related errors


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/a887ada273852e3e.json. Report an issue: GitHub.