mirror of https://github.com/rust-lang/rust.git
offset_of: allow (unstably) taking the offset of slice tail fields
This commit is contained in:
parent
16e8803579
commit
eb584a23bf
|
@ -832,9 +832,10 @@ fn codegen_stmt<'tcx>(
|
||||||
let val = match null_op {
|
let val = match null_op {
|
||||||
NullOp::SizeOf => layout.size.bytes(),
|
NullOp::SizeOf => layout.size.bytes(),
|
||||||
NullOp::AlignOf => layout.align.abi.bytes(),
|
NullOp::AlignOf => layout.align.abi.bytes(),
|
||||||
NullOp::OffsetOf(fields) => {
|
NullOp::OffsetOf(fields) => fx
|
||||||
layout.offset_of_subfield(fx, fields.iter()).bytes()
|
.tcx
|
||||||
}
|
.offset_of_subfield(ParamEnv::reveal_all(), layout, fields.iter())
|
||||||
|
.bytes(),
|
||||||
NullOp::UbChecks => {
|
NullOp::UbChecks => {
|
||||||
let val = fx.tcx.sess.ub_checks();
|
let val = fx.tcx.sess.ub_checks();
|
||||||
let val = CValue::by_val(
|
let val = CValue::by_val(
|
||||||
|
|
|
@ -680,7 +680,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
bx.cx().const_usize(val)
|
bx.cx().const_usize(val)
|
||||||
}
|
}
|
||||||
mir::NullOp::OffsetOf(fields) => {
|
mir::NullOp::OffsetOf(fields) => {
|
||||||
let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes();
|
let val = bx
|
||||||
|
.tcx()
|
||||||
|
.offset_of_subfield(bx.param_env(), layout, fields.iter())
|
||||||
|
.bytes();
|
||||||
bx.cx().const_usize(val)
|
bx.cx().const_usize(val)
|
||||||
}
|
}
|
||||||
mir::NullOp::UbChecks => {
|
mir::NullOp::UbChecks => {
|
||||||
|
|
|
@ -253,7 +253,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
Scalar::from_target_usize(val, self)
|
Scalar::from_target_usize(val, self)
|
||||||
}
|
}
|
||||||
mir::NullOp::OffsetOf(fields) => {
|
mir::NullOp::OffsetOf(fields) => {
|
||||||
let val = layout.offset_of_subfield(self, fields.iter()).bytes();
|
let val = self
|
||||||
|
.tcx
|
||||||
|
.offset_of_subfield(self.param_env, layout, fields.iter())
|
||||||
|
.bytes();
|
||||||
Scalar::from_target_usize(val, self)
|
Scalar::from_target_usize(val, self)
|
||||||
}
|
}
|
||||||
mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.ub_checks()),
|
mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.ub_checks()),
|
||||||
|
|
|
@ -559,6 +559,8 @@ declare_features! (
|
||||||
(unstable, offset_of_enum, "1.75.0", Some(120141)),
|
(unstable, offset_of_enum, "1.75.0", Some(120141)),
|
||||||
/// Allows using multiple nested field accesses in offset_of!
|
/// Allows using multiple nested field accesses in offset_of!
|
||||||
(unstable, offset_of_nested, "1.77.0", Some(120140)),
|
(unstable, offset_of_nested, "1.77.0", Some(120140)),
|
||||||
|
/// Allows using fields with slice type in offset_of!
|
||||||
|
(unstable, offset_of_slice, "CURRENT_RUSTC_VERSION", Some(126151)),
|
||||||
/// Allows using `#[optimize(X)]`.
|
/// Allows using `#[optimize(X)]`.
|
||||||
(unstable, optimize_attribute, "1.34.0", Some(54882)),
|
(unstable, optimize_attribute, "1.34.0", Some(54882)),
|
||||||
/// Allows postfix match `expr.match { ... }`
|
/// Allows postfix match `expr.match { ... }`
|
||||||
|
|
|
@ -3363,7 +3363,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
let field_ty = self.field_ty(expr.span, field, args);
|
let field_ty = self.field_ty(expr.span, field, args);
|
||||||
|
|
||||||
// FIXME: DSTs with static alignment should be allowed
|
// Enums are anyway always sized. But just to safeguard against future
|
||||||
|
// language extensions, let's double-check.
|
||||||
self.require_type_is_sized(field_ty, expr.span, ObligationCauseCode::Misc);
|
self.require_type_is_sized(field_ty, expr.span, ObligationCauseCode::Misc);
|
||||||
|
|
||||||
if field.vis.is_accessible_from(sub_def_scope, self.tcx) {
|
if field.vis.is_accessible_from(sub_def_scope, self.tcx) {
|
||||||
|
@ -3391,8 +3392,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
{
|
{
|
||||||
let field_ty = self.field_ty(expr.span, field, args);
|
let field_ty = self.field_ty(expr.span, field, args);
|
||||||
|
|
||||||
// FIXME: DSTs with static alignment should be allowed
|
if self.tcx.features().offset_of_slice {
|
||||||
self.require_type_is_sized(field_ty, expr.span, ObligationCauseCode::Misc);
|
self.require_type_has_static_alignment(
|
||||||
|
field_ty,
|
||||||
|
expr.span,
|
||||||
|
ObligationCauseCode::Misc,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.require_type_is_sized(
|
||||||
|
field_ty,
|
||||||
|
expr.span,
|
||||||
|
ObligationCauseCode::Misc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if field.vis.is_accessible_from(def_scope, self.tcx) {
|
if field.vis.is_accessible_from(def_scope, self.tcx) {
|
||||||
self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
|
self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
|
||||||
|
@ -3412,10 +3424,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
if let Ok(index) = field.as_str().parse::<usize>()
|
if let Ok(index) = field.as_str().parse::<usize>()
|
||||||
&& field.name == sym::integer(index)
|
&& field.name == sym::integer(index)
|
||||||
{
|
{
|
||||||
for ty in tys.iter().take(index + 1) {
|
|
||||||
self.require_type_is_sized(ty, expr.span, ObligationCauseCode::Misc);
|
|
||||||
}
|
|
||||||
if let Some(&field_ty) = tys.get(index) {
|
if let Some(&field_ty) = tys.get(index) {
|
||||||
|
if self.tcx.features().offset_of_slice {
|
||||||
|
self.require_type_has_static_alignment(
|
||||||
|
field_ty,
|
||||||
|
expr.span,
|
||||||
|
ObligationCauseCode::Misc,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.require_type_is_sized(
|
||||||
|
field_ty,
|
||||||
|
expr.span,
|
||||||
|
ObligationCauseCode::Misc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
field_indices.push((FIRST_VARIANT, index.into()));
|
field_indices.push((FIRST_VARIANT, index.into()));
|
||||||
current_container = field_ty;
|
current_container = field_ty;
|
||||||
|
|
||||||
|
|
|
@ -386,6 +386,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn require_type_has_static_alignment(
|
||||||
|
&self,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
code: traits::ObligationCauseCode<'tcx>,
|
||||||
|
) {
|
||||||
|
if !ty.references_error() {
|
||||||
|
let tail =
|
||||||
|
self.tcx.struct_tail_with_normalize(ty, |ty| self.normalize(span, ty), || {});
|
||||||
|
// Sized types have static alignment, and so do slices.
|
||||||
|
if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) {
|
||||||
|
// Nothing else is required here.
|
||||||
|
} else {
|
||||||
|
// We can't be sure, let's required full `Sized`.
|
||||||
|
let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
|
||||||
|
self.require_type_meets(ty, span, code, lang_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register_bound(
|
pub fn register_bound(
|
||||||
&self,
|
&self,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
|
|
|
@ -1351,3 +1351,37 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {}
|
impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {}
|
||||||
|
|
||||||
|
impl<'tcx> TyCtxt<'tcx> {
|
||||||
|
pub fn offset_of_subfield<I>(
|
||||||
|
self,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
mut layout: TyAndLayout<'tcx>,
|
||||||
|
indices: I,
|
||||||
|
) -> Size
|
||||||
|
where
|
||||||
|
I: Iterator<Item = (VariantIdx, FieldIdx)>,
|
||||||
|
{
|
||||||
|
let cx = LayoutCx { tcx: self, param_env };
|
||||||
|
let mut offset = Size::ZERO;
|
||||||
|
|
||||||
|
for (variant, field) in indices {
|
||||||
|
layout = layout.for_variant(&cx, variant);
|
||||||
|
let index = field.index();
|
||||||
|
offset += layout.fields.offset(index);
|
||||||
|
layout = layout.field(&cx, index);
|
||||||
|
if !layout.is_sized() {
|
||||||
|
// If it is not sized, then the tail must still have at least a known static alignment.
|
||||||
|
let tail = self.struct_tail_erasing_lifetimes(layout.ty, param_env);
|
||||||
|
if !matches!(tail.kind(), ty::Slice(..)) {
|
||||||
|
bug!(
|
||||||
|
"offset of not-statically-aligned field (type {:?}) cannot be computed statically",
|
||||||
|
layout.ty
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use rustc_middle::bug;
|
||||||
use rustc_middle::mir::interpret::{InterpResult, Scalar};
|
use rustc_middle::mir::interpret::{InterpResult, Scalar};
|
||||||
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_mir_dataflow::value_analysis::{
|
use rustc_mir_dataflow::value_analysis::{
|
||||||
Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
|
Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
|
||||||
|
@ -285,9 +285,11 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
|
||||||
let val = match null_op {
|
let val = match null_op {
|
||||||
NullOp::SizeOf if layout.is_sized() => layout.size.bytes(),
|
NullOp::SizeOf if layout.is_sized() => layout.size.bytes(),
|
||||||
NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(),
|
NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(),
|
||||||
NullOp::OffsetOf(fields) => {
|
NullOp::OffsetOf(fields) => self
|
||||||
layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
|
.ecx
|
||||||
}
|
.tcx
|
||||||
|
.offset_of_subfield(self.ecx.param_env(), layout, fields.iter())
|
||||||
|
.bytes(),
|
||||||
_ => return ValueOrPlace::Value(FlatSet::Top),
|
_ => return ValueOrPlace::Value(FlatSet::Top),
|
||||||
};
|
};
|
||||||
FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx))
|
FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx))
|
||||||
|
|
|
@ -95,7 +95,7 @@ use rustc_middle::bug;
|
||||||
use rustc_middle::mir::interpret::GlobalAlloc;
|
use rustc_middle::mir::interpret::GlobalAlloc;
|
||||||
use rustc_middle::mir::visit::*;
|
use rustc_middle::mir::visit::*;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
@ -484,9 +484,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
||||||
let val = match null_op {
|
let val = match null_op {
|
||||||
NullOp::SizeOf => layout.size.bytes(),
|
NullOp::SizeOf => layout.size.bytes(),
|
||||||
NullOp::AlignOf => layout.align.abi.bytes(),
|
NullOp::AlignOf => layout.align.abi.bytes(),
|
||||||
NullOp::OffsetOf(fields) => {
|
NullOp::OffsetOf(fields) => self
|
||||||
layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
|
.ecx
|
||||||
}
|
.tcx
|
||||||
|
.offset_of_subfield(self.ecx.param_env(), layout, fields.iter())
|
||||||
|
.bytes(),
|
||||||
NullOp::UbChecks => return None,
|
NullOp::UbChecks => return None,
|
||||||
};
|
};
|
||||||
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
|
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
|
||||||
|
|
|
@ -625,9 +625,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
let val = match null_op {
|
let val = match null_op {
|
||||||
NullOp::SizeOf => op_layout.size.bytes(),
|
NullOp::SizeOf => op_layout.size.bytes(),
|
||||||
NullOp::AlignOf => op_layout.align.abi.bytes(),
|
NullOp::AlignOf => op_layout.align.abi.bytes(),
|
||||||
NullOp::OffsetOf(fields) => {
|
NullOp::OffsetOf(fields) => self
|
||||||
op_layout.offset_of_subfield(self, fields.iter()).bytes()
|
.tcx
|
||||||
}
|
.offset_of_subfield(self.param_env, op_layout, fields.iter())
|
||||||
|
.bytes(),
|
||||||
NullOp::UbChecks => return None,
|
NullOp::UbChecks => return None,
|
||||||
};
|
};
|
||||||
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
|
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
|
||||||
|
|
|
@ -1304,6 +1304,7 @@ symbols! {
|
||||||
offset_of,
|
offset_of,
|
||||||
offset_of_enum,
|
offset_of_enum,
|
||||||
offset_of_nested,
|
offset_of_nested,
|
||||||
|
offset_of_slice,
|
||||||
ok_or_else,
|
ok_or_else,
|
||||||
omit_gdb_pretty_printer_section,
|
omit_gdb_pretty_printer_section,
|
||||||
on,
|
on,
|
||||||
|
|
|
@ -256,29 +256,6 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
||||||
Ty::is_transparent(self)
|
Ty::is_transparent(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset_of_subfield<C, I>(self, cx: &C, indices: I) -> Size
|
|
||||||
where
|
|
||||||
Ty: TyAbiInterface<'a, C>,
|
|
||||||
I: Iterator<Item = (VariantIdx, FieldIdx)>,
|
|
||||||
{
|
|
||||||
let mut layout = self;
|
|
||||||
let mut offset = Size::ZERO;
|
|
||||||
|
|
||||||
for (variant, field) in indices {
|
|
||||||
layout = layout.for_variant(cx, variant);
|
|
||||||
let index = field.index();
|
|
||||||
offset += layout.fields.offset(index);
|
|
||||||
layout = layout.field(cx, index);
|
|
||||||
assert!(
|
|
||||||
layout.is_sized(),
|
|
||||||
"offset of unsized field (type {:?}) cannot be computed statically",
|
|
||||||
layout.ty
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
offset
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the one field that is not a 1-ZST.
|
/// Finds the one field that is not a 1-ZST.
|
||||||
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
|
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
|
||||||
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)>
|
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)>
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
use std::mem::offset_of;
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
a: u8,
|
||||||
|
b: (u8, u8),
|
||||||
|
c: [i32],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct T {
|
||||||
|
x: i32,
|
||||||
|
y: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tup = (i32, [i32]);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
offset_of!(S, c); //~ ERROR size for values of type `[i32]` cannot be known at compilation time
|
||||||
|
}
|
||||||
|
|
||||||
|
fn other() {
|
||||||
|
offset_of!(T, y); //~ ERROR size for values of type `[i32]` cannot be known at compilation time
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tuple() {
|
||||||
|
offset_of!(Tup, 1); //~ ERROR size for values of type `[i32]` cannot be known at compilation time
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
|
||||||
|
--> $DIR/feature-gate-offset-of-slice.rs:17:5
|
||||||
|
|
|
||||||
|
LL | offset_of!(S, c);
|
||||||
|
| ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
||||||
|
= help: the trait `Sized` is not implemented for `[i32]`
|
||||||
|
= note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
|
||||||
|
--> $DIR/feature-gate-offset-of-slice.rs:21:5
|
||||||
|
|
|
||||||
|
LL | offset_of!(T, y);
|
||||||
|
| ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
||||||
|
= help: within `S`, the trait `Sized` is not implemented for `[i32]`, which is required by `S: Sized`
|
||||||
|
note: required because it appears within the type `S`
|
||||||
|
--> $DIR/feature-gate-offset-of-slice.rs:3:8
|
||||||
|
|
|
||||||
|
LL | struct S {
|
||||||
|
| ^
|
||||||
|
= note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
|
||||||
|
--> $DIR/feature-gate-offset-of-slice.rs:25:5
|
||||||
|
|
|
||||||
|
LL | offset_of!(Tup, 1);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
||||||
|
= help: the trait `Sized` is not implemented for `[i32]`
|
||||||
|
= note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -49,3 +49,7 @@ fn delta() {
|
||||||
fn generic_with_maybe_sized<T: ?Sized>() -> usize {
|
fn generic_with_maybe_sized<T: ?Sized>() -> usize {
|
||||||
offset_of!(Delta<T>, z) //~ ERROR the size for values of type
|
offset_of!(Delta<T>, z) //~ ERROR the size for values of type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn illformed_tuple() {
|
||||||
|
offset_of!(([u8], u8), 1); //~ ERROR the size for values of type
|
||||||
|
}
|
||||||
|
|
|
@ -81,6 +81,15 @@ LL - fn generic_with_maybe_sized<T: ?Sized>() -> usize {
|
||||||
LL + fn generic_with_maybe_sized<T>() -> usize {
|
LL + fn generic_with_maybe_sized<T>() -> usize {
|
||||||
|
|
|
|
||||||
|
|
||||||
error: aborting due to 8 previous errors
|
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
|
||||||
|
--> $DIR/offset-of-dst-field.rs:54:16
|
||||||
|
|
|
||||||
|
LL | offset_of!(([u8], u8), 1);
|
||||||
|
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
||||||
|
= help: the trait `Sized` is not implemented for `[u8]`
|
||||||
|
= note: only the last element of a tuple may have a dynamically sized type
|
||||||
|
|
||||||
|
error: aborting due to 9 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0277`.
|
For more information about this error, try `rustc --explain E0277`.
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
//@run-pass
|
||||||
|
#![feature(offset_of_slice, offset_of_nested)]
|
||||||
|
|
||||||
|
use std::mem::offset_of;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct S {
|
||||||
|
a: u8,
|
||||||
|
b: (u8, u8),
|
||||||
|
c: [i32],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct T {
|
||||||
|
x: i8,
|
||||||
|
y: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tup = (i16, [i32]);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(offset_of!(S, c), 4);
|
||||||
|
assert_eq!(offset_of!(T, y), 4);
|
||||||
|
assert_eq!(offset_of!(T, y.c), 8);
|
||||||
|
assert_eq!(offset_of!(Tup, 1), 4);
|
||||||
|
}
|
Loading…
Reference in New Issue