mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-13 12:36:47 +00:00
offset_of
This commit is contained in:
parent
b92a41c676
commit
511e457c4b
@ -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>),
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)),
|
||||
|
@ -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}")
|
||||
|
@ -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();
|
||||
|
@ -2306,7 +2306,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
Rvalue::AddressOf(..)
|
||||
| Rvalue::ThreadLocalRef(..)
|
||||
| Rvalue::Len(..)
|
||||
| Rvalue::Discriminant(..) => {}
|
||||
| Rvalue::Discriminant(..)
|
||||
| Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -301,6 +301,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
||||
| ExprKind::If(_, _, _)
|
||||
| ExprKind::IncludedBytes(..)
|
||||
| ExprKind::InlineAsm(_)
|
||||
| ExprKind::OffsetOf(_, _)
|
||||
| ExprKind::Let(_, _, _)
|
||||
| ExprKind::Lit(_)
|
||||
| ExprKind::Loop(_, _, _)
|
||||
|
@ -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,
|
||||
|
99
compiler/rustc_builtin_macros/src/offset_of.rs
Normal file
99
compiler/rustc_builtin_macros/src/offset_of.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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)?;
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -514,6 +514,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||
Rvalue::NullaryOp(op, _) => match op {
|
||||
NullOp::SizeOf => {}
|
||||
NullOp::AlignOf => {}
|
||||
NullOp::OffsetOf(_) => {}
|
||||
},
|
||||
|
||||
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
|
||||
|
@ -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);
|
||||
|
@ -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}`,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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, ..) => {
|
||||
|
@ -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(..)
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
@ -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>>,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 => {
|
||||
|
@ -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.
|
||||
|
@ -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]),
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -557,6 +557,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
| ExprKind::ConstBlock { .. }
|
||||
| ExprKind::StaticRef { .. }
|
||||
| ExprKind::InlineAsm { .. }
|
||||
| ExprKind::OffsetOf { .. }
|
||||
| ExprKind::Yield { .. }
|
||||
| ExprKind::ThreadLocalRef(_)
|
||||
| ExprKind::Call { .. } => {
|
||||
|
@ -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 { .. }
|
||||
|
@ -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 { .. }
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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(..), _) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 { .. }
|
||||
|
@ -203,6 +203,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
|
||||
| ExprKind::Break(..)
|
||||
| ExprKind::Continue(..)
|
||||
| ExprKind::Ret(..)
|
||||
| ExprKind::OffsetOf(..)
|
||||
| ExprKind::Struct(..)
|
||||
| ExprKind::Repeat(..)
|
||||
| ExprKind::Yield(..) => {
|
||||
|
@ -1037,6 +1037,7 @@ symbols! {
|
||||
object_safe_for_dispatch,
|
||||
of,
|
||||
offset,
|
||||
offset_of,
|
||||
omit_gdb_pretty_printer_section,
|
||||
on,
|
||||
on_unimplemented,
|
||||
|
@ -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> {
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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...
|
||||
}
|
||||
|
@ -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)]
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(_)
|
||||
|
@ -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(_, _, _, _) |
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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(..)
|
||||
|
@ -662,6 +662,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
|
||||
| ExprKind::Path(_)
|
||||
| ExprKind::Continue(_)
|
||||
| ExprKind::InlineAsm(_)
|
||||
| ExprKind::OffsetOf(..)
|
||||
| ExprKind::Err(_) => (),
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
|
@ -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)
|
||||
|
@ -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(..)
|
||||
|
43
tests/mir-opt/const_prop/offset_of.main.ConstProp.diff
Normal file
43
tests/mir-opt/const_prop/offset_of.main.ConstProp.diff
Normal 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
|
||||
}
|
||||
}
|
||||
|
25
tests/mir-opt/const_prop/offset_of.rs
Normal file
25
tests/mir-opt/const_prop/offset_of.rs
Normal 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);
|
||||
}
|
26
tests/ui/liveness/liveness-offset-of.rs
Normal file
26
tests/ui/liveness/liveness-offset-of.rs
Normal 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);
|
||||
}
|
33
tests/ui/liveness/liveness-offset-of.stderr
Normal file
33
tests/ui/liveness/liveness-offset-of.stderr
Normal 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
|
||||
|
9
tests/ui/offset-of/offset-of-arg-count.rs
Normal file
9
tests/ui/offset-of/offset-of-arg-count.rs
Normal 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
|
||||
}
|
20
tests/ui/offset-of/offset-of-arg-count.stderr
Normal file
20
tests/ui/offset-of/offset-of-arg-count.stderr
Normal 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
|
||||
|
14
tests/ui/offset-of/offset-of-dst-field.rs
Normal file
14
tests/ui/offset-of/offset-of-dst-field.rs
Normal 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
|
||||
}
|
11
tests/ui/offset-of/offset-of-dst-field.stderr
Normal file
11
tests/ui/offset-of/offset-of-dst-field.stderr
Normal 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`.
|
16
tests/ui/offset-of/offset-of-private.rs
Normal file
16
tests/ui/offset-of/offset-of-private.rs
Normal 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
|
||||
}
|
9
tests/ui/offset-of/offset-of-private.stderr
Normal file
9
tests/ui/offset-of/offset-of-private.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user