offset_of

This commit is contained in:
DrMeepster 2022-09-11 00:37:49 -07:00
parent b92a41c676
commit 511e457c4b
71 changed files with 841 additions and 38 deletions

View File

@ -1271,6 +1271,7 @@ impl Expr {
ExprKind::Continue(..) => ExprPrecedence::Continue,
ExprKind::Ret(..) => ExprPrecedence::Ret,
ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm,
ExprKind::OffsetOf(..) => ExprPrecedence::OffsetOf,
ExprKind::MacCall(..) => ExprPrecedence::Mac,
ExprKind::Struct(..) => ExprPrecedence::Struct,
ExprKind::Repeat(..) => ExprPrecedence::Repeat,
@ -1469,6 +1470,9 @@ pub enum ExprKind {
/// Output of the `asm!()` macro.
InlineAsm(P<InlineAsm>),
/// Output of the `offset_of!()` macro.
OffsetOf(P<Ty>, Vec<Ident>),
/// A macro invocation; pre-expansion.
MacCall(P<MacCall>),

View File

@ -1456,6 +1456,12 @@ pub fn noop_visit_expr<T: MutVisitor>(
}
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
ExprKind::FormatArgs(fmt) => vis.visit_format_args(fmt),
ExprKind::OffsetOf(container, fields) => {
vis.visit_ty(container);
for field in fields {
vis.visit_ident(field);
}
}
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
ExprKind::Struct(se) => {
let StructExpr { qself, path, fields, rest } = se.deref_mut();

View File

@ -269,6 +269,7 @@ pub enum ExprPrecedence {
Index,
Try,
InlineAsm,
OffsetOf,
Mac,
FormatArgs,
@ -335,7 +336,8 @@ impl ExprPrecedence {
| ExprPrecedence::Try
| ExprPrecedence::InlineAsm
| ExprPrecedence::Mac
| ExprPrecedence::FormatArgs => PREC_POSTFIX,
| ExprPrecedence::FormatArgs
| ExprPrecedence::OffsetOf => PREC_POSTFIX,
// Never need parens
ExprPrecedence::Array

View File

@ -909,6 +909,12 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::Paren(subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(asm) => visitor.visit_inline_asm(asm),
ExprKind::FormatArgs(f) => visitor.visit_format_args(f),
ExprKind::OffsetOf(container, fields) => {
visitor.visit_ty(container);
for &field in fields {
visitor.visit_ident(field);
}
}
ExprKind::Yield(optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}

View File

@ -289,6 +289,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
}
ExprKind::FormatArgs(fmt) => self.lower_format_args(e.span, fmt),
ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf(
self.lower_ty(
container,
&mut ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf),
),
self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))),
),
ExprKind::Struct(se) => {
let rest = match &se.rest {
StructRest::Base(e) => Some(self.lower_expr(e)),

View File

@ -283,6 +283,7 @@ enum ImplTraitPosition {
FieldTy,
Cast,
ImplSelf,
OffsetOf,
}
impl std::fmt::Display for ImplTraitPosition {
@ -313,6 +314,7 @@ impl std::fmt::Display for ImplTraitPosition {
ImplTraitPosition::FieldTy => "field types",
ImplTraitPosition::Cast => "cast types",
ImplTraitPosition::ImplSelf => "impl headers",
ImplTraitPosition::OffsetOf => "`offset_of!` params",
};
write!(f, "{name}")

View File

@ -549,6 +549,27 @@ impl<'a> State<'a> {
self.end();
self.pclose();
}
ast::ExprKind::OffsetOf(container, fields) => {
// FIXME: This should have its own syntax, distinct from a macro invocation.
self.word("offset_of!");
self.popen();
self.rbox(0, Inconsistent);
self.print_type(container);
self.word(",");
self.space();
let (&first, rest) =
fields.split_first().expect("offset_of! should have at least 1 field");
self.print_ident(first);
for &field in rest {
self.word(".");
self.print_ident(field);
}
self.end();
}
ast::ExprKind::MacCall(m) => self.print_mac(m),
ast::ExprKind::Paren(e) => {
self.popen();

View File

@ -2306,7 +2306,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Rvalue::AddressOf(..)
| Rvalue::ThreadLocalRef(..)
| Rvalue::Len(..)
| Rvalue::Discriminant(..) => {}
| Rvalue::Discriminant(..)
| Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {}
}
}

View File

@ -149,3 +149,6 @@ builtin_macros_format_pos_mismatch = {$n} positional {$n ->
[one] argument
*[more] arguments
} in format string, but {$desc}
builtin_macros_offset_of_expected_field = expected field
builtin_macros_offset_of_expected_two_args = expected 2 arguments

View File

@ -301,6 +301,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::If(_, _, _)
| ExprKind::IncludedBytes(..)
| ExprKind::InlineAsm(_)
| ExprKind::OffsetOf(_, _)
| ExprKind::Let(_, _, _)
| ExprKind::Lit(_)
| ExprKind::Loop(_, _, _)

View File

@ -45,6 +45,7 @@ mod format;
mod format_foreign;
mod global_allocator;
mod log_syntax;
mod offset_of;
mod source_util;
mod test;
mod trace_macros;
@ -92,6 +93,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
line: source_util::expand_line,
log_syntax: log_syntax::expand_log_syntax,
module_path: source_util::expand_mod,
offset_of: offset_of::expand_offset_of,
option_env: env::expand_option_env,
core_panic: edition_panic::expand_panic,
std_panic: edition_panic::expand_panic,

View File

@ -0,0 +1,99 @@
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::PResult;
use rustc_expand::base::{self, *};
use rustc_macros::Diagnostic;
use rustc_parse::parser::Parser;
use rustc_span::{symbol::Ident, Span};
#[derive(Diagnostic)]
#[diag(builtin_macros_offset_of_expected_field)]
struct ExpectedField {
#[primary_span]
span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_offset_of_expected_two_args)]
struct ExpectedTwoArgs {
#[primary_span]
span: Span,
}
fn parse_field<'a>(cx: &ExtCtxt<'a>, p: &mut Parser<'a>) -> PResult<'a, Ident> {
let token = p.token.uninterpolate();
let field = match token.kind {
token::Ident(name, _) => Ident::new(name, token.span),
token::Literal(token::Lit { kind: token::Integer, symbol, suffix: None }) => {
Ident::new(symbol, token.span)
}
_ => return Err(cx.create_err(ExpectedField { span: p.token.span })),
};
p.bump();
Ok(field)
}
fn parse_args<'a>(
cx: &mut ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
) -> PResult<'a, (P<ast::Ty>, Vec<Ident>)> {
let mut p = cx.new_parser_from_tts(tts);
let container = p.parse_ty()?;
p.expect(&token::Comma)?;
if p.eat(&token::Eof) {
return Err(cx.create_err(ExpectedTwoArgs { span: sp }));
}
let mut fields = Vec::new();
loop {
let field = parse_field(cx, &mut p)?;
fields.push(field);
if p.eat(&token::Dot) {
continue;
}
p.eat(&token::Comma);
if !p.eat(&token::Eof) {
return Err(cx.create_err(ExpectedTwoArgs { span: sp }));
}
break;
}
Ok((container, fields))
}
pub fn expand_offset_of<'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> {
match parse_args(cx, sp, tts) {
Ok((container, fields)) => {
let expr = P(ast::Expr {
id: ast::DUMMY_NODE_ID,
kind: ast::ExprKind::OffsetOf(container, fields),
span: sp,
attrs: ast::AttrVec::new(),
tokens: None,
});
MacEager::expr(expr)
}
Err(mut err) => {
err.emit();
DummyResult::any(sp)
}
}
}

View File

@ -781,12 +781,15 @@ fn codegen_stmt<'tcx>(
let operand = operand.load_scalar(fx);
lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
}
Rvalue::NullaryOp(null_op, ty) => {
Rvalue::NullaryOp(ref null_op, ty) => {
assert!(lval.layout().ty.is_sized(fx.tcx, ParamEnv::reveal_all()));
let layout = fx.layout_of(fx.monomorphize(ty));
let val = match null_op {
NullOp::SizeOf => layout.size.bytes(),
NullOp::AlignOf => layout.align.abi.bytes(),
NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(fx, fields.iter().map(|f| f.index())).bytes()
}
};
let val = CValue::const_val(fx, fx.layout_of(fx.tcx.types.usize), val.into());
lval.write_cvalue(fx, val);

View File

@ -666,13 +666,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
mir::Rvalue::NullaryOp(null_op, ty) => {
mir::Rvalue::NullaryOp(ref null_op, ty) => {
let ty = self.monomorphize(ty);
assert!(bx.cx().type_is_sized(ty));
let layout = bx.cx().layout_of(ty);
let val = match null_op {
mir::NullOp::SizeOf => layout.size.bytes(),
mir::NullOp::AlignOf => layout.align.abi.bytes(),
mir::NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(bx.cx(), fields.iter().map(|f| f.index())).bytes()
}
};
let val = bx.cx().const_usize(val);
let tcx = self.cx.tcx();

View File

@ -280,10 +280,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.write_immediate(*val, &dest)?;
}
NullaryOp(null_op, ty) => {
NullaryOp(ref null_op, ty) => {
let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?;
let layout = self.layout_of(ty)?;
if layout.is_unsized() {
if let mir::NullOp::SizeOf | mir::NullOp::AlignOf = null_op && layout.is_unsized() {
// FIXME: This should be a span_bug (#80742)
self.tcx.sess.delay_span_bug(
self.frame().current_span(),
@ -294,6 +294,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let val = match null_op {
mir::NullOp::SizeOf => layout.size.bytes(),
mir::NullOp::AlignOf => layout.align.abi.bytes(),
mir::NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(self, fields.iter().map(|f| f.index())).bytes()
}
};
self.write_scalar(Scalar::from_target_usize(val, self), &dest)?;
}

View File

@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
Rvalue::Cast(_, _, _) => {}
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {}
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_), _) => {}
Rvalue::ShallowInitBox(_, _) => {}
Rvalue::UnaryOp(_, operand) => {

View File

@ -514,6 +514,7 @@ impl<'tcx> Validator<'_, 'tcx> {
Rvalue::NullaryOp(op, _) => match op {
NullOp::SizeOf => {}
NullOp::AlignOf => {}
NullOp::OffsetOf(_) => {}
},
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),

View File

@ -8,9 +8,10 @@ use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{
traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, ProjectionElem,
RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
TerminatorKind, UnOp, UnwindAction, VarDebugInfo, VarDebugInfoContents, START_BLOCK,
MirPass, MirPhase, NonDivergingIntrinsic, NullOp, Operand, Place, PlaceElem, PlaceRef,
ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
Terminator, TerminatorKind, UnOp, UnwindAction, VarDebugInfo, VarDebugInfoContents,
START_BLOCK,
};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
use rustc_mir_dataflow::impls::MaybeStorageLive;
@ -711,10 +712,54 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
}
Rvalue::NullaryOp(NullOp::OffsetOf(fields), container) => {
let fail_out_of_bounds = |this: &Self, location, field, ty| {
this.fail(location, format!("Out of bounds field {field:?} for {ty:?}"));
};
let mut current_ty = *container;
for &field in fields {
match current_ty.kind() {
ty::Tuple(fields) => {
let Some(&f_ty) = fields.get(field.as_usize()) else {
fail_out_of_bounds(self, location, field, current_ty);
return;
};
current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
}
ty::Adt(adt_def, substs) => {
if adt_def.is_enum() {
self.fail(
location,
format!("Cannot get field offset from enum {current_ty:?}"),
);
return;
}
let Some(field) = adt_def.non_enum_variant().fields.get(field) else {
fail_out_of_bounds(self, location, field, current_ty);
return;
};
let f_ty = field.ty(self.tcx, substs);
current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
}
_ => {
self.fail(
location,
format!("Cannot get field offset from non-adt type {current_ty:?}"),
);
return;
}
}
}
}
Rvalue::Repeat(_, _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::AddressOf(_, _)
| Rvalue::NullaryOp(_, _)
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _)
| Rvalue::Discriminant(_) => {}
}
self.super_rvalue(rvalue, location);

View File

@ -1715,6 +1715,7 @@ impl Expr<'_> {
ExprKind::Continue(..) => ExprPrecedence::Continue,
ExprKind::Ret(..) => ExprPrecedence::Ret,
ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm,
ExprKind::OffsetOf(..) => ExprPrecedence::OffsetOf,
ExprKind::Struct(..) => ExprPrecedence::Struct,
ExprKind::Repeat(..) => ExprPrecedence::Repeat,
ExprKind::Yield(..) => ExprPrecedence::Yield,
@ -1774,6 +1775,7 @@ impl Expr<'_> {
| ExprKind::Loop(..)
| ExprKind::Assign(..)
| ExprKind::InlineAsm(..)
| ExprKind::OffsetOf(..)
| ExprKind::AssignOp(..)
| ExprKind::Lit(_)
| ExprKind::ConstBlock(..)
@ -1818,7 +1820,7 @@ impl Expr<'_> {
pub fn can_have_side_effects(&self) -> bool {
match self.peel_drop_temps().kind {
ExprKind::Path(_) | ExprKind::Lit(_) => false,
ExprKind::Path(_) | ExprKind::Lit(_) | ExprKind::OffsetOf(..) => false,
ExprKind::Type(base, _)
| ExprKind::Unary(_, base)
| ExprKind::Field(base, _)
@ -2022,6 +2024,9 @@ pub enum ExprKind<'hir> {
/// Inline assembly (from `asm!`), with its outputs and inputs.
InlineAsm(&'hir InlineAsm<'hir>),
/// Field offset (`offset_of!`)
OffsetOf(&'hir Ty<'hir>, &'hir [Ident]),
/// A struct or struct-like variant literal expression.
///
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`,

View File

@ -786,6 +786,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
ExprKind::InlineAsm(ref asm) => {
visitor.visit_inline_asm(asm, expression.hir_id);
}
ExprKind::OffsetOf(ref container, ref fields) => {
visitor.visit_ty(container);
walk_list!(visitor, visit_ident, fields.iter().copied());
}
ExprKind::Yield(ref subexpression, _) => {
visitor.visit_expr(subexpression);
}

View File

@ -1551,6 +1551,24 @@ impl<'a> State<'a> {
self.word("asm!");
self.print_inline_asm(asm);
}
hir::ExprKind::OffsetOf(container, ref fields) => {
self.word("offset_of!(");
self.print_type(container);
self.word(",");
self.space();
let (&first, rest) =
fields.split_first().expect("offset_of! should have at least 1 field");
self.print_ident(first);
for &field in rest {
self.word(".");
self.print_ident(field);
}
self.word(")");
}
hir::ExprKind::Yield(expr, _) => {
self.word_space("yield");
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);

View File

@ -309,6 +309,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id));
self.check_expr_asm(asm)
}
ExprKind::OffsetOf(container, ref fields) => {
self.check_offset_of(container, fields, expr)
}
ExprKind::Break(destination, ref expr_opt) => {
self.check_expr_break(destination, expr_opt.as_deref(), expr)
}
@ -2450,15 +2453,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
base_did: DefId,
return_ty: Option<Ty<'tcx>>,
) -> ErrorGuaranteed {
let struct_path = self.tcx().def_path_str(base_did);
let kind_name = self.tcx().def_descr(base_did);
let mut err = struct_span_err!(
self.tcx().sess,
field.span,
E0616,
"field `{field}` of {kind_name} `{struct_path}` is private",
);
err.span_label(field.span, "private field");
let mut err = self.private_field_err(field, base_did);
// Also check if an accessible method exists, which is often what is meant.
if self.method_exists(field, expr_t, expr.hir_id, false, return_ty)
&& !self.expr_in_place(expr.hir_id)
@ -2698,6 +2694,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err
}
fn private_field_err(
&self,
field: Ident,
base_did: DefId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let struct_path = self.tcx().def_path_str(base_did);
let kind_name = self.tcx().def_descr(base_did);
let mut err = struct_span_err!(
self.tcx().sess,
field.span,
E0616,
"field `{field}` of {kind_name} `{struct_path}` is private",
);
err.span_label(field.span, "private field");
err
}
pub(crate) fn get_field_candidates_considering_privacy(
&self,
span: Span,
@ -3042,4 +3056,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.mk_unit()
}
}
fn check_offset_of(
&self,
container: &'tcx hir::Ty<'tcx>,
fields: &[Ident],
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
let container = self.to_ty(container).normalized;
let mut field_indices = Vec::with_capacity(fields.len());
let mut current_container = container;
for &field in fields {
let container = self.structurally_resolved_type(expr.span, current_container);
match container.kind() {
ty::Adt(container_def, substs) if !container_def.is_enum() => {
let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
let (ident, def_scope) =
self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
let fields = &container_def.non_enum_variant().fields;
if let Some((index, field)) = fields
.iter_enumerated()
.find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == ident)
{
let field_ty = self.field_ty(expr.span, field, substs);
self.require_type_is_sized(field_ty, expr.span, traits::MiscObligation);
if field.vis.is_accessible_from(def_scope, self.tcx) {
self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
} else {
self.private_field_err(ident, container_def.did()).emit();
}
// Save the index of all fields regardless of their visibility in case
// of error recovery.
field_indices.push(index);
current_container = field_ty;
continue;
}
}
ty::Tuple(tys) => {
let fstr = field.as_str();
if let Ok(index) = fstr.parse::<usize>() {
if fstr == index.to_string() {
if let Some(&field_ty) = tys.get(index) {
field_indices.push(index.into());
current_container = field_ty;
continue;
}
}
}
}
_ => (),
};
self.no_such_field_err(field, container, expr.hir_id).emit();
break;
}
self.typeck_results
.borrow_mut()
.offset_of_data_mut()
.insert(expr.hir_id, (container, field_indices));
self.tcx.types.usize
}
}

View File

@ -300,6 +300,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
hir::ExprKind::Continue(..)
| hir::ExprKind::Lit(..)
| hir::ExprKind::ConstBlock(..)
| hir::ExprKind::OffsetOf(..)
| hir::ExprKind::Err(_) => {}
hir::ExprKind::Loop(blk, ..) => {

View File

@ -215,6 +215,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::InlineAsm(..)
| ExprKind::OffsetOf(..)
| ExprKind::Struct(..)
| ExprKind::Repeat(..)
| ExprKind::Yield(..)
@ -485,6 +486,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
| ExprKind::Field(..)
| ExprKind::Index(..)
| ExprKind::InlineAsm(..)
| ExprKind::OffsetOf(..)
| ExprKind::Let(..)
| ExprKind::Lit(..)
| ExprKind::Path(..)

View File

@ -381,6 +381,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
| hir::ExprKind::Struct(..)
| hir::ExprKind::Repeat(..)
| hir::ExprKind::InlineAsm(..)
| hir::ExprKind::OffsetOf(..)
| hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)),
}
}

View File

@ -70,6 +70,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
wbcx.visit_user_provided_tys();
wbcx.visit_user_provided_sigs();
wbcx.visit_generator_interior_types();
wbcx.visit_offset_of_container_types();
wbcx.typeck_results.rvalue_scopes =
mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes);
@ -295,7 +296,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
self.visit_field_id(field.hir_id);
}
}
hir::ExprKind::Field(..) => {
hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => {
self.visit_field_id(e.hir_id);
}
hir::ExprKind::ConstBlock(anon_const) => {
@ -682,6 +683,28 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
}
}
fn visit_offset_of_container_types(&mut self) {
let fcx_typeck_results = self.fcx.typeck_results.borrow();
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
for (local_id, &(container, ref indices)) in
fcx_typeck_results.offset_of_data().items_in_stable_order()
{
let hir_id = hir::HirId { owner: common_hir_owner, local_id };
if cfg!(debug_assertions) && container.needs_infer() {
span_bug!(
hir_id.to_span(self.fcx.tcx),
"writeback: `{:?}` has inference variables",
container
);
};
self.typeck_results.offset_of_data_mut().insert(hir_id, (container, indices.clone()));
}
}
fn resolve<T>(&mut self, x: T, span: &dyn Locatable) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,

View File

@ -1211,12 +1211,14 @@ pub enum AggregateKind<'tcx> {
Generator(DefId, SubstsRef<'tcx>, hir::Movability),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum NullOp {
/// Returns the size of a value of that type
SizeOf,
/// Returns the minimum alignment of a type
AlignOf,
/// Returns the offset of a field
OffsetOf(Vec<FieldIdx>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -1286,6 +1288,6 @@ mod size_asserts {
static_assert_size!(Operand<'_>, 24);
static_assert_size!(Place<'_>, 16);
static_assert_size!(PlaceElem<'_>, 24);
static_assert_size!(Rvalue<'_>, 40);
static_assert_size!(Rvalue<'_>, 48);
// tidy-alphabetical-end
}

View File

@ -188,7 +188,9 @@ impl<'tcx> Rvalue<'tcx> {
}
Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx),
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => tcx.types.usize,
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
tcx.types.usize
}
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
AggregateKind::Array(ty) => tcx.mk_array(ty, ops.len() as u64),
AggregateKind::Tuple => {

View File

@ -481,6 +481,11 @@ pub enum ExprKind<'tcx> {
},
/// Inline assembly, i.e. `asm!()`.
InlineAsm(Box<InlineAsmExpr<'tcx>>),
/// Field offset (`offset_of!`)
OffsetOf {
container: Ty<'tcx>,
fields: Vec<FieldIdx>,
},
/// An expression taking a reference to a thread local.
ThreadLocalRef(DefId),
/// A `yield` expression.

View File

@ -160,6 +160,7 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
}
}
}
OffsetOf { container: _, fields: _ } => {}
ThreadLocalRef(_) => {}
Yield { value } => visitor.visit_expr(&visitor.thir()[value]),
}

View File

@ -208,6 +208,9 @@ pub struct TypeckResults<'tcx> {
/// Contains the data for evaluating the effect of feature `capture_disjoint_fields`
/// on closure size.
pub closure_size_eval: FxHashMap<LocalDefId, ClosureSizeProfileData<'tcx>>,
/// Container types and field indices of `offset_of!` expressions
offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<FieldIdx>)>,
}
/// Whenever a value may be live across a generator yield, the type of that value winds up in the
@ -280,6 +283,7 @@ impl<'tcx> TypeckResults<'tcx> {
generator_interior_predicates: Default::default(),
treat_byte_string_as_slice: Default::default(),
closure_size_eval: Default::default(),
offset_of_data: Default::default(),
}
}
@ -530,6 +534,14 @@ impl<'tcx> TypeckResults<'tcx> {
pub fn coercion_casts(&self) -> &ItemLocalSet {
&self.coercion_casts
}
pub fn offset_of_data(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<FieldIdx>)> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.offset_of_data }
}
pub fn offset_of_data_mut(&mut self) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<FieldIdx>)> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.offset_of_data }
}
}
/// Validate that the given HirId (respectively its `local_id` part) can be

View File

@ -557,6 +557,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::ConstBlock { .. }
| ExprKind::StaticRef { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::OffsetOf { .. }
| ExprKind::Yield { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::Call { .. } => {

View File

@ -481,6 +481,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}))))
}
ExprKind::OffsetOf { container, ref fields } => {
block.and(Rvalue::NullaryOp(NullOp::OffsetOf(fields.clone()), container))
}
ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }

View File

@ -53,8 +53,7 @@ impl Category {
| ExprKind::Borrow { .. }
| ExprKind::AddressOf { .. }
| ExprKind::Yield { .. }
| ExprKind::Call { .. }
| ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
| ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
ExprKind::Array { .. }
| ExprKind::Tuple { .. }
@ -67,7 +66,9 @@ impl Category {
| ExprKind::Repeat { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::ThreadLocalRef(_) => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
| ExprKind::ThreadLocalRef(_)
| ExprKind::InlineAsm { .. }
| ExprKind::OffsetOf { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
ExprKind::ConstBlock { .. }
| ExprKind::Literal { .. }

View File

@ -561,7 +561,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::ZstLiteral { .. }
| ExprKind::ConstParam { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::StaticRef { .. } => {
| ExprKind::StaticRef { .. }
| ExprKind::OffsetOf { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() {
// should be handled above
Category::Rvalue(RvalueFunc::Into) => false,

View File

@ -323,6 +323,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::Box { .. }
| ExprKind::If { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::OffsetOf { .. }
| ExprKind::LogicalOp { .. }
| ExprKind::Use { .. } => {
// We don't need to save the old value and restore it

View File

@ -664,6 +664,14 @@ impl<'tcx> Cx<'tcx> {
line_spans: asm.line_spans,
})),
hir::ExprKind::OffsetOf(_, _) => {
let data = self.typeck_results.offset_of_data();
let &(container, ref indices) = data.get(expr.hir_id).unwrap();
let fields = indices.iter().copied().collect();
ExprKind::OffsetOf { container, fields }
}
hir::ExprKind::ConstBlock(ref anon_const) => {
let ty = self.typeck_results().node_type(anon_const.hir_id);
let did = anon_const.def_id.to_def_id();

View File

@ -519,6 +519,19 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_inline_asm_expr(&**expr, depth_lvl + 2);
print_indented!(self, "}", depth_lvl);
}
OffsetOf { container, fields } => {
print_indented!(self, "InlineAsm {", depth_lvl);
print_indented!(self, format!("container: {:?}", container), depth_lvl + 1);
print_indented!(self, "fields: [", depth_lvl + 1);
for field in fields.iter() {
print_indented!(self, format!("{:?}", field), depth_lvl + 2);
print_indented!(self, ",", depth_lvl + 1);
}
print_indented!(self, "]", depth_lvl + 1);
print_indented!(self, "}", depth_lvl);
}
ThreadLocalRef(def_id) => {
print_indented!(self, "ThreadLocalRef {", depth_lvl);
print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1);

View File

@ -360,7 +360,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
| Rvalue::AddressOf(..)
| Rvalue::Discriminant(..)
| Rvalue::Len(..)
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {}
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {}
}
}

View File

@ -303,8 +303,7 @@ fn find_determining_place<'tcx>(
| Rvalue::NullaryOp(_, _)
| Rvalue::ShallowInitBox(_, _)
| Rvalue::UnaryOp(_, Operand::Constant(_))
| Rvalue::Cast(_, Operand::Constant(_), _)
=> return None,
| Rvalue::Cast(_, Operand::Constant(_), _) => return None,
}
}

View File

@ -237,6 +237,39 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
}
}
fn handle_offset_of(&mut self, expr: &'tcx hir::Expr<'tcx>) {
let data = self.typeck_results().offset_of_data();
let &(container, ref indices) =
data.get(expr.hir_id).expect("no offset_of_data for offset_of");
let mut last_did = expr.hir_id.owner.to_def_id();
let mut current_ty = container;
for &index in indices {
match current_ty.kind() {
ty::Adt(def, subst) => {
let field = &def.non_enum_variant().fields[index];
self.insert_def_id(field.did);
let field_ty = field.ty(self.tcx, subst);
last_did = field.did;
current_ty =
self.tcx.normalize_erasing_regions(self.tcx.param_env(field.did), field_ty);
}
// we don't need to mark tuple fields as live,
// but we may need to mark subfields
ty::Tuple(tys) => {
current_ty = self.tcx.normalize_erasing_regions(
self.tcx.param_env(last_did),
tys[index.as_usize()],
);
}
_ => span_bug!(expr.span, "named field access on non-ADT"),
}
}
}
fn mark_live_symbols(&mut self) {
let mut scanned = LocalDefIdSet::default();
while let Some(id) = self.worklist.pop() {
@ -405,6 +438,9 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
hir::ExprKind::Closure(cls) => {
self.insert_def_id(cls.def_id.to_def_id());
}
hir::ExprKind::OffsetOf(..) => {
self.handle_offset_of(expr);
}
_ => (),
}

View File

@ -302,7 +302,8 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
[
ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type,
DropTemps, Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index,
Path, AddrOf, Break, Continue, Ret, InlineAsm, Struct, Repeat, Yield, Err
Path, AddrOf, Break, Continue, Ret, InlineAsm, OffsetOf, Struct, Repeat, Yield,
Err
]
);
hir_visit::walk_expr(self, e)
@ -568,7 +569,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
InlineAsm, FormatArgs, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err
InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err
]
);
ast_visit::walk_expr(self, e)

View File

@ -473,6 +473,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
| hir::ExprKind::Struct(..)
| hir::ExprKind::Repeat(..)
| hir::ExprKind::InlineAsm(..)
| hir::ExprKind::OffsetOf(..)
| hir::ExprKind::Type(..)
| hir::ExprKind::Err(_)
| hir::ExprKind::Path(hir::QPath::TypeRelative(..))
@ -1129,7 +1130,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
| hir::ExprKind::ConstBlock(..)
| hir::ExprKind::Err(_)
| hir::ExprKind::Path(hir::QPath::TypeRelative(..))
| hir::ExprKind::Path(hir::QPath::LangItem(..)) => succ,
| hir::ExprKind::Path(hir::QPath::LangItem(..))
| hir::ExprKind::OffsetOf(..) => succ,
// Note that labels have been resolved, so we don't need to look
// at the label ident
@ -1418,6 +1420,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
| hir::ExprKind::ConstBlock(..)
| hir::ExprKind::Block(..)
| hir::ExprKind::AddrOf(..)
| hir::ExprKind::OffsetOf(..)
| hir::ExprKind::Struct(..)
| hir::ExprKind::Repeat(..)
| hir::ExprKind::Closure { .. }

View File

@ -203,6 +203,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::OffsetOf(..)
| ExprKind::Struct(..)
| ExprKind::Repeat(..)
| ExprKind::Yield(..) => {

View File

@ -1037,6 +1037,7 @@ symbols! {
object_safe_for_dispatch,
of,
offset,
offset_of,
omit_gdb_pretty_printer_section,
on,
on_unimplemented,

View File

@ -124,6 +124,21 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
{
Ty::is_unit(self)
}
pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = usize>) -> Size
where
Ty: TyAbiInterface<'a, C>,
{
let mut layout = self;
let mut offset = Size::ZERO;
for index in indices {
offset += layout.fields.offset(index);
layout = layout.field(cx, index);
}
offset
}
}
impl<'a, Ty> TyAndLayout<'a, Ty> {

View File

@ -256,6 +256,7 @@ fn recurse_build<'tcx>(
ExprKind::VarRef { .. }
| ExprKind::UpvarRef { .. }
| ExprKind::StaticRef { .. }
| ExprKind::OffsetOf { .. }
| ExprKind::ThreadLocalRef(_) => {
error(GenericConstantTooComplexSub::OperationNotSupported(node.span))?
}
@ -347,6 +348,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
| thir::ExprKind::ZstLiteral { .. }
| thir::ExprKind::StaticRef { .. }
| thir::ExprKind::InlineAsm(_)
| thir::ExprKind::OffsetOf { .. }
| thir::ExprKind::ThreadLocalRef(_)
| thir::ExprKind::Yield { .. } => false,
}

View File

@ -1279,3 +1279,45 @@ pub trait SizedTypeProperties: Sized {
#[doc(hidden)]
#[unstable(feature = "sized_type_properties", issue = "none")]
impl<T> SizedTypeProperties for T {}
/// Expands to the offset in bytes of a field from the beginning of the given type.
///
/// Only structs, unions and tuples are supported.
///
/// Nested field accesses may be used, but not array indexes like in `C`'s `offsetof`.
///
/// Note that the output of this macro is not stable, except for `#[repr(C)]` types.
///
/// # Examples
///
/// ```
/// #![feature(offset_of)]
///
/// use std::mem;
/// #[repr(C)]
/// struct FieldStruct {
/// first: u8,
/// second: u16,
/// third: u8
/// }
///
/// assert_eq!(mem::offset_of!(FieldStruct, first), 0);
/// assert_eq!(mem::offset_of!(FieldStruct, second), 2);
/// assert_eq!(mem::offset_of!(FieldStruct, third), 4);
///
/// #[repr(C)]
/// struct NestedA {
/// b: NestedB
/// }
///
/// #[repr(C)]
/// struct NestedB(u8);
///
/// assert_eq!(mem::offset_of!(NestedA, b.0), 0);
/// ```
#[unstable(feature = "offset_of", issue = "106655")]
#[rustc_builtin_macro]
#[cfg(not(bootstrap))]
pub macro offset_of($Container:ty, $($fields:tt).+ $(,)?) {
// ...implementation defined...
}

View File

@ -109,6 +109,7 @@
#![feature(utf8_chunks)]
#![feature(is_ascii_octdigit)]
#![feature(get_many_mut)]
#![cfg_attr(not(bootstrap), feature(offset_of))]
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(fuzzy_provenance_casts)]

View File

@ -364,3 +364,77 @@ fn const_maybe_uninit() {
assert_eq!(FIELD_BY_FIELD, Foo { x: 1, y: 2 });
}
#[test]
#[cfg(not(bootstrap))]
fn offset_of() {
#[repr(C)]
struct Foo {
x: u8,
y: u16,
z: Bar,
}
#[repr(C)]
struct Bar(u8, u8);
assert_eq!(offset_of!(Foo, x), 0);
assert_eq!(offset_of!(Foo, y), 2);
assert_eq!(offset_of!(Foo, z.0), 4);
assert_eq!(offset_of!(Foo, z.1), 5);
// Layout of tuples is unstable
assert!(offset_of!((u8, u16), 0) <= size_of::<(u8, u16)>() - 1);
assert!(offset_of!((u8, u16), 1) <= size_of::<(u8, u16)>() - 2);
}
#[test]
#[cfg(not(bootstrap))]
fn const_offset_of() {
#[repr(C)]
struct Foo {
x: u8,
y: u16,
}
const X_OFFSET: usize = offset_of!(Foo, x);
const Y_OFFSET: usize = offset_of!(Foo, y);
assert_eq!(X_OFFSET, 0);
assert_eq!(Y_OFFSET, 2);
}
#[test]
#[cfg(not(bootstrap))]
fn offset_of_without_const_promotion() {
#[repr(C)]
struct Foo<SuppressConstPromotion> {
x: u8,
y: u16,
_scp: SuppressConstPromotion,
}
// Normally, offset_of is always const promoted.
// The generic parameter prevents this from happening.
// This is needed to test the codegen impl of offset_of
fn inner<SuppressConstPromotion>() {
assert_eq!(offset_of!(Foo<SuppressConstPromotion>, x), 0);
assert_eq!(offset_of!(Foo<SuppressConstPromotion>, y), 2);
}
inner::<()>();
}
#[test]
#[cfg(not(bootstrap))]
fn offset_of_dst() {
#[repr(C)]
struct Foo {
x: u8,
y: u16,
slice: [u8],
}
assert_eq!(offset_of!(Foo, x), 0);
assert_eq!(offset_of!(Foo, y), 2);
}

View File

@ -226,7 +226,8 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
| InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
})
.fold(NeverLoopResult::Otherwise, combine_seq),
ExprKind::Yield(_, _)
ExprKind::OffsetOf(_, _)
| ExprKind::Yield(_, _)
| ExprKind::Closure { .. }
| ExprKind::Path(_)
| ExprKind::ConstBlock(_)

View File

@ -342,6 +342,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
ExprKind::DropTemps(_) |
ExprKind::Err(_) |
ExprKind::InlineAsm(_) |
ExprKind::OffsetOf(_, _) |
ExprKind::Let(_) |
ExprKind::Lit(_) |
ExprKind::Loop(_, _, _, _) |

View File

@ -558,6 +558,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("InlineAsm(_)");
out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
},
ExprKind::OffsetOf(container, ref fields) => {
bind!(self, container, fields);
kind!("OffsetOf({container}, {fields})");
}
ExprKind::Struct(qpath, fields, base) => {
bind!(self, qpath, fields);
opt_bind!(self, base);

View File

@ -218,7 +218,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
| ExprKind::AddrOf(..)
| ExprKind::Struct(..)
| ExprKind::Repeat(..)
| ExprKind::Block(Block { stmts: [], .. }, _) => (),
| ExprKind::Block(Block { stmts: [], .. }, _)
| ExprKind::OffsetOf(..) => (),
// Assignment might be to a local defined earlier, so don't eagerly evaluate.
// Blocks with multiple statements might be expensive, so don't eagerly evaluate.

View File

@ -301,6 +301,9 @@ impl HirEqInterExpr<'_, '_, '_> {
(&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
(&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
(&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
(&ExprKind::OffsetOf(l_container, ref l_fields), &ExprKind::OffsetOf(r_container, ref r_fields)) => {
self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name)
},
_ => false,
};
(is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
@ -701,6 +704,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
}
},
ExprKind::OffsetOf(container, fields) => {
self.hash_ty(container);
for field in fields {
self.hash_name(field.name);
}
},
ExprKind::Let(Let { pat, init, ty, .. }) => {
self.hash_expr(init);
if let Some(ty) = ty {

View File

@ -194,7 +194,7 @@ fn check_rvalue<'tcx>(
))
}
},
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() {

View File

@ -139,6 +139,7 @@ impl<'a> Sugg<'a> {
| hir::ExprKind::Field(..)
| hir::ExprKind::Index(..)
| hir::ExprKind::InlineAsm(..)
| hir::ExprKind::OffsetOf(..)
| hir::ExprKind::ConstBlock(..)
| hir::ExprKind::Lit(..)
| hir::ExprKind::Loop(..)
@ -197,6 +198,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::ForLoop(..)
| ast::ExprKind::Index(..)
| ast::ExprKind::InlineAsm(..)
| ast::ExprKind::OffsetOf(..)
| ast::ExprKind::ConstBlock(..)
| ast::ExprKind::Lit(..)
| ast::ExprKind::IncludedBytes(..)

View File

@ -662,6 +662,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
| ExprKind::Path(_)
| ExprKind::Continue(_)
| ExprKind::InlineAsm(_)
| ExprKind::OffsetOf(..)
| ExprKind::Err(_) => (),
}
ControlFlow::Continue(())

View File

@ -345,6 +345,7 @@ pub(crate) fn format_expr(
// Style Guide RFC for InlineAsm variant pending
// https://github.com/rust-dev-tools/fmt-rfcs/issues/152
ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()),
ast::ExprKind::OffsetOf(..) => Some(context.snippet(expr.span).to_owned()),
ast::ExprKind::TryBlock(ref block) => {
if let rw @ Some(_) =
rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape)

View File

@ -499,6 +499,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
| ast::ExprKind::Field(..)
| ast::ExprKind::IncludedBytes(..)
| ast::ExprKind::InlineAsm(..)
| ast::ExprKind::OffsetOf(..)
| ast::ExprKind::Let(..)
| ast::ExprKind::Path(..)
| ast::ExprKind::Range(..)

View File

@ -0,0 +1,43 @@
- // MIR for `main` before ConstProp
+ // MIR for `main` after ConstProp
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/offset_of.rs:+0:11: +0:11
let _1: usize; // in scope 0 at $DIR/offset_of.rs:+1:9: +1:10
scope 1 {
debug x => _1; // in scope 1 at $DIR/offset_of.rs:+1:9: +1:10
let _2: usize; // in scope 1 at $DIR/offset_of.rs:+2:9: +2:10
scope 2 {
debug y => _2; // in scope 2 at $DIR/offset_of.rs:+2:9: +2:10
let _3: usize; // in scope 2 at $DIR/offset_of.rs:+3:9: +3:11
scope 3 {
debug z0 => _3; // in scope 3 at $DIR/offset_of.rs:+3:9: +3:11
let _4: usize; // in scope 3 at $DIR/offset_of.rs:+4:9: +4:11
scope 4 {
debug z1 => _4; // in scope 4 at $DIR/offset_of.rs:+4:9: +4:11
}
}
}
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/offset_of.rs:+1:9: +1:10
- _1 = OffsetOf([0])(Foo); // scope 0 at $DIR/offset_of.rs:+1:13: +1:31
+ _1 = const 0_usize; // scope 0 at $DIR/offset_of.rs:+1:13: +1:31
StorageLive(_2); // scope 1 at $DIR/offset_of.rs:+2:9: +2:10
- _2 = OffsetOf([1])(Foo); // scope 1 at $DIR/offset_of.rs:+2:13: +2:31
+ _2 = const 2_usize; // scope 1 at $DIR/offset_of.rs:+2:13: +2:31
StorageLive(_3); // scope 2 at $DIR/offset_of.rs:+3:9: +3:11
- _3 = OffsetOf([2, 0])(Foo); // scope 2 at $DIR/offset_of.rs:+3:14: +3:34
+ _3 = const 4_usize; // scope 2 at $DIR/offset_of.rs:+3:14: +3:34
StorageLive(_4); // scope 3 at $DIR/offset_of.rs:+4:9: +4:11
- _4 = OffsetOf([2, 1])(Foo); // scope 3 at $DIR/offset_of.rs:+4:14: +4:34
+ _4 = const 5_usize; // scope 3 at $DIR/offset_of.rs:+4:14: +4:34
StorageDead(_4); // scope 3 at $DIR/offset_of.rs:+5:1: +5:2
StorageDead(_3); // scope 2 at $DIR/offset_of.rs:+5:1: +5:2
StorageDead(_2); // scope 1 at $DIR/offset_of.rs:+5:1: +5:2
StorageDead(_1); // scope 0 at $DIR/offset_of.rs:+5:1: +5:2
return; // scope 0 at $DIR/offset_of.rs:+5:2: +5:2
}
}

View File

@ -0,0 +1,25 @@
// unit-test
// compile-flags: -O
// EMIT_MIR offset_of.main.ConstProp.diff
#![feature(offset_of)]
use std::mem::offset_of;
#[repr(C)]
struct Foo {
x: u8,
y: u16,
z: Bar,
}
#[repr(C)]
struct Bar(u8, u8);
fn main() {
let x = offset_of!(Foo, x);
let y = offset_of!(Foo, y);
let z0 = offset_of!(Foo, z.0);
let z1 = offset_of!(Foo, z.1);
}

View File

@ -0,0 +1,26 @@
#![feature(offset_of)]
#![deny(dead_code)]
use std::mem::offset_of;
struct Alpha {
a: (),
b: (), //~ ERROR field `b` is never read
c: Beta,
}
struct Beta {
a: (), //~ ERROR field `a` is never read
b: (),
}
struct Gamma {
a: (), //~ ERROR field `a` is never read
b: (),
}
fn main() {
offset_of!(Alpha, a);
offset_of!(Alpha, c.b);
offset_of!((Gamma,), 0.b);
}

View File

@ -0,0 +1,33 @@
error: field `b` is never read
--> $DIR/liveness-offset-of.rs:8:5
|
LL | struct Alpha {
| ----- field in this struct
LL | a: (),
LL | b: (),
| ^
|
note: the lint level is defined here
--> $DIR/liveness-offset-of.rs:2:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^
error: field `a` is never read
--> $DIR/liveness-offset-of.rs:13:5
|
LL | struct Beta {
| ---- field in this struct
LL | a: (),
| ^
error: field `a` is never read
--> $DIR/liveness-offset-of.rs:18:5
|
LL | struct Gamma {
| ----- field in this struct
LL | a: (),
| ^
error: aborting due to 3 previous errors

View File

@ -0,0 +1,9 @@
#![feature(offset_of)]
use std::mem::offset_of;
fn main() {
offset_of!(NotEnoughArguments); //~ ERROR expected one of
offset_of!(NotEnoughArgumentsWithAComma, ); //~ ERROR expected 2 arguments
offset_of!(Container, field, too many arguments); //~ ERROR expected 2 arguments
}

View File

@ -0,0 +1,20 @@
error: expected one of `!`, `(`, `+`, `,`, `::`, or `<`, found `<eof>`
--> $DIR/offset-of-arg-count.rs:6:16
|
LL | offset_of!(NotEnoughArguments);
| ^^^^^^^^^^^^^^^^^^ expected one of `!`, `(`, `+`, `,`, `::`, or `<`
error: expected 2 arguments
--> $DIR/offset-of-arg-count.rs:7:5
|
LL | offset_of!(NotEnoughArgumentsWithAComma, );
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected 2 arguments
--> $DIR/offset-of-arg-count.rs:8:5
|
LL | offset_of!(Container, field, too many arguments);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors

View File

@ -0,0 +1,14 @@
#![feature(offset_of)]
use std::mem::offset_of;
#[repr(C)]
struct Foo {
x: u8,
y: u16,
slice: [u8],
}
fn main() {
offset_of!(Foo, slice); //~ ERROR the size for values of type
}

View File

@ -0,0 +1,11 @@
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/offset-of-dst-field.rs:13:5
|
LL | offset_of!(Foo, slice);
| ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,16 @@
#![feature(offset_of)]
use std::mem::offset_of;
mod m {
#[repr(C)]
pub struct Foo {
pub public: u8,
private: u8,
}
}
fn main() {
offset_of!(m::Foo, public);
offset_of!(m::Foo, private); //~ ERROR field `private` of struct `Foo` is private
}

View File

@ -0,0 +1,9 @@
error[E0616]: field `private` of struct `Foo` is private
--> $DIR/offset-of-private.rs:15:24
|
LL | offset_of!(m::Foo, private);
| ^^^^^^^ private field
error: aborting due to previous error
For more information about this error, try `rustc --explain E0616`.