From 511e457c4ba88097ee46ef07b7ef80ce4dd4a3c5 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Sun, 11 Sep 2022 00:37:49 -0700 Subject: [PATCH 01/10] offset_of --- compiler/rustc_ast/src/ast.rs | 4 + compiler/rustc_ast/src/mut_visit.rs | 6 + compiler/rustc_ast/src/util/parser.rs | 4 +- compiler/rustc_ast/src/visit.rs | 6 + compiler/rustc_ast_lowering/src/expr.rs | 7 ++ compiler/rustc_ast_lowering/src/lib.rs | 2 + .../rustc_ast_pretty/src/pprust/state/expr.rs | 21 ++++ compiler/rustc_borrowck/src/type_check/mod.rs | 3 +- compiler/rustc_builtin_macros/messages.ftl | 3 + .../src/assert/context.rs | 1 + compiler/rustc_builtin_macros/src/lib.rs | 2 + .../rustc_builtin_macros/src/offset_of.rs | 99 +++++++++++++++++ compiler/rustc_codegen_cranelift/src/base.rs | 5 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 5 +- .../rustc_const_eval/src/interpret/step.rs | 7 +- .../src/transform/check_consts/check.rs | 2 +- .../src/transform/promote_consts.rs | 1 + .../src/transform/validate.rs | 53 ++++++++- compiler/rustc_hir/src/hir.rs | 7 +- compiler/rustc_hir/src/intravisit.rs | 4 + compiler/rustc_hir_pretty/src/lib.rs | 18 +++ compiler/rustc_hir_typeck/src/expr.rs | 105 ++++++++++++++++-- .../rustc_hir_typeck/src/expr_use_visitor.rs | 1 + .../drop_ranges/cfg_build.rs | 2 + .../src/mem_categorization.rs | 1 + compiler/rustc_hir_typeck/src/writeback.rs | 25 ++++- compiler/rustc_middle/src/mir/syntax.rs | 6 +- compiler/rustc_middle/src/mir/tcx.rs | 4 +- compiler/rustc_middle/src/thir.rs | 5 + compiler/rustc_middle/src/thir/visit.rs | 1 + .../rustc_middle/src/ty/typeck_results.rs | 12 ++ .../src/build/expr/as_place.rs | 1 + .../src/build/expr/as_rvalue.rs | 4 + .../src/build/expr/category.rs | 7 +- .../rustc_mir_build/src/build/expr/into.rs | 3 +- .../rustc_mir_build/src/check_unsafety.rs | 1 + compiler/rustc_mir_build/src/thir/cx/expr.rs | 8 ++ compiler/rustc_mir_build/src/thir/print.rs | 13 +++ .../src/move_paths/builder.rs | 2 +- .../src/separate_const_switch.rs | 3 +- compiler/rustc_passes/src/dead.rs | 36 ++++++ compiler/rustc_passes/src/hir_stats.rs | 5 +- compiler/rustc_passes/src/liveness.rs | 5 +- compiler/rustc_passes/src/naked_functions.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/abi/mod.rs | 15 +++ compiler/rustc_ty_utils/src/consts.rs | 2 + library/core/src/mem/mod.rs | 42 +++++++ library/core/tests/lib.rs | 1 + library/core/tests/mem.rs | 74 ++++++++++++ .../clippy_lints/src/loops/never_loop.rs | 3 +- .../matches/significant_drop_in_scrutinee.rs | 1 + .../clippy/clippy_lints/src/utils/author.rs | 4 + .../clippy/clippy_utils/src/eager_or_lazy.rs | 3 +- .../clippy/clippy_utils/src/hir_utils.rs | 9 ++ .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- src/tools/clippy/clippy_utils/src/sugg.rs | 2 + src/tools/clippy/clippy_utils/src/visitors.rs | 1 + src/tools/rustfmt/src/expr.rs | 1 + src/tools/rustfmt/src/utils.rs | 1 + .../const_prop/offset_of.main.ConstProp.diff | 43 +++++++ tests/mir-opt/const_prop/offset_of.rs | 25 +++++ tests/ui/liveness/liveness-offset-of.rs | 26 +++++ tests/ui/liveness/liveness-offset-of.stderr | 33 ++++++ .../{ => macros}/user-defined-macro-rules.rs | 0 tests/ui/offset-of/offset-of-arg-count.rs | 9 ++ tests/ui/offset-of/offset-of-arg-count.stderr | 20 ++++ tests/ui/offset-of/offset-of-dst-field.rs | 14 +++ tests/ui/offset-of/offset-of-dst-field.stderr | 11 ++ tests/ui/offset-of/offset-of-private.rs | 16 +++ tests/ui/offset-of/offset-of-private.stderr | 9 ++ 71 files changed, 841 insertions(+), 38 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/offset_of.rs create mode 100644 tests/mir-opt/const_prop/offset_of.main.ConstProp.diff create mode 100644 tests/mir-opt/const_prop/offset_of.rs create mode 100644 tests/ui/liveness/liveness-offset-of.rs create mode 100644 tests/ui/liveness/liveness-offset-of.stderr rename tests/ui/{ => macros}/user-defined-macro-rules.rs (100%) create mode 100644 tests/ui/offset-of/offset-of-arg-count.rs create mode 100644 tests/ui/offset-of/offset-of-arg-count.stderr create mode 100644 tests/ui/offset-of/offset-of-dst-field.rs create mode 100644 tests/ui/offset-of/offset-of-dst-field.stderr create mode 100644 tests/ui/offset-of/offset-of-private.rs create mode 100644 tests/ui/offset-of/offset-of-private.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ab0409efb3b29..c0fec0cf56581 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -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), + /// Output of the `offset_of!()` macro. + OffsetOf(P, Vec), + /// A macro invocation; pre-expansion. MacCall(P), diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 694d31d8f1fd4..7603bebb17899 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1456,6 +1456,12 @@ pub fn noop_visit_expr( } 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(); diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 3893875e9a405..24b4bd8623f04 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -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 diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ac9b321b71c6f..8a6b5d5c9052d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -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); } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0d212b3e130a2..6863100d9bac0 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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)), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bfd9956b0043e..c969d70960810 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -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}") diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 776bf54244ed1..d31332b3b91db 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -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(); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 375eca1b29d3d..52651aa40c92c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2306,7 +2306,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Rvalue::AddressOf(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Len(..) - | Rvalue::Discriminant(..) => {} + | Rvalue::Discriminant(..) + | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} } } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 83dc1ac50e55d..fca6012a408c1 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -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 diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index c9e3cd486f855..090e00616fb48 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -301,6 +301,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::If(_, _, _) | ExprKind::IncludedBytes(..) | ExprKind::InlineAsm(_) + | ExprKind::OffsetOf(_, _) | ExprKind::Let(_, _, _) | ExprKind::Lit(_) | ExprKind::Loop(_, _, _) diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 134d64ce9cc89..8f86ef44aa3ab 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -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, diff --git a/compiler/rustc_builtin_macros/src/offset_of.rs b/compiler/rustc_builtin_macros/src/offset_of.rs new file mode 100644 index 0000000000000..f5c04015340f0 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/offset_of.rs @@ -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, Vec)> { + 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 { + 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) + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index f5301f9f7f10b..f481290583e06 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -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); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index bd11d47500a55..94de19a9c2935 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -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(); diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 9a366364e769e..4ed83f1db0e96 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -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)?; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 9dad947905397..9d138f176f51c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -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) => { diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 7919aed097a45..2ed84bddfa3ce 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -514,6 +514,7 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::NullaryOp(op, _) => match op { NullOp::SizeOf => {} NullOp::AlignOf => {} + NullOp::OffsetOf(_) => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 8aee019e99494..119fe9801e425 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -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); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 54b328e78f8ee..52ed9660256cb 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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}`, diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 234256ab553c5..df0047d82e12e 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -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); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 74f5b359021d2..1e05003c19ce0 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -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); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 0c0a7515d9c3b..653f2f2886f79 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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>, ) -> 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::() { + 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 + } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ee1c6fbfd650f..94b6a0f8f47d7 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -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, ..) => { diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index 3e9a9ce1b3114..28c44aa5703e8 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -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(..) diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 6c861b5930a1e..f5fca14eca88f 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -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)), } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index e876fa27593d4..9432a5840b276 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -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(&mut self, x: T, span: &dyn Locatable) -> T where T: TypeFoldable>, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index c38a347809f4f..6d7316a32627f 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -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), } #[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 } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 4f00abf7fabc1..5ca8241344832 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -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 => { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 7d79a13d3fde3..084446697cb1a 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -481,6 +481,11 @@ pub enum ExprKind<'tcx> { }, /// Inline assembly, i.e. `asm!()`. InlineAsm(Box>), + /// Field offset (`offset_of!`) + OffsetOf { + container: Ty<'tcx>, + fields: Vec, + }, /// An expression taking a reference to a thread local. ThreadLocalRef(DefId), /// A `yield` expression. diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 5614528c4cb25..5c7ec31cf93d3 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -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]), } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 47943b94c3b18..ef8955b1d3a31 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -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>, + + /// Container types and field indices of `offset_of!` expressions + offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec)>, } /// 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)> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.offset_of_data } + } + + pub fn offset_of_data_mut(&mut self) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec)> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.offset_of_data } + } } /// Validate that the given HirId (respectively its `local_id` part) can be diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index fb775766c6541..7ec57add66b59 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -557,6 +557,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ConstBlock { .. } | ExprKind::StaticRef { .. } | ExprKind::InlineAsm { .. } + | ExprKind::OffsetOf { .. } | ExprKind::Yield { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::Call { .. } => { diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 8631749a524be..a7fdb961248ce 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -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 { .. } diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs index d33401f07645e..3cc104ad96ac3 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -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 { .. } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 9b38ac1cc4ce7..29ff916d2cc9c 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -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, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 03a7f2d70faeb..121ba8166afaa 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -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 diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8e2e92e6f6a94..5329f6dd9d959 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -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(); diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index ed61d6ee78b1d..86d20327f60f5 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -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); diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 64ed7a29f6f3d..736ca62cacccc 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -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(..), _) => {} } } diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs index ef367faf6a707..2479856b727da 100644 --- a/compiler/rustc_mir_transform/src/separate_const_switch.rs +++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs @@ -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, } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 5cfe691df17a1..82b55df106c7e 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -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); + } _ => (), } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 47e032758f23d..dc5e454074ded 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -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) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index a8471ce3b6fb2..b39a8c5598f91 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -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 { .. } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index c398467f03ef8..cf8d9300a1167 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -203,6 +203,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> { | ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) + | ExprKind::OffsetOf(..) | ExprKind::Struct(..) | ExprKind::Repeat(..) | ExprKind::Yield(..) => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d6ee7ac34aaca..9891915d076dd 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1037,6 +1037,7 @@ symbols! { object_safe_for_dispatch, of, offset, + offset_of, omit_gdb_pretty_printer_section, on, on_unimplemented, diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 8d2e92cc76c6f..589cd3cf96b3e 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -124,6 +124,21 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { { Ty::is_unit(self) } + + pub fn offset_of_subfield(self, cx: &C, indices: impl Iterator) -> 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> { diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index b67607a4db63e..e96df77d8598e 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -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, } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 30ec73cabf849..85745334e9ff7 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1279,3 +1279,45 @@ pub trait SizedTypeProperties: Sized { #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] impl 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... +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index f460da35dd3e6..84859a54c2604 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -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)] diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index f7740a114e738..c08fc6fd2fb1f 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -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 { + 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() { + assert_eq!(offset_of!(Foo, x), 0); + assert_eq!(offset_of!(Foo, 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); +} diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index f0a1b1dfe5628..5f1fdf00be8c3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -226,7 +226,8 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, 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(_) diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 04225beeb704b..7945275393c04 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -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(_, _, _, _) | diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 2dac807c420e3..01927b6b5f10d 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -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); diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 28c8571706135..3df40942e7b5a 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -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. diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 3ee7147828bd5..d972ed82c258b 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -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 { diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 354b6d71aa466..ecd712f32dc1f 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -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() { diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index a5a4a921d94ec..e81eadceec0aa 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -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(..) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 1dc19bac98444..5dcd71cef127e 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -662,6 +662,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( | ExprKind::Path(_) | ExprKind::Continue(_) | ExprKind::InlineAsm(_) + | ExprKind::OffsetOf(..) | ExprKind::Err(_) => (), } ControlFlow::Continue(()) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index ac96bedf2fe86..b76e3a2a87c99 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -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) diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index a26375ee64384..ca1716574071b 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -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(..) diff --git a/tests/mir-opt/const_prop/offset_of.main.ConstProp.diff b/tests/mir-opt/const_prop/offset_of.main.ConstProp.diff new file mode 100644 index 0000000000000..0437ec81f1e02 --- /dev/null +++ b/tests/mir-opt/const_prop/offset_of.main.ConstProp.diff @@ -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 + } + } + diff --git a/tests/mir-opt/const_prop/offset_of.rs b/tests/mir-opt/const_prop/offset_of.rs new file mode 100644 index 0000000000000..54e19890c36f0 --- /dev/null +++ b/tests/mir-opt/const_prop/offset_of.rs @@ -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); +} diff --git a/tests/ui/liveness/liveness-offset-of.rs b/tests/ui/liveness/liveness-offset-of.rs new file mode 100644 index 0000000000000..461f546f3eecc --- /dev/null +++ b/tests/ui/liveness/liveness-offset-of.rs @@ -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); +} diff --git a/tests/ui/liveness/liveness-offset-of.stderr b/tests/ui/liveness/liveness-offset-of.stderr new file mode 100644 index 0000000000000..32d13c8ed2cc5 --- /dev/null +++ b/tests/ui/liveness/liveness-offset-of.stderr @@ -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 + diff --git a/tests/ui/user-defined-macro-rules.rs b/tests/ui/macros/user-defined-macro-rules.rs similarity index 100% rename from tests/ui/user-defined-macro-rules.rs rename to tests/ui/macros/user-defined-macro-rules.rs diff --git a/tests/ui/offset-of/offset-of-arg-count.rs b/tests/ui/offset-of/offset-of-arg-count.rs new file mode 100644 index 0000000000000..163b07454ecdf --- /dev/null +++ b/tests/ui/offset-of/offset-of-arg-count.rs @@ -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 +} diff --git a/tests/ui/offset-of/offset-of-arg-count.stderr b/tests/ui/offset-of/offset-of-arg-count.stderr new file mode 100644 index 0000000000000..ebecc982c5174 --- /dev/null +++ b/tests/ui/offset-of/offset-of-arg-count.stderr @@ -0,0 +1,20 @@ +error: expected one of `!`, `(`, `+`, `,`, `::`, or `<`, found `` + --> $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 + diff --git a/tests/ui/offset-of/offset-of-dst-field.rs b/tests/ui/offset-of/offset-of-dst-field.rs new file mode 100644 index 0000000000000..b741cad05c269 --- /dev/null +++ b/tests/ui/offset-of/offset-of-dst-field.rs @@ -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 +} diff --git a/tests/ui/offset-of/offset-of-dst-field.stderr b/tests/ui/offset-of/offset-of-dst-field.stderr new file mode 100644 index 0000000000000..a490f384da562 --- /dev/null +++ b/tests/ui/offset-of/offset-of-dst-field.stderr @@ -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`. diff --git a/tests/ui/offset-of/offset-of-private.rs b/tests/ui/offset-of/offset-of-private.rs new file mode 100644 index 0000000000000..0291b7825cabc --- /dev/null +++ b/tests/ui/offset-of/offset-of-private.rs @@ -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 +} diff --git a/tests/ui/offset-of/offset-of-private.stderr b/tests/ui/offset-of/offset-of-private.stderr new file mode 100644 index 0000000000000..8a186dd5a02e7 --- /dev/null +++ b/tests/ui/offset-of/offset-of-private.stderr @@ -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`. From 61f23e0003af417f0103ffe01718a3198ef9451e Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Sat, 11 Mar 2023 16:01:25 -0800 Subject: [PATCH 02/10] intern offsetof fields --- compiler/rustc_const_eval/src/transform/validate.rs | 2 +- compiler/rustc_middle/src/mir/syntax.rs | 8 ++++---- compiler/rustc_middle/src/mir/type_foldable.rs | 2 +- compiler/rustc_middle/src/thir.rs | 4 ++-- compiler/rustc_middle/src/ty/codec.rs | 11 +++++++++++ compiler/rustc_middle/src/ty/context.rs | 11 +++++++++++ compiler/rustc_mir_build/src/build/expr/as_rvalue.rs | 4 ++-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 2 +- 8 files changed, 33 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 119fe9801e425..9ab2b13d9d161 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -719,7 +719,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let mut current_ty = *container; - for &field in fields { + for field in fields.iter() { match current_ty.kind() { ty::Tuple(fields) => { let Some(&f_ty) = fields.get(field.as_usize()) else { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 6d7316a32627f..d7d0366e101ab 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -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. /// @@ -1212,13 +1212,13 @@ pub enum AggregateKind<'tcx> { } #[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] -pub enum NullOp { +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(Vec), + OffsetOf(&'tcx List), } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -1288,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<'_>, 48); + static_assert_size!(Rvalue<'_>, 40); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index ace856b9f95e0..06874741bb065 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -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> for &'tcx [InlineAsmTemplatePiece] { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 084446697cb1a..fa76579f75b5d 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -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}; @@ -484,7 +484,7 @@ pub enum ExprKind<'tcx> { /// Field offset (`offset_of!`) OffsetOf { container: Ty<'tcx>, - fields: Vec, + fields: &'tcx List, }, /// An expression taking a reference to a thread local. ThreadLocalRef(DefId), diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 8ef4a46a733aa..5454d406dd128 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -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>> RefDecodable<'tcx, D> for ty::List>> RefDecodable<'tcx, D> for ty::List { + fn decode(decoder: &mut D) -> &'tcx Self { + let len = decoder.read_usize(); + decoder + .interner() + .mk_fields_from_iter((0..len).map::(|_| Decodable::decode(decoder))) + } +} + impl_decodable_via_ref! { &'tcx ty::TypeckResults<'tcx>, &'tcx ty::List>, @@ -412,6 +422,7 @@ impl_decodable_via_ref! { &'tcx mir::coverage::CodeRegion, &'tcx ty::List, &'tcx ty::List>, + &'tcx ty::List, } #[macro_export] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f9d3ffc8f0063..e4fa01ddae189 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -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>, } 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(), } } @@ -1585,6 +1587,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> { @@ -2253,6 +2256,14 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_place_elems(xs)) } + pub fn mk_fields_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply>, + { + T::collect_and_apply(iter, |xs| self.mk_fields(xs)) + } + pub fn mk_substs_trait( self, self_ty: Ty<'tcx>, diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index a7fdb961248ce..fbde0b28f54ea 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -481,8 +481,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { })))) } - ExprKind::OffsetOf { container, ref fields } => { - block.and(Rvalue::NullaryOp(NullOp::OffsetOf(fields.clone()), container)) + ExprKind::OffsetOf { container, fields } => { + block.and(Rvalue::NullaryOp(NullOp::OffsetOf(fields), container)) } ExprKind::Literal { .. } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 5329f6dd9d959..ce13d522aae0d 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -667,7 +667,7 @@ impl<'tcx> Cx<'tcx> { 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(); + let fields = tcx.mk_fields_from_iter(indices.iter().copied()); ExprKind::OffsetOf { container, fields } } From b95852b93c8a7b70f9238b0bbd787de68c9241bd Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Tue, 11 Apr 2023 16:38:00 -0700 Subject: [PATCH 03/10] test improvements --- compiler/rustc_middle/src/mir/mod.rs | 6 ++- library/core/tests/mem.rs | 21 +++++++++ ...diff => offset_of.concrete.ConstProp.diff} | 24 +++++----- .../offset_of.generic.ConstProp.diff | 39 ++++++++++++++++ tests/mir-opt/const_prop/offset_of.rs | 44 ++++++++++++++----- tests/ui/liveness/liveness-offset-of.rs | 18 ++++++++ tests/ui/liveness/liveness-offset-of.stderr | 19 +++++++- 7 files changed, 147 insertions(+), 24 deletions(-) rename tests/mir-opt/const_prop/{offset_of.main.ConstProp.diff => offset_of.concrete.ConstProp.diff} (67%) create mode 100644 tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index f985aae9a2255..e72771b61f8ba 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2048,7 +2048,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)) diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index c08fc6fd2fb1f..2351406ddd252 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -438,3 +438,24 @@ fn offset_of_dst() { assert_eq!(offset_of!(Foo, x), 0); assert_eq!(offset_of!(Foo, y), 2); } + +#[test] +#[cfg(not(bootstrap))] +fn offset_of_addr() { + #[repr(C)] + struct Foo { + x: u8, + y: u16, + z: Bar, + } + + #[repr(C)] + struct Bar(u8, u8); + + let base = Foo { x: 0, y: 0, z: Bar(0, 0) }; + + assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, x), ptr::addr_of!(base.x).addr()); + assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, y), ptr::addr_of!(base.y).addr()); + assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.0), ptr::addr_of!(base.z.0).addr()); + assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.1), ptr::addr_of!(base.z.1).addr()); +} diff --git a/tests/mir-opt/const_prop/offset_of.main.ConstProp.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff similarity index 67% rename from tests/mir-opt/const_prop/offset_of.main.ConstProp.diff rename to tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff index 0437ec81f1e02..4e2f1b39d2b16 100644 --- a/tests/mir-opt/const_prop/offset_of.main.ConstProp.diff +++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff @@ -1,8 +1,8 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp +- // MIR for `concrete` before ConstProp ++ // MIR for `concrete` after ConstProp - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/offset_of.rs:+0:11: +0:11 + fn concrete() -> () { + let mut _0: (); // return place in scope 0 at $DIR/offset_of.rs:+0:15: +0:15 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 @@ -22,17 +22,17 @@ 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 +- _1 = OffsetOf(Alpha, [0]); // scope 0 at $DIR/offset_of.rs:+1:13: +1:33 ++ _1 = const 4_usize; // scope 0 at $DIR/offset_of.rs:+1:13: +1:33 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 +- _2 = OffsetOf(Alpha, [1]); // scope 1 at $DIR/offset_of.rs:+2:13: +2:33 ++ _2 = const 0_usize; // scope 1 at $DIR/offset_of.rs:+2:13: +2:33 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 +- _3 = OffsetOf(Alpha, [2, 0]); // scope 2 at $DIR/offset_of.rs:+3:14: +3:36 ++ _3 = const 2_usize; // scope 2 at $DIR/offset_of.rs:+3:14: +3:36 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 +- _4 = OffsetOf(Alpha, [2, 1]); // scope 3 at $DIR/offset_of.rs:+4:14: +4:36 ++ _4 = const 3_usize; // scope 3 at $DIR/offset_of.rs:+4:14: +4:36 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 diff --git a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff new file mode 100644 index 0000000000000..5c6cb47089e82 --- /dev/null +++ b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff @@ -0,0 +1,39 @@ +- // MIR for `generic` before ConstProp ++ // MIR for `generic` after ConstProp + + fn generic() -> () { + let mut _0: (); // return place in scope 0 at $DIR/offset_of.rs:+0:17: +0:17 + let _1: usize; // in scope 0 at $DIR/offset_of.rs:+1:9: +1:11 + scope 1 { + debug gx => _1; // in scope 1 at $DIR/offset_of.rs:+1:9: +1:11 + let _2: usize; // in scope 1 at $DIR/offset_of.rs:+2:9: +2:11 + scope 2 { + debug gy => _2; // in scope 2 at $DIR/offset_of.rs:+2:9: +2:11 + let _3: usize; // in scope 2 at $DIR/offset_of.rs:+3:9: +3:11 + scope 3 { + debug dx => _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 dy => _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:11 + _1 = OffsetOf(Gamma, [0]); // scope 0 at $DIR/offset_of.rs:+1:14: +1:37 + StorageLive(_2); // scope 1 at $DIR/offset_of.rs:+2:9: +2:11 + _2 = OffsetOf(Gamma, [1]); // scope 1 at $DIR/offset_of.rs:+2:14: +2:37 + StorageLive(_3); // scope 2 at $DIR/offset_of.rs:+3:9: +3:11 + _3 = OffsetOf(Delta, [1]); // scope 2 at $DIR/offset_of.rs:+3:14: +3:37 + StorageLive(_4); // scope 3 at $DIR/offset_of.rs:+4:9: +4:11 + _4 = OffsetOf(Delta, [2]); // scope 3 at $DIR/offset_of.rs:+4:14: +4:37 + 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 + } + } + diff --git a/tests/mir-opt/const_prop/offset_of.rs b/tests/mir-opt/const_prop/offset_of.rs index 54e19890c36f0..eabdf84807986 100644 --- a/tests/mir-opt/const_prop/offset_of.rs +++ b/tests/mir-opt/const_prop/offset_of.rs @@ -1,25 +1,49 @@ // unit-test // compile-flags: -O -// EMIT_MIR offset_of.main.ConstProp.diff - #![feature(offset_of)] +use std::marker::PhantomData; use std::mem::offset_of; -#[repr(C)] -struct Foo { +struct Alpha { + x: u8, + y: u16, + z: Beta, +} + +struct Beta(u8, u8); + +struct Gamma { x: u8, y: u16, - z: Bar, + _t: T, } #[repr(C)] -struct Bar(u8, u8); +struct Delta { + _phantom: PhantomData, + x: u8, + y: u16, +} + +// EMIT_MIR offset_of.concrete.ConstProp.diff +fn concrete() { + let x = offset_of!(Alpha, x); + let y = offset_of!(Alpha, y); + let z0 = offset_of!(Alpha, z.0); + let z1 = offset_of!(Alpha, z.1); +} + +// EMIT_MIR offset_of.generic.ConstProp.diff +fn generic() { + let gx = offset_of!(Gamma, x); + let gy = offset_of!(Gamma, y); + let dx = offset_of!(Delta, x); + let dy = offset_of!(Delta, y); +} 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); + concrete(); + generic::<()>(); } diff --git a/tests/ui/liveness/liveness-offset-of.rs b/tests/ui/liveness/liveness-offset-of.rs index 461f546f3eecc..da91de3862fc8 100644 --- a/tests/ui/liveness/liveness-offset-of.rs +++ b/tests/ui/liveness/liveness-offset-of.rs @@ -19,8 +19,26 @@ struct Gamma { b: (), } +struct Delta { + a: (), + b: (), //~ ERROR field `b` is never read +} + +trait Trait { + type Assoc; +} +impl Trait for () { + type Assoc = Delta; +} + +struct Project { + a: u8, //~ ERROR field `a` is never read + b: ::Assoc, +} + fn main() { offset_of!(Alpha, a); offset_of!(Alpha, c.b); offset_of!((Gamma,), 0.b); + offset_of!(Project::<()>, b.a); } diff --git a/tests/ui/liveness/liveness-offset-of.stderr b/tests/ui/liveness/liveness-offset-of.stderr index 32d13c8ed2cc5..afc4c590eebbd 100644 --- a/tests/ui/liveness/liveness-offset-of.stderr +++ b/tests/ui/liveness/liveness-offset-of.stderr @@ -29,5 +29,22 @@ LL | struct Gamma { LL | a: (), | ^ -error: aborting due to 3 previous errors +error: field `b` is never read + --> $DIR/liveness-offset-of.rs:24:5 + | +LL | struct Delta { + | ----- field in this struct +LL | a: (), +LL | b: (), + | ^ + +error: field `a` is never read + --> $DIR/liveness-offset-of.rs:35:5 + | +LL | struct Project { + | ------- field in this struct +LL | a: u8, + | ^ + +error: aborting due to 5 previous errors From b92c2f792c33fbb180358997c5368f336f8912d2 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Sun, 16 Apr 2023 14:13:29 -0700 Subject: [PATCH 04/10] fix incorrect param env in dead code lint --- compiler/rustc_passes/src/dead.rs | 9 +++-- library/core/tests/mem.rs | 2 +- .../dead-code/offset-of-correct-param-env.rs | 40 +++++++++++++++++++ .../dead-code/offset-of.rs} | 0 .../dead-code/offset-of.stderr} | 0 5 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 tests/ui/lint/dead-code/offset-of-correct-param-env.rs rename tests/ui/{liveness/liveness-offset-of.rs => lint/dead-code/offset-of.rs} (100%) rename tests/ui/{liveness/liveness-offset-of.stderr => lint/dead-code/offset-of.stderr} (100%) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 82b55df106c7e..170b0b91e5796 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -242,7 +242,9 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { 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 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 { @@ -253,15 +255,14 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { 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); + 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( - self.tcx.param_env(last_did), + param_env, tys[index.as_usize()], ); } diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index 2351406ddd252..b2a7df6b2e955 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -458,4 +458,4 @@ fn offset_of_addr() { assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, y), ptr::addr_of!(base.y).addr()); assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.0), ptr::addr_of!(base.z.0).addr()); assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.1), ptr::addr_of!(base.z.1).addr()); -} +} \ No newline at end of file diff --git a/tests/ui/lint/dead-code/offset-of-correct-param-env.rs b/tests/ui/lint/dead-code/offset-of-correct-param-env.rs new file mode 100644 index 0000000000000..b7444049a88d9 --- /dev/null +++ b/tests/ui/lint/dead-code/offset-of-correct-param-env.rs @@ -0,0 +1,40 @@ +// check-pass + +#![feature(offset_of)] +#![deny(dead_code)] + +// This struct contains a projection that can only be normalized after getting the field type. +struct A { + a: ::EquateParamTo, +} + +// This is the inner struct that we want to get. +struct MyFieldIsNotDead { + not_dead: u8, +} + +// These are some helpers. +// Inside the param env of `test`, we want to make it so that it considers T=MyFieldIsNotDead. +struct GenericIsEqual(T); +trait Project { + type EquateParamTo; +} +impl Project for GenericIsEqual { + type EquateParamTo = T; +} + +fn test() -> usize +where + GenericIsEqual: Project, +{ + // The first field of the A that we construct here is `> as Project>::EquateParamTo`. + // Typeck normalizes this and figures that the not_dead field is totally fine and accessible. + // But importantly, the normalization ends up with T, which, as we've declared in our param env is MyFieldDead. + // When we're in the param env of the `a` field, the where bound above is not in scope, so we don't know what T is - it's generic. + // We cannot access a field on T. Boom! + std::mem::offset_of!(A>, a.not_dead) +} + +fn main() { + test::(); +} \ No newline at end of file diff --git a/tests/ui/liveness/liveness-offset-of.rs b/tests/ui/lint/dead-code/offset-of.rs similarity index 100% rename from tests/ui/liveness/liveness-offset-of.rs rename to tests/ui/lint/dead-code/offset-of.rs diff --git a/tests/ui/liveness/liveness-offset-of.stderr b/tests/ui/lint/dead-code/offset-of.stderr similarity index 100% rename from tests/ui/liveness/liveness-offset-of.stderr rename to tests/ui/lint/dead-code/offset-of.stderr From 631ea7cc1510c4559ff6a4108b8a3f7b9e615908 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Sun, 16 Apr 2023 14:21:11 -0700 Subject: [PATCH 05/10] use P<[Ident]> instead of Vec --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_builtin_macros/src/offset_of.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index c0fec0cf56581..1e4d3ba47f472 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1471,7 +1471,7 @@ pub enum ExprKind { InlineAsm(P), /// Output of the `offset_of!()` macro. - OffsetOf(P, Vec), + OffsetOf(P, P<[Ident]>), /// A macro invocation; pre-expansion. MacCall(P), diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7603bebb17899..99f1f4bd9685f 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1458,7 +1458,7 @@ pub fn noop_visit_expr( ExprKind::FormatArgs(fmt) => vis.visit_format_args(fmt), ExprKind::OffsetOf(container, fields) => { vis.visit_ty(container); - for field in fields { + for field in fields.iter_mut() { vis.visit_ident(field); } } diff --git a/compiler/rustc_builtin_macros/src/offset_of.rs b/compiler/rustc_builtin_macros/src/offset_of.rs index f5c04015340f0..0ef3e000e414c 100644 --- a/compiler/rustc_builtin_macros/src/offset_of.rs +++ b/compiler/rustc_builtin_macros/src/offset_of.rs @@ -41,7 +41,7 @@ fn parse_args<'a>( cx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, -) -> PResult<'a, (P, Vec)> { +) -> PResult<'a, (P, P<[Ident]>)> { let mut p = cx.new_parser_from_tts(tts); let container = p.parse_ty()?; @@ -71,7 +71,7 @@ fn parse_args<'a>( break; } - Ok((container, fields)) + Ok((container, fields.into())) } pub fn expand_offset_of<'cx>( From 2bcb0182532ba42dde98dfb0262b52f34acd384f Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Sun, 16 Apr 2023 14:44:09 -0700 Subject: [PATCH 06/10] fmt --- compiler/rustc_passes/src/dead.rs | 9 +++------ library/core/tests/mem.rs | 2 +- .../lint/dead-code/offset-of-correct-param-env.rs | 14 ++++++++------ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 170b0b91e5796..3ae5b45d33024 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -255,16 +255,13 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { 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); + 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()], - ); + current_ty = + self.tcx.normalize_erasing_regions(param_env, tys[index.as_usize()]); } _ => span_bug!(expr.span, "named field access on non-ADT"), } diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index b2a7df6b2e955..2351406ddd252 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -458,4 +458,4 @@ fn offset_of_addr() { assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, y), ptr::addr_of!(base.y).addr()); assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.0), ptr::addr_of!(base.z.0).addr()); assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.1), ptr::addr_of!(base.z.1).addr()); -} \ No newline at end of file +} diff --git a/tests/ui/lint/dead-code/offset-of-correct-param-env.rs b/tests/ui/lint/dead-code/offset-of-correct-param-env.rs index b7444049a88d9..ae5d5a68e6a61 100644 --- a/tests/ui/lint/dead-code/offset-of-correct-param-env.rs +++ b/tests/ui/lint/dead-code/offset-of-correct-param-env.rs @@ -27,14 +27,16 @@ fn test() -> usize where GenericIsEqual: Project, { - // The first field of the A that we construct here is `> as Project>::EquateParamTo`. - // Typeck normalizes this and figures that the not_dead field is totally fine and accessible. - // But importantly, the normalization ends up with T, which, as we've declared in our param env is MyFieldDead. - // When we're in the param env of the `a` field, the where bound above is not in scope, so we don't know what T is - it's generic. - // We cannot access a field on T. Boom! + // The first field of the A that we construct here is + // `> as Project>::EquateParamTo`. + // Typeck normalizes this and figures that the not_dead field is totally fine and accessible. + // But importantly, the normalization ends up with T, which, as we've declared in our param + // env is MyFieldDead. When we're in the param env of the `a` field, the where bound above + // is not in scope, so we don't know what T is - it's generic. + // We cannot access a field on T. Boom! std::mem::offset_of!(A>, a.not_dead) } fn main() { test::(); -} \ No newline at end of file +} From f92294f76b130f44659a2e8a1fc4b3bd16c5b20b Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Sun, 16 Apr 2023 15:40:59 -0700 Subject: [PATCH 07/10] bless --- tests/ui/lint/dead-code/offset-of.stderr | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/lint/dead-code/offset-of.stderr b/tests/ui/lint/dead-code/offset-of.stderr index afc4c590eebbd..ed2916461cde8 100644 --- a/tests/ui/lint/dead-code/offset-of.stderr +++ b/tests/ui/lint/dead-code/offset-of.stderr @@ -1,5 +1,5 @@ error: field `b` is never read - --> $DIR/liveness-offset-of.rs:8:5 + --> $DIR/offset-of.rs:8:5 | LL | struct Alpha { | ----- field in this struct @@ -8,13 +8,13 @@ LL | b: (), | ^ | note: the lint level is defined here - --> $DIR/liveness-offset-of.rs:2:9 + --> $DIR/offset-of.rs:2:9 | LL | #![deny(dead_code)] | ^^^^^^^^^ error: field `a` is never read - --> $DIR/liveness-offset-of.rs:13:5 + --> $DIR/offset-of.rs:13:5 | LL | struct Beta { | ---- field in this struct @@ -22,7 +22,7 @@ LL | a: (), | ^ error: field `a` is never read - --> $DIR/liveness-offset-of.rs:18:5 + --> $DIR/offset-of.rs:18:5 | LL | struct Gamma { | ----- field in this struct @@ -30,7 +30,7 @@ LL | a: (), | ^ error: field `b` is never read - --> $DIR/liveness-offset-of.rs:24:5 + --> $DIR/offset-of.rs:24:5 | LL | struct Delta { | ----- field in this struct @@ -39,7 +39,7 @@ LL | b: (), | ^ error: field `a` is never read - --> $DIR/liveness-offset-of.rs:35:5 + --> $DIR/offset-of.rs:35:5 | LL | struct Project { | ------- field in this struct From 3206960ec660f45169c03afb789f0e4ef4e4f193 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Fri, 21 Apr 2023 01:44:17 -0700 Subject: [PATCH 08/10] minor tweaks --- compiler/rustc_ast_pretty/src/pprust/state/expr.rs | 13 ++++++------- compiler/rustc_const_eval/src/interpret/step.rs | 2 +- compiler/rustc_hir_pretty/src/lib.rs | 13 ++++++------- compiler/rustc_hir_typeck/src/expr.rs | 1 + compiler/rustc_mir_build/src/build/expr/category.rs | 4 ++-- compiler/rustc_mir_build/src/thir/print.rs | 2 +- library/core/src/mem/mod.rs | 2 +- src/tools/rustfmt/src/expr.rs | 3 +-- 8 files changed, 19 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index d31332b3b91db..aeb0c76202041 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -558,14 +558,13 @@ impl<'a> State<'a> { self.word(","); self.space(); - let (&first, rest) = - fields.split_first().expect("offset_of! should have at least 1 field"); + if let Some((&first, rest)) = fields.split_first() { + self.print_ident(first); - self.print_ident(first); - - for &field in rest { - self.word("."); - self.print_ident(field); + for &field in rest { + self.word("."); + self.print_ident(field); + } } self.end(); diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 4ed83f1db0e96..4d3a11935085d 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -287,7 +287,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // 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)); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 1e05003c19ce0..2db4f1e50d48e 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1557,14 +1557,13 @@ impl<'a> State<'a> { self.word(","); self.space(); - let (&first, rest) = - fields.split_first().expect("offset_of! should have at least 1 field"); + if let Some((&first, rest)) = fields.split_first() { + self.print_ident(first); - self.print_ident(first); - - for &field in rest { - self.word("."); - self.print_ident(field); + for &field in rest { + self.word("."); + self.print_ident(field); + } } self.word(")"); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 653f2f2886f79..3ffc583d43f61 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3084,6 +3084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { 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) { diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs index 3cc104ad96ac3..d9aa461c19d40 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -53,7 +53,8 @@ impl Category { | ExprKind::Borrow { .. } | ExprKind::AddressOf { .. } | ExprKind::Yield { .. } - | ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)), + | ExprKind::Call { .. } + | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::Into)), ExprKind::Array { .. } | ExprKind::Tuple { .. } @@ -67,7 +68,6 @@ impl Category { | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::ThreadLocalRef(_) - | ExprKind::InlineAsm { .. } | ExprKind::OffsetOf { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::ConstBlock { .. } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 86d20327f60f5..9fec3ce16ee90 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -520,7 +520,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "}", depth_lvl); } OffsetOf { container, fields } => { - print_indented!(self, "InlineAsm {", depth_lvl); + print_indented!(self, "OffsetOf {", depth_lvl); print_indented!(self, format!("container: {:?}", container), depth_lvl + 1); print_indented!(self, "fields: [", depth_lvl + 1); diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 85745334e9ff7..7d2f297152365 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1319,5 +1319,5 @@ impl SizedTypeProperties for T {} #[rustc_builtin_macro] #[cfg(not(bootstrap))] pub macro offset_of($Container:ty, $($fields:tt).+ $(,)?) { - // ...implementation defined... + /* compiler built-in */ } diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index b76e3a2a87c99..a0882eff707f2 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -345,7 +345,6 @@ 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) @@ -400,7 +399,7 @@ pub(crate) fn format_expr( } } ast::ExprKind::Underscore => Some("_".to_owned()), - ast::ExprKind::FormatArgs(..) | ast::ExprKind::IncludedBytes(..) => { + ast::ExprKind::FormatArgs(..) | ast::ExprKind::IncludedBytes(..) | ast::ExprKind::OffsetOf(..) => { // These do not occur in the AST because macros aren't expanded. unreachable!() } From a642563d490e6f15353b88881abe4b3b9ae5b9f4 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Fri, 21 Apr 2023 01:53:34 -0700 Subject: [PATCH 09/10] major test improvements --- library/core/tests/mem.rs | 123 ++++++++++++++++-- .../dead-code/offset-of-correct-param-env.rs | 2 +- .../auxiliary/offset-of-staged-api.rs | 33 +++++ tests/ui/offset-of/offset-of-dst-field.rs | 29 ++++- tests/ui/offset-of/offset-of-dst-field.stderr | 24 +++- tests/ui/offset-of/offset-of-enum.rs | 13 ++ tests/ui/offset-of/offset-of-enum.stderr | 19 +++ .../offset-of-unstable-with-feature.rs | 20 +++ tests/ui/offset-of/offset-of-unstable.rs | 31 +++++ tests/ui/offset-of/offset-of-unstable.stderr | 79 +++++++++++ 10 files changed, 349 insertions(+), 24 deletions(-) create mode 100644 tests/ui/offset-of/auxiliary/offset-of-staged-api.rs create mode 100644 tests/ui/offset-of/offset-of-enum.rs create mode 100644 tests/ui/offset-of/offset-of-enum.stderr create mode 100644 tests/ui/offset-of/offset-of-unstable-with-feature.rs create mode 100644 tests/ui/offset-of/offset-of-unstable.rs create mode 100644 tests/ui/offset-of/offset-of-unstable.stderr diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index 2351406ddd252..7f03381690141 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -388,6 +388,115 @@ fn offset_of() { assert!(offset_of!((u8, u16), 1) <= size_of::<(u8, u16)>() - 2); } +#[test] +#[cfg(not(bootstrap))] +fn offset_of_union() { + #[repr(C)] + union Foo { + x: u8, + y: u16, + z: Bar, + } + + #[repr(C)] + #[derive(Copy, Clone)] + struct Bar(u8, u8); + + assert_eq!(offset_of!(Foo, x), 0); + assert_eq!(offset_of!(Foo, y), 0); + assert_eq!(offset_of!(Foo, z.0), 0); + assert_eq!(offset_of!(Foo, z.1), 1); +} + +#[test] +#[cfg(not(bootstrap))] +fn offset_of_dst() { + #[repr(C)] + struct Alpha { + x: u8, + y: u16, + z: [u8], + } + + trait Trait {} + + #[repr(C)] + struct Beta { + x: u8, + y: u16, + z: dyn Trait, + } + + extern "C" { + type Extern; + } + + #[repr(C)] + struct Gamma { + x: u8, + y: u16, + z: Extern, + } + + assert_eq!(offset_of!(Alpha, x), 0); + assert_eq!(offset_of!(Alpha, y), 2); + + assert_eq!(offset_of!(Beta, x), 0); + assert_eq!(offset_of!(Beta, y), 2); + + assert_eq!(offset_of!(Gamma, x), 0); + assert_eq!(offset_of!(Gamma, y), 2); +} + +#[test] +#[cfg(not(bootstrap))] +fn offset_of_packed() { + #[repr(C, packed)] + struct Foo { + x: u8, + y: u16, + } + + assert_eq!(offset_of!(Foo, x), 0); + assert_eq!(offset_of!(Foo, y), 1); +} + +#[test] +#[cfg(not(bootstrap))] +fn offset_of_projection() { + #[repr(C)] + struct Foo { + x: u8, + y: u16, + } + + trait Projector { + type Type; + } + + impl Projector for () { + type Type = Foo; + } + + assert_eq!(offset_of!(<() as Projector>::Type, x), 0); + assert_eq!(offset_of!(<() as Projector>::Type, y), 2); +} + +#[test] +#[cfg(not(bootstrap))] +fn offset_of_alias() { + #[repr(C)] + struct Foo { + x: u8, + y: u16, + } + + type Bar = Foo; + + assert_eq!(offset_of!(Bar, x), 0); + assert_eq!(offset_of!(Bar, y), 2); +} + #[test] #[cfg(not(bootstrap))] fn const_offset_of() { @@ -425,20 +534,6 @@ fn offset_of_without_const_promotion() { 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); -} - #[test] #[cfg(not(bootstrap))] fn offset_of_addr() { diff --git a/tests/ui/lint/dead-code/offset-of-correct-param-env.rs b/tests/ui/lint/dead-code/offset-of-correct-param-env.rs index ae5d5a68e6a61..2c6fcef250049 100644 --- a/tests/ui/lint/dead-code/offset-of-correct-param-env.rs +++ b/tests/ui/lint/dead-code/offset-of-correct-param-env.rs @@ -33,7 +33,7 @@ where // But importantly, the normalization ends up with T, which, as we've declared in our param // env is MyFieldDead. When we're in the param env of the `a` field, the where bound above // is not in scope, so we don't know what T is - it's generic. - // We cannot access a field on T. Boom! + // If we use the wrong param env, the lint will ICE. std::mem::offset_of!(A>, a.not_dead) } diff --git a/tests/ui/offset-of/auxiliary/offset-of-staged-api.rs b/tests/ui/offset-of/auxiliary/offset-of-staged-api.rs new file mode 100644 index 0000000000000..088086cc58052 --- /dev/null +++ b/tests/ui/offset-of/auxiliary/offset-of-staged-api.rs @@ -0,0 +1,33 @@ +#![crate_type = "lib"] +#![feature(staged_api)] +#![stable(feature = "stable_test_feature", since = "1.0")] + +#[unstable(feature = "unstable_test_feature", issue = "none")] +pub struct Unstable { + #[unstable(feature = "unstable_test_feature", issue = "none")] + pub unstable: u8, +} + +#[stable(feature = "stable_test_feature", since = "1.0")] +pub struct Stable { + #[stable(feature = "stable_test_feature", since = "1.0")] + pub stable: u8, +} + +#[stable(feature = "stable_test_feature", since = "1.0")] +pub struct StableWithUnstableField { + #[unstable(feature = "unstable_test_feature", issue = "none")] + pub unstable: u8, +} + +#[stable(feature = "stable_test_feature", since = "1.0")] +pub struct StableWithUnstableFieldType { + #[stable(feature = "stable_test_feature", since = "1.0")] + pub stable: Unstable, +} + +#[unstable(feature = "unstable_test_feature", issue = "none")] +pub struct UnstableWithStableFieldType { + #[unstable(feature = "unstable_test_feature", issue = "none")] + pub unstable: Stable, +} diff --git a/tests/ui/offset-of/offset-of-dst-field.rs b/tests/ui/offset-of/offset-of-dst-field.rs index b741cad05c269..a0269ca2d1251 100644 --- a/tests/ui/offset-of/offset-of-dst-field.rs +++ b/tests/ui/offset-of/offset-of-dst-field.rs @@ -1,14 +1,33 @@ -#![feature(offset_of)] +#![feature(offset_of, extern_types)] use std::mem::offset_of; -#[repr(C)] -struct Foo { +struct Alpha { x: u8, y: u16, - slice: [u8], + z: [u8], +} + +trait Trait {} + +struct Beta { + x: u8, + y: u16, + z: dyn Trait, +} + +extern { + type Extern; +} + +struct Gamma { + x: u8, + y: u16, + z: Extern, } fn main() { - offset_of!(Foo, slice); //~ ERROR the size for values of type + offset_of!(Alpha, z); //~ ERROR the size for values of type + offset_of!(Beta, z); //~ ERROR the size for values of type + offset_of!(Gamma, z); //~ ERROR the size for values of type } diff --git a/tests/ui/offset-of/offset-of-dst-field.stderr b/tests/ui/offset-of/offset-of-dst-field.stderr index a490f384da562..8e88015b07a74 100644 --- a/tests/ui/offset-of/offset-of-dst-field.stderr +++ b/tests/ui/offset-of/offset-of-dst-field.stderr @@ -1,11 +1,27 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:13:5 + --> $DIR/offset-of-dst-field.rs:30:5 | -LL | offset_of!(Foo, slice); - | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time +LL | offset_of!(Alpha, z); + | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u8]` -error: aborting due to previous error +error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:31:5 + | +LL | offset_of!(Beta, z); + | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Trait + 'static)` + +error[E0277]: the size for values of type `Extern` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:32:5 + | +LL | offset_of!(Gamma, z); + | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `Extern` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/offset-of/offset-of-enum.rs b/tests/ui/offset-of/offset-of-enum.rs new file mode 100644 index 0000000000000..d73505821ff53 --- /dev/null +++ b/tests/ui/offset-of/offset-of-enum.rs @@ -0,0 +1,13 @@ +#![feature(offset_of)] + +use std::mem::offset_of; + +enum Alpha { + One(u8), + Two(u8), +} + +fn main() { + offset_of!(Alpha::One, 0); //~ ERROR expected type, found variant `Alpha::One` + offset_of!(Alpha, Two.0); //~ ERROR no field `Two` on type `Alpha` +} diff --git a/tests/ui/offset-of/offset-of-enum.stderr b/tests/ui/offset-of/offset-of-enum.stderr new file mode 100644 index 0000000000000..6958d199fbdb4 --- /dev/null +++ b/tests/ui/offset-of/offset-of-enum.stderr @@ -0,0 +1,19 @@ +error[E0573]: expected type, found variant `Alpha::One` + --> $DIR/offset-of-enum.rs:11:16 + | +LL | offset_of!(Alpha::One, 0); + | ^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `Alpha` + +error[E0609]: no field `Two` on type `Alpha` + --> $DIR/offset-of-enum.rs:12:23 + | +LL | offset_of!(Alpha, Two.0); + | ^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0573, E0609. +For more information about an error, try `rustc --explain E0573`. diff --git a/tests/ui/offset-of/offset-of-unstable-with-feature.rs b/tests/ui/offset-of/offset-of-unstable-with-feature.rs new file mode 100644 index 0000000000000..7d2eb46c056eb --- /dev/null +++ b/tests/ui/offset-of/offset-of-unstable-with-feature.rs @@ -0,0 +1,20 @@ +// check-pass +// aux-build:offset-of-staged-api.rs + +#![feature(offset_of, unstable_test_feature)] + +use std::mem::offset_of; + +extern crate offset_of_staged_api; + +use offset_of_staged_api::*; + +fn main() { + offset_of!(Unstable, unstable); + offset_of!(Stable, stable); + offset_of!(StableWithUnstableField, unstable); + offset_of!(StableWithUnstableFieldType, stable); + offset_of!(StableWithUnstableFieldType, stable.unstable); + offset_of!(UnstableWithStableFieldType, unstable); + offset_of!(UnstableWithStableFieldType, unstable.stable); +} diff --git a/tests/ui/offset-of/offset-of-unstable.rs b/tests/ui/offset-of/offset-of-unstable.rs new file mode 100644 index 0000000000000..1e19f2091f29f --- /dev/null +++ b/tests/ui/offset-of/offset-of-unstable.rs @@ -0,0 +1,31 @@ +// aux-build:offset-of-staged-api.rs + +#![feature(offset_of)] + +use std::mem::offset_of; + +extern crate offset_of_staged_api; + +use offset_of_staged_api::*; + +fn main() { + offset_of!( + //~^ ERROR use of unstable library feature + Unstable, //~ ERROR use of unstable library feature + unstable + ); + offset_of!(Stable, stable); + offset_of!(StableWithUnstableField, unstable); //~ ERROR use of unstable library feature + offset_of!(StableWithUnstableFieldType, stable); + offset_of!(StableWithUnstableFieldType, stable.unstable); //~ ERROR use of unstable library feature + offset_of!( + //~^ ERROR use of unstable library feature + UnstableWithStableFieldType, //~ ERROR use of unstable library feature + unstable + ); + offset_of!( + //~^ ERROR use of unstable library feature + UnstableWithStableFieldType, //~ ERROR use of unstable library feature + unstable.stable + ); +} diff --git a/tests/ui/offset-of/offset-of-unstable.stderr b/tests/ui/offset-of/offset-of-unstable.stderr new file mode 100644 index 0000000000000..25811a061d7f0 --- /dev/null +++ b/tests/ui/offset-of/offset-of-unstable.stderr @@ -0,0 +1,79 @@ +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/offset-of-unstable.rs:14:9 + | +LL | Unstable, + | ^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/offset-of-unstable.rs:23:9 + | +LL | UnstableWithStableFieldType, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/offset-of-unstable.rs:28:9 + | +LL | UnstableWithStableFieldType, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/offset-of-unstable.rs:12:5 + | +LL | / offset_of!( +LL | | +LL | | Unstable, +LL | | unstable +LL | | ); + | |_____^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/offset-of-unstable.rs:18:5 + | +LL | offset_of!(StableWithUnstableField, unstable); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/offset-of-unstable.rs:20:5 + | +LL | offset_of!(StableWithUnstableFieldType, stable.unstable); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/offset-of-unstable.rs:21:5 + | +LL | / offset_of!( +LL | | +LL | | UnstableWithStableFieldType, +LL | | unstable +LL | | ); + | |_____^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/offset-of-unstable.rs:26:5 + | +LL | / offset_of!( +LL | | +LL | | UnstableWithStableFieldType, +LL | | unstable.stable +LL | | ); + | |_____^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0658`. From 99abe44135e84de2029186384c3ffbd1ad860cca Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Fri, 21 Apr 2023 08:59:30 -0700 Subject: [PATCH 10/10] rustfmt fmt --- src/tools/rustfmt/src/expr.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index a0882eff707f2..824e42191364f 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -399,7 +399,9 @@ pub(crate) fn format_expr( } } ast::ExprKind::Underscore => Some("_".to_owned()), - ast::ExprKind::FormatArgs(..) | ast::ExprKind::IncludedBytes(..) | ast::ExprKind::OffsetOf(..) => { + ast::ExprKind::FormatArgs(..) + | ast::ExprKind::IncludedBytes(..) + | ast::ExprKind::OffsetOf(..) => { // These do not occur in the AST because macros aren't expanded. unreachable!() }