diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs index cf2b0d009acab..52465059bd276 100644 --- a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs +++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs @@ -3,7 +3,7 @@ use super::prelude::*; pub(crate) struct LoopMatchParser; impl NoArgsAttributeParser for LoopMatchParser { const PATH: &[Symbol] = &[sym::loop_match]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Expression)]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Loop)]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::LoopMatch; } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 42c6828ef57b7..26ebcad2f2720 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -68,6 +68,7 @@ pub(crate) mod stability; pub(crate) mod test_attrs; pub(crate) mod traits; pub(crate) mod transparency; +pub(crate) mod unroll; pub(crate) mod util; type AcceptFn = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess>, &ArgParser); diff --git a/compiler/rustc_attr_parsing/src/attributes/unroll.rs b/compiler/rustc_attr_parsing/src/attributes/unroll.rs new file mode 100644 index 0000000000000..45eaf5729860e --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/unroll.rs @@ -0,0 +1,56 @@ +use rustc_ast::{LitIntType, LitKind}; +use rustc_hir::attrs::UnrollAttr; +use rustc_lint_defs::builtin::ILL_FORMED_ATTRIBUTE_INPUT; + +use super::prelude::*; + +pub(crate) struct UnrollParser; +impl SingleAttributeParser for UnrollParser { + const PATH: &[Symbol] = &[sym::unroll]; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Loop), + Allow(Target::ForLoop), + Allow(Target::While), + ]); + const TEMPLATE: AttributeTemplate = template!( + Word, + List: &["always", "never", ""], + "https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute" + ); + + fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { + match args { + ArgParser::NoArgs => Some(AttributeKind::Unroll(UnrollAttr::Hint)), + ArgParser::List(list) => { + let l = cx.expect_single(list)?; + + if let Some(lit) = l.as_lit() { + if let LitKind::Int(val, LitIntType::Unsuffixed) = lit.kind { + let Ok(val) = u32::try_from(val.get()) else { + cx.adcx().expected_integer_literal_in_range( + l.span(), + 0, + u32::MAX as isize, + ); + return None; + }; + return Some(AttributeKind::Unroll(UnrollAttr::Count(val))); + } + } + + match l.meta_item().and_then(|i| i.path().word_sym()) { + Some(sym::always) => Some(AttributeKind::Unroll(UnrollAttr::Always)), + Some(sym::never) => Some(AttributeKind::Unroll(UnrollAttr::Never)), + _ => { + cx.adcx().expected_specific_argument(l.span(), &[sym::always, sym::never]); + None + } + } + } + ArgParser::NameValue(_) => { + cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); + return None; + } + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 750fe10a483fc..9f9f88f3392dc 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -58,6 +58,7 @@ use crate::attributes::stability::*; use crate::attributes::test_attrs::*; use crate::attributes::traits::*; use crate::attributes::transparency::*; +use crate::attributes::unroll::*; use crate::attributes::{AttributeParser as _, AttributeSafety, Combine, Single, WithoutArgs}; use crate::parser::{ ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, NameValueParser, @@ -224,6 +225,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single>, Single>, diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index d05f1baf63dad..2317ebc8bf4d1 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -465,5 +465,8 @@ pub(crate) const ALL_TARGETS: &'static [Policy] = { kind: rustc_hir::target::GenericParamKind::Type, has_default: true, }), + Allow(Target::Loop), + Allow(Target::ForLoop), + Allow(Target::While), ] }; diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 134bc5006dd00..deebfa3c7d094 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -14,6 +14,8 @@ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; +use rustc_hir::Attribute; +use rustc_hir::attrs::{AttributeKind, UnrollAttr}; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ @@ -336,6 +338,50 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } + fn br_with_attrs(&mut self, dest: &'ll BasicBlock, attributes: &[Attribute]) { + unsafe { + let val = llvm::LLVMBuildBr(self.llbuilder, dest); + + let mut nodes = Vec::new(); + + for attribute in attributes { + if let Attribute::Parsed(AttributeKind::Unroll(unroll)) = attribute { + // UnrollAttr::Count needs a second operand, the provided count, but the other + // unroll hints do not. + let md_node = if let UnrollAttr::Count(count) = unroll { + let unroll_meta = self.create_metadata("llvm.loop.unroll.count".as_bytes()); + let count = + llvm::LLVMValueAsMetadata(self.get_const_i32(u64::from(*count))); + self.md_node_in_context(&[unroll_meta, count]) + } else { + let metadata_str = match unroll { + UnrollAttr::Hint => "llvm.loop.unroll.enable", + UnrollAttr::Always => "llvm.loop.unroll.full", + UnrollAttr::Never => "llvm.loop.unroll.disable", + _ => unreachable!(), + }; + let unroll_meta = self.create_metadata(metadata_str.as_bytes()); + self.md_node_in_context(&[unroll_meta]) + }; + nodes.push(md_node); + } + } + + if nodes.len() > 0 { + nodes.insert(0, nodes[0]); + // Create the loop metadata node + let loop_meta_mdnode = self.set_metadata_node(val, llvm::MD_loop, &nodes); + + // Look up the metadata node as a value + let loop_meta_val = llvm::LLVMGetMetadata(val, llvm::MD_loop).unwrap(); + + // Replace the first entry with a reference to itself + // This is required by LLVM. See the LangRef page for llvm.loop metadata. + llvm::LLVMReplaceMDNodeOperandWith(loop_meta_val, 0, loop_meta_mdnode); + } + } + } + fn cond_br( &mut self, cond: &'ll Value, diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index a7b5c71b51285..1a7bb9f6f6997 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1073,9 +1073,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { instruction: &'ll Value, kind_id: MetadataKindId, md_list: &[&'ll Metadata], - ) { + ) -> &'ll Metadata { let md = self.md_node_in_context(md_list); self.set_metadata(instruction, kind_id, md); + md } /// Helper method for the sequence of calls: diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index a240bca80955a..5c4a0d31d0ba8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -961,6 +961,10 @@ unsafe extern "C" { pub(crate) fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char; pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value); + pub(crate) safe fn LLVMGetMetadata<'a>( + Val: &'a Value, + KindID: MetadataKindId, + ) -> Option<&'a Value>; pub(crate) safe fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: MetadataKindId, Node: &'a Value); pub(crate) fn LLVMGlobalSetMetadata<'a>( Val: &'a Value, @@ -990,6 +994,7 @@ unsafe extern "C" { Name: *const c_char, Val: &'a Value, ); + pub(crate) fn LLVMReplaceMDNodeOperandWith(Val: &Value, index: u32, replacement: &Metadata); // Operations on scalar constants pub(crate) fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value; diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b6b95c5f12aae..aad2a221ddb6f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -4,6 +4,7 @@ use rustc_abi::{Align, BackendRepr, ExternAbi, HasDataLayout, Reg, Size, Wrappin use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::packed::Pu128; +use rustc_hir::Attribute; use rustc_hir::lang_items::LangItem; use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; @@ -135,6 +136,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { bx: &mut Bx, target: mir::BasicBlock, mergeable_succ: bool, + attributes: &[Attribute], ) -> MergingSucc { let (needs_landing_pad, is_cleanupret) = self.llbb_characteristics(fx, target); if mergeable_succ && !needs_landing_pad && !is_cleanupret { @@ -150,7 +152,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { // to a trampoline. bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget)); } else { - bx.br(lltarget); + bx.br_with_attrs(lltarget, attributes); } MergingSucc::False } @@ -285,7 +287,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { bx.lifetime_end(tmp, size); } fx.store_return(bx, ret_dest, &fn_abi.ret, llret); - self.funclet_br(fx, bx, target, mergeable_succ) + self.funclet_br(fx, bx, target, mergeable_succ, &[]) } else { bx.unreachable(); MergingSucc::False @@ -353,7 +355,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { bx.codegen_inline_asm(template, operands, options, line_spans, instance, None, None); if let Some(target) = destination { - self.funclet_br(fx, bx, target, mergeable_succ) + self.funclet_br(fx, bx, target, mergeable_succ, &[]) } else { bx.unreachable(); MergingSucc::False @@ -608,7 +610,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let ty::InstanceKind::DropGlue(_, None) = drop_fn.def { // we don't actually need to drop anything. - return helper.funclet_br(self, bx, target, mergeable_succ); + return helper.funclet_br(self, bx, target, mergeable_succ, &[]); } let place = self.codegen_place(bx, location.as_ref()); @@ -717,7 +719,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Don't codegen the panic block if success if known. if const_cond == Some(expected) { - return helper.funclet_br(self, bx, target, mergeable_succ); + return helper.funclet_br(self, bx, target, mergeable_succ, &[]); } // Because we're branching to a panic block (either a `#[cold]` one @@ -851,7 +853,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if is_valid { // a NOP let target = target.unwrap(); - return Some(helper.funclet_br(self, bx, target, mergeable_succ)); + return Some(helper.funclet_br(self, bx, target, mergeable_succ, &[])); } let layout = bx.layout_of(ty); @@ -925,7 +927,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::InstanceKind::DropGlue(_, None) => { // Empty drop glue; a no-op. let target = target.unwrap(); - return helper.funclet_br(self, bx, target, mergeable_succ); + return helper.funclet_br(self, bx, target, mergeable_succ, &[]); } ty::InstanceKind::Intrinsic(def_id) => { let intrinsic = bx.tcx().intrinsic(def_id).unwrap(); @@ -1013,7 +1015,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match intrinsic_result { IntrinsicResult::Operand(_) | IntrinsicResult::WroteIntoPlace => { return if let Some(target) = target { - helper.funclet_br(self, bx, target, mergeable_succ) + helper.funclet_br(self, bx, target, mergeable_succ, &[]) } else { bx.unreachable(); MergingSucc::False @@ -1123,7 +1125,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &ArgAbi { layout: result_layout, mode: PassMode::Direct(ArgAttributes::new()) }, llret, ); - return helper.funclet_br(self, bx, target, mergeable_succ); + return helper.funclet_br(self, bx, target, mergeable_succ, &[]); } else { bx.unreachable(); return MergingSucc::False; @@ -1567,7 +1569,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::TerminatorKind::Goto { target } => { - helper.funclet_br(self, bx, target, mergeable_succ()) + helper.funclet_br(self, bx, target, mergeable_succ(), &terminator.attributes) } mir::TerminatorKind::SwitchInt { ref discr, ref targets } => { diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 5092f28a33f7b..67d4a61ef8183 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -2,6 +2,7 @@ use std::assert_matches; use std::ops::Deref; use rustc_abi::{Align, Scalar, Size, WrappingRange}; +use rustc_hir::Attribute; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; @@ -77,6 +78,9 @@ pub trait BuilderMethods<'a, 'tcx>: fn ret_void(&mut self); fn ret(&mut self, v: Self::Value); fn br(&mut self, dest: Self::BasicBlock); + fn br_with_attrs(&mut self, dest: Self::BasicBlock, _attributes: &[Attribute]) { + self.br(dest) + } fn cond_br( &mut self, cond: Self::Value, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c61b8d66af426..f0fee5cc50013 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -401,6 +401,8 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // - https://github.com/rust-lang/rust/issues/130494 gated!(pin_v2, pin_ergonomics, experimental!(pin_v2)), + gated!(unroll, loop_hints, experimental!(loop_hints)), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index da45596d0b74e..2d7e8185ce48f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -606,6 +606,8 @@ declare_features! ( (unstable, link_arg_attribute, "1.76.0", Some(99427)), /// Target features on loongarch. (unstable, loongarch_target_feature, "1.73.0", Some(150252)), + /// Allows use of loop optimization hints via attributes. + (unstable, loop_hints, "CURRENT_RUSTC_VERSION", Some(156874)), /// Allows fused `loop`/`match` for direct intraprocedural jumps. (incomplete, loop_match, "1.90.0", Some(132306)), /// Target features on m68k. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 4ff56a640c19e..fd3ffccacea82 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -129,6 +129,14 @@ impl InlineAttr { } } +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, StableHash, PrintAttribute)] +pub enum UnrollAttr { + Hint, + Always, + Never, + Count(u32), +} + #[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq, StableHash, PrintAttribute)] pub enum InstructionSetAttr { ArmA32, @@ -1615,6 +1623,9 @@ pub enum AttributeKind { limit: Limit, }, + /// Represents `#[unroll]` + Unroll(UnrollAttr), + /// Represents `#[unstable_feature_bound]`. UnstableFeatureBound(ThinVec<(Symbol, Span)>), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 7169fa433ffd8..1403951580668 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -197,6 +197,7 @@ impl AttributeKind { ThreadLocal => No, TrackCaller(..) => Yes, TypeLengthLimit { .. } => No, + Unroll(..) => No, UnstableFeatureBound(..) => No, UnstableRemoved(..) => Yes, Used { .. } => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 9d14f9de3078d..6cb7420b19112 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -192,7 +192,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed, AttrId); -print_disp!(u8, u16, u32, u128, usize, bool, NonZero, Limit); +print_disp!(u8, u16, u32, u64, u128, usize, bool, NonZero, Limit); print_debug!( Symbol, Ident, diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 50705736f9627..2c5615a13f402 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -67,6 +67,9 @@ pub enum Target { MacroCall, Crate, Delegation { mac: bool }, + ForLoop, + While, + Loop, } impl Display for Target { @@ -113,7 +116,10 @@ impl Target { | Target::MacroCall | Target::Crate | Target::WherePredicate - | Target::Delegation { .. } => false, + | Target::Delegation { .. } + | Target::Loop + | Target::While + | Target::ForLoop => false, } } @@ -256,6 +262,9 @@ impl Target { match &expr.kind { ast::ExprKind::Closure(..) | ast::ExprKind::Gen(..) => Self::Closure, ast::ExprKind::Paren(e) => Self::from_expr(&e), + ast::ExprKind::ForLoop { .. } => Self::ForLoop, + ast::ExprKind::Loop(..) => Self::Loop, + ast::ExprKind::While(..) => Self::While, _ => Self::Expression, } } @@ -307,6 +316,9 @@ impl Target { Target::MacroCall => "macro call", Target::Crate => "crate", Target::Delegation { .. } => "delegation", + Target::Loop => "loop", + Target::ForLoop => "for loop", + Target::While => "while loop", } } @@ -358,6 +370,9 @@ impl Target { Target::MacroCall => "macro calls", Target::Crate => "crates", Target::Delegation { .. } => "delegations", + Target::ForLoop => "for loops", + Target::Loop => "loops", + Target::While => "while loops", } } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 57c2883ef42e4..2a50532ee74c6 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1730,11 +1730,11 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(BasicBlockData<'_>, 152); + static_assert_size!(BasicBlockData<'_>, 176); static_assert_size!(LocalDecl<'_>, 40); static_assert_size!(SourceScopeData<'_>, 64); static_assert_size!(Statement<'_>, 56); - static_assert_size!(Terminator<'_>, 96); + static_assert_size!(Terminator<'_>, 120); static_assert_size!(VarDebugInfo<'_>, 88); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 1cd11bbd9ce4f..81ef78713f2be 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -4,7 +4,7 @@ use std::slice; use rustc_ast::InlineAsmOptions; use rustc_data_structures::packed::Pu128; -use rustc_hir::LangItem; +use rustc_hir::{Attribute, LangItem}; use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use smallvec::{SmallVec, smallvec}; @@ -413,6 +413,7 @@ impl fmt::Display for AssertKind { pub struct Terminator<'tcx> { pub source_info: SourceInfo, pub kind: TerminatorKind<'tcx>, + pub attributes: Vec, } impl<'tcx> Terminator<'tcx> { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index e6e911dece742..ebc8903a65438 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -516,7 +516,7 @@ macro_rules! make_mir_visitor { terminator: &$($mutability)? Terminator<'tcx>, location: Location ) { - let Terminator { source_info, kind } = terminator; + let Terminator { source_info, kind, attributes: _ } = terminator; self.visit_source_info(source_info); match kind { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 0cc57e5021f86..e0d2ad2be98a7 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -15,9 +15,10 @@ use std::sync::Arc; use rustc_abi::{FieldIdx, Integer, Size, VariantIdx}; use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece, Mutability}; +use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd}; +use rustc_hir::{Attribute, BindingMode, ByRef, HirId, MatchSource, RangeEnd}; use rustc_index::{IndexVec, newtype_index}; use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeVisitable}; use rustc_span::def_id::LocalDefId; @@ -59,6 +60,7 @@ macro_rules! thir_with_elements { #[derive(Debug, StableHash, Clone)] pub struct Thir<'tcx> { pub body_type: BodyTy<'tcx>, + pub attributes: FxIndexMap>, $( pub $name: IndexVec<$id, $value>, )* @@ -68,6 +70,7 @@ macro_rules! thir_with_elements { pub fn new(body_type: BodyTy<'tcx>) -> Thir<'tcx> { Thir { body_type, + attributes: FxIndexMap::default(), $( $name: IndexVec::new(), )* diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 08a8491bab6c5..c398873e75e2f 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -241,6 +241,7 @@ TrivialTypeTraversalImpls! { rustc_abi::VariantIdx, rustc_ast::InlineAsmOptions, rustc_ast::InlineAsmTemplatePiece, + rustc_hir::Attribute, rustc_hir::CoroutineKind, rustc_hir::HirId, rustc_hir::MatchSource, diff --git a/compiler/rustc_mir_build/src/builder/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs index bb97d006734e9..449223313f126 100644 --- a/compiler/rustc_mir_build/src/builder/cfg.rs +++ b/compiler/rustc_mir_build/src/builder/cfg.rs @@ -122,7 +122,7 @@ impl<'tcx> CFG<'tcx> { block: BasicBlock, source_info: SourceInfo, kind: TerminatorKind<'tcx>, - ) { + ) -> &mut Terminator<'tcx> { debug!("terminating block {:?} <- {:?}", block, kind); debug_assert!( self.block_data(block).terminator.is_none(), @@ -130,11 +130,18 @@ impl<'tcx> CFG<'tcx> { block, self.block_data(block) ); - self.block_data_mut(block).terminator = Some(Terminator { source_info, kind }); + self.block_data_mut(block).terminator = + Some(Terminator { source_info, kind, attributes: Vec::new() }); + self.block_data_mut(block).terminator.as_mut().unwrap() } /// In the `origin` block, push a `goto -> target` terminator. - pub(crate) fn goto(&mut self, origin: BasicBlock, source_info: SourceInfo, target: BasicBlock) { + pub(crate) fn goto( + &mut self, + origin: BasicBlock, + source_info: SourceInfo, + target: BasicBlock, + ) -> &mut Terminator<'tcx> { self.terminate(origin, source_info, TerminatorKind::Goto { target }) } } diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index b227923292779..2a2822b3cca9c 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs @@ -318,6 +318,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { data.terminator = Some(Terminator { source_info: SourceInfo { span, scope: self.source_scope }, kind: terminator, + attributes: Vec::new(), }); Ok(data) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 6ab1995751791..93018cea5ec65 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -238,7 +238,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block(); - this.cfg.goto(body_block_end, source_info, loop_block); + + let goto = this.cfg.goto(body_block_end, source_info, loop_block); + if let Some(attrs) = this.thir.attributes.get(&expr_id) { + goto.attributes = attrs.clone(); + } // Loops are only exited by `break` expressions. None diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index f1df90f93fd63..3246dab73dcbf 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -26,7 +26,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TerminatorKind::FalseEdge { real_target, imaginary_target }, ); } else { - self.cfg.goto(from_block, source_info, real_target) + self.cfg.goto(from_block, source_info, real_target); } } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b32d7dce4f4d3..27c8387bc17b6 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -57,6 +57,25 @@ impl<'tcx> ThirBuildCx<'tcx> { trace!(?expr.ty); + let mut attrs = Vec::new(); + + if let ExprKind::Loop { .. } = expr.kind { + // For loops defined with loop and while, the expr already has the attrs + if let rustc_hir::Node::Block(_) = self.tcx.parent_hir_node(hir_expr.hir_id) { + attrs = rustc_hir::attrs::HasAttrs::get_attrs(hir_expr.hir_id, &self.tcx).to_vec(); + } + + // For loop desugaring puts us pretty deep down the HIR tree + if let rustc_hir::Node::Arm(arm) = self.tcx.parent_hir_node(hir_expr.hir_id) { + if let rustc_hir::Node::Expr(expr) = self.tcx.parent_hir_node(arm.hir_id) { + if let rustc_hir::Node::Expr(expr) = self.tcx.parent_hir_node(expr.hir_id) { + attrs = + rustc_hir::attrs::HasAttrs::get_attrs(expr.hir_id, &self.tcx).to_vec(); + } + } + } + } + // Now apply adjustments, if any. if self.apply_adjustments { for adjustment in self.typeck_results.expr_adjustments(hir_expr) { @@ -68,16 +87,19 @@ impl<'tcx> ThirBuildCx<'tcx> { trace!(?expr.ty, "after adjustments"); + let ty = expr.ty; + let value = self.thir.exprs.push(expr); + + if !attrs.is_empty() { + self.thir.attributes.insert(value, attrs); + } + // Finally, wrap this up in the expr's scope. expr = Expr { temp_scope_id: expr_scope.local_id, - ty: expr.ty, + ty, span: hir_expr.span, - kind: ExprKind::Scope { - region_scope: expr_scope, - value: self.thir.exprs.push(expr), - hir_id: hir_expr.hir_id, - }, + kind: ExprKind::Scope { region_scope: expr_scope, value, hir_id: hir_expr.hir_id }, }; // OK, all done! diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 85f23b8332a1c..d0e63cf2b2826 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -21,7 +21,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { blocks.push(mir::BasicBlockData::new_stmts( std::iter::repeat(&nop).cloned().take(n).collect(), - Some(mir::Terminator { source_info, kind }), + Some(mir::Terminator { source_info, kind, attributes: Vec::new() }), false, )) }; diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index d96c7d84dbefd..fa98cc2f932be 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -86,7 +86,11 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { let cur_len = body.basic_blocks.len(); let mut new_block = |source_info: SourceInfo, is_cleanup: bool, target: BasicBlock| { let block = BasicBlockData::new( - Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), + Some(Terminator { + source_info, + kind: TerminatorKind::Goto { target }, + attributes: Vec::new(), + }), is_cleanup, ); let idx = cur_len + new_blocks.len(); diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index 46d5773102583..055f64078a315 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -95,7 +95,11 @@ fn add_move_for_packed_drop<'tcx>( let storage_dead_block = patch.new_block(BasicBlockData::new_stmts( vec![Statement::new(source_info, StatementKind::StorageDead(temp))], - Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), + Some(Terminator { + source_info, + kind: TerminatorKind::Goto { target }, + attributes: Vec::new(), + }), is_cleanup, )); diff --git a/compiler/rustc_mir_transform/src/check_enums.rs b/compiler/rustc_mir_transform/src/check_enums.rs index 56f3ddc962528..173289482e703 100644 --- a/compiler/rustc_mir_transform/src/check_enums.rs +++ b/compiler/rustc_mir_transform/src/check_enums.rs @@ -61,6 +61,7 @@ impl<'tcx> crate::MirPass<'tcx> for CheckEnums { basic_blocks[block].terminator = Some(Terminator { source_info, kind: TerminatorKind::Goto { target: new_block }, + attributes: Vec::new(), }); } EnumCheckType::Direct { source_op, discr, op_size, valid_discrs } => { @@ -395,6 +396,7 @@ fn insert_direct_enum_check<'tcx>( invalid_discr_block, ), }, + attributes: Vec::new(), }); // Abort in case of an invalid enum discriminant. @@ -414,6 +416,7 @@ fn insert_direct_enum_check<'tcx>( // make a failing UB check turn into much worse UB when we start unwinding. unwind: UnwindAction::Unreachable, }, + attributes: Vec::new(), }); } @@ -459,6 +462,7 @@ fn insert_uninhabited_enum_check<'tcx>( // make a failing UB check turn into much worse UB when we start unwinding. unwind: UnwindAction::Unreachable, }, + attributes: Vec::new(), }); } @@ -536,5 +540,6 @@ fn insert_niche_check<'tcx>( // make a failing UB check turn into much worse UB when we start unwinding. unwind: UnwindAction::Unreachable, }, + attributes: Vec::new(), }); } diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs index 4f913c1fca026..810d0ca44bb04 100644 --- a/compiler/rustc_mir_transform/src/check_pointers.rs +++ b/compiler/rustc_mir_transform/src/check_pointers.rs @@ -116,6 +116,7 @@ pub(crate) fn check_pointers<'tcx, F>( // worse UB when we start unwinding. unwind: UnwindAction::Unreachable, }, + attributes: Vec::new(), }); } } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 4ae6e9a8885d1..4b1070a8e1be5 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -261,7 +261,7 @@ impl<'tcx> TransformVisitor<'tcx> { body.basic_blocks_mut().push(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: Vec::new() }), false, )); @@ -633,6 +633,7 @@ fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local bb_data.terminator = Some(Terminator { source_info: terminator.source_info, kind: TerminatorKind::Goto { target: target.unwrap() }, + attributes: Vec::new(), }); local } @@ -1094,7 +1095,7 @@ fn insert_switch<'tcx>( 0, BasicBlockData::new_stmts( vec![assign], - Some(Terminator { source_info, kind: switch }), + Some(Terminator { source_info, kind: switch, attributes: Vec::new() }), false, ), ); @@ -1106,7 +1107,10 @@ fn insert_switch<'tcx>( fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock { let source_info = SourceInfo::outermost(body.span); - body.basic_blocks_mut().push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) + body.basic_blocks_mut().push(BasicBlockData::new( + Some(Terminator { source_info, kind, attributes: Vec::new() }), + false, + )) } fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) -> Statement<'tcx> { @@ -1129,7 +1133,7 @@ fn insert_poll_ready_block<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> Ba let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().push(BasicBlockData::new_stmts( [return_poll_ready_assign(tcx, source_info)].to_vec(), - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: Vec::new() }), false, )) } @@ -1217,7 +1221,12 @@ fn generate_poison_block_and_redirect_unwinds_there<'tcx>( let source_info = SourceInfo::outermost(body.span); let poison_block = body.basic_blocks_mut().push(BasicBlockData::new_stmts( vec![transform.set_discr(VariantIdx::new(CoroutineArgs::POISONED), source_info)], - Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), + Some(Terminator { + source_info, + kind: TerminatorKind::UnwindResume, + + attributes: Vec::new(), + }), true, )); @@ -1228,8 +1237,12 @@ fn generate_poison_block_and_redirect_unwinds_there<'tcx>( // An existing `Resume` terminator is redirected to jump to our dedicated // "poisoning block" above. if idx != poison_block { - *block.terminator_mut() = - Terminator { source_info, kind: TerminatorKind::Goto { target: poison_block } }; + *block.terminator_mut() = Terminator { + source_info, + kind: TerminatorKind::Goto { target: poison_block }, + + attributes: Vec::new(), + }; } } else if !block.is_cleanup // Any terminators that *can* unwind but don't have an unwind target set are also @@ -1375,7 +1388,12 @@ fn create_cases<'tcx>( // Then jump to the real target let block = body.basic_blocks_mut().push(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), + Some(Terminator { + source_info, + kind: TerminatorKind::Goto { target }, + + attributes: Vec::new(), + }), false, )); diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 372f7117a0119..d24142ea38bb4 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -115,6 +115,7 @@ fn build_pin_fut<'tcx>( call_source: CallSource::Misc, fn_span: span, }, + attributes: Vec::new(), }), false, )); @@ -174,6 +175,7 @@ fn build_poll_switch<'tcx>( unreachable_block, ), }, + attributes: Vec::new(), }), false, )) @@ -489,6 +491,7 @@ pub(super) fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body Terminator { source_info, kind: TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut: _ }, + .. } => { if let Some(local) = place.as_local() && local == SELF_ARG @@ -552,8 +555,10 @@ pub(super) fn insert_clean_drop<'tcx>( }; // Create a block to destroy an unresumed coroutines. This can only destroy upvars. - body.basic_blocks_mut() - .push(BasicBlockData::new(Some(Terminator { source_info, kind: term }), false)) + body.basic_blocks_mut().push(BasicBlockData::new( + Some(Terminator { source_info, kind: term, attributes: Vec::new() }), + false, + )) } #[tracing::instrument(level = "trace", skip(tcx, transform, body))] @@ -748,7 +753,8 @@ pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>( drop: None, async_fut: None, }; - body.basic_blocks_mut()[call_bb].terminator = Some(Terminator { source_info, kind }); + body.basic_blocks_mut()[call_bb].terminator = + Some(Terminator { source_info, kind, attributes: Vec::new() }); if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_proxy_async", &body) { dumper.dump_mir(&body); diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index b0fc5e90f07bd..e93be9807d6cc 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -72,6 +72,7 @@ impl<'tcx> MockBlocks<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)), kind, + attributes: Vec::new(), }), false, )) diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index ae99339c02178..a7a1a191a402c 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -173,6 +173,7 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { discr: parent_op, targets: eq_targets, }, + attributes: Vec::new(), }), bbs[parent].is_cleanup, ); @@ -231,6 +232,7 @@ fn evaluate_candidate<'tcx>( let Terminator { kind: TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr }, source_info, + attributes: _, } = bbs[child].terminator() else { return None; diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index c835ff7dbe070..1c2612fa5451f 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -1394,7 +1394,7 @@ where #[instrument(level = "trace", skip(self), ret)] fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock { self.elaborator.patch().new_block(BasicBlockData::new( - Some(Terminator { source_info: self.source_info, kind: k }), + Some(Terminator { source_info: self.source_info, kind: k, attributes: Vec::new() }), unwind.is_cleanup(), )) } @@ -1408,7 +1408,7 @@ where ) -> BasicBlock { self.elaborator.patch().new_block(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info: self.source_info, kind: k }), + Some(Terminator { source_info: self.source_info, kind: k, attributes: Vec::new() }), unwind.is_cleanup(), )) } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 31871c62fa7a2..8d50b6b2c2308 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -872,6 +872,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( Some(Terminator { source_info: terminator.source_info, kind: TerminatorKind::Goto { target: block }, + attributes: Vec::new(), }), caller_body[block].is_cleanup, ); @@ -1010,6 +1011,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( caller_body[callsite.block].terminator = Some(Terminator { source_info: callsite.source_info, kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, + attributes: Vec::new(), }); // Copy required constants from the callee_body into the caller_body. Although we are only diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index 015bae56cf57e..ab3a064cc12da 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -92,6 +92,7 @@ impl<'tcx> MirPatch<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::UnwindResume, + attributes: Vec::new(), }), true, )); @@ -108,6 +109,7 @@ impl<'tcx> MirPatch<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::Unreachable, + attributes: Vec::new(), }), true, )); @@ -124,6 +126,7 @@ impl<'tcx> MirPatch<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::Unreachable, + attributes: Vec::new(), }), false, )); @@ -142,6 +145,7 @@ impl<'tcx> MirPatch<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::UnwindTerminate(reason), + attributes: Vec::new(), }), true, )); diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index f3e86c4eef75d..8db4a572a072e 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -746,6 +746,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { Some(Terminator { source_info: SourceInfo::outermost(span), kind: TerminatorKind::Return, + attributes: Vec::new(), }), false, )) @@ -834,6 +835,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { Terminator { source_info: terminator.source_info, kind: mem::replace(&mut terminator.kind, TerminatorKind::Goto { target }), + attributes: Vec::new(), } }; diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 7f92199dec455..9d41580062ea1 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -315,7 +315,10 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) let return_block = BasicBlock::new(1); let mut blocks = IndexVec::with_capacity(2); let block = |blocks: &mut IndexVec<_, _>, kind| { - blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) + blocks.push(BasicBlockData::new( + Some(Terminator { source_info, kind, attributes: Vec::new() }), + false, + )) }; if ty.is_some() { block(&mut blocks, TerminatorKind::Goto { target: return_block }); @@ -365,6 +368,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) call_source: CallSource::Misc, fn_span: span, }, + attributes: Vec::new(), }); } else { let patch = { @@ -522,7 +526,7 @@ fn build_thread_local_shim<'tcx>( Rvalue::ThreadLocalRef(def_id), ))), )], - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: Vec::new() }), false, )]); @@ -610,7 +614,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { let source_info = self.source_info(); self.blocks.push(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind }), + Some(Terminator { source_info, kind, attributes: Vec::new() }), is_cleanup, )) } @@ -957,7 +961,7 @@ fn build_call_shim<'tcx>( let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| { blocks.push(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind }), + Some(Terminator { source_info, kind, attributes: Vec::new() }), is_cleanup, )) }; @@ -1086,7 +1090,7 @@ pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { let start_block = BasicBlockData::new_stmts( vec![statement], - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: Vec::new() }), false, ); @@ -1139,7 +1143,7 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t let statements = vec![stmt]; let start_block = BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: Vec::new() }), false, ); let source = MirSource::from_instance(ty::InstanceKind::FnPtrAddrShim(def_id, self_ty)); @@ -1237,7 +1241,7 @@ fn build_construct_coroutine_by_move_shim<'tcx>( let statements = vec![stmt]; let start_block = BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: Vec::new() }), false, ); diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 3a0d0ce474d77..4da0b3ed3ad31 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -82,7 +82,10 @@ pub(super) fn build_async_drop_shim<'tcx>( let return_block = BasicBlock::new(1); let mut blocks = IndexVec::with_capacity(2); let block = |blocks: &mut IndexVec<_, _>, kind| { - blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) + blocks.push(BasicBlockData::new( + Some(Terminator { source_info, kind, attributes: Vec::new() }), + false, + )) }; block( &mut blocks, @@ -374,6 +377,8 @@ fn build_adrop_for_adrop_shim<'tcx>( call_source: CallSource::Misc, fn_span: span, }, + + attributes: Vec::new(), }), false, )); @@ -397,11 +402,13 @@ fn build_adrop_for_adrop_shim<'tcx>( call_source: CallSource::Misc, fn_span: span, }, + + attributes: Vec::new(), }), false, )); blocks.push(BasicBlockData::new( - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: Vec::new() }), false, )); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f6765d604acb3..b4d987a749c0e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -406,6 +406,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::TestRunner(..) => (), AttributeKind::ThreadLocal => (), AttributeKind::TypeLengthLimit { .. } => (), + AttributeKind::Unroll(..) => (), AttributeKind::UnstableFeatureBound(..) => (), AttributeKind::UnstableRemoved(..) => (), AttributeKind::Used { .. } => (), @@ -874,7 +875,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::ExprField | Target::Crate | Target::MacroCall - | Target::Delegation { .. } => None, + | Target::Delegation { .. } + | Target::Loop + | Target::ForLoop + | Target::While => None, } { self.tcx.dcx().emit_err(errors::DocAliasBadLocation { span, location }); return; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7869731ef82f7..bed83e0f46e10 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1200,6 +1200,7 @@ symbols! { loongarch64, loongarch_target_feature, loop_break_value, + loop_hints, loop_match, lr, lt, @@ -2175,6 +2176,7 @@ symbols! { unreachable_display, unreachable_macro, unrestricted_attribute_tokens, + unroll, unsafe_attributes, unsafe_binders, unsafe_block_in_unsafe_fn, diff --git a/src/doc/unstable-book/src/language-features/loop-hints.md b/src/doc/unstable-book/src/language-features/loop-hints.md new file mode 100644 index 0000000000000..cb090ab4c44d9 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/loop-hints.md @@ -0,0 +1,24 @@ +# `loop_hints` + +The tracking issue for this feature is: [#156874] + +[#156874]: https://github.com/rust-lang/rust/issues/156874 + +------ + +Loop unrolling can be a powerful optimization but like inlining, it is sometimes useful to +manually provide hints to optimizations. + +`#[unroll]` will encourage unrolling of a loop. + +`#[unroll(always)]` is a stronger hint and can cause optimizations to completely ignore the code +side growth from repeating a loop body. + +`#[unroll(never)]` is a strong hint to not unroll the loop at all. Note that other loop +optimizations may still be applied. + +`#[unroll(N)]` is a hint to unroll `N` iterations of the loop. + +In all cases these are just hints and may be ignored. But unlike function inlining hints, +loops tend to be heavily modified during compilation, which can make obeying hints challenging. +If the attribute doesn't do what you want, please file an issue. diff --git a/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs new file mode 100644 index 0000000000000..c132d0d4beaa7 --- /dev/null +++ b/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs @@ -0,0 +1,64 @@ +//@ compile-flags: -O -Cno-prepopulate-passes + +#![crate_type = "lib"] +#![feature(loop_hints)] + +// This test ensures that we emit the expected LLVM metadata for loop hint attributes. +// It does not test that loops are optimized as expected, because successful unrolling removes the +// branch and thus the metadata. + +unsafe extern "C" { + fn maybe_has_side_effect(); +} + +#[no_mangle] +pub fn unroll_hint() { + // CHECK-LABEL: @unroll_hint + // CHECK: !llvm.loop ![[HINT:[0-9]+]] + #[unroll] + for _ in 0..10 { + unsafe { maybe_has_side_effect() } + } +} + +#[no_mangle] +pub fn unroll_full() { + // CHECK-LABEL: @unroll_full + // CHECK: !llvm.loop ![[FULL:[0-9]+]] + #[unroll(always)] + for _ in 0..10 { + unsafe { maybe_has_side_effect() } + } +} + +#[no_mangle] +pub fn unroll_never() { + // CHECK-LABEL: @unroll_never + // CHECK: !llvm.loop ![[DISABLE:[0-9]+]] + #[unroll(never)] + for _ in 0..10 { + unsafe { maybe_has_side_effect() } + } +} + +#[no_mangle] +pub fn unroll_count() { + // CHECK-LABEL: @unroll_count + // CHECK: !llvm.loop ![[COUNT:[0-9]+]] + #[unroll(5)] + for _ in 0..10 { + unsafe { maybe_has_side_effect() } + } +} + +// CHECK: ![[HINT]] = distinct !{![[HINT]], ![[INNER_HINT:[0-9]+]]} +// CHECK: ![[INNER_HINT]] = !{!"llvm.loop.unroll.enable"} + +// CHECK: ![[FULL]] = distinct !{![[FULL]], ![[INNER_FULL:[0-9]+]]} +// CHECK: ![[INNER_FULL]] = !{!"llvm.loop.unroll.full"} + +// CHECK: ![[DISABLE]] = distinct !{![[DISABLE]], ![[INNER_DISABLE:[0-9]+]]} +// CHECK: ![[INNER_DISABLE]] = !{!"llvm.loop.unroll.disable"} + +// CHECK: ![[COUNT]] = distinct !{![[COUNT]], ![[INNER_COUNT:[0-9]+]]} +// CHECK: ![[INNER_COUNT]] = !{!"llvm.loop.unroll.count", i32 5} diff --git a/tests/codegen-llvm/loop-attrs/unroll-for-works.rs b/tests/codegen-llvm/loop-attrs/unroll-for-works.rs new file mode 100644 index 0000000000000..6aa2ae6057b53 --- /dev/null +++ b/tests/codegen-llvm/loop-attrs/unroll-for-works.rs @@ -0,0 +1,39 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] +#![feature(loop_hints)] + +unsafe extern "C" { + fn maybe_has_side_effect(); +} + +#[no_mangle] +pub fn unroll_full() { + // CHECK-LABEL: @unroll_full + // CHECK-COUNT-512: tail call void @maybe_has_side_effect() + #[unroll(always)] + for _ in 0..512 { + unsafe { maybe_has_side_effect() } + } +} + +#[no_mangle] +pub fn unroll_never() { + // CHECK-LABEL: @unroll_never + // CHECK: tail call void @maybe_has_side_effect() + // CHECK-NOT: tail call void @maybe_has_side_effect() + #[unroll(never)] + for _ in 0..3 { + unsafe { maybe_has_side_effect() } + } +} + +#[no_mangle] +pub fn unroll_count() { + // CHECK-LABEL: @unroll_count + // CHECK-COUNT-5: tail call void @maybe_has_side_effect() + #[unroll(5)] + for _ in 0..10 { + unsafe { maybe_has_side_effect() } + } +} diff --git a/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs new file mode 100644 index 0000000000000..a8f59da031784 --- /dev/null +++ b/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs @@ -0,0 +1,30 @@ +//@ compile-flags: -O -Cno-prepopulate-passes + +#![crate_type = "lib"] +#![feature(loop_hints)] + +// This test ensures that we emit the expected LLVM metadata for loop hint attributes. +// It does not test that loops are optimized as expected, because successful unrolling removes the +// branch and thus the metadata. + +unsafe extern "C" { + fn maybe_has_side_effect(); +} + +#[no_mangle] +pub fn unroll_hint() { + // CHECK-LABEL: @unroll_hint + // CHECK: !llvm.loop ![[HINT:[0-9]+]] + let mut i = 0; + #[unroll] + loop { + unsafe { maybe_has_side_effect() } + i += 1; + if i >= 10 { + break; + } + } +} + +// CHECK: ![[HINT]] = distinct !{![[HINT]], ![[INNER_HINT:[0-9]+]]} +// CHECK: ![[INNER_HINT]] = !{!"llvm.loop.unroll.enable"} diff --git a/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs new file mode 100644 index 0000000000000..5dd3ae9c2379e --- /dev/null +++ b/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs @@ -0,0 +1,72 @@ +//@ compile-flags: -O -Cno-prepopulate-passes + +#![crate_type = "lib"] +#![feature(loop_hints)] + +// This test ensures that we emit the expected LLVM metadata for loop hint attributes. +// It does not test that loops are optimized as expected, because successful unrolling removes the +// branch and thus the metadata. + +unsafe extern "C" { + fn maybe_has_side_effect(); +} + +#[no_mangle] +pub fn unroll_hint() { + // CHECK-LABEL: @unroll_hint + // CHECK: !llvm.loop ![[HINT:[0-9]+]] + let mut i = 0; + #[unroll] + while i < 10 { + unsafe { maybe_has_side_effect() } + i += 1; + } +} + +#[no_mangle] +pub fn unroll_full() { + // CHECK-LABEL: @unroll_full + // CHECK: !llvm.loop ![[FULL:[0-9]+]] + let mut i = 0; + #[unroll(always)] + while i < 10 { + unsafe { maybe_has_side_effect() } + i += 1; + } +} + +#[no_mangle] +pub fn unroll_never() { + // CHECK-LABEL: @unroll_never + // CHECK: !llvm.loop ![[DISABLE:[0-9]+]] + let mut i = 0; + #[unroll(never)] + while i < 10 { + unsafe { maybe_has_side_effect() } + i += 1; + } +} + +#[no_mangle] +pub fn unroll_count() { + // CHECK-LABEL: @unroll_count + // CHECK: !llvm.loop ![[COUNT:[0-9]+]] + let mut i = 0; + #[unroll(5)] + while i < 10 { + unsafe { maybe_has_side_effect() } + i += 1; + } +} + +// CHECK: ![[HINT]] = distinct !{![[HINT]], ![[INNER_HINT:[0-9]+]]} +// CHECK: ![[INNER_HINT]] = !{!"llvm.loop.unroll.enable"} + +// CHECK: ![[FULL]] = distinct !{![[FULL]], ![[INNER_FULL:[0-9]+]]} +// CHECK: ![[INNER_FULL]] = !{!"llvm.loop.unroll.full"} + +// CHECK: ![[DISABLE]] = distinct !{![[DISABLE]], ![[INNER_DISABLE:[0-9]+]]} +// CHECK: ![[INNER_DISABLE]] = !{!"llvm.loop.unroll.disable"} + +// CHECK: ![[COUNT]] = distinct !{![[COUNT]], ![[INNER_COUNT:[0-9]+]]} +// CHECK: ![[INNER_COUNT]] = !{!"llvm.loop.unroll.count", i32 5} diff --git a/tests/ui/attributes/unroll/invalid-unroll.rs b/tests/ui/attributes/unroll/invalid-unroll.rs new file mode 100644 index 0000000000000..8696cefe818f7 --- /dev/null +++ b/tests/ui/attributes/unroll/invalid-unroll.rs @@ -0,0 +1,19 @@ +#![feature(loop_hints)] +#![crate_type = "lib"] + +pub fn main() { + #[unroll(please)] //~ ERROR malformed `unroll` attribute input + for _ in 0..10 {} + + #[unroll("never")] //~ ERROR malformed `unroll` attribute input + for _ in 0..10 {} + + #[unroll()] //~ ERROR malformed `unroll` attribute input + for _ in 0..10 {} + + #[unroll(-1)] //~ ERROR expected a literal + for _ in 0..10 {} + + #[unroll(1.5)] //~ ERROR malformed `unroll` attribute input + for _ in 0..10 {} +} diff --git a/tests/ui/attributes/unroll/invalid-unroll.stderr b/tests/ui/attributes/unroll/invalid-unroll.stderr new file mode 100644 index 0000000000000..97c55d99f8e59 --- /dev/null +++ b/tests/ui/attributes/unroll/invalid-unroll.stderr @@ -0,0 +1,56 @@ +error[E0539]: malformed `unroll` attribute input + --> $DIR/invalid-unroll.rs:5:5 + | +LL | #[unroll(please)] + | ^^^^^^^^^------^^ + | | + | valid arguments are `always` or `never` + | + = note: for more information, visit + +error[E0539]: malformed `unroll` attribute input + --> $DIR/invalid-unroll.rs:8:5 + | +LL | #[unroll("never")] + | ^^^^^^^^^-------^^ + | | + | valid arguments are `always` or `never` + | + = note: for more information, visit + +error[E0805]: malformed `unroll` attribute input + --> $DIR/invalid-unroll.rs:11:5 + | +LL | #[unroll()] + | ^^^^^^^^--^ + | | + | expected an argument here + | + = note: for more information, visit + +error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression + --> $DIR/invalid-unroll.rs:14:14 + | +LL | #[unroll(-1)] + | ^^ expressions are not allowed here + | +help: negative numbers are not literals, try removing the `-` sign + | +LL - #[unroll(-1)] +LL + #[unroll(1)] + | + +error[E0539]: malformed `unroll` attribute input + --> $DIR/invalid-unroll.rs:17:5 + | +LL | #[unroll(1.5)] + | ^^^^^^^^^---^^ + | | + | valid arguments are `always` or `never` + | + = note: for more information, visit + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0539, E0805. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/feature-gates/feature-gate-loop-hints.rs b/tests/ui/feature-gates/feature-gate-loop-hints.rs new file mode 100644 index 0000000000000..37c90f6081888 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-loop-hints.rs @@ -0,0 +1,4 @@ +fn main() { + #[unroll] //~ ERROR the `#[loop_hints]` attribute is an experimental feature + for _ in 0..10 {} +} diff --git a/tests/ui/feature-gates/feature-gate-loop-hints.stderr b/tests/ui/feature-gates/feature-gate-loop-hints.stderr new file mode 100644 index 0000000000000..cecbbdecac27e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-loop-hints.stderr @@ -0,0 +1,13 @@ +error[E0658]: the `#[loop_hints]` attribute is an experimental feature + --> $DIR/feature-gate-loop-hints.rs:2:5 + | +LL | #[unroll] + | ^^^^^^^^^ + | + = note: see issue #156874 for more information + = help: add `#![feature(loop_hints)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/loop-match/invalid-attribute.rs b/tests/ui/loop-match/invalid-attribute.rs index a5d7daac58357..4e4fe41c6ecb9 100644 --- a/tests/ui/loop-match/invalid-attribute.rs +++ b/tests/ui/loop-match/invalid-attribute.rs @@ -36,7 +36,8 @@ fn main() { || {}; { - #[loop_match] //~ ERROR should be applied to a loop + #[loop_match] //~ ERROR attribute cannot be used on + //~^ ERROR `#[loop_match]` should be applied to a loop #[const_continue] //~ ERROR should be applied to a break expression 5 }; diff --git a/tests/ui/loop-match/invalid-attribute.stderr b/tests/ui/loop-match/invalid-attribute.stderr index c23452b9b8448..4db75e1e57b04 100644 --- a/tests/ui/loop-match/invalid-attribute.stderr +++ b/tests/ui/loop-match/invalid-attribute.stderr @@ -4,7 +4,7 @@ error: `#[loop_match]` attribute cannot be used on crates LL | #![loop_match] | ^^^^^^^^^^^^^^ | - = help: `#[loop_match]` can be applied to + = help: `#[loop_match]` can only be applied to loops error: `#[const_continue]` attribute cannot be used on crates --> $DIR/invalid-attribute.rs:7:1 @@ -20,7 +20,7 @@ error: `#[loop_match]` attribute cannot be used on foreign functions LL | #[loop_match] | ^^^^^^^^^^^^^ | - = help: `#[loop_match]` can be applied to + = help: `#[loop_match]` can only be applied to loops error: `#[const_continue]` attribute cannot be used on foreign functions --> $DIR/invalid-attribute.rs:11:5 @@ -36,7 +36,7 @@ error: `#[loop_match]` attribute cannot be used on structs LL | #[loop_match] | ^^^^^^^^^^^^^ | - = help: `#[loop_match]` can be applied to + = help: `#[loop_match]` can only be applied to loops error: `#[const_continue]` attribute cannot be used on structs --> $DIR/invalid-attribute.rs:16:1 @@ -52,7 +52,7 @@ error: `#[loop_match]` attribute cannot be used on required trait methods LL | #[loop_match] | ^^^^^^^^^^^^^ | - = help: `#[loop_match]` can be applied to + = help: `#[loop_match]` can only be applied to loops error: `#[const_continue]` attribute cannot be used on required trait methods --> $DIR/invalid-attribute.rs:25:5 @@ -68,7 +68,7 @@ error: `#[loop_match]` attribute cannot be used on functions LL | #[loop_match] | ^^^^^^^^^^^^^ | - = help: `#[loop_match]` can be applied to + = help: `#[loop_match]` can only be applied to loops error: `#[const_continue]` attribute cannot be used on functions --> $DIR/invalid-attribute.rs:30:1 @@ -84,7 +84,7 @@ error: `#[loop_match]` attribute cannot be used on closures LL | #[loop_match] | ^^^^^^^^^^^^^ | - = help: `#[loop_match]` can be applied to + = help: `#[loop_match]` can only be applied to loops error: `#[const_continue]` attribute cannot be used on closures --> $DIR/invalid-attribute.rs:35:5 @@ -94,22 +94,30 @@ LL | #[const_continue] | = help: `#[const_continue]` can be applied to +error: `#[loop_match]` attribute cannot be used on expressions + --> $DIR/invalid-attribute.rs:39:9 + | +LL | #[loop_match] + | ^^^^^^^^^^^^^ + | + = help: `#[loop_match]` can only be applied to loops + error: `#[loop_match]` should be applied to a loop --> $DIR/invalid-attribute.rs:39:9 | LL | #[loop_match] | ^^^^^^^^^^^^^ -LL | #[const_continue] +... LL | 5 | - not a loop error: `#[const_continue]` should be applied to a break expression - --> $DIR/invalid-attribute.rs:40:9 + --> $DIR/invalid-attribute.rs:41:9 | LL | #[const_continue] | ^^^^^^^^^^^^^^^^^ LL | 5 | - not a break expression -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/thir-print/str-patterns.stdout b/tests/ui/thir-print/str-patterns.stdout index 09212e7d68aee..da1f86b8fc591 100644 --- a/tests/ui/thir-print/str-patterns.stdout +++ b/tests/ui/thir-print/str-patterns.stdout @@ -3,6 +3,7 @@ Thir { body_type: Fn( fn(&'{erased} str), ), + attributes: {}, arms: [ Arm { pattern: Pat { @@ -291,6 +292,7 @@ Thir { body_type: Const( &'{erased} str, ), + attributes: {}, arms: [], blocks: [], exprs: [ diff --git a/tests/ui/thir-print/thir-flat-const-variant.stdout b/tests/ui/thir-print/thir-flat-const-variant.stdout index 908684094ec37..82e197330cca8 100644 --- a/tests/ui/thir-print/thir-flat-const-variant.stdout +++ b/tests/ui/thir-print/thir-flat-const-variant.stdout @@ -3,6 +3,7 @@ Thir { body_type: Const( Foo, ), + attributes: {}, arms: [], blocks: [], exprs: [ @@ -64,6 +65,7 @@ Thir { body_type: Const( Foo, ), + attributes: {}, arms: [], blocks: [], exprs: [ @@ -125,6 +127,7 @@ Thir { body_type: Const( Foo, ), + attributes: {}, arms: [], blocks: [], exprs: [ @@ -186,6 +189,7 @@ Thir { body_type: Const( Foo, ), + attributes: {}, arms: [], blocks: [], exprs: [ @@ -247,6 +251,7 @@ Thir { body_type: Fn( fn(), ), + attributes: {}, arms: [], blocks: [ Block { diff --git a/tests/ui/thir-print/thir-flat.stdout b/tests/ui/thir-print/thir-flat.stdout index 37106427745ea..76df9f4394315 100644 --- a/tests/ui/thir-print/thir-flat.stdout +++ b/tests/ui/thir-print/thir-flat.stdout @@ -3,6 +3,7 @@ Thir { body_type: Fn( fn(), ), + attributes: {}, arms: [], blocks: [ Block {