denoland/deno · error · syn::Error

first field must be the base type for cppgc inheritance

Error message

first field must be the base type for cppgc inheritance

What it means

After finding the first field, the CppgcInherits derive verifies its type is exactly the base named in #[cppgc_inherits_from(Base)] (types_equal check) because the generated offset/layout code assumes field 0 IS the base subobject. A first field of any other type — or the right base placed second — fails with this compile error pointing at the field's type span.

Source

Thrown at libs/ops/cppgc.rs:70

    .predicates
    .push(parse_quote!(__TransitiveBase: deno_core::cppgc::Base));

  ensure_repr_c(&attrs, ident.span())?;

  let first_field = first_field(&data).ok_or_else(|| {
    Error::new(
      ident.span(),
      "cppgc inheritance requires at least one field",
    )
  })?;

  let (field_path, field_ty_span) = match &first_field.field {
    FieldRef::Named(ident, ty_span) => (quote!(#ident), *ty_span),
    FieldRef::Unnamed(idx, ty_span) => (quote!(#idx), *ty_span),
  };

  if !types_equal(&first_field.ty, &base) {
    return Err(Error::new(
      field_ty_span,
      "first field must be the base type for cppgc inheritance",
    ));
  }

  let (transitive_impl_generics, _, transitive_where_clause) =
    impl_generics.split_for_impl();
  let (base_impl_generics, base_ty_generics, base_where_clause) =
    generics.split_for_impl();

  let offset_assert = quote! {
    const _: () = {
      const OFFSET: usize = ::core::mem::offset_of!(#ident #base_ty_generics, #field_path);
      assert!(OFFSET == 0, "base field must be at offset 0");
    };
  };
  let size_align_assert = quote! {
    const _: () = {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Move the base to be the literal first field: struct Rectangle { base: Shape, width: GcCell<f64> }.
  2. Make the first field's type token-for-token the same type you passed to #[cppgc_inherits_from(...)] — no aliases, references, or Boxes.
  3. If you changed the base type, update #[cppgc_inherits_from(...)] and #[op2(inherit = ...)] to match.

Example fix

// before
#[derive(CppgcInherits)]
#[cppgc_inherits_from(Shape)]
#[repr(C)]
pub struct Rectangle {
  width: GcCell<f64>, // error: first field must be the base type for cppgc inheritance
  base: Shape,
}

// after
#[derive(CppgcInherits)]
#[cppgc_inherits_from(Shape)]
#[repr(C)]
pub struct Rectangle {
  base: Shape, // first field, exact base type
  width: GcCell<f64>,
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: struct Rectangle { width: GcCell<f64>, base: Shape } (base not first), or struct Rectangle { shape: Shape, ... } where the field is named differently than the attribute's type — the check is on the field's TYPE, so any first field whose type is not the exact #[cppgc_inherits_from(...)] argument triggers it.

Common situations: Reordering fields (Rust allows any order; the derive does not); renaming the base type or the attribute argument so they no longer match textually; using a type alias or wrapper (Box<Shape>) instead of the bare base type.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/b67f5e98ca874e38. Report an issue: GitHub.