Skip to content

Commit ee53692

Browse files
committed
Auto merge of #106934 - DrMeepster:offset_of, r=WaffleLapkin
Add offset_of! macro (RFC 3308) Implements rust-lang/rfcs#3308 (tracking issue #106655) by adding the built in macro `core::mem::offset_of`. Two of the future possibilities are also implemented: * Nested field accesses (without array indexing) * DST support (for `Sized` fields) I wrote this a few months ago, before the RFC merged. Now that it's merged, I decided to rebase and finish it. cc `@thomcc` (RFC author)
2 parents 5262752 + 3acabc8 commit ee53692

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-0
lines changed

core/src/mem/mod.rs

+42
Original file line numberDiff line numberDiff line change
@@ -1279,3 +1279,45 @@ pub trait SizedTypeProperties: Sized {
12791279
#[doc(hidden)]
12801280
#[unstable(feature = "sized_type_properties", issue = "none")]
12811281
impl<T> SizedTypeProperties for T {}
1282+
1283+
/// Expands to the offset in bytes of a field from the beginning of the given type.
1284+
///
1285+
/// Only structs, unions and tuples are supported.
1286+
///
1287+
/// Nested field accesses may be used, but not array indexes like in `C`'s `offsetof`.
1288+
///
1289+
/// Note that the output of this macro is not stable, except for `#[repr(C)]` types.
1290+
///
1291+
/// # Examples
1292+
///
1293+
/// ```
1294+
/// #![feature(offset_of)]
1295+
///
1296+
/// use std::mem;
1297+
/// #[repr(C)]
1298+
/// struct FieldStruct {
1299+
/// first: u8,
1300+
/// second: u16,
1301+
/// third: u8
1302+
/// }
1303+
///
1304+
/// assert_eq!(mem::offset_of!(FieldStruct, first), 0);
1305+
/// assert_eq!(mem::offset_of!(FieldStruct, second), 2);
1306+
/// assert_eq!(mem::offset_of!(FieldStruct, third), 4);
1307+
///
1308+
/// #[repr(C)]
1309+
/// struct NestedA {
1310+
/// b: NestedB
1311+
/// }
1312+
///
1313+
/// #[repr(C)]
1314+
/// struct NestedB(u8);
1315+
///
1316+
/// assert_eq!(mem::offset_of!(NestedA, b.0), 0);
1317+
/// ```
1318+
#[unstable(feature = "offset_of", issue = "106655")]
1319+
#[rustc_builtin_macro]
1320+
#[cfg(not(bootstrap))]
1321+
pub macro offset_of($Container:ty, $($fields:tt).+ $(,)?) {
1322+
/* compiler built-in */
1323+
}

core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
#![feature(utf8_chunks)]
110110
#![feature(is_ascii_octdigit)]
111111
#![feature(get_many_mut)]
112+
#![cfg_attr(not(bootstrap), feature(offset_of))]
112113
#![deny(unsafe_op_in_unsafe_fn)]
113114
#![deny(fuzzy_provenance_casts)]
114115

core/tests/mem.rs

+190
Original file line numberDiff line numberDiff line change
@@ -364,3 +364,193 @@ fn const_maybe_uninit() {
364364

365365
assert_eq!(FIELD_BY_FIELD, Foo { x: 1, y: 2 });
366366
}
367+
368+
#[test]
369+
#[cfg(not(bootstrap))]
370+
fn offset_of() {
371+
#[repr(C)]
372+
struct Foo {
373+
x: u8,
374+
y: u16,
375+
z: Bar,
376+
}
377+
378+
#[repr(C)]
379+
struct Bar(u8, u8);
380+
381+
assert_eq!(offset_of!(Foo, x), 0);
382+
assert_eq!(offset_of!(Foo, y), 2);
383+
assert_eq!(offset_of!(Foo, z.0), 4);
384+
assert_eq!(offset_of!(Foo, z.1), 5);
385+
386+
// Layout of tuples is unstable
387+
assert!(offset_of!((u8, u16), 0) <= size_of::<(u8, u16)>() - 1);
388+
assert!(offset_of!((u8, u16), 1) <= size_of::<(u8, u16)>() - 2);
389+
}
390+
391+
#[test]
392+
#[cfg(not(bootstrap))]
393+
fn offset_of_union() {
394+
#[repr(C)]
395+
union Foo {
396+
x: u8,
397+
y: u16,
398+
z: Bar,
399+
}
400+
401+
#[repr(C)]
402+
#[derive(Copy, Clone)]
403+
struct Bar(u8, u8);
404+
405+
assert_eq!(offset_of!(Foo, x), 0);
406+
assert_eq!(offset_of!(Foo, y), 0);
407+
assert_eq!(offset_of!(Foo, z.0), 0);
408+
assert_eq!(offset_of!(Foo, z.1), 1);
409+
}
410+
411+
#[test]
412+
#[cfg(not(bootstrap))]
413+
fn offset_of_dst() {
414+
#[repr(C)]
415+
struct Alpha {
416+
x: u8,
417+
y: u16,
418+
z: [u8],
419+
}
420+
421+
trait Trait {}
422+
423+
#[repr(C)]
424+
struct Beta {
425+
x: u8,
426+
y: u16,
427+
z: dyn Trait,
428+
}
429+
430+
extern "C" {
431+
type Extern;
432+
}
433+
434+
#[repr(C)]
435+
struct Gamma {
436+
x: u8,
437+
y: u16,
438+
z: Extern,
439+
}
440+
441+
assert_eq!(offset_of!(Alpha, x), 0);
442+
assert_eq!(offset_of!(Alpha, y), 2);
443+
444+
assert_eq!(offset_of!(Beta, x), 0);
445+
assert_eq!(offset_of!(Beta, y), 2);
446+
447+
assert_eq!(offset_of!(Gamma, x), 0);
448+
assert_eq!(offset_of!(Gamma, y), 2);
449+
}
450+
451+
#[test]
452+
#[cfg(not(bootstrap))]
453+
fn offset_of_packed() {
454+
#[repr(C, packed)]
455+
struct Foo {
456+
x: u8,
457+
y: u16,
458+
}
459+
460+
assert_eq!(offset_of!(Foo, x), 0);
461+
assert_eq!(offset_of!(Foo, y), 1);
462+
}
463+
464+
#[test]
465+
#[cfg(not(bootstrap))]
466+
fn offset_of_projection() {
467+
#[repr(C)]
468+
struct Foo {
469+
x: u8,
470+
y: u16,
471+
}
472+
473+
trait Projector {
474+
type Type;
475+
}
476+
477+
impl Projector for () {
478+
type Type = Foo;
479+
}
480+
481+
assert_eq!(offset_of!(<() as Projector>::Type, x), 0);
482+
assert_eq!(offset_of!(<() as Projector>::Type, y), 2);
483+
}
484+
485+
#[test]
486+
#[cfg(not(bootstrap))]
487+
fn offset_of_alias() {
488+
#[repr(C)]
489+
struct Foo {
490+
x: u8,
491+
y: u16,
492+
}
493+
494+
type Bar = Foo;
495+
496+
assert_eq!(offset_of!(Bar, x), 0);
497+
assert_eq!(offset_of!(Bar, y), 2);
498+
}
499+
500+
#[test]
501+
#[cfg(not(bootstrap))]
502+
fn const_offset_of() {
503+
#[repr(C)]
504+
struct Foo {
505+
x: u8,
506+
y: u16,
507+
}
508+
509+
const X_OFFSET: usize = offset_of!(Foo, x);
510+
const Y_OFFSET: usize = offset_of!(Foo, y);
511+
512+
assert_eq!(X_OFFSET, 0);
513+
assert_eq!(Y_OFFSET, 2);
514+
}
515+
516+
#[test]
517+
#[cfg(not(bootstrap))]
518+
fn offset_of_without_const_promotion() {
519+
#[repr(C)]
520+
struct Foo<SuppressConstPromotion> {
521+
x: u8,
522+
y: u16,
523+
_scp: SuppressConstPromotion,
524+
}
525+
526+
// Normally, offset_of is always const promoted.
527+
// The generic parameter prevents this from happening.
528+
// This is needed to test the codegen impl of offset_of
529+
fn inner<SuppressConstPromotion>() {
530+
assert_eq!(offset_of!(Foo<SuppressConstPromotion>, x), 0);
531+
assert_eq!(offset_of!(Foo<SuppressConstPromotion>, y), 2);
532+
}
533+
534+
inner::<()>();
535+
}
536+
537+
#[test]
538+
#[cfg(not(bootstrap))]
539+
fn offset_of_addr() {
540+
#[repr(C)]
541+
struct Foo {
542+
x: u8,
543+
y: u16,
544+
z: Bar,
545+
}
546+
547+
#[repr(C)]
548+
struct Bar(u8, u8);
549+
550+
let base = Foo { x: 0, y: 0, z: Bar(0, 0) };
551+
552+
assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, x), ptr::addr_of!(base.x).addr());
553+
assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, y), ptr::addr_of!(base.y).addr());
554+
assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.0), ptr::addr_of!(base.z.0).addr());
555+
assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.1), ptr::addr_of!(base.z.1).addr());
556+
}

0 commit comments

Comments
 (0)