diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index f80411d80..01b764229 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -222,6 +222,7 @@ pub struct Container { identifier: Identifier, has_flatten: bool, serde_path: Option, + is_packed: bool, } /// Styles of representing an enum. @@ -592,6 +593,27 @@ impl Container { } } + use proc_macro2::Delimiter; + let is_packed = item.attrs.iter().any(|attr| { + match attr.style { + syn::AttrStyle::Outer => attr + .path + .get_ident() + .map_or(false, |ident| *ident == "repr") + && syn::parse2::(attr.tokens.clone()) + .ok() + .filter(|g| g.delimiter() == Delimiter::Parenthesis) + .map(|g| g.stream().to_string()) + .map_or(false, |repr| { + let repr = repr.trim(); + repr == "packed" + || repr.starts_with("packed(") + || repr.starts_with("packed ") + }), + _ => false + } + }); + Container { name: Name::from_attrs(unraw(&item.ident), ser_name, de_name, None), transparent: transparent.get(), @@ -611,6 +633,7 @@ impl Container { identifier: decide_identifier(cx, item, field_identifier, variant_identifier), has_flatten: false, serde_path: serde_path.get(), + is_packed, } } @@ -662,6 +685,10 @@ impl Container { self.remote.as_ref() } + pub fn is_packed(&self) -> bool { + self.is_packed + } + pub fn identifier(&self) -> Identifier { self.identifier } diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 37b9a8af7..6163026b6 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -87,6 +87,9 @@ struct Parameters { /// Type has a `serde(remote = "...")` attribute. is_remote: bool, + + /// Type has a repr(packed) attribute. + is_packed: bool, } impl Parameters { @@ -103,6 +106,8 @@ impl Parameters { None => cont.ident.clone().into(), }; + let is_packed = cont.attrs.is_packed(); + let generics = build_generics(cont); Parameters { @@ -110,6 +115,7 @@ impl Parameters { this, generics, is_remote, + is_packed, } } @@ -1238,9 +1244,19 @@ fn mut_if(is_mut: bool) -> Option { fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStream { let self_var = ¶ms.self_var; match (params.is_remote, field.attrs.getter()) { - (false, None) => quote!(&#self_var.#member), + (false, None) => { + if params.is_packed { + quote!(&{let copy = #self_var.#member; copy }) + } else { + quote!(&#self_var.#member) + } + } (true, None) => { - let inner = quote!(&#self_var.#member); + let inner = if params.is_packed { + quote!(&{let copy = #self_var.#member; copy }) + } else { + quote!(&#self_var.#member) + }; let ty = field.ty; quote!(_serde::private::ser::constrain::<#ty>(#inner)) }