rust-lang/rust · error

cannot derive on union

Error message

cannot derive on union

What it means

`visitable_derive` (in rustc_macros/src/visitable.rs) generates `Walkable`/`MutWalkable` impls for the rustc_ast IR traversal by visiting each field ref/ref-mut. Unions have no per-field access pattern the generator can express, so the guard at visitable.rs:5 panics on `syn::Data::Union`.

Source

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

use quote::quote;
use synstructure::BindingInfo;

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

    let has_attr = |bind: &BindingInfo<'_>, name| {
        let mut found = false;
        bind.ast().attrs.iter().for_each(|attr| {
            if !attr.path().is_ident("visitable") {
                return;
            }
            let _ = attr.parse_nested_meta(|nested| {
                if nested.path.is_ident(name) {
                    found = true;
                }
                Ok(())
            });
        });
        found
    };

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Remove `#[derive(Visitable)]` from the union.
  2. Restructure the IR node as a struct or enum so the derive can enumerate fields/variants.
  3. Manually implement `Walkable`/`MutWalkable` to visit only the field indicated by an external discriminant.

Example fix

// before
#[derive(Visitable)]
union Node {
    expr: Expr,
    item: Item,
}

// after
#[derive(Visitable)]
enum Node {
    Expr(Expr),
    Item(Item),
}
Defensive patterns

Strategy: validation

Validate before calling

// `visitable.rs` derive — same union rejection as the other TypeFoldable /
// TypeVisitable family members.
fn reject_union_for_derive(item: &syn::Item) -> Result<(), String> {
    if let syn::Item::Union(u) = item {
        return Err(format!(
            "union `{}` cannot derive visitable; manual impl required",
            u.ident));
    }
    Ok(())
}

Type guard

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

Prevention

When it happens

Trigger: Annotating a `union` with `#[derive(Visitable)]` (the AST `Walkable`/`MutWalkable` derive used by rustc_ast/rustc_parse visitors); macro expansion matches the union arm and panics.

Common situations: Adding AST-walking derives to a union-shaped IR node during parser/AST refactoring. Copying a derive block from a struct AST node onto a union. Introducing a union for FFI inside AST code and inheriting nearby derives.

Related errors


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