Auto merge of #106934 - DrMeepster:offset_of, r=WaffleLapkin
Add offset_of! macro (RFC 3308) Implements https://github.com/rust-lang/rfcs/pull/3308 (tracking issue #106655) by adding the built in macro `core::mem::offset_of`. Two of the future possibilities are also implemented: * Nested field accesses (without array indexing) * DST support (for `Sized` fields) I wrote this a few months ago, before the RFC merged. Now that it's merged, I decided to rebase and finish it. cc `@thomcc` (RFC author)
This commit is contained in:
commit
80a2ec49a4
83 changed files with 1355 additions and 42 deletions
|
@ -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>, P<[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.iter_mut() {
|
||||
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,26 @@ 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();
|
||||
|
||||
if let Some((&first, rest)) = fields.split_first() {
|
||||
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>, P<[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.into()))
|
||||
}
|
||||
|
||||
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,20 +280,23 @@ 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(),
|
||||
&format!("Nullary MIR operator called for unsized type {}", ty),
|
||||
&format!("{null_op:?} MIR operator called for unsized type {ty}"),
|
||||
);
|
||||
throw_inval!(SizeOfUnsizedType(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(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.iter() {
|
||||
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,23 @@ 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();
|
||||
|
||||
if let Some((&first, rest)) = fields.split_first() {
|
||||
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,78 @@ 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);
|
||||
|
||||
// FIXME: DSTs with static alignment should be allowed
|
||||
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>>,
|
||||
|
|
|
@ -2041,7 +2041,11 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
}
|
||||
UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
|
||||
Discriminant(ref place) => write!(fmt, "discriminant({:?})", place),
|
||||
NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t),
|
||||
NullaryOp(ref op, ref t) => match op {
|
||||
NullOp::SizeOf => write!(fmt, "SizeOf({:?})", t),
|
||||
NullOp::AlignOf => write!(fmt, "AlignOf({:?})", t),
|
||||
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({:?}, {:?})", t, fields),
|
||||
},
|
||||
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
||||
let muta = tcx.static_mutability(did).unwrap().prefix_str();
|
||||
write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
|
||||
|
|
|
@ -1115,7 +1115,7 @@ pub enum Rvalue<'tcx> {
|
|||
CheckedBinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>),
|
||||
|
||||
/// Computes a value as described by the operation.
|
||||
NullaryOp(NullOp, Ty<'tcx>),
|
||||
NullaryOp(NullOp<'tcx>, Ty<'tcx>),
|
||||
|
||||
/// Exactly like `BinaryOp`, but less operands.
|
||||
///
|
||||
|
@ -1211,12 +1211,14 @@ pub enum AggregateKind<'tcx> {
|
|||
Generator(DefId, SubstsRef<'tcx>, hir::Movability),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
pub enum NullOp {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
pub enum NullOp<'tcx> {
|
||||
/// 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(&'tcx List<FieldIdx>),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -16,7 +16,6 @@ TrivialTypeTraversalAndLiftImpls! {
|
|||
UserTypeAnnotationIndex,
|
||||
BorrowKind,
|
||||
CastKind,
|
||||
NullOp,
|
||||
hir::Movability,
|
||||
BasicBlock,
|
||||
SwitchTargets,
|
||||
|
@ -26,6 +25,7 @@ TrivialTypeTraversalAndLiftImpls! {
|
|||
|
||||
TrivialTypeTraversalImpls! {
|
||||
ConstValue<'tcx>,
|
||||
NullOp<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx [InlineAsmTemplatePiece] {
|
||||
|
|
|
@ -20,7 +20,7 @@ use rustc_middle::mir::interpret::AllocId;
|
|||
use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Mutability, UnOp};
|
||||
use rustc_middle::ty::adjustment::PointerCast;
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::{self, AdtDef, FnSig, Ty, UpvarSubsts};
|
||||
use rustc_middle::ty::{self, AdtDef, FnSig, List, Ty, UpvarSubsts};
|
||||
use rustc_middle::ty::{CanonicalUserType, CanonicalUserTypeAnnotation};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
||||
|
@ -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: &'tcx List<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]),
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::FieldIdx;
|
||||
pub use rustc_type_ir::{TyDecoder, TyEncoder};
|
||||
use std::hash::Hash;
|
||||
use std::intrinsics;
|
||||
|
@ -401,6 +402,15 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<FieldIdx> {
|
||||
fn decode(decoder: &mut D) -> &'tcx Self {
|
||||
let len = decoder.read_usize();
|
||||
decoder
|
||||
.interner()
|
||||
.mk_fields_from_iter((0..len).map::<FieldIdx, _>(|_| Decodable::decode(decoder)))
|
||||
}
|
||||
}
|
||||
|
||||
impl_decodable_via_ref! {
|
||||
&'tcx ty::TypeckResults<'tcx>,
|
||||
&'tcx ty::List<Ty<'tcx>>,
|
||||
|
@ -412,6 +422,7 @@ impl_decodable_via_ref! {
|
|||
&'tcx mir::coverage::CodeRegion,
|
||||
&'tcx ty::List<ty::BoundVariableKind>,
|
||||
&'tcx ty::List<ty::Predicate<'tcx>>,
|
||||
&'tcx ty::List<FieldIdx>,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
|
|
@ -155,6 +155,7 @@ pub struct CtxtInterners<'tcx> {
|
|||
layout: InternedSet<'tcx, LayoutS>,
|
||||
adt_def: InternedSet<'tcx, AdtDefData>,
|
||||
external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
|
||||
fields: InternedSet<'tcx, List<FieldIdx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> CtxtInterners<'tcx> {
|
||||
|
@ -178,6 +179,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
layout: Default::default(),
|
||||
adt_def: Default::default(),
|
||||
external_constraints: Default::default(),
|
||||
fields: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1571,6 +1573,7 @@ slice_interners!(
|
|||
projs: pub mk_projs(ProjectionKind),
|
||||
place_elems: pub mk_place_elems(PlaceElem<'tcx>),
|
||||
bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
|
||||
fields: pub mk_fields(FieldIdx),
|
||||
);
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
|
@ -2239,6 +2242,14 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
T::collect_and_apply(iter, |xs| self.mk_place_elems(xs))
|
||||
}
|
||||
|
||||
pub fn mk_fields_from_iter<I, T>(self, iter: I) -> T::Output
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: CollectAndApply<FieldIdx, &'tcx List<FieldIdx>>,
|
||||
{
|
||||
T::collect_and_apply(iter, |xs| self.mk_fields(xs))
|
||||
}
|
||||
|
||||
pub fn mk_substs_trait(
|
||||
self,
|
||||
self_ty: Ty<'tcx>,
|
||||
|
|
|
@ -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, fields } => {
|
||||
block.and(Rvalue::NullaryOp(NullOp::OffsetOf(fields), container))
|
||||
}
|
||||
|
||||
ExprKind::Literal { .. }
|
||||
| ExprKind::NamedConst { .. }
|
||||
| ExprKind::NonHirLiteral { .. }
|
||||
|
|
|
@ -67,7 +67,8 @@ impl Category {
|
|||
| ExprKind::Repeat { .. }
|
||||
| ExprKind::Assign { .. }
|
||||
| ExprKind::AssignOp { .. }
|
||||
| ExprKind::ThreadLocalRef(_) => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
|
||||
| ExprKind::ThreadLocalRef(_)
|
||||
| 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 = tcx.mk_fields_from_iter(indices.iter().copied());
|
||||
|
||||
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, "OffsetOf {", 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,37 @@ 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 body_did = self.typeck_results().hir_owner.to_def_id();
|
||||
let param_env = self.tcx.param_env(body_did);
|
||||
|
||||
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);
|
||||
|
||||
current_ty = self.tcx.normalize_erasing_regions(param_env, 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(param_env, 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 +436,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,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue