Add Rust-side `const` `assert!`s about offsets of fields.
PiperOrigin-RevId: 516556972
diff --git a/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl b/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
index 6ec5055..5f26709 100644
--- a/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
+++ b/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
@@ -136,12 +136,15 @@
Returns:
LinkingContext for linking in the generated "..._cc_api_impl.rs".
"""
- deps = [DepVariantInfo(
- crate_info = target_crate[CrateInfo],
- dep_info = target_crate[DepInfo],
- cc_info = target_crate[CcInfo],
- build_info = None,
- )]
+ deps = [
+ DepVariantInfo(
+ crate_info = dep[CrateInfo],
+ dep_info = dep[DepInfo],
+ cc_info = dep[CcInfo],
+ build_info = None,
+ )
+ for dep in ctx.attr._rs_deps_for_bindings + [target_crate]
+ ]
dep_variant_info = compile_rust(ctx, ctx.attr, rs_out_file, [], deps)
return dep_variant_info.cc_info.linking_context
@@ -256,6 +259,12 @@
allow_single_file = True,
cfg = "exec",
),
+ "_rs_deps_for_bindings": attr.label_list(
+ doc = "Dependencies needed to build the Rust sources generated by cc_bindings_from_rs.",
+ default = [
+ "@crate_index//:memoffset",
+ ],
+ ),
"_rustfmt": attr.label(
default = "//nowhere/llvm/rust:main_sysroot/bin/rustfmt",
executable = True,
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 55423ee..a186532 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -1101,6 +1101,8 @@
struct Field {
cc_name: TokenStream,
cc_type: CcSnippet,
+ rs_name: TokenStream,
+ is_public: bool,
offset: u64,
}
let ty = tcx.type_of(core.def_id).subst_identity();
@@ -1124,14 +1126,31 @@
all_fields_in_source_order
.enumerate()
.map(|(index, field_def)| -> Result<Field> {
- let cc_name = format_cc_ident(field_def.ident(tcx).as_str())
+ let name = field_def.ident(tcx);
+ let cc_name = format_cc_ident(name.as_str())
.unwrap_or_else(|_err| format_ident!("__field{index}").into_token_stream());
// TODO(lukasza): Upon `format_ty_for_cc` errors we should replace *individual*
// fields with an opaque blob of bytes (rather than failing to format *all*
// fields and replacing all of them with a blob of bytes).
let cc_type = format_ty_for_cc(input, field_def.ty(tcx, substs_ref))?;
+ let rs_name = {
+ let name_starts_with_digit = name
+ .as_str()
+ .chars()
+ .next()
+ .expect("Empty names are unexpected (here and in general)")
+ .is_ascii_digit();
+ if name_starts_with_digit {
+ let index = Literal::usize_unsuffixed(index);
+ quote! { #index }
+ } else {
+ let name = make_rs_ident(name.as_str());
+ quote! { #name }
+ }
+ };
+ let is_public = field_def.vis == ty::Visibility::Public;
let offset = 0; // Offsets will be fixed by FieldsShape::Arbitrary branch below..
- Ok(Field { cc_name, cc_type, offset })
+ Ok(Field { cc_name, cc_type, rs_name, is_public, offset })
})
.collect::<Result<Vec<_>>>()
.map(|mut fields| {
@@ -1156,7 +1175,8 @@
})
};
let adt_cc_name = &core.cc_name;
- let field_assertions = match fields.as_ref() {
+ let adt_rs_name = &core.rs_name;
+ let cc_field_assertions = match fields.as_ref() {
Err(_err) => quote! {},
Ok(fields) => fields
.iter()
@@ -1166,6 +1186,18 @@
})
.collect(),
};
+ let rs_field_assertions = match fields.as_ref() {
+ Err(_err) => quote! {},
+ Ok(fields) => fields
+ .iter()
+ .filter(|Field { is_public, .. }| *is_public)
+ .map(|Field { rs_name, offset, .. }| {
+ let expected_offset = Literal::u64_unsuffixed(*offset);
+ let actual_offset = quote! { memoffset::offset_of!(#adt_rs_name, #rs_name) };
+ quote! { const _: () = assert!(#actual_offset == #expected_offset); }
+ })
+ .collect(),
+ };
let (impl_item_main_apis, impl_item_other_snippets) = tcx
.inherent_impls(core.def_id)
@@ -1244,7 +1276,7 @@
}
};
prereqs.fwd_decls.remove(&local_def_id);
- let assertions_method_decl = if field_assertions.is_empty() {
+ let assertions_method_decl = if cc_field_assertions.is_empty() {
quote! {}
} else {
// We put the assertions in a method so that they can read private member
@@ -1271,12 +1303,12 @@
};
let impl_details = {
let mut cc = {
- let assertions_method_def = if field_assertions.is_empty() {
+ let assertions_method_def = if cc_field_assertions.is_empty() {
quote! {}
} else {
quote! {
inline void #adt_cc_name::__crubit_field_offset_assertions() {
- #field_assertions
+ #cc_field_assertions
}
}
};
@@ -1294,10 +1326,10 @@
};
cc.prereqs.defs.insert(local_def_id);
let rs = {
- let rs_name = &core.rs_name;
quote! {
- const _: () = assert!(::std::mem::size_of::<#rs_name>() == #size);
- const _: () = assert!(::std::mem::align_of::<#rs_name>() == #alignment);
+ const _: () = assert!(::std::mem::size_of::<#adt_rs_name>() == #size);
+ const _: () = assert!(::std::mem::align_of::<#adt_rs_name>() == #alignment);
+ #rs_field_assertions
}
};
MixedSnippet { cc, rs }
@@ -1671,8 +1703,12 @@
assert_rs_matches!(
bindings.rs_body,
quote! {
+ // No point replicating test coverage of
+ // `test_format_item_struct_with_fields`.
const _: () = assert!(::std::mem::size_of::<::rust_out::Point>() == 8);
const _: () = assert!(::std::mem::align_of::<::rust_out::Point>() == 4);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::Point, x) == 0);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::Point, y) == 4);
}
);
});
@@ -3230,6 +3266,8 @@
quote! {
const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 8);
const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, x) == 0);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, y) == 4);
}
);
});
@@ -3240,7 +3278,7 @@
#[test]
fn test_format_item_struct_with_tuple() {
let test_src = r#"
- pub struct TupleStruct(i32, i32);
+ pub struct TupleStruct(pub i32, pub i32);
const _: () = assert!(std::mem::size_of::<TupleStruct>() == 8);
const _: () = assert!(std::mem::align_of::<TupleStruct>() == 4);
"#;
@@ -3294,6 +3332,8 @@
quote! {
const _: () = assert!(::std::mem::size_of::<::rust_out::TupleStruct>() == 8);
const _: () = assert!(::std::mem::align_of::<::rust_out::TupleStruct>() == 4);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::TupleStruct, 0) == 0);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::TupleStruct, 1) == 4);
}
);
});
@@ -3352,6 +3392,12 @@
quote! {
const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 8);
const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field2)
+ == 0);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field1)
+ == 4);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field3)
+ == 6);
}
);
});
@@ -3402,6 +3448,10 @@
quote! {
const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 6);
const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 1);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field1)
+ == 0);
+ const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field2)
+ == 2);
}
);
});