diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index 844daa32a04ac..b576c5038404d 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -169,6 +169,7 @@ language_item_table! { Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None; CloneFn, sym::clone_fn, clone_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + CloneFromFn, sym::clone_from_fn, clone_from_fn, Target::Method(MethodKind::Trait { body: true }), GenericRequirement::None; UseCloned, sym::use_cloned, use_cloned_trait, Target::Trait, GenericRequirement::None; TrivialClone, sym::trivial_clone, trivial_clone_trait, Target::Trait, GenericRequirement::None; Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index b294328b0bb17..b0045c359160d 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -8,7 +8,7 @@ use rustc_session::parse::ParseSess; use rustc_span::FileName; pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) { - for raw_attr in attrs { + let injected = attrs.iter().filter_map(|raw_attr| { let source = format!("#![{raw_attr}]"); let parse = || -> Result>> { let tokens = source_str_to_stream( @@ -22,16 +22,15 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) { }) .map_err(|e| vec![e]) }; - let meta = match parse() { - Ok(meta) => meta, + match parse() { + Ok(meta) => Some(meta), Err(errs) => { for err in errs { err.emit(); } - continue; + None } - }; - - krate.attrs.push(meta); - } + } + }); + krate.attrs.splice(0..0, injected); } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index bdbd43de07a53..d74c6dd68564b 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -478,6 +478,8 @@ pub(crate) fn default_optimisation_attrs<'ll>( sess: &Session, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); + // Either attribute satisfies LLVM's combined size predicate, but the software pipeliner reads + // `optsize` alone, so a `z` build needs both. match sess.opts.optimize { OptLevel::Size => { attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx)); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 693d8e6adb1c3..e43b9ba6359ed 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -119,15 +119,15 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar target_machine_factory(tcx.sess, tcx.backend_optimization_level(()))(tcx.dcx(), config) } -fn to_llvm_opt_settings(cfg: config::OptLevel) -> (llvm::CodeGenOptLevel, llvm::CodeGenOptSize) { +// `s` and `z` are the same codegen level; what tells LLVM that they are size builds is the +// per-function attribute, not this setting. +fn to_llvm_opt_level(cfg: config::OptLevel) -> llvm::CodeGenOptLevel { use self::config::OptLevel::*; match cfg { - No => (llvm::CodeGenOptLevel::None, llvm::CodeGenOptSizeNone), - Less => (llvm::CodeGenOptLevel::Less, llvm::CodeGenOptSizeNone), - More => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeNone), - Aggressive => (llvm::CodeGenOptLevel::Aggressive, llvm::CodeGenOptSizeNone), - Size => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeDefault), - SizeMin => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeAggressive), + No => llvm::CodeGenOptLevel::None, + Less => llvm::CodeGenOptLevel::Less, + More | Size | SizeMin => llvm::CodeGenOptLevel::Default, + Aggressive => llvm::CodeGenOptLevel::Aggressive, } } @@ -184,7 +184,7 @@ pub(crate) fn target_machine_factory( let reloc_model = to_llvm_relocation_model(sess.relocation_model()); - let (opt_level, _) = to_llvm_opt_settings(optlvl); + let opt_level = to_llvm_opt_level(optlvl); let float_abi = to_llvm_float_abi(sess.target.llvm_floatabi); let ffunction_sections = @@ -594,8 +594,7 @@ pub(crate) unsafe fn llvm_optimize<'ll>( vectorize_slp = false; vectorize_loop = false; } else { - unroll_loops = - opt_level != config::OptLevel::Size && opt_level != config::OptLevel::SizeMin; + unroll_loops = opt_level.objective() != config::OptimizationObjective::Size; merge_functions = config.merge_functions; vectorize_slp = config.vectorize_slp; vectorize_loop = config.vectorize_loop; diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 40d5e08578faf..6ba3a82de659f 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1071,6 +1071,11 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &attrs); } + fn apply_default_optimisation_attrs(&self, llfn: &'ll Value) { + let attrs = attributes::default_optimisation_attrs(self, self.sess()); + attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &attrs); + } + fn declare_c_main(&self, fn_type: Self::Type) -> Option { let entry_name = self.sess().target.entry_name.as_ref(); if self.get_declared_value(entry_name).is_none() { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 594f4cbcf759d..38af3011b47e7 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1707,6 +1707,7 @@ fn gen_fn<'a, 'll, 'tcx>( let llfn = cx.declare_fn(name, fn_abi, None); cx.set_frame_pointer_type(llfn); cx.apply_target_cpu_attr(llfn); + cx.apply_default_optimisation_attrs(llfn); // FIXME(eddyb) find a nicer way to do this. llvm::set_linkage(llfn, llvm::Linkage::InternalLinkage); let llbb = Builder::append_block(cx, llfn, "entry-block"); diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index f9863da230dfb..e77fed0913ec2 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -10,7 +10,6 @@ use rustc_abi::{AddressSpace, Align, Size, WrappingRange}; use rustc_llvm::RustString; pub(crate) use self::CallConv::*; -pub(crate) use self::CodeGenOptSize::*; pub(crate) use self::conversions::*; pub(crate) use self::ffi::*; pub(crate) use self::metadata_kind::*; @@ -195,14 +194,6 @@ impl AttributePlace { } } -#[derive(Copy, Clone, PartialEq)] -#[repr(C)] -pub(crate) enum CodeGenOptSize { - CodeGenOptSizeNone = 0, - CodeGenOptSizeDefault = 1, - CodeGenOptSizeAggressive = 2, -} - pub(crate) fn SetInstructionCallConv(instr: &Value, cc: CallConv) { unsafe { LLVMSetInstructionCallConv(instr, cc as c_uint); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index ffb9130ed1a08..b92e79c8f43ec 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -407,6 +407,7 @@ impl<'a> GccLinker<'a> { self.link_arg(&arg); } + // The plugin has no distinct size setting: `s` and `z` both map to `O2`, as `More` does. let opt_level = match self.sess.opts.optimize { config::OptLevel::No => "O0", config::OptLevel::Less => "O1", @@ -698,9 +699,7 @@ impl<'a> Linker for GccLinker<'a> { // GNU-style linkers support optimization with -O. GNU ld doesn't // need a numeric argument, but other linkers do. - if self.sess.opts.optimize == config::OptLevel::More - || self.sess.opts.optimize == config::OptLevel::Aggressive - { + if self.sess.optimization_objective() == config::OptimizationObjective::Speed { self.link_arg("-O1"); } } @@ -1021,7 +1020,7 @@ impl<'a> Linker for MsvcLinker<'a> { // MSVC's ICF (Identical COMDAT Folding) link optimization is // slow for Rust and thus we disable it by default when not in // optimization build. - if self.sess.opts.optimize != config::OptLevel::No { + if self.sess.optimization_objective() != config::OptimizationObjective::None { self.link_arg("/OPT:REF,ICF"); } else { // It is necessary to specify NOICF here, because /OPT:REF diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 44958c376a46a..406e5a5b04c61 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -23,8 +23,8 @@ use rustc_metadata::fs::copy_to_stdout; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{ - self, JobsMode, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes, - SwitchWithOptPath, + self, JobsMode, Lto, OptLevel, OptimizationObjective, OutFileName, OutputFilenames, OutputType, + Passes, SwitchWithOptPath, }; use rustc_session::{BorrowedIncrCompSession, IncrCompSession, Session}; use rustc_span::source_map::SourceMap; @@ -231,8 +231,7 @@ impl ModuleConfig { // Copy what clang does by turning on loop vectorization at O2 and // slp vectorization at O3. vectorize_loop: !sess.opts.cg.no_vectorize_loops - && (sess.opts.optimize == config::OptLevel::More - || sess.opts.optimize == config::OptLevel::Aggressive), + && sess.optimization_objective() == OptimizationObjective::Speed, vectorize_slp: !sess.opts.cg.no_vectorize_slp && sess.opts.optimize == config::OptLevel::Aggressive, @@ -247,13 +246,10 @@ impl ModuleConfig { // O2 and O3) since it can be useful for reducing code size. merge_functions: match sess.merge_functions() { MergeFunctions::Disabled => false, - MergeFunctions::Trampolines | MergeFunctions::Aliases => { - use config::OptLevel::*; - match sess.opts.optimize { - Aggressive | More | SizeMin | Size => true, - Less | No => false, - } - } + MergeFunctions::Trampolines | MergeFunctions::Aliases => matches!( + sess.optimization_objective(), + OptimizationObjective::Speed | OptimizationObjective::Size + ), }, emit_lifetime_markers: sess.emit_lifetime_markers(), diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 1f820a17195bb..ae35dcfe42e60 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -555,6 +555,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // `main` should respect same config for frame pointer elimination as rest of code cx.set_frame_pointer_type(llfn); cx.apply_target_cpu_attr(llfn); + // The shim has no item behind it, so it does not go through the per-item attribute path. + cx.apply_default_optimisation_attrs(llfn); let llbb = Bx::append_block(cx, llfn, "top"); let mut bx = Bx::build(cx, llbb); @@ -1217,23 +1219,16 @@ impl CrateInfo { pub(crate) fn provide(providers: &mut Providers) { providers.backend_optimization_level = |tcx, cratenum| { - let for_speed = match tcx.sess.opts.optimize { - // If globally no optimisation is done, #[optimize] has no effect. - // - // This is done because if we ended up "upgrading" to `-O2` here, we’d populate the - // pass manager and it is likely that some module-wide passes (such as inliner or - // cross-function constant propagation) would ignore the `optnone` annotation we put - // on the functions, thus necessarily involving these functions into optimisations. - config::OptLevel::No => return config::OptLevel::No, - // If globally optimise-speed is already specified, just use that level. - config::OptLevel::Less => return config::OptLevel::Less, - config::OptLevel::More => return config::OptLevel::More, - config::OptLevel::Aggressive => return config::OptLevel::Aggressive, - // If globally optimize-for-size has been requested, use -O2 instead (if optimize(size) - // are present). - config::OptLevel::Size => config::OptLevel::More, - config::OptLevel::SizeMin => config::OptLevel::More, - }; + let global = tcx.sess.opts.optimize; + + // Nothing to promote outside a size build. At `-Copt-level=0` the upgrade would populate + // the pass manager, and its module-wide passes ignore the `optnone` annotation. + if global.objective() != config::OptimizationObjective::Size { + return global; + } + + // A speed-marked body in a size build needs a speed level, and `-O2` is the lowest one. + let for_speed = config::OptLevel::More; let defids = tcx.collect_and_partition_mono_items(cratenum).all_mono_items; @@ -1242,11 +1237,7 @@ pub(crate) fn provide(providers: &mut Providers) { matches!(optimize, OptimizeAttr::Speed) }); - if any_for_speed { - return for_speed; - } - - tcx.sess.opts.optimize + if any_for_speed { for_speed } else { global } }; } diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index add7128a2974b..7e48e86290ee8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -28,6 +28,9 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn sess(&self) -> &Session; fn set_frame_pointer_type(&self, llfn: Self::Function); fn apply_target_cpu_attr(&self, llfn: Self::Function); + /// Applies the compilation's optimization attributes to a function that no item produced, + /// such as the C entry shim. + fn apply_default_optimisation_attrs(&self, _llfn: Self::Function) {} /// Declares the extern "C" main function for the entry point. Returns None if the symbol /// already exists. fn declare_c_main(&self, fn_type: Self::FunctionSignature) -> Option; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 52b584f12213a..20576ce4dd034 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1346,11 +1346,33 @@ pub struct BasicBlockData<'tcx> { } impl<'tcx> BasicBlockData<'tcx> { - pub fn new(terminator: Option>, is_cleanup: bool) -> BasicBlockData<'tcx> { + pub fn new(terminator: Terminator<'tcx>, is_cleanup: bool) -> BasicBlockData<'tcx> { BasicBlockData::new_stmts(Vec::new(), terminator, is_cleanup) } pub fn new_stmts( + statements: Vec>, + terminator: Terminator<'tcx>, + is_cleanup: bool, + ) -> BasicBlockData<'tcx> { + BasicBlockData { + statements, + after_last_stmt_debuginfos: StmtDebugInfos::default(), + terminator: Some(terminator), + is_cleanup, + } + } + + /// This is being phased out. Use [`BasicBlockData::new`] instead. + pub fn maybe_unterminated( + terminator: Option>, + is_cleanup: bool, + ) -> BasicBlockData<'tcx> { + BasicBlockData::maybe_unterminated_stmts(Vec::new(), terminator, is_cleanup) + } + + /// This is being phased out. Use [`BasicBlockData::new_stmts`] instead. + pub fn maybe_unterminated_stmts( statements: Vec>, terminator: Option>, is_cleanup: bool, diff --git a/compiler/rustc_mir_build/src/builder/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs index 75377f761f401..76bb5009a6ec1 100644 --- a/compiler/rustc_mir_build/src/builder/cfg.rs +++ b/compiler/rustc_mir_build/src/builder/cfg.rs @@ -21,7 +21,7 @@ impl<'tcx> CFG<'tcx> { // it as #[inline(never)] to keep rustc's stack use in check. #[inline(never)] pub(crate) fn start_new_block(&mut self) -> BasicBlock { - self.basic_blocks.push(BasicBlockData::new(None, false)) + self.basic_blocks.push(BasicBlockData::maybe_unterminated(None, false)) } pub(crate) fn start_new_cleanup_block(&mut self) -> BasicBlock { diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index f6c8658544b43..6d0c415b7358a 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -64,7 +64,7 @@ pub(super) fn build_custom_mir<'tcx>( }; body.local_decls.push(LocalDecl::new(return_ty, return_ty_span)); - body.basic_blocks_mut().push(BasicBlockData::new(None, false)); + body.basic_blocks_mut().push(BasicBlockData::maybe_unterminated(None, false)); body.source_scopes.push(SourceScopeData { span, parent_scope: None, diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index b227923292779..0cc73e381ea2b 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs @@ -199,7 +199,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { match &self.thir[stmt].kind { StmtKind::Let { pattern, initializer: Some(initializer), .. } => { let (var, ..) = self.parse_var(pattern)?; - let data = BasicBlockData::new( + let data = BasicBlockData::maybe_unterminated( None, parse_by_kind!(self, *initializer, _, "basic block declaration", @variant(mir_basic_block, Normal) => false, @@ -303,7 +303,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { ExprKind::Block { block } => &self.thir[*block], ); - let mut data = BasicBlockData::new(None, is_cleanup); + let mut data = BasicBlockData::maybe_unterminated(None, is_cleanup); for stmt_id in &*block.stmts { let stmt = self.statement_as_expr(*stmt_id)?; let span = self.thir[stmt].span; diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 4e97ddd991e2d..6e8df5e0809ba 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -833,7 +833,7 @@ fn mir_for_direct_const<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Body<'tc let rvalue = Rvalue::Use(Operand::Constant(Box::new(value)), WithRetag::Yes); let statement_kind = StatementKind::Assign(Box::new((Place::return_place(), rvalue))); let statement = Statement::new(source_info, statement_kind); - let terminator = Some(Terminator { source_info, kind: TerminatorKind::Return }); + let terminator = Terminator { source_info, kind: TerminatorKind::Return }; let block = BasicBlockData::new_stmts(vec![statement], terminator, false); let basic_blocks = IndexVec::from_elem_n(block, 1); diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index bfdf96a48f401..5f9545860b6bb 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 }), + mir::Terminator { source_info, kind }, false, )) }; diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index 17e99f506e0f2..5bf932a42e43a 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -88,7 +88,7 @@ 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) }), + Terminator { source_info, kind: TerminatorKind::goto(target) }, 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 41c63d4857df8..1be185bd5b6bd 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 @@ -94,7 +94,7 @@ 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) }), + Terminator { source_info, kind: TerminatorKind::goto(target) }, is_cleanup, )); diff --git a/compiler/rustc_mir_transform/src/check_enums.rs b/compiler/rustc_mir_transform/src/check_enums.rs index 260028c570461..6cc12f48debb8 100644 --- a/compiler/rustc_mir_transform/src/check_enums.rs +++ b/compiler/rustc_mir_transform/src/check_enums.rs @@ -244,7 +244,7 @@ fn split_block( let block_data = &mut basic_blocks[location.block]; // Drain every statement after this one and move the current terminator to a new basic block. - let new_block = BasicBlockData::new_stmts( + let new_block = BasicBlockData::maybe_unterminated_stmts( block_data.statements.split_off(location.statement_index), block_data.terminator.take(), block_data.is_cleanup, @@ -348,7 +348,7 @@ fn insert_direct_enum_check<'tcx>( new_block: BasicBlock, ) { // Insert a new target block that is branched to in case of an invalid discriminant. - let invalid_discr_block_data = BasicBlockData::new(None, false); + let invalid_discr_block_data = BasicBlockData::maybe_unterminated(None, false); let invalid_discr_block = basic_blocks.push(invalid_discr_block_data); let block_data = &mut basic_blocks[current_block]; let discr_place = insert_discr_cast_to_u128( diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs index 25336280926fe..0c73e3af0eb00 100644 --- a/compiler/rustc_mir_transform/src/check_pointers.rs +++ b/compiler/rustc_mir_transform/src/check_pointers.rs @@ -235,7 +235,7 @@ fn split_block( let block_data = &mut basic_blocks[location.block]; // Drain every statement after this one and move the current terminator to a new basic block. - let new_block = BasicBlockData::new_stmts( + let new_block = BasicBlockData::maybe_unterminated_stmts( block_data.statements.split_off(location.statement_index), block_data.terminator.take(), block_data.is_cleanup, diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index c6bd539eb6eee..69f950c6396e0 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -377,7 +377,7 @@ pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>( body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info); // call coroutine_drop() - let call_bb = body.basic_blocks_mut().push(BasicBlockData::new(None, false)); + let call_bb = body.basic_blocks_mut().push(BasicBlockData::maybe_unterminated(None, false)); // return Poll::Ready() let ret_bb = insert_poll_ready_block(tcx, &mut body); diff --git a/compiler/rustc_mir_transform/src/coroutine/mod.rs b/compiler/rustc_mir_transform/src/coroutine/mod.rs index 9bf85b56a4c66..c62982764f937 100644 --- a/compiler/rustc_mir_transform/src/coroutine/mod.rs +++ b/compiler/rustc_mir_transform/src/coroutine/mod.rs @@ -252,7 +252,7 @@ impl<'tcx> TransformVisitor<'tcx> { body.basic_blocks_mut().push(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Terminator { source_info, kind: TerminatorKind::Return }, false, )); @@ -722,7 +722,7 @@ fn insert_switch<'tcx>( // Add the switch as entry block, and put the former entry block at the end. let former_entry = std::mem::replace( &mut body.basic_blocks_mut()[START_BLOCK], - BasicBlockData::new_stmts(vec![assign], None, false), + BasicBlockData::maybe_unterminated_stmts(vec![assign], None, false), ); let former_entry = body.basic_blocks_mut().push(former_entry); @@ -742,7 +742,7 @@ 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(Terminator { source_info, kind }, false)) } fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) -> Statement<'tcx> { @@ -761,7 +761,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 }), + Terminator { source_info, kind: TerminatorKind::Return }, false, )) } @@ -813,7 +813,7 @@ fn insert_poll_pending_block<'tcx>( let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().push(BasicBlockData::new_stmts( [return_poll_pending(tcx, source_info, return_type)].to_vec(), - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Terminator { source_info, kind: TerminatorKind::Return }, false, )) } @@ -847,7 +847,7 @@ 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 }), + Terminator { source_info, kind: TerminatorKind::UnwindResume }, true, )); @@ -1049,7 +1049,7 @@ 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) }), + Terminator { source_info, kind: TerminatorKind::goto(target) }, false, )); diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index 9fcd2a3028f84..cef8d42fb1539 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -69,10 +69,10 @@ impl<'tcx> MockBlocks<'tcx> { }; let next_hi = next_lo + BytePos(1); self.blocks.push(BasicBlockData::new( - Some(Terminator { + Terminator { source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)), kind, - }), + }, false, )) } diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 6a34f0167354b..cc4f3e2b3911b 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -167,14 +167,14 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { // Create `bbEq` in example above let eq_switch = BasicBlockData::new( - Some(Terminator { + Terminator { source_info: bbs[parent].terminator().source_info, kind: TerminatorKind::SwitchInt { // switch on the first discriminant, so we can mark the second one as dead discr: parent_op, targets: eq_targets, }, - }), + }, bbs[parent].is_cleanup, ); diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index dd58a72512007..0fc322e8d768b 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -1618,7 +1618,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 }), + Terminator { source_info: self.source_info, kind: k }, unwind.is_cleanup(), )) } @@ -1632,7 +1632,7 @@ where ) -> BasicBlock { self.elaborator.patch().new_block(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info: self.source_info, kind: k }), + Terminator { source_info: self.source_info, kind: k }, unwind.is_cleanup(), )) } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 501d2c38df170..bae72f0198a03 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -868,10 +868,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( // Prepare a new block for code that should execute when call returns. We don't use // target block directly since it might have other predecessors. let data = BasicBlockData::new( - Some(Terminator { - source_info: terminator.source_info, - kind: TerminatorKind::goto(block), - }), + Terminator { source_info: terminator.source_info, kind: TerminatorKind::goto(block) }, caller_body[block].is_cleanup, ); Some(caller_body.basic_blocks_mut().push(data)) diff --git a/compiler/rustc_mir_transform/src/move_elimination.rs b/compiler/rustc_mir_transform/src/move_elimination.rs index 95a7f8cf237b5..3717dcc864c5b 100644 --- a/compiler/rustc_mir_transform/src/move_elimination.rs +++ b/compiler/rustc_mir_transform/src/move_elimination.rs @@ -742,7 +742,7 @@ fn get_or_split_edge<'tcx>( } let source_info = body.basic_blocks[pred].terminator().source_info; let split_bb = patcher.new_block(BasicBlockData::new( - Some(Terminator { source_info, kind: TerminatorKind::goto(succ) }), + Terminator { source_info, kind: TerminatorKind::goto(succ) }, body.basic_blocks[succ].is_cleanup, )); patcher.mutate_terminator(body, pred, |kind| { diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index 739fa5e37ab13..f1476108557a8 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -89,10 +89,10 @@ impl<'tcx> MirPatch<'tcx> { } let bb = self.new_block(BasicBlockData::new( - Some(Terminator { + Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::UnwindResume, - }), + }, true, )); self.resume_block = Some(bb); @@ -105,10 +105,10 @@ impl<'tcx> MirPatch<'tcx> { } let bb = self.new_block(BasicBlockData::new( - Some(Terminator { + Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::Unreachable, - }), + }, true, )); self.unreachable_cleanup_block = Some(bb); @@ -121,10 +121,10 @@ impl<'tcx> MirPatch<'tcx> { } let bb = self.new_block(BasicBlockData::new( - Some(Terminator { + Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::Unreachable, - }), + }, false, )); self.unreachable_no_cleanup_block = Some(bb); @@ -139,10 +139,10 @@ impl<'tcx> MirPatch<'tcx> { } let bb = self.new_block(BasicBlockData::new( - Some(Terminator { + Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::UnwindTerminate(reason), - }), + }, true, )); self.terminate_block = Some((bb, reason)); diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 7ad164956cbfd..5be8a1b8523c8 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -752,10 +752,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { fn new_block(&mut self) -> BasicBlock { let span = self.promoted.span; self.promoted.basic_blocks_mut().push(BasicBlockData::new( - Some(Terminator { - source_info: SourceInfo::outermost(span), - kind: TerminatorKind::Return, - }), + Terminator { source_info: SourceInfo::outermost(span), kind: TerminatorKind::Return }, false, )) } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 170a13ac173ea..abe3490fb7886 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -279,7 +279,7 @@ pub fn build_drop_shim<'tcx>( let return_block = BasicBlock::new(1); let mut blocks = IndexVec::with_capacity(2); let block = |blocks: &mut IndexVec<_, _>, kind| -> BasicBlock { - blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) + blocks.push(BasicBlockData::new(Terminator { source_info, kind }, false)) }; if ty.is_some() { block(&mut blocks, TerminatorKind::goto(return_block)); @@ -493,7 +493,7 @@ fn build_thread_local_shim<'tcx>(tcx: TyCtxt<'tcx>, shim: ty::ShimKind<'tcx>) -> Rvalue::ThreadLocalRef(def_id), ))), )], - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Terminator { source_info, kind: TerminatorKind::Return }, false, )]); @@ -579,7 +579,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { let source_info = self.source_info(); self.blocks.push(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind }), + Terminator { source_info, kind }, is_cleanup, )) } @@ -933,7 +933,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 }), + Terminator { source_info, kind }, is_cleanup, )) }; @@ -1060,7 +1060,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 }), + Terminator { source_info, kind: TerminatorKind::Return }, false, ); @@ -1143,7 +1143,7 @@ fn build_fn_ptr_as_ptr_shim<'tcx>( let start_block = BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Terminator { source_info, kind: TerminatorKind::Return }, false, ); let source = MirSource::from_shim(ty::ShimKind::FnPtrAsPtr(def_id, self_ty)); @@ -1178,7 +1178,7 @@ fn build_fn_ptr_from_ptr_shim<'tcx>( let start_block = BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return }), + Terminator { source_info, kind: TerminatorKind::Return }, false, ); let source = MirSource::from_shim(ty::ShimKind::FnPtrFromPtr(def_id, self_ty)); @@ -1276,7 +1276,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 }), + Terminator { source_info, kind: TerminatorKind::Return }, 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 510da5cbce89b..70de1cc6a2d63 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -85,7 +85,7 @@ 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| -> BasicBlock { - blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) + blocks.push(BasicBlockData::new(Terminator { source_info, kind }, false)) }; block( &mut blocks, @@ -386,7 +386,7 @@ fn build_adrop_for_adrop_shim<'tcx>( // call Pin::new_unchecked(&mut impl_cor) blocks.push(BasicBlockData::new_stmts( statements, - Some(Terminator { + Terminator { source_info, kind: TerminatorKind::Call { func: Operand::function_handle(tcx, pin_fn, &[cor_ref.into()], span), @@ -397,7 +397,7 @@ fn build_adrop_for_adrop_shim<'tcx>( call_source: CallSource::Misc, fn_span: span, }, - }), + }, false, )); // When dropping async drop coroutine, we continue its execution: @@ -405,7 +405,7 @@ fn build_adrop_for_adrop_shim<'tcx>( let poll_fn = tcx.require_lang_item(LangItem::FuturePoll, span); let resume_ctx = Place::from(Local::new(2)); blocks.push(BasicBlockData::new( - Some(Terminator { + Terminator { source_info, kind: TerminatorKind::Call { func: Operand::function_handle(tcx, poll_fn, &[impl_ty.into()], span), @@ -420,13 +420,11 @@ fn build_adrop_for_adrop_shim<'tcx>( call_source: CallSource::Misc, fn_span: span, }, - }), - false, - )); - blocks.push(BasicBlockData::new( - Some(Terminator { source_info, kind: TerminatorKind::Return }), + }, false, )); + blocks + .push(BasicBlockData::new(Terminator { source_info, kind: TerminatorKind::Return }, false)); let source = MirSource::from_shim(shim); let mut body = new_body(source, blocks, locals, sig.inputs().len(), span); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b3d30997aca2a..2665cb92fc8d0 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -126,6 +126,33 @@ impl OptLevel { _ => 2, } } + + /// Maps a general opt-level onto the objective it is trying to achieve. + pub fn objective(&self) -> OptimizationObjective { + match self { + OptLevel::No => OptimizationObjective::None, + OptLevel::Less => OptimizationObjective::Balanced, + OptLevel::More | OptLevel::Aggressive => OptimizationObjective::Speed, + OptLevel::Size | OptLevel::SizeMin => OptimizationObjective::Size, + } + } +} + +/// What a compilation, or a single item, is being asked to optimize *for*. +/// +/// Unlike [`OptLevel`], speed and size are alternatives here rather than degrees of the same +/// thing. Sites that map a level onto an LLVM or linker flag, where `s` and `z` produce different +/// flags, still match on [`OptLevel`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum OptimizationObjective { + /// Nothing is optimized, so there is no objective (`-Copt-level=0`). + None, + /// Optimize, without a strong preference for speed over size (`-Copt-level=1`). + Balanced, + /// Favor runtime speed over binary size (`-Copt-level=2`/`3`, `#[optimize(speed)]`). + Speed, + /// Favor binary size over runtime speed (`-Copt-level=s`/`z`, `#[optimize(size)]`). + Size, } /// This is what the `LtoCli` values get mapped to after resolving defaults and @@ -1572,10 +1599,9 @@ impl Options { pub fn share_generics(&self) -> bool { match self.unstable_opts.share_generics { Some(setting) => setting, - None => match self.optimize { - OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, - OptLevel::More | OptLevel::Aggressive => false, - }, + // Sharing instantiations means fewer copies, at the cost of leaving them opaque to + // speed optimizations. That is a good trade unless speed is what is asked for. + None => self.optimize.objective() != OptimizationObjective::Speed, } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index b69c26639e7e8..9bf2de0466233 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -988,6 +988,11 @@ impl Session { self.opts.unstable_opts.mir_opt_level.unwrap_or_else(|| self.opts.optimize.mir_opt_level()) } + /// What this compilation optimizes for; see [`config::OptimizationObjective`]. + pub fn optimization_objective(&self) -> config::OptimizationObjective { + self.opts.optimize.objective() + } + /// Calculates the flavor of LTO to use for this compilation. pub fn lto(&self) -> config::Lto { // If our target has codegen requirements ignore the command line diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a7f8e37687b18..2170f610f0939 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -648,6 +648,7 @@ symbols! { clone_closures, clone_fn, clone_from, + clone_from_fn, close_fn, closure, closure_lifetime_binder, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 9640f9add604c..2bdffe20f5bcd 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -271,30 +271,29 @@ fn resolve_associated_item<'tcx>( } traits::ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, _) => { if tcx.is_lang_item(trait_ref.def_id, LangItem::Clone) { - // FIXME(eddyb) use lang items for methods instead of names. - let name = tcx.item_name(trait_item_id); - if name == sym::clone { - let self_ty = trait_ref.self_ty(); - match self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(..) => (), - ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Tuple(..) => {} - _ => return Ok(None), - }; + let self_ty = trait_ref.self_ty(); + match self_ty.kind() { + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Tuple(..) => {} + _ => return Ok(None), + }; - Some(Instance { + match tcx.as_lang_item(trait_item_id) { + Some(LangItem::CloneFn) => Some(Instance { def: ty::InstanceKind::Shim(ty::ShimKind::Clone(trait_item_id, self_ty)), args: rcvr_args, - }) - } else { - assert_eq!(name, sym::clone_from); - - // Use the default `fn clone_from` from `trait Clone`. - let args = tcx.erase_and_anonymize_regions(rcvr_args); - Some(ty::Instance::new_raw(trait_item_id, args)) + }), + Some(LangItem::CloneFromFn) => { + // Use the default `fn clone_from` from `trait Clone`. + let args = tcx.erase_and_anonymize_regions(rcvr_args); + Some(ty::Instance::new_raw(trait_item_id, args)) + } + _ => bug!("unknown `Clone` item"), } } else if tcx.is_lang_item(trait_ref.def_id, LangItem::FnPtrTrait) { let self_ty = trait_ref.self_ty(); diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 61528873a38c7..a549e12c33995 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -242,6 +242,7 @@ pub const trait Clone: Sized { /// allocations. #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[lang = "clone_from_fn"] fn clone_from(&mut self, source: &Self) where Self: [const] Destruct, diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 96981a75d98f2..fb86481c42443 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -56,6 +56,78 @@ macro_rules! int_impl { #[stable(feature = "int_bits_const", since = "1.53.0")] pub const BITS: u32 = <$UnsignedT>::BITS; + /// Tests the value of a specific bit. + /// + /// # Panics + /// + /// If overflow checks are enabled, this method will panic if the provided index is + /// greater than or equal to [`BITS`]. + /// + /// [`BITS`]: Self::BITS + /// + /// # Examples + /// + /// ``` + /// #![feature(get_set_bit)] + /// + #[doc = concat!("let n = 0b111_1001", stringify!($SelfT), ";")] + /// + /// assert!(n.bit(0)); + /// assert!(!n.bit(1)); + /// assert!(!n.bit(2)); + /// assert!(n.bit(3)); + /// assert!(n.bit(4)); + /// assert!(n.bit(5)); + /// assert!(n.bit(6)); + /// ``` + #[unstable(feature = "get_set_bit", issue = "147702")] + #[rustc_const_unstable(feature = "get_set_bit", issue = "147702")] + #[rustc_inherit_overflow_checks] + #[inline] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[track_caller] + pub const fn bit(self, index: u32) -> bool { + self.cast_unsigned().bit(index) + } + + /// Sets the value of a specific bit. + /// + /// # Panics + /// + /// If overflow checks are enabled, this method will panic if the provided index is + /// greater than or equal to [`BITS`]. + /// + /// [`BITS`]: Self::BITS + /// + /// # Examples + /// + /// ``` + /// #![feature(get_set_bit)] + /// + #[doc = concat!("let mut n = 0b101_0101", stringify!($SelfT), ";")] + /// + /// n = n.set_bit(0, false); + /// n = n.set_bit(1, true); + /// n = n.set_bit(2, false); + /// n = n.set_bit(3, true); + /// n = n.set_bit(4, false); + /// n = n.set_bit(5, true); + /// n = n.set_bit(6, false); + /// + /// assert_eq!(n, 0b010_1010); + /// ``` + #[unstable(feature = "get_set_bit", issue = "147702")] + #[rustc_const_unstable(feature = "get_set_bit", issue = "147702")] + #[rustc_inherit_overflow_checks] + #[inline] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[track_caller] + pub const fn set_bit(self, index: u32, value: bool) -> Self { + self.cast_unsigned().set_bit(index, value).cast_signed() + } + /// Returns the number of ones in the binary representation of `self`. /// /// # Examples diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 84107ec3f1883..e202621673604 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -60,6 +60,83 @@ macro_rules! uint_impl { #[stable(feature = "int_bits_const", since = "1.53.0")] pub const BITS: u32 = Self::MAX.count_ones(); + /// Tests the value of a specific bit. + /// + /// # Panics + /// + /// If overflow checks are enabled, this method will panic if the provided index is + /// greater than or equal to [`BITS`]. + /// + /// [`BITS`]: Self::BITS + /// + /// # Examples + /// + /// ``` + /// #![feature(get_set_bit)] + /// + #[doc = concat!("let n = 0b0111_1001", stringify!($SelfT), ";")] + /// + /// assert!(n.bit(0)); + /// assert!(!n.bit(1)); + /// assert!(!n.bit(2)); + /// assert!(n.bit(3)); + /// assert!(n.bit(4)); + /// assert!(n.bit(5)); + /// assert!(n.bit(6)); + /// assert!(!n.bit(7)); + /// ``` + #[unstable(feature = "get_set_bit", issue = "147702")] + #[rustc_const_unstable(feature = "get_set_bit", issue = "147702")] + #[rustc_inherit_overflow_checks] + #[inline] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[track_caller] + pub const fn bit(self, index: u32) -> bool { + (self & (1 << index)) != 0 + } + + /// Sets the value of a specific bit. + /// + /// # Panics + /// + /// If overflow checks are enabled, this method will panic if the provided index is + /// greater than or equal [`BITS`]. + /// + /// [`BITS`]: Self::BITS + /// + /// # Examples + /// + /// ``` + /// #![feature(get_set_bit)] + /// + #[doc = concat!("let mut n = 0b0101_0101", stringify!($SelfT), ";")] + /// + /// n = n.set_bit(0, false); + /// n = n.set_bit(1, true); + /// n = n.set_bit(2, false); + /// n = n.set_bit(3, true); + /// n = n.set_bit(4, false); + /// n = n.set_bit(5, true); + /// n = n.set_bit(6, false); + /// n = n.set_bit(7, true); + /// + /// assert_eq!(n, 0b1010_1010); + /// ``` + #[unstable(feature = "get_set_bit", issue = "147702")] + #[rustc_const_unstable(feature = "get_set_bit", issue = "147702")] + #[rustc_inherit_overflow_checks] + #[inline] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[track_caller] + pub const fn set_bit(mut self, index: u32, value: bool) -> Self { + self &= !(1 << index); + self |= (value as $SelfT) << index; + + self + } + /// Returns the number of ones in the binary representation of `self`. /// /// # Examples diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 4e1b1a1aa6f6e..c9c07fe817cac 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -71,6 +71,7 @@ #![feature(freeze)] #![feature(future_join)] #![feature(generic_assert_internals)] +#![feature(get_set_bit)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(int_from_ascii)] diff --git a/library/coretests/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs index f391fe23cd1f5..6165edc6dda1e 100644 --- a/library/coretests/tests/num/int_macros.rs +++ b/library/coretests/tests/num/int_macros.rs @@ -32,7 +32,6 @@ macro_rules! int_module { } test_runtime_and_compiletime! { - fn test_rem_euclid() { assert_eq_const_safe!($T: (-1 as $T).rem_euclid(MIN), MAX); } @@ -73,6 +72,58 @@ macro_rules! int_module { const _1: $T = !0; test_runtime_and_compiletime! { + fn test_bit() { + assert!(!A.bit(0)); + assert!(!A.bit(1)); + assert!(A.bit(2)); + assert!(A.bit(3)); + assert!(!A.bit(4)); + assert!(A.bit(5)); + assert!(!A.bit(6)); + + assert!(B.bit(0)); + assert!(!B.bit(1)); + assert!(!B.bit(2)); + assert!(!B.bit(3)); + assert!(!B.bit(4)); + assert!(B.bit(5)); + assert!(!B.bit(6)); + + assert!(C.bit(0)); + assert!(!C.bit(1)); + assert!(!C.bit(2)); + assert!(C.bit(3)); + assert!(C.bit(4)); + assert!(C.bit(5)); + assert!(C.bit(6)); + } + + fn test_set_bit() { + assert_eq_const_safe!($T: A.set_bit(0, true), 0b0101101); + assert_eq_const_safe!($T: A.set_bit(1, true), 0b0101110); + assert_eq_const_safe!($T: A.set_bit(2, false), 0b0101000); + assert_eq_const_safe!($T: A.set_bit(3, false), 0b0100100); + assert_eq_const_safe!($T: A.set_bit(4, true), 0b0111100); + assert_eq_const_safe!($T: A.set_bit(5, false), 0b0001100); + assert_eq_const_safe!($T: A.set_bit(6, true), 0b1101100); + + assert_eq_const_safe!($T: B.set_bit(0, false), 0b0100000); + assert_eq_const_safe!($T: B.set_bit(1, true), 0b0100011); + assert_eq_const_safe!($T: B.set_bit(2, true), 0b0100101); + assert_eq_const_safe!($T: B.set_bit(3, true), 0b0101001); + assert_eq_const_safe!($T: B.set_bit(4, true), 0b0110001); + assert_eq_const_safe!($T: B.set_bit(5, false), 0b0000001); + assert_eq_const_safe!($T: B.set_bit(6, true), 0b1100001); + + assert_eq_const_safe!($T: C.set_bit(0, false), 0b1111000); + assert_eq_const_safe!($T: C.set_bit(1, true), 0b1111011); + assert_eq_const_safe!($T: C.set_bit(2, true), 0b1111101); + assert_eq_const_safe!($T: C.set_bit(3, false), 0b1110001); + assert_eq_const_safe!($T: C.set_bit(4, false), 0b1101001); + assert_eq_const_safe!($T: C.set_bit(5, false), 0b1011001); + assert_eq_const_safe!($T: C.set_bit(6, false), 0b0111001); + } + fn test_count_ones() { assert_eq_const_safe!(u32: A.count_ones(), 3); assert_eq_const_safe!(u32: B.count_ones(), 2); diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index 4dfcb1b8b4688..9d500d1578b64 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -35,6 +35,58 @@ macro_rules! uint_module { const _1: $T = !0; test_runtime_and_compiletime! { + fn test_bit() { + assert!(!A.bit(0)); + assert!(!A.bit(1)); + assert!(A.bit(2)); + assert!(A.bit(3)); + assert!(!A.bit(4)); + assert!(A.bit(5)); + assert!(!A.bit(6)); + + assert!(B.bit(0)); + assert!(!B.bit(1)); + assert!(!B.bit(2)); + assert!(!B.bit(3)); + assert!(!B.bit(4)); + assert!(B.bit(5)); + assert!(!B.bit(6)); + + assert!(C.bit(0)); + assert!(!C.bit(1)); + assert!(!C.bit(2)); + assert!(C.bit(3)); + assert!(C.bit(4)); + assert!(C.bit(5)); + assert!(C.bit(6)); + } + + fn test_set_bit() { + assert_eq_const_safe!($T: A.set_bit(0, true), 0b0101101); + assert_eq_const_safe!($T: A.set_bit(1, true), 0b0101110); + assert_eq_const_safe!($T: A.set_bit(2, false), 0b0101000); + assert_eq_const_safe!($T: A.set_bit(3, false), 0b0100100); + assert_eq_const_safe!($T: A.set_bit(4, true), 0b0111100); + assert_eq_const_safe!($T: A.set_bit(5, false), 0b0001100); + assert_eq_const_safe!($T: A.set_bit(6, true), 0b1101100); + + assert_eq_const_safe!($T: B.set_bit(0, false), 0b0100000); + assert_eq_const_safe!($T: B.set_bit(1, true), 0b0100011); + assert_eq_const_safe!($T: B.set_bit(2, true), 0b0100101); + assert_eq_const_safe!($T: B.set_bit(3, true), 0b0101001); + assert_eq_const_safe!($T: B.set_bit(4, true), 0b0110001); + assert_eq_const_safe!($T: B.set_bit(5, false), 0b0000001); + assert_eq_const_safe!($T: B.set_bit(6, true), 0b1100001); + + assert_eq_const_safe!($T: C.set_bit(0, false), 0b1111000); + assert_eq_const_safe!($T: C.set_bit(1, true), 0b1111011); + assert_eq_const_safe!($T: C.set_bit(2, true), 0b1111101); + assert_eq_const_safe!($T: C.set_bit(3, false), 0b1110001); + assert_eq_const_safe!($T: C.set_bit(4, false), 0b1101001); + assert_eq_const_safe!($T: C.set_bit(5, false), 0b1011001); + assert_eq_const_safe!($T: C.set_bit(6, false), 0b0111001); + } + fn test_count_ones() { assert!(A.count_ones() == 3); assert!(B.count_ones() == 2); diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 0f80646449e1f..9c8b9e6f3a2e6 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -172,7 +172,9 @@ impl Socket { } pub fn poll_connected(&self, timeout: Duration) -> io::Result<()> { - let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; + let mut pollfd: libc::pollfd = unsafe { mem::zeroed() }; + pollfd.fd = self.as_raw_fd(); + pollfd.events = libc::POLLOUT; if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { return Err(io::Error::ZERO_TIMEOUT); diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 53bbdfc988a8f..4b0bbdaf073ca 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -88,13 +88,16 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { )))] 'poll: { use crate::sys::io::errno; - let pfds: &mut [_] = &mut [ - libc::pollfd { fd: 0, events: 0, revents: 0 }, - libc::pollfd { fd: 1, events: 0, revents: 0 }, - libc::pollfd { fd: 2, events: 0, revents: 0 }, - ]; - while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 { + let mut pfds: [libc::pollfd; 3] = crate::mem::zeroed(); + pfds[0].fd = 0; + pfds[0].events = 0; + pfds[1].fd = 1; + pfds[1].events = 0; + pfds[2].fd = 2; + pfds[2].events = 0; + + while libc::poll(pfds.as_mut_ptr(), pfds.len() as libc::nfds_t, 0) == -1 { match errno() { libc::EINTR => continue, #[cfg(target_vendor = "unikraft")] diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index 6011492ed27c9..23383e32fcf68 100755 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -157,22 +157,10 @@ def concat_multi_lines(f): re.X | re.UNICODE, ) -DEPRECATED_LINE_PATTERN = re.compile( - r"//\s+@", - re.X | re.UNICODE, -) - def get_commands(template): with io.open(template, encoding="utf-8") as f: for lineno, line in concat_multi_lines(f): - if DEPRECATED_LINE_PATTERN.search(line): - print_err( - lineno, - line, - "Deprecated command syntax, replace `// @` with `//@ `", - ) - continue m = LINE_PATTERN.search(line) if not m: continue diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7b2cad75050c7..7d6d33c58b14f 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1652,7 +1652,7 @@ fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Optionⓘ", + " ⓘ", ty = Escape(&format!("{:#}", print_type(ty, cx))), ) }) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index b92325a4389a3..532f40608b2e4 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1683,16 +1683,15 @@ so that we can apply CSS-filters to change the arrow color in themes */ position: relative; } -.notable-trait-badge-container > a.tooltip { +.notable-trait-badge-container > .tooltip { position: absolute; width: 16px; line-height: 1.5rem; - vertical-align: center; left: calc(-1 * var(--docblock-indent)); text-decoration: none; } -.notable-trait-badge-container > a:not(.tooltip) { +.notable-trait-badge-container > a { display: flex; align-items: center; width: fit-content; @@ -2035,24 +2034,23 @@ Without this extra selector, an empty line with a weird border appears out of no border-right: 3px solid var(--target-border-color); } -a.tooltip { +span.tooltip { font-family: var(--font-family); -} - -.code-header a.tooltip { + cursor: pointer; color: inherit; margin-right: 15px; position: relative; + display: inline; } -.code-header a.tooltip:hover { +span.tooltip:hover { color: var(--link-color); } /* placeholder thunk so that the mouse can easily travel from "(i)" to popover the resulting "hover tunnel" is a stepped triangle, approximating https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown */ -a.tooltip:hover::after { +span.tooltip:hover::after { position: absolute; top: calc(100% - 10px); left: -15px; @@ -2999,7 +2997,7 @@ in src-script.js and main.js left: -11px; } - .notable-trait-badge-container > a.tooltip { + .notable-trait-badge-container > span.tooltip { left: calc(-1 * var(--docblock-indent) - 11px); } diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 4297ddf484561..95e3ea851ffec 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -31,7 +31,7 @@

{% endmatch %} {% if !notable_trait_badges.is_empty() %}
{# #} - ⓘ + ⓘ {% for badge in notable_trait_badges.iter() %} { + ($($arg:tt)*) => {{ let status = ::colored::Colorize::red("ERROR: "); let status = ::colored::Colorize::bold(status); eprint!("{status}"); eprintln!($($arg)*); - }; + }}; } #[macro_export] diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 33b75f6ab860b..4e99ab99f6059 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -548,9 +548,12 @@ fn iter_directives( }; for directive_str in extra_directives { - let directive_line = line_directive(testfile, LineNumber::ZERO, directive_str) - .unwrap_or_else(|| panic!("bad extra-directive line: {directive_str:?}")); - it(&directive_line); + if let Some(Ok(directive_line)) = line_directive(testfile, LineNumber::ZERO, directive_str) + { + it(&directive_line); + } else { + panic!("bad extra-directive line: {directive_str:?}") + } } for directive_line in &file_directives.lines { diff --git a/src/tools/compiletest/src/directives/file.rs b/src/tools/compiletest/src/directives/file.rs index bde935729121f..8bd6de5041934 100644 --- a/src/tools/compiletest/src/directives/file.rs +++ b/src/tools/compiletest/src/directives/file.rs @@ -33,7 +33,8 @@ impl<'a> FileDirectives<'a> { } if let Some(directive_line) = line_directive(path, line_number, ln) { - lines.push(directive_line); + let Ok(line) = directive_line.map_err(|e| e.raise_fatal()); + lines.push(line); } } diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs index 87e803921a853..8c3fe05b6ef7c 100644 --- a/src/tools/compiletest/src/directives/line.rs +++ b/src/tools/compiletest/src/directives/line.rs @@ -3,56 +3,59 @@ use std::fmt; use camino::Utf8Path; use crate::directives::LineNumber; - -const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; +use crate::{error, help}; /// If the given line begins with the appropriate comment prefix for a directive, /// returns a struct containing various parts of the directive. pub(crate) fn line_directive<'a>( file_path: &'a Utf8Path, line_number: LineNumber, - original_line: &'a str, -) -> Option> { - // Ignore lines that don't start with the comment prefix. - let after_comment = - original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start(); + line: &'a str, +) -> Option, Error<'a>>> { + let error = |kind| Some(Err(Error { kind, file_path, line_number })); + + let comment = line.trim_start().strip_prefix("//")?; + let trimmed_comment = comment.trim_start(); + + let (leading_revision, trimmed_comment) = trimmed_comment + .strip_prefix('[') + .and_then(|after_open_bracket| after_open_bracket.split_once(']')) + .map(|(revision, after_close_bracket)| (Some(revision), after_close_bracket.trim_start())) + .unwrap_or((None, trimmed_comment)); + + let mut raw_directive = trimmed_comment.strip_prefix("@")?.trim_start(); + + if let Some(revision) = leading_revision { + let context = &line[..line.substr_range(raw_directive).unwrap().start]; + return error(ErrorKind::LeadingRevisionCondition { revision, context }); + } + + if trimmed_comment.len() != comment.len() { + return error(ErrorKind::WhitespaceBeforeAtSign); + } - let revision; - let raw_directive; + let mut revision = None; - if let Some(after_open_bracket) = after_comment.strip_prefix('[') { - // A comment like `//@[foo]` only applies to revision `foo`. + if let Some(after_open_bracket) = raw_directive.strip_prefix('[') { let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else { - panic!( - "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" - ) + return error(ErrorKind::UnclosedRevisionCondition); }; revision = Some(line_revision); raw_directive = after_close_bracket.trim_start(); if line_revision.contains(",") { - let suggestion: Vec<_> = line_revision - .split(",") - .map(|revision| { - format!("{COMPILETEST_DIRECTIVE_PREFIX} [{revision}]: {raw_directive}") - }) - .collect(); - panic!( - "malformed condition directive: multiple revisions aren't supported yet in `{}`, split them like\n{}", - original_line, - suggestion.join("\n"), - ); + return error(ErrorKind::CommaInRevisionCondition { + revision: line_revision, + directive: raw_directive, + }); } - } else { - revision = None; - raw_directive = after_comment; - }; + } // The directive name ends at the first occurrence of colon, space, or end-of-string. let name = raw_directive.split([':', ' ']).next().expect("split is never empty"); - Some(DirectiveLine { file_path, line_number, revision, raw_directive, name }) + Some(Ok(DirectiveLine { file_path, line_number, revision, raw_directive, name })) } /// The (partly) broken-down contents of a line containing a test directive, @@ -70,6 +73,7 @@ pub(crate) fn line_directive<'a>( /// ^^^^^^^^^^^^^^^^^ raw_directive /// ^^^^^^^^^^^^^ name /// ``` +#[cfg_attr(test, derive(Debug))] pub(crate) struct DirectiveLine<'a> { /// Path of the file containing this line. /// @@ -88,7 +92,10 @@ pub(crate) struct DirectiveLine<'a> { /// /// This is "raw" because the directive's name and colon-separated value /// (if present) have not yet been extracted or checked. + #[cfg(not(test))] raw_directive: &'a str, + #[cfg(test)] + pub(crate) raw_directive: &'a str, /// Name of the directive. /// @@ -130,3 +137,60 @@ impl<'ln> DirectiveLine<'ln> { self.raw_directive } } + +#[cfg_attr(test, derive(Debug))] +pub(crate) struct Error<'a> { + pub(crate) kind: ErrorKind<'a>, + pub(crate) file_path: &'a Utf8Path, + pub(crate) line_number: LineNumber, +} + +#[cfg_attr(test, derive(Debug))] +pub(crate) enum ErrorKind<'a> { + LeadingRevisionCondition { revision: &'a str, context: &'a str }, + WhitespaceBeforeAtSign, + UnclosedRevisionCondition, + CommaInRevisionCondition { revision: &'a str, directive: &'a str }, +} + +impl Error<'_> { + // FIXME: Just report the error, poison the context and keep going. + pub(crate) fn raise_fatal(self) -> ! { + let Self { kind, file_path, line_number } = self; + + let error = |message| error!("malformed directive: {message} at {file_path}:{line_number}"); + + match kind { + ErrorKind::LeadingRevisionCondition { revision, context } => { + error("found revision condition before `@` instead of after"); + help!("rewrite the start of the line to `//@[{revision}] ` (from `{context}`)"); + } + ErrorKind::WhitespaceBeforeAtSign => { + error("found whitespace between `//` and `@`"); + help!("if you meant to write a directive remove the whitespace to form `//@`"); + help!( + "if you meant to literally write `@` in a code comment add `\\` before `@` or \ + rewrap the contents of the comment so `@` doesn't come first" + ); + } + ErrorKind::UnclosedRevisionCondition => { + error("revision condition is missing a closing bracket `]`"); + } + ErrorKind::CommaInRevisionCondition { revision, directive } => { + error( + "found commas in revision condition but multiple revisions aren't supported yet", + ); + + let suggestion = revision + .split(",") + .map(|revision| format!("//@[{revision}]: {directive}")) + .collect::>() + .join("\n"); + + help!("consider splitting the directive like\n{suggestion}"); + } + } + + panic!("fatal error") + } +} diff --git a/src/tools/compiletest/src/directives/line_number.rs b/src/tools/compiletest/src/directives/line_number.rs index ce5f200f470c8..07ae043d1c716 100644 --- a/src/tools/compiletest/src/directives/line_number.rs +++ b/src/tools/compiletest/src/directives/line_number.rs @@ -1,7 +1,7 @@ /// A line number in a file. Internally the first line has index 1. /// If it is 0 it means "no specific line" (used e.g. for implied directives). /// When `Display`:ed, the first line is `1`. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub(crate) struct LineNumber(usize); impl LineNumber { diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 6facfe362c4eb..03900706e042f 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -1,9 +1,11 @@ +use std::assert_matches; use std::collections::BTreeSet; use camino::Utf8Path; use semver::Version; use crate::common::{Config, Debugger, TestMode}; +use crate::directives::line::{DirectiveLine, Error, ErrorKind}; use crate::directives::{ self, AuxProps, DIRECTIVE_HANDLERS_MAP, DirectivesCache, EarlyProps, Edition, EditionRange, FileDirectives, KNOWN_DIRECTIVE_NAMES_SET, LineNumber, extract_llvm_version, @@ -654,18 +656,116 @@ fn test_miropt_mode_forbidden_revisions() { } #[test] -#[should_panic(expected = "malformed condition directive: multiple revisions aren't supported yet")] +fn test_ok_directive() { + let line = "//@ aux-build: file.rs"; + + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!( + result, + Some(Ok(DirectiveLine { + revision: None, + name: "aux-build", + raw_directive: "aux-build: file.rs", + .. + })) + ); +} + +#[test] +fn indented_directive() { + let line = " //@ key: value"; + + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!( + result, + Some(Ok(DirectiveLine { revision: None, name: "key", raw_directive: "key: value", .. })) + ); +} + +#[test] +fn test_doc_comment_at_sign_not_a_directive() { + let line = "///@ aux-build: file.rs"; + + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!(result, None); +} + +#[test] fn test_multiple_revisions_in_directive() { - let directive = "//@ [foo,bar] compile-flags: -Z hello"; + // The problem: If we don't reject this early, this will be seen as a single revision. + let line = "//@ [foo,bar] compile-flags: -Z hello"; + + let result = line_directive(Utf8Path::new("foo.txt"), LineNumber::ZERO, line); + assert_matches!( + result, + Some(Err(Error { + kind: ErrorKind::CommaInRevisionCondition { + revision: "foo,bar", + directive: "compile-flags: -Z hello" + }, + .. + })) + ); +} + +#[test] +fn test_whitespace_before_at_sign_in_directive() { + let line = "// @ check-pass"; + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!(result, Some(Err(Error { kind: ErrorKind::WhitespaceBeforeAtSign, .. }))); +} - // The problem: this is seen as a single revision. - let line_directive = line_directive(Utf8Path::new("foo.txt"), LineNumber::ZERO, directive); - assert!(line_directive.is_some()); - assert_eq!(Some("foo,bar"), line_directive.unwrap().revision); +#[test] +fn test_leading_revision_condition_in_directive_0() { + let line = "//[leading]@ compile-flags: -Aunused"; + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!( + result, + Some(Err(Error { + kind: ErrorKind::LeadingRevisionCondition { + revision: "leading", + context: "//[leading]@ " + }, + .. + })) + ); +} - // The solution for now: forbid directives from having multiple revisions. - let config: Config = cfg().build(); - parse_early_props(&config, directive); +#[test] +fn test_leading_revision_condition_in_directive_1() { + let line = "// [leading] @ compile-flags: -Aunused"; + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!( + result, + Some(Err(Error { + kind: ErrorKind::LeadingRevisionCondition { + revision: "leading", + context: "// [leading] @ " + }, + .. + })) + ); +} + +#[test] +fn test_not_a_leading_revision_condition_in_directive_0() { + let line = "// [leading @ compile-flags: -Aunused"; + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!(result, None); +} + +#[test] +fn test_not_a_leading_revision_condition_in_directive_1() { + let line = "// [closure@file.rs]"; + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!(result, None); +} + +#[test] +fn test_unclosed_revision_condition_in_directive() { + let line = "//@[revision edition: 2021"; + let result = line_directive(Utf8Path::new("file.rs"), LineNumber::ZERO, line); + assert_matches!(result, Some(Err(Error { kind: ErrorKind::UnclosedRevisionCondition, .. }))); } #[test] @@ -1153,8 +1253,9 @@ fn parse_edition_range(line: &str) -> Option { let config = cfg().build(); let line_with_comment = format!("//@ {line}"); - let line = - line_directive(Utf8Path::new("tmp.rs"), LineNumber::ZERO, &line_with_comment).unwrap(); + let line = line_directive(Utf8Path::new("tmp.rs"), LineNumber::ZERO, &line_with_comment) + .unwrap() + .unwrap(); super::parse_edition_range(&config, &line) } diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index 7046c981b28fd..d48097bacd556 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -40,9 +40,8 @@ impl DebuggerCommands { continue; } - let Some(directive) = line_directive(file, line_number, &line) else { - continue; - }; + let Some(directive) = line_directive(file, line_number, &line) else { continue }; + let Ok(directive) = directive.map_err(|e| e.raise_fatal()); if !directive.applies_to_test_revision(test_revision) { continue; diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index c3487565c23ed..026004c52d277 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -57,10 +57,6 @@ static LINE_PATTERN: LazyLock = LazyLock::new(|| { .unwrap() }); -static DEPRECATED_LINE_PATTERN: LazyLock = LazyLock::new(|| { - RegexBuilder::new(r"//\s+@").ignore_whitespace(true).unicode(true).build().unwrap() -}); - fn print_err(msg: &str, lineno: usize) { eprintln!("Invalid directive: {} on line {}", msg, lineno) } @@ -72,18 +68,9 @@ fn get_directives(template: &str) -> Result, ()> { let file = fs::read_to_string(template).unwrap(); for (lineno, line) in file.split('\n').enumerate() { + let Some(cap) = LINE_PATTERN.captures(line) else { continue }; let lineno = lineno + 1; - if DEPRECATED_LINE_PATTERN.is_match(line) { - print_err("Deprecated directive syntax, replace `// @` with `//@ `", lineno); - errors = true; - continue; - } - - let Some(cap) = LINE_PATTERN.captures(line) else { - continue; - }; - let negated = &cap["negated"] == "!"; let args_str = cap.name("args").map(|m| m.as_str()).unwrap_or_default(); diff --git a/tests/codegen-llvm/loop-vectorize-in-opt-sz.rs b/tests/codegen-llvm/loop-vectorize-in-opt-sz.rs new file mode 100644 index 0000000000000..f26a558f17ea4 --- /dev/null +++ b/tests/codegen-llvm/loop-vectorize-in-opt-sz.rs @@ -0,0 +1,23 @@ +//@ revisions: O2 S Z +//@[O2] compile-flags: -Copt-level=2 +//@[S] compile-flags: -Copt-level=s +//@[Z] compile-flags: -Copt-level=z +//@ only-x86_64 + +// Loop vectorization is enabled only for the speed objective, so both `-Copt-level=s` and `z` leave +// it off. The loop below is long enough that LLVM at `-O2` does vectorize it. + +#![crate_type = "lib"] + +// CHECK-LABEL: @scale_add( +// O2: fmul <4 x float> +// O2: fadd <4 x float> +// O2: store <4 x float> +// S-NOT: <4 x float> +// Z-NOT: <4 x float> +#[no_mangle] +pub fn scale_add(a: &[f32], b: &[f32], c: &mut [f32]) { + for i in 0..64 { + c[i] = a[i] * b[i] + 3.0; + } +} diff --git a/tests/codegen-llvm/merge-functions.rs b/tests/codegen-llvm/merge-functions.rs index b9d3727ce112e..8c15791c5ae74 100644 --- a/tests/codegen-llvm/merge-functions.rs +++ b/tests/codegen-llvm/merge-functions.rs @@ -1,5 +1,6 @@ -//@ revisions: O Os +//@ revisions: O Os Oz //@[Os] compile-flags: -Copt-level=s +//@[Oz] compile-flags: -Copt-level=z //@[O] compile-flags: -Copt-level=3 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/opt-size-attr-shim.rs b/tests/codegen-llvm/opt-size-attr-shim.rs new file mode 100644 index 0000000000000..a82a3219ca5b1 --- /dev/null +++ b/tests/codegen-llvm/opt-size-attr-shim.rs @@ -0,0 +1,30 @@ +//@ revisions: O2 S Z +//@[O2] compile-flags: -Copt-level=2 +//@[S] compile-flags: -Copt-level=s +//@[Z] compile-flags: -Copt-level=z +//@ ignore-wasi wasi codegens the main symbol differently + +// The C entry shim has no item behind it, so it does not go through the per-item attribute path. +// It still has to carry the size attributes, or `main` is the one definition in a size build that +// LLVM optimizes for speed. + +#![crate_type = "bin"] + +// CHECK-LABEL: define {{.*}} @main( +// CHECK-SAME: [[MAIN_ATTRS:#[0-9]+]] + +// O2: attributes [[MAIN_ATTRS]] = +// O2-NOT: optsize +// O2-NOT: minsize + +// S: attributes [[MAIN_ATTRS]] = +// S-NOT: minsize +// S-SAME: optsize + +// Z: attributes [[MAIN_ATTRS]] = +// Z-SAME: minsize +// Z-SAME: optsize + +fn main() { + std::hint::black_box(0); +} diff --git a/tests/codegen-llvm/opt-size-attr.rs b/tests/codegen-llvm/opt-size-attr.rs new file mode 100644 index 0000000000000..7784e8d1c8e47 --- /dev/null +++ b/tests/codegen-llvm/opt-size-attr.rs @@ -0,0 +1,29 @@ +//@ revisions: O2 S Z +//@[O2] compile-flags: -Copt-level=2 +//@[S] compile-flags: -Copt-level=s +//@[Z] compile-flags: -Copt-level=z + +// `s` gives `optsize` while `z` gives both `minsize` and `optsize`. This is the only coverage of +// the `-Copt-level` path; `optimize-attr-1.rs` reaches these via `#[optimize(size)]`. + +#![crate_type = "lib"] + +// CHECK-LABEL: @f( +// CHECK-SAME: [[F_ATTRS:#[0-9]+]] + +// O2: attributes [[F_ATTRS]] = +// O2-NOT: optsize +// O2-NOT: minsize + +// S: attributes [[F_ATTRS]] = +// S-NOT: minsize +// S-SAME: optsize + +// Z: attributes [[F_ATTRS]] = +// Z-SAME: minsize +// Z-SAME: optsize + +#[no_mangle] +pub fn f(x: u32) -> u32 { + x + 1 +} diff --git a/tests/codegen-llvm/terminating-catchpad.rs b/tests/codegen-llvm/terminating-catchpad.rs index cea5d48ab99e5..4f56a245af6ad 100644 --- a/tests/codegen-llvm/terminating-catchpad.rs +++ b/tests/codegen-llvm/terminating-catchpad.rs @@ -10,9 +10,9 @@ // - `catchpad ... [ptr null]` on Wasm (otherwise LLVM gets confused) // - `catchpad ... [ptr null, i32 64, ptr null]` on Windows (otherwise we catch SEH exceptions) // -// Unlike on windows, on Wasm, we specifically do want to catch foreign -// exceptions. To catch only C++ exceptions we'd need to call -// @llvm.wasm.get.exception and @llvm.wasm.get.ehselector in the catchpad. +// Unlike on windows, on Wasm, we specifically do want to catch foreign exceptions. To catch only +// C++ exceptions we'd need to call @llvm.wasm.get.exception and @llvm.wasm.get.ehselector in the +// catchpad. #![feature(no_core, lang_items, rustc_attrs)] #![crate_type = "lib"] diff --git a/tests/codegen-llvm/unroll-in-opt-sz.rs b/tests/codegen-llvm/unroll-in-opt-sz.rs new file mode 100644 index 0000000000000..a76c8b3fba51c --- /dev/null +++ b/tests/codegen-llvm/unroll-in-opt-sz.rs @@ -0,0 +1,31 @@ +//@ revisions: O2 S Z +//@[O2] compile-flags: -Copt-level=2 +//@[S] compile-flags: -Copt-level=s +//@[Z] compile-flags: -Copt-level=z + +// `-Copt-level=s`/`z` pass `PTO.LoopUnrolling = false`, so LLVM's unroller runs `OnlyWhenForced`. +// The O2 side is asserted too, so disabling unrolling everywhere cannot pass. Calls are counted +// because this knob emits no `llvm.loop.unroll.*` metadata. + +#![crate_type = "lib"] + +unsafe extern "C" { + fn side_effect(i: u32); +} + +// O2-LABEL: @four_iters( +// O2-COUNT-4: tail call void @side_effect +// S-LABEL: @four_iters( +// S: phi i32 +// S: tail call void @side_effect +// S-NOT: tail call void @side_effect +// Z-LABEL: @four_iters( +// Z: phi i32 +// Z: tail call void @side_effect +// Z-NOT: tail call void @side_effect +#[no_mangle] +pub fn four_iters() { + for i in 0..4u32 { + unsafe { side_effect(i) } + } +} diff --git a/tests/rustdoc-gui/notable-trait-badges.goml b/tests/rustdoc-gui/notable-trait-badges.goml index 927c4e34cccd4..39a4f09fa4d4e 100644 --- a/tests/rustdoc-gui/notable-trait-badges.goml +++ b/tests/rustdoc-gui/notable-trait-badges.goml @@ -26,20 +26,44 @@ define-function: ( store-position: (".top-doc > .docblock", {"x": top_docblock_x}) assert-position: (".notable-trait-badge-container", {"x": |top_docblock_x|}) store-position: (".top-doc > summary::before", {"x": top_docblock_toggle_x}) - assert-position: (".notable-trait-badge-container > a.tooltip", {"x": |top_docblock_toggle_x|}) - store-position: (".notable-trait-badge-container > a.tooltip + a", {"x": notable_trait_badge_first_x}) - store-position: (".notable-trait-badge-container > a.tooltip", {"x": notable_trait_tooltip_x}) - store-size: (".notable-trait-badge-container > a.tooltip", {"width": notable_trait_tooltip_width}) + assert-position: ( + ".notable-trait-badge-container > .tooltip", + {"x": |top_docblock_toggle_x|}, + ) + store-position: ( + ".notable-trait-badge-container > .tooltip + a", + {"x": notable_trait_badge_first_x}, + ) + store-position: ( + ".notable-trait-badge-container > .tooltip", + {"x": notable_trait_tooltip_x}, + ) + store-size: ( + ".notable-trait-badge-container > .tooltip", + {"width": notable_trait_tooltip_width}, + ) assert: |notable_trait_tooltip_x| + |notable_trait_tooltip_width| < |notable_trait_badge_first_x| set-window-size: (320, 480) store-position: (".top-doc > .docblock", {"x": top_docblock_x}) assert-position: (".notable-trait-badge-container", {"x": |top_docblock_x|}) store-position: (".top-doc > summary::before", {"x": top_docblock_toggle_x}) - assert-position: (".notable-trait-badge-container > a.tooltip", {"x": |top_docblock_toggle_x|}) - store-position: (".notable-trait-badge-container > a.tooltip + a", {"x": notable_trait_badge_first_x}) - store-position: (".notable-trait-badge-container > a.tooltip", {"x": notable_trait_tooltip_x}) - store-size: (".notable-trait-badge-container > a.tooltip", {"width": notable_trait_tooltip_width}) + assert-position: ( + ".notable-trait-badge-container > .tooltip", + {"x": |top_docblock_toggle_x|}, + ) + store-position: ( + ".notable-trait-badge-container > .tooltip + a", + {"x": notable_trait_badge_first_x}, + ) + store-position: ( + ".notable-trait-badge-container > .tooltip", + {"x": notable_trait_tooltip_x}, + ) + store-size: ( + ".notable-trait-badge-container > .tooltip", + {"width": notable_trait_tooltip_width}, + ) assert: |notable_trait_tooltip_x| + |notable_trait_tooltip_width| < |notable_trait_badge_first_x| } ) @@ -83,7 +107,7 @@ call-function: ("test-badges", { }) assert-false: ".tooltip.popover" -click: ".notable-trait-badge-container > a.tooltip" +click: ".notable-trait-badge-container > .tooltip" wait-for: ".tooltip.popover" assert-text: (".tooltip.popover h3", "Notable traits for NotableTraitColors") assert-count: (".tooltip.popover .where", 8) diff --git a/tests/rustdoc-gui/tooltips.goml b/tests/rustdoc-gui/tooltips.goml index 6e79196be2731..f6310283aacf4 100644 --- a/tests/rustdoc-gui/tooltips.goml +++ b/tests/rustdoc-gui/tooltips.goml @@ -6,7 +6,7 @@ define-function: ( block { go-to: "file://" + |DOC_PATH| + "/test_docs/" + |path| assert-css: ( - "a.tooltip", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ALL, + ".tooltip", {"font-family": '"Source Serif 4", NanumBarunGothic, serif'}, ALL, ) } ) diff --git a/tests/rustdoc-html/display/where.SWhere_TraitWhere_item-decl.html b/tests/rustdoc-html/display/where.SWhere_TraitWhere_item-decl.html index c0fea95878005..ca7012b80f7bb 100644 --- a/tests/rustdoc-html/display/where.SWhere_TraitWhere_item-decl.html +++ b/tests/rustdoc-html/display/where.SWhere_TraitWhere_item-decl.html @@ -5,7 +5,7 @@ // Provided methods fn func(self) where Self: Sized { ... } - fn lines(self) -> Lines<Self> ⓘ + fn lines(self) -> Lines<Self> ⓘ where Self: Sized { ... } fn merge<T>(self, a: T) where Self: Sized, diff --git a/tests/rustdoc-html/notable-trait/doc-notable_trait.rs b/tests/rustdoc-html/notable-trait/doc-notable_trait.rs index 83fe6172e9471..0a4eb7e86c874 100644 --- a/tests/rustdoc-html/notable-trait/doc-notable_trait.rs +++ b/tests/rustdoc-html/notable-trait/doc-notable_trait.rs @@ -9,7 +9,7 @@ impl SomeTrait for Wrapper {} #[doc(notable_trait)] pub trait SomeTrait { //@ has doc_notable_trait/trait.SomeTrait.html - //@ has - '//a[@class="tooltip"]/@data-notable-ty' 'Wrapper' + //@ has - '//span[@class="tooltip"]/@data-notable-ty' 'Wrapper' //@ snapshot wrap-me - '//script[@id="notable-traits-data"]' fn wrap_me(self) -> Wrapper where Self: Sized { Wrapper { @@ -23,7 +23,7 @@ impl SomeTrait for SomeStruct {} impl SomeStruct { //@ has doc_notable_trait/struct.SomeStruct.html - //@ has - '//a[@class="tooltip"]/@data-notable-ty' 'SomeStruct' + //@ has - '//span[@class="tooltip"]/@data-notable-ty' 'SomeStruct' //@ snapshot some-struct-new - '//script[@id="notable-traits-data"]' pub fn new() -> SomeStruct { SomeStruct @@ -31,7 +31,7 @@ impl SomeStruct { } //@ has doc_notable_trait/fn.bare_fn.html -//@ has - '//a[@class="tooltip"]/@data-notable-ty' 'SomeStruct' +//@ has - '//span[@class="tooltip"]/@data-notable-ty' 'SomeStruct' //@ snapshot bare-fn - '//script[@id="notable-traits-data"]' pub fn bare_fn() -> SomeStruct { SomeStruct diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs index dc7b349ede2f3..ae18248bd3dd1 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs @@ -8,8 +8,9 @@ pub trait Pos {} // A negative impl must not produce a badge. //@ has 'foo/struct.T.html' -//@ count - '//div[@class="notable-trait-badge-container"]/a' 2 -//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' +//@ count - '//div[@class="notable-trait-badge-container"]/span' 1 +//@ count - '//div[@class="notable-trait-badge-container"]/a' 1 +//@ has - '//div[@class="notable-trait-badge-container"]/span' 'ⓘ' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Pos.html"]' 'Pos' pub struct T; impl !Neg for T {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs index 2d29fbeedf80e..a007ec2388862 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs @@ -9,8 +9,9 @@ pub trait Derived: Base {} //@ has 'foo/struct.S.html' // Implementing `Derived` requires implementing the notable supertrait `Base`, // so its badge shows up. -//@ count - '//div[@class="notable-trait-badge-container"]/a' 2 -//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' +//@ count - '//div[@class="notable-trait-badge-container"]/a' 1 +//@ count - '//div[@class="notable-trait-badge-container"]/span' 1 +//@ has - '//div[@class="notable-trait-badge-container"]/span' 'ⓘ' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Base.html"]' 'Base' pub struct S; impl Base for S {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs index a249a100e6541..87981770c3e0e 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs @@ -9,10 +9,8 @@ extern crate notable_dep; use notable_dep::Spaceship; //@ has 'foo/struct.Rocket.html' -// The badge is present... //@ has - '//div[@class="notable-trait-badge-container"]/a' 'Spaceship' -// ...but unlinked: no badge carries an `href`. -//@ count - '//div[@class="notable-trait-badge-container"]/a[@href]' 1 -//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' +// We ensure that the `tabindex` is set so you can focus the `span`. +//@ has - '//div[@class="notable-trait-badge-container"]/span/@tabindex' '0' pub struct Rocket; impl Spaceship for Rocket {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge.rs index 7692201bc6140..56750ca211505 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge.rs @@ -12,9 +12,9 @@ pub trait Plain {} //@ has 'foo/struct.Tagged.html' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Labeled.html"][@title="foo::Labeled"]' 'Labeled' // Badges are sorted by trait name, so `AlsoLabeled` precedes `Labeled`. -//@ has - '//div[@class="notable-trait-badge-container"]/a[1]' 'ⓘ' -//@ has - '//div[@class="notable-trait-badge-container"]/a[2]' 'AlsoLabeled' -//@ has - '//div[@class="notable-trait-badge-container"]/a[3]' 'Labeled' +//@ has - '//div[@class="notable-trait-badge-container"]/span[1]' 'ⓘ' +//@ has - '//div[@class="notable-trait-badge-container"]/a[1]' 'AlsoLabeled' +//@ has - '//div[@class="notable-trait-badge-container"]/a[2]' 'Labeled' pub struct Tagged; impl Labeled for Tagged {} impl AlsoLabeled for Tagged {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-generics.rs b/tests/rustdoc-html/notable-trait/notable-trait-generics.rs index 60d09661aff52..37dbf4f76c8b1 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-generics.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-generics.rs @@ -11,7 +11,7 @@ pub mod generic_return { impl NotableTrait for Wrapper {} //@ has notable_trait_generics/generic_return/fn.returning.html - //@ !has - '//a[@class="tooltip"]/@data-notable-ty' 'Wrapper' + //@ count - '//span[@class="tooltip"]' 0 pub fn returning() -> Wrapper { loop {} } @@ -28,7 +28,8 @@ pub mod generic_impl { impl NotableTrait for Wrapper {} //@ has notable_trait_generics/generic_impl/fn.returning.html - //@ has - '//a[@class="tooltip"]/@data-notable-ty' 'Wrapper' + //@ count - '//span[@class="tooltip"]' 1 + //@ has - '//span[@class="tooltip"]/@data-notable-ty' 'Wrapper' pub fn returning() -> Wrapper { loop {} } diff --git a/tests/rustdoc-html/notable-trait/spotlight-from-dependency.rs b/tests/rustdoc-html/notable-trait/spotlight-from-dependency.rs index 992102f87c7c6..78dc6652326f3 100644 --- a/tests/rustdoc-html/notable-trait/spotlight-from-dependency.rs +++ b/tests/rustdoc-html/notable-trait/spotlight-from-dependency.rs @@ -3,7 +3,7 @@ use std::iter::Iterator; //@ has foo/struct.Odd.html -//@ has - '//*[@id="method.new"]//a[@class="tooltip"]/@data-notable-ty' 'Odd' +//@ has - '//*[@id="method.new"]//*[@class="tooltip"]/@data-notable-ty' 'Odd' //@ snapshot odd - '//script[@id="notable-traits-data"]' pub struct Odd { current: usize, diff --git a/tests/ui/attributes/z-crate-attr/ordering-cli.rs b/tests/ui/attributes/z-crate-attr/ordering-cli.rs new file mode 100644 index 0000000000000..877a25939b9f3 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/ordering-cli.rs @@ -0,0 +1,6 @@ +// Multiple `-Zcrate-attr` flags apply in command-line order, so the later `deny` wins. +//@ compile-flags: -Zcrate-attr=warn(unused) -Zcrate-attr=deny(unused) + +fn foo() {} //~ ERROR function `foo` is never used + +fn main() {} diff --git a/tests/ui/attributes/z-crate-attr/ordering-cli.stderr b/tests/ui/attributes/z-crate-attr/ordering-cli.stderr new file mode 100644 index 0000000000000..84c47716e62fb --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/ordering-cli.stderr @@ -0,0 +1,15 @@ +error: function `foo` is never used + --> $DIR/ordering-cli.rs:4:4 + | +LL | fn foo() {} + | ^^^ + | +note: the lint level is defined here + --> :1:9 + | +LL | #![deny(unused)] + | ^^^^^^ + = note: `#[deny(dead_code)]` implied by `#[deny(unused)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/z-crate-attr/ordering-source.rs b/tests/ui/attributes/z-crate-attr/ordering-source.rs new file mode 100644 index 0000000000000..914851d3bb055 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/ordering-source.rs @@ -0,0 +1,9 @@ +// RFC 3791: `-Zcrate-attr` applies before the crate's own attributes, so `warn` here wins. +//@ check-pass +//@ compile-flags: -Zcrate-attr=deny(unused) + +#![warn(unused)] + +fn foo() {} //~ WARN function `foo` is never used + +fn main() {} diff --git a/tests/ui/attributes/z-crate-attr/ordering-source.stderr b/tests/ui/attributes/z-crate-attr/ordering-source.stderr new file mode 100644 index 0000000000000..66cf78738bf7c --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/ordering-source.stderr @@ -0,0 +1,15 @@ +warning: function `foo` is never used + --> $DIR/ordering-source.rs:7:4 + | +LL | fn foo() {} + | ^^^ + | +note: the lint level is defined here + --> $DIR/ordering-source.rs:5:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(dead_code)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + diff --git a/tests/ui/const-generics/issues/issue-86820.rs b/tests/ui/const-generics/issues/issue-86820.rs index c2c0c1a8d0390..51a160cb213b3 100644 --- a/tests/ui/const-generics/issues/issue-86820.rs +++ b/tests/ui/const-generics/issues/issue-86820.rs @@ -5,7 +5,7 @@ use std::ops::BitAnd; const C: fn() = || is_set(); fn is_set() { - 0xffu8.bit::<0>(); + Bits::bit::<0>(0xffu8); } trait Bits { diff --git a/tests/ui/consts/const-fn-variant-ctor.rs b/tests/ui/consts/const-fn-variant-ctor.rs index b56f3145c11c3..17a8b04954c7c 100644 --- a/tests/ui/consts/const-fn-variant-ctor.rs +++ b/tests/ui/consts/const-fn-variant-ctor.rs @@ -1,5 +1,5 @@ //@ check-pass -// @ needs-rustc-debug-assertions +//@ needs-rustc-debug-assertions //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs index 6f3ec783a4bfe..821509a2e8879 100644 --- a/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs +++ b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs @@ -2,8 +2,6 @@ // support for tail calls to work correctly. Since there doesn't seem to be a usecase for tail // calling extern "rust-call" functions (extern "rust-call" only shows up in closures, which we // disallow tail-calling anyway), we just disallow that. -// -// @ ignore-backends: gcc #![expect(incomplete_features)] #![feature(explicit_tail_calls)] #![feature(unboxed_closures)] diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr index 6662836672097..52a97fd30e1fc 100644 --- a/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr +++ b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr @@ -1,5 +1,5 @@ error: ABI does not support guaranteed tail calls - --> $DIR/rust-call.rs:12:5 + --> $DIR/rust-call.rs:10:5 | LL | become a(()); | ^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs index 66442ac874516..35d16fa60e1a6 100644 --- a/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs +++ b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs @@ -6,9 +6,8 @@ // // let _ = infcx.take_opaque_types(); // -// @lcnr: Because of this I decided to not bother and cause this to fail instead. -// In the future we can definitely modify the compiler to accept this -// again. +// NOTE(@lcnr): Because of this I decided to not bother and cause this to fail instead. +// In the future we can definitely modify the compiler to accept this again. #![feature(type_alias_impl_trait)] diff --git a/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr index d7f6606d2099a..ef170101b4455 100644 --- a/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr +++ b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait` for type `(TAIT, TAIT)` - --> $DIR/coherence_different_hidden_ty.rs:21:1 + --> $DIR/coherence_different_hidden_ty.rs:20:1 | LL | impl Trait for (TAIT, TAIT) {} | --------------------------- first implementation here