Skip to content

Commit 80a2ec4

Browse files
committed
Auto merge of rust-lang#106934 - DrMeepster:offset_of, r=WaffleLapkin
Add offset_of! macro (RFC 3308) Implements rust-lang/rfcs#3308 (tracking issue rust-lang#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 0fd50f3 + 99abe44 commit 80a2ec4

File tree

83 files changed

+1355
-42
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+1355
-42
lines changed

compiler/rustc_ast/src/ast.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,7 @@ impl Expr {
12711271
ExprKind::Continue(..) => ExprPrecedence::Continue,
12721272
ExprKind::Ret(..) => ExprPrecedence::Ret,
12731273
ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm,
1274+
ExprKind::OffsetOf(..) => ExprPrecedence::OffsetOf,
12741275
ExprKind::MacCall(..) => ExprPrecedence::Mac,
12751276
ExprKind::Struct(..) => ExprPrecedence::Struct,
12761277
ExprKind::Repeat(..) => ExprPrecedence::Repeat,
@@ -1469,6 +1470,9 @@ pub enum ExprKind {
14691470
/// Output of the `asm!()` macro.
14701471
InlineAsm(P<InlineAsm>),
14711472

1473+
/// Output of the `offset_of!()` macro.
1474+
OffsetOf(P<Ty>, P<[Ident]>),
1475+
14721476
/// A macro invocation; pre-expansion.
14731477
MacCall(P<MacCall>),
14741478

compiler/rustc_ast/src/mut_visit.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1456,6 +1456,12 @@ pub fn noop_visit_expr<T: MutVisitor>(
14561456
}
14571457
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
14581458
ExprKind::FormatArgs(fmt) => vis.visit_format_args(fmt),
1459+
ExprKind::OffsetOf(container, fields) => {
1460+
vis.visit_ty(container);
1461+
for field in fields.iter_mut() {
1462+
vis.visit_ident(field);
1463+
}
1464+
}
14591465
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
14601466
ExprKind::Struct(se) => {
14611467
let StructExpr { qself, path, fields, rest } = se.deref_mut();

compiler/rustc_ast/src/util/parser.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ pub enum ExprPrecedence {
269269
Index,
270270
Try,
271271
InlineAsm,
272+
OffsetOf,
272273
Mac,
273274
FormatArgs,
274275

@@ -335,7 +336,8 @@ impl ExprPrecedence {
335336
| ExprPrecedence::Try
336337
| ExprPrecedence::InlineAsm
337338
| ExprPrecedence::Mac
338-
| ExprPrecedence::FormatArgs => PREC_POSTFIX,
339+
| ExprPrecedence::FormatArgs
340+
| ExprPrecedence::OffsetOf => PREC_POSTFIX,
339341

340342
// Never need parens
341343
ExprPrecedence::Array

compiler/rustc_ast/src/visit.rs

+6
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,12 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
909909
ExprKind::Paren(subexpression) => visitor.visit_expr(subexpression),
910910
ExprKind::InlineAsm(asm) => visitor.visit_inline_asm(asm),
911911
ExprKind::FormatArgs(f) => visitor.visit_format_args(f),
912+
ExprKind::OffsetOf(container, fields) => {
913+
visitor.visit_ty(container);
914+
for &field in fields {
915+
visitor.visit_ident(field);
916+
}
917+
}
912918
ExprKind::Yield(optional_expression) => {
913919
walk_list!(visitor, visit_expr, optional_expression);
914920
}

compiler/rustc_ast_lowering/src/expr.rs

+7
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
289289
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
290290
}
291291
ExprKind::FormatArgs(fmt) => self.lower_format_args(e.span, fmt),
292+
ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf(
293+
self.lower_ty(
294+
container,
295+
&mut ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf),
296+
),
297+
self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))),
298+
),
292299
ExprKind::Struct(se) => {
293300
let rest = match &se.rest {
294301
StructRest::Base(e) => Some(self.lower_expr(e)),

compiler/rustc_ast_lowering/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ enum ImplTraitPosition {
283283
FieldTy,
284284
Cast,
285285
ImplSelf,
286+
OffsetOf,
286287
}
287288

288289
impl std::fmt::Display for ImplTraitPosition {
@@ -313,6 +314,7 @@ impl std::fmt::Display for ImplTraitPosition {
313314
ImplTraitPosition::FieldTy => "field types",
314315
ImplTraitPosition::Cast => "cast types",
315316
ImplTraitPosition::ImplSelf => "impl headers",
317+
ImplTraitPosition::OffsetOf => "`offset_of!` params",
316318
};
317319

318320
write!(f, "{name}")

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

+20
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,26 @@ impl<'a> State<'a> {
549549
self.end();
550550
self.pclose();
551551
}
552+
ast::ExprKind::OffsetOf(container, fields) => {
553+
// FIXME: This should have its own syntax, distinct from a macro invocation.
554+
self.word("offset_of!");
555+
self.popen();
556+
self.rbox(0, Inconsistent);
557+
self.print_type(container);
558+
self.word(",");
559+
self.space();
560+
561+
if let Some((&first, rest)) = fields.split_first() {
562+
self.print_ident(first);
563+
564+
for &field in rest {
565+
self.word(".");
566+
self.print_ident(field);
567+
}
568+
}
569+
570+
self.end();
571+
}
552572
ast::ExprKind::MacCall(m) => self.print_mac(m),
553573
ast::ExprKind::Paren(e) => {
554574
self.popen();

compiler/rustc_borrowck/src/type_check/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2306,7 +2306,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
23062306
Rvalue::AddressOf(..)
23072307
| Rvalue::ThreadLocalRef(..)
23082308
| Rvalue::Len(..)
2309-
| Rvalue::Discriminant(..) => {}
2309+
| Rvalue::Discriminant(..)
2310+
| Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {}
23102311
}
23112312
}
23122313

compiler/rustc_builtin_macros/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,6 @@ builtin_macros_format_pos_mismatch = {$n} positional {$n ->
149149
[one] argument
150150
*[more] arguments
151151
} in format string, but {$desc}
152+
builtin_macros_offset_of_expected_field = expected field
153+
154+
builtin_macros_offset_of_expected_two_args = expected 2 arguments

compiler/rustc_builtin_macros/src/assert/context.rs

+1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
301301
| ExprKind::If(_, _, _)
302302
| ExprKind::IncludedBytes(..)
303303
| ExprKind::InlineAsm(_)
304+
| ExprKind::OffsetOf(_, _)
304305
| ExprKind::Let(_, _, _)
305306
| ExprKind::Lit(_)
306307
| ExprKind::Loop(_, _, _)

compiler/rustc_builtin_macros/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod format;
4545
mod format_foreign;
4646
mod global_allocator;
4747
mod log_syntax;
48+
mod offset_of;
4849
mod source_util;
4950
mod test;
5051
mod trace_macros;
@@ -92,6 +93,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
9293
line: source_util::expand_line,
9394
log_syntax: log_syntax::expand_log_syntax,
9495
module_path: source_util::expand_mod,
96+
offset_of: offset_of::expand_offset_of,
9597
option_env: env::expand_option_env,
9698
core_panic: edition_panic::expand_panic,
9799
std_panic: edition_panic::expand_panic,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use rustc_ast as ast;
2+
use rustc_ast::ptr::P;
3+
use rustc_ast::token;
4+
use rustc_ast::tokenstream::TokenStream;
5+
use rustc_errors::PResult;
6+
use rustc_expand::base::{self, *};
7+
use rustc_macros::Diagnostic;
8+
use rustc_parse::parser::Parser;
9+
use rustc_span::{symbol::Ident, Span};
10+
11+
#[derive(Diagnostic)]
12+
#[diag(builtin_macros_offset_of_expected_field)]
13+
struct ExpectedField {
14+
#[primary_span]
15+
span: Span,
16+
}
17+
18+
#[derive(Diagnostic)]
19+
#[diag(builtin_macros_offset_of_expected_two_args)]
20+
struct ExpectedTwoArgs {
21+
#[primary_span]
22+
span: Span,
23+
}
24+
25+
fn parse_field<'a>(cx: &ExtCtxt<'a>, p: &mut Parser<'a>) -> PResult<'a, Ident> {
26+
let token = p.token.uninterpolate();
27+
let field = match token.kind {
28+
token::Ident(name, _) => Ident::new(name, token.span),
29+
token::Literal(token::Lit { kind: token::Integer, symbol, suffix: None }) => {
30+
Ident::new(symbol, token.span)
31+
}
32+
_ => return Err(cx.create_err(ExpectedField { span: p.token.span })),
33+
};
34+
35+
p.bump();
36+
37+
Ok(field)
38+
}
39+
40+
fn parse_args<'a>(
41+
cx: &mut ExtCtxt<'a>,
42+
sp: Span,
43+
tts: TokenStream,
44+
) -> PResult<'a, (P<ast::Ty>, P<[Ident]>)> {
45+
let mut p = cx.new_parser_from_tts(tts);
46+
47+
let container = p.parse_ty()?;
48+
49+
p.expect(&token::Comma)?;
50+
51+
if p.eat(&token::Eof) {
52+
return Err(cx.create_err(ExpectedTwoArgs { span: sp }));
53+
}
54+
55+
let mut fields = Vec::new();
56+
57+
loop {
58+
let field = parse_field(cx, &mut p)?;
59+
fields.push(field);
60+
61+
if p.eat(&token::Dot) {
62+
continue;
63+
}
64+
65+
p.eat(&token::Comma);
66+
67+
if !p.eat(&token::Eof) {
68+
return Err(cx.create_err(ExpectedTwoArgs { span: sp }));
69+
}
70+
71+
break;
72+
}
73+
74+
Ok((container, fields.into()))
75+
}
76+
77+
pub fn expand_offset_of<'cx>(
78+
cx: &'cx mut ExtCtxt<'_>,
79+
sp: Span,
80+
tts: TokenStream,
81+
) -> Box<dyn base::MacResult + 'cx> {
82+
match parse_args(cx, sp, tts) {
83+
Ok((container, fields)) => {
84+
let expr = P(ast::Expr {
85+
id: ast::DUMMY_NODE_ID,
86+
kind: ast::ExprKind::OffsetOf(container, fields),
87+
span: sp,
88+
attrs: ast::AttrVec::new(),
89+
tokens: None,
90+
});
91+
92+
MacEager::expr(expr)
93+
}
94+
Err(mut err) => {
95+
err.emit();
96+
DummyResult::any(sp)
97+
}
98+
}
99+
}

compiler/rustc_codegen_cranelift/src/base.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -781,12 +781,15 @@ fn codegen_stmt<'tcx>(
781781
let operand = operand.load_scalar(fx);
782782
lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
783783
}
784-
Rvalue::NullaryOp(null_op, ty) => {
784+
Rvalue::NullaryOp(ref null_op, ty) => {
785785
assert!(lval.layout().ty.is_sized(fx.tcx, ParamEnv::reveal_all()));
786786
let layout = fx.layout_of(fx.monomorphize(ty));
787787
let val = match null_op {
788788
NullOp::SizeOf => layout.size.bytes(),
789789
NullOp::AlignOf => layout.align.abi.bytes(),
790+
NullOp::OffsetOf(fields) => {
791+
layout.offset_of_subfield(fx, fields.iter().map(|f| f.index())).bytes()
792+
}
790793
};
791794
let val = CValue::const_val(fx, fx.layout_of(fx.tcx.types.usize), val.into());
792795
lval.write_cvalue(fx, val);

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -666,13 +666,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
666666
}
667667
}
668668

669-
mir::Rvalue::NullaryOp(null_op, ty) => {
669+
mir::Rvalue::NullaryOp(ref null_op, ty) => {
670670
let ty = self.monomorphize(ty);
671671
assert!(bx.cx().type_is_sized(ty));
672672
let layout = bx.cx().layout_of(ty);
673673
let val = match null_op {
674674
mir::NullOp::SizeOf => layout.size.bytes(),
675675
mir::NullOp::AlignOf => layout.align.abi.bytes(),
676+
mir::NullOp::OffsetOf(fields) => {
677+
layout.offset_of_subfield(bx.cx(), fields.iter().map(|f| f.index())).bytes()
678+
}
676679
};
677680
let val = bx.cx().const_usize(val);
678681
let tcx = self.cx.tcx();

compiler/rustc_const_eval/src/interpret/step.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -280,20 +280,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
280280
self.write_immediate(*val, &dest)?;
281281
}
282282

283-
NullaryOp(null_op, ty) => {
283+
NullaryOp(ref null_op, ty) => {
284284
let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?;
285285
let layout = self.layout_of(ty)?;
286-
if layout.is_unsized() {
286+
if let mir::NullOp::SizeOf | mir::NullOp::AlignOf = null_op && layout.is_unsized() {
287287
// FIXME: This should be a span_bug (#80742)
288288
self.tcx.sess.delay_span_bug(
289289
self.frame().current_span(),
290-
&format!("Nullary MIR operator called for unsized type {}", ty),
290+
&format!("{null_op:?} MIR operator called for unsized type {ty}"),
291291
);
292292
throw_inval!(SizeOfUnsizedType(ty));
293293
}
294294
let val = match null_op {
295295
mir::NullOp::SizeOf => layout.size.bytes(),
296296
mir::NullOp::AlignOf => layout.align.abi.bytes(),
297+
mir::NullOp::OffsetOf(fields) => {
298+
layout.offset_of_subfield(self, fields.iter().map(|f| f.index())).bytes()
299+
}
297300
};
298301
self.write_scalar(Scalar::from_target_usize(val, self), &dest)?;
299302
}

compiler/rustc_const_eval/src/transform/check_consts/check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
558558

559559
Rvalue::Cast(_, _, _) => {}
560560

561-
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {}
561+
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_), _) => {}
562562
Rvalue::ShallowInitBox(_, _) => {}
563563

564564
Rvalue::UnaryOp(_, operand) => {

compiler/rustc_const_eval/src/transform/promote_consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ impl<'tcx> Validator<'_, 'tcx> {
514514
Rvalue::NullaryOp(op, _) => match op {
515515
NullOp::SizeOf => {}
516516
NullOp::AlignOf => {}
517+
NullOp::OffsetOf(_) => {}
517518
},
518519

519520
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),

0 commit comments

Comments
 (0)