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_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/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/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 3bf73c67bc0c1..d3d3cef8d7860 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -608,8 +608,9 @@ pub(crate) fn build_impl( }; let trait_ = associated_trait .map(|t| clean_trait_ref_with_constraints(cx, ty::Binder::dummy(t), ThinVec::new())); - if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() - && polarity != ty::ImplPolarity::Negative + if trait_ + .as_ref() + .is_some_and(|t| polarity != ty::ImplPolarity::Negative && t.is_deref_trait(tcx)) { super::build_deref_target_impls(cx, &trait_items, ret); } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b8e9d8a3f4633..79840366fc74c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3064,7 +3064,7 @@ fn clean_impl<'tcx>( // If this impl block is a positive implementation of the Deref trait, then we // need to try inlining the target's inherent impl blocks as well. - if trait_.as_ref().is_some_and(|t| tcx.lang_items().deref_trait() == Some(t.def_id())) + if trait_.as_ref().is_some_and(|t| t.is_deref_trait(tcx)) && tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative { build_deref_target_impls(cx, &items, &mut ret); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2dcf3d51fc509..a200b6c3c14dc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -21,7 +21,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{BodyId, Mutability}; use rustc_index::IndexVec; use rustc_metadata::rendered_const; -use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, SimplifiedType}; use rustc_middle::ty::{self, Ty, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, span_of_fragments, @@ -1348,6 +1348,22 @@ pub(crate) struct PolyTrait { pub(crate) generic_params: Vec, } +// Also used in `write_shared.rs` when generating the JS `impl` file for aliased types. +// +// FIXME(checked_type_alias): Once the feature is complete or stable, rewrite this +// to use type unification. +// Be aware of `tests/rustdoc-html/type-alias/deeply-nested-112515.rs` which might +// regress. +pub(crate) fn impl_may_apply_to_type_alias( + tcx: TyCtxt<'_>, + alias_def_id: DefId, + impl_def_id: DefId, +) -> bool { + let alias_ty = tcx.type_of(alias_def_id).skip_binder(); + let for_ty = tcx.type_of(impl_def_id).skip_binder(); + DeepRejectCtxt::relate_infer_infer(tcx).types_may_unify(alias_ty, for_ty) +} + /// Rustdoc's representation of types, mostly based on the [`hir::Ty`]. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum Type { @@ -1437,7 +1453,7 @@ impl Type { /// /// An owned type is also the same as its borrowed variants (this is commutative), /// but `&T` is not the same as `&mut T`. - pub(crate) fn is_doc_subtype_of(&self, other: &Self, cache: &Cache) -> bool { + pub(crate) fn is_doc_subtype_of(&self, other: &Self, cx: &Context<'_>) -> bool { // Strip the references so that it can compare the actual types, unless both are references. // If both are references, leave them alone and compare the mutabilities later. let (self_cleared, other_cleared) = if !self.is_borrowed_ref() || !other.is_borrowed_ref() { @@ -1446,29 +1462,33 @@ impl Type { (self, other) }; - // FIXME: `Cache` does not have the data required to unwrap type aliases, - // so we just assume they are equal. - // This is only remotely acceptable because we were previously - // assuming all types were equal when used - // as a generic parameter of a type in `Deref::Target`. + // FIXME(checked_type_alias): Once the feature is complete or stable, rewrite this + // to use type unification. + // Be aware of `tests/rustdoc-html/type-alias/deeply-nested-112515.rs` which might + // regress. if self_cleared.is_type_alias() || other_cleared.is_type_alias() { - return true; + let cache = cx.cache(); + if let Some(self_def_id) = self_cleared.def_id(&cache) + && let Some(other_def_id) = other_cleared.def_id(&cache) + { + return impl_may_apply_to_type_alias(cx.tcx(), self_def_id, other_def_id); + } } match (self_cleared, other_cleared) { // Recursive cases. (Type::Tuple(a), Type::Tuple(b)) => { - a.iter().eq_by(b, |a, b| a.is_doc_subtype_of(b, cache)) + a.iter().eq_by(b, |a, b| a.is_doc_subtype_of(b, cx)) } - (Type::Slice(a), Type::Slice(b)) => a.is_doc_subtype_of(b, cache), - (Type::Array(a, al), Type::Array(b, bl)) => al == bl && a.is_doc_subtype_of(b, cache), + (Type::Slice(a), Type::Slice(b)) => a.is_doc_subtype_of(b, cx), + (Type::Array(a, al), Type::Array(b, bl)) => al == bl && a.is_doc_subtype_of(b, cx), (Type::RawPointer(mutability, type_), Type::RawPointer(b_mutability, b_type_)) => { - mutability == b_mutability && type_.is_doc_subtype_of(b_type_, cache) + mutability == b_mutability && type_.is_doc_subtype_of(b_type_, cx) } ( Type::BorrowedRef { mutability, type_, .. }, Type::BorrowedRef { mutability: b_mutability, type_: b_type_, .. }, - ) => mutability == b_mutability && type_.is_doc_subtype_of(b_type_, cache), + ) => mutability == b_mutability && type_.is_doc_subtype_of(b_type_, cx), // Placeholders are equal to all other types. (Type::Infer, _) | (_, Type::Infer) => true, // Generics match everything on the right, but not on the left. @@ -1482,13 +1502,13 @@ impl Type { a.def_id() == b.def_id() && a.generics() .zip(b.generics()) - .map(|(ag, bg)| ag.zip(bg).all(|(at, bt)| at.is_doc_subtype_of(bt, cache))) + .map(|(ag, bg)| ag.zip(bg).all(|(at, bt)| at.is_doc_subtype_of(bt, cx))) .unwrap_or(true) } // Other cases, such as primitives, just use recursion. (a, b) => a - .def_id(cache) - .and_then(|a| Some((a, b.def_id(cache)?))) + .def_id(cx.cache()) + .and_then(|a| Some((a, b.def_id(cx.cache())?))) .map(|(a, b)| a == b) .unwrap_or(false), } @@ -2117,6 +2137,10 @@ impl Path { } }) } + + pub(crate) fn is_deref_trait(&self, tcx: TyCtxt<'_>) -> bool { + tcx.lang_items().deref_trait() == Some(self.def_id()) + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index 29a8fd5e2678e..13437e5a008c6 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -69,16 +69,3 @@ fn should_not_trim() { run_test("\t line1 \n\t line2", "line1 \nline2"); run_test(" \tline1 \n \tline2", "line1 \nline2"); } - -#[test] -fn is_same_generic() { - use crate::clean::types::{PrimitiveType, Type}; - use crate::formats::cache::Cache; - create_default_session_globals_then(|| { - let cache = Cache::new(false, false); - let generic = Type::Generic(sym::character('T')); - let unit = Type::Primitive(PrimitiveType::Unit); - assert!(!generic.is_doc_subtype_of(&unit, &cache)); - assert!(unit.is_doc_subtype_of(&generic, &cache)); - }) -} diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs index e9cbefea4a982..6135c0d7b3160 100644 --- a/src/librustdoc/formats/mod.rs +++ b/src/librustdoc/formats/mod.rs @@ -4,6 +4,7 @@ pub(crate) mod renderer; pub(crate) use renderer::{FormatRenderer, run_format}; use rustc_hir::def_id::DefId; +use rustc_middle::ty::TyCtxt; use crate::clean::{self, ItemId}; use crate::html::render::Context; @@ -26,6 +27,14 @@ impl Impl { self.inner_impl().trait_.as_ref().map(|t| t.def_id()) } + pub(crate) fn is_deref_trait(&self, tcx: TyCtxt<'_>) -> bool { + self.trait_did().is_some_and(|did| Some(did) == tcx.lang_items().deref_trait()) + } + + pub(crate) fn is_deref_mut_trait(&self, tcx: TyCtxt<'_>) -> bool { + self.trait_did().is_some_and(|did| Some(did) == tcx.lang_items().deref_mut_trait()) + } + /// This function is used to extract a `DefId` to be used as a key for the `Cache::impls` field. /// /// It allows to prevent having duplicated implementations showing up (the biggest issue was diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 3583bab95f68b..ca0aeb8c39115 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -39,7 +39,7 @@ mod sorted_template; mod type_layout; mod write_shared; -use std::borrow::Cow; +use std::borrow::{Borrow, Cow}; use std::cmp::Ordering; use std::collections::{BTreeMap, VecDeque}; use std::fmt::{self, Display as _, Write}; @@ -1402,16 +1402,77 @@ fn render_all_impls( Ok(()) } +fn aliased_type_deref_impl<'a>( + cx: &'a Context<'_>, + alias_def_id: DefId, + aliased_def_id: DefId, +) -> Option<(&'a Impl, bool)> { + let tcx = cx.tcx(); + let applies = |impl_: &Impl| { + impl_.impl_item.item_id.as_def_id().is_some_and(|impl_def_id| { + clean::types::impl_may_apply_to_type_alias(tcx, alias_def_id, impl_def_id) + }) + }; + let impls = cx.cache().impls.get(&aliased_def_id)?; + let mut deref_impl = None; + let mut has_deref_mut = false; + + for impl_ in impls { + if impl_.is_deref_trait(tcx) { + if !impl_.is_negative_trait_impl() && applies(impl_) { + deref_impl = Some(impl_); + } + } else if impl_.is_deref_mut_trait(tcx) && applies(impl_) { + has_deref_mut = true; + } + } + Some((deref_impl?, has_deref_mut)) +} + +/// Try to get the `Deref` trait `Impl` for `item`. If it cannot be found, it will try to do the +/// same with `aliased_type`. Returns a tuple containing the `Deref` `Impl`, and a bool set to +/// `true` if `DerefMut` is also implemented. +fn get_deref_impls<'a, I: Borrow>( + cx: &'a Context<'_>, + trait_impls: &'a [I], + item: DefId, + aliased_type: Option, +) -> Option<(&'a Impl, bool)> { + let tcx = cx.tcx(); + let mut impls = trait_impls.iter().map(|t| t.borrow()); + if let Some(impl_) = + impls.clone().find(|t| t.is_deref_trait(tcx) && !t.is_negative_trait_impl()) + { + let has_deref_mut = impls.any(|t| t.is_deref_mut_trait(tcx)); + Some((impl_, has_deref_mut)) + } else { + aliased_type.and_then(|aliased_did| aliased_type_deref_impl(cx, item, aliased_did)) + } +} + fn render_assoc_items( cx: &Context<'_>, containing_item: &clean::Item, it: DefId, what: AssocItemRender<'_>, +) -> impl fmt::Display { + render_assoc_items_with_aliased_type(cx, containing_item, it, what, None) +} + +fn render_assoc_items_with_aliased_type( + cx: &Context<'_>, + containing_item: &clean::Item, + it: DefId, + what: AssocItemRender<'_>, + aliased_type: Option, ) -> impl fmt::Display { fmt::from_fn(move |f| { let mut derefs = DefIdSet::default(); derefs.insert(it); - render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs) + if let Some(aliased_type) = aliased_type { + derefs.insert(aliased_type); + } + render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs, aliased_type) }) } @@ -1422,10 +1483,15 @@ fn render_assoc_items_inner( it: DefId, what: AssocItemRender<'_>, derefs: &mut DefIdSet, + aliased_type: Option, ) -> fmt::Result { info!("Documenting associated items of {:?}", containing_item.name); let cache = &cx.shared.cache; - let Some(impls) = cache.impls.get(&it) else { return Ok(()) }; + let impls = match cache.impls.get(&it) { + Some(impls) => impls.as_slice(), + None if aliased_type.is_some() => &[], + None => return Ok(()), + }; let (mut inherent_impls, trait_impls): (Vec<_>, _) = impls.iter().partition(|i| i.inner_impl().trait_.is_none()); if !inherent_impls.is_empty() { @@ -1451,9 +1517,8 @@ fn render_assoc_items_inner( // we should not show methods from `[MaybeUninit]`. // this `retain` filters out any instances where // the types do not line up perfectly. - inherent_impls.retain(|impl_| { - type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache) - }); + inherent_impls + .retain(|impl_| type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx)); let derived_id = cx.derive_id(&id); if let Some(def_id) = type_.def_id(cx.cache()) { cx.deref_id_map.borrow_mut().insert(def_id, id.clone()); @@ -1513,17 +1578,11 @@ fn render_assoc_items_inner( } } - if !trait_impls.is_empty() { - let deref_impl = trait_impls.iter().find(|t| { - t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl() - }); - if let Some(impl_) = deref_impl { - let has_deref_mut = trait_impls - .iter() - .any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); - render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?; - } + if let Some((impl_, has_deref_mut)) = get_deref_impls(cx, &trait_impls, it, aliased_type) { + render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?; + } + if !trait_impls.is_empty() { // If we were already one level into rendering deref methods, we don't want to render // anything after recursing into any further deref methods above. if let AssocItemRender::DerefFor { .. } = what { @@ -1580,11 +1639,11 @@ fn render_deref_methods( return Ok(()); } } - render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?; + render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None)?; } else if let Some(prim) = target.primitive_type() && let Some(&did) = cache.primitive_locations.get(&prim) { - render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?; + render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None)?; } Ok(()) } @@ -1641,7 +1700,7 @@ fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option) -> (String, .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive) .filter(|impl_| { // Two different types might have the same did, without actually being the same. - ty.is_doc_subtype_of(&impl_.for_, cx.cache()) + ty.is_doc_subtype_of(&impl_.for_, cx) }) .filter_map(|impl_| { if let Some(trait_) = &impl_.trait_ diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index fe495f8558f77..8de8bd23cadbd 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -22,9 +22,9 @@ use super::{ AssocItemLink, AssocItemRender, Context, ImplRenderingParameters, RenderMode, collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, - render_assoc_item, render_assoc_items, render_attributes_in_code, render_impl, - render_repr_attribute_in_code, render_rightside, render_stability_since_raw, - render_stability_since_raw_with_extra, write_section_heading, + render_assoc_item, render_assoc_items, render_assoc_items_with_aliased_type, + render_attributes_in_code, render_impl, render_repr_attribute_in_code, render_rightside, + render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean::{self, ItemKind}; use crate::config::ModuleSorting; @@ -1488,7 +1488,13 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { fn render_assoc_items(&self) -> impl fmt::Display { let def_id = self.it.item_id.expect_def_id(); - render_assoc_items(self.cx, self.it, def_id, AssocItemRender::All) + render_assoc_items_with_aliased_type( + self.cx, + self.it, + def_id, + AssocItemRender::All, + if self.is_type_alias { Some(self.def_id) } else { None }, + ) } fn render_union(&self) -> impl Display { @@ -1631,7 +1637,13 @@ impl<'clean> DisplayEnum<'clean> { write!( w, "{}{}", - render_assoc_items(cx, it, def_id, AssocItemRender::All), + render_assoc_items_with_aliased_type( + cx, + it, + def_id, + AssocItemRender::All, + if is_type_alias { Some(self.def_id) } else { None }, + ), document_type_layout(cx, layout_def_id) ) } @@ -2084,7 +2096,13 @@ impl<'a> DisplayStruct<'a> { w, "{}{}{}", item_fields(cx, it, self.fields, self.ctor_kind), - render_assoc_items(cx, it, def_id, AssocItemRender::All), + render_assoc_items_with_aliased_type( + cx, + it, + def_id, + AssocItemRender::All, + if is_type_alias { Some(self.def_id) } else { None }, + ), document_type_layout(cx, def_id), ) } diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 90077b1aa5f9e..e754854d13bd6 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -5,7 +5,7 @@ use std::fmt; use askama::Template; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::CtorKind; -use rustc_hir::def_id::{DefIdMap, DefIdSet}; +use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet}; use tracing::debug; use super::{Context, ItemSection, impl_trait_key, item_ty_to_section}; @@ -284,7 +284,7 @@ fn sidebar_struct<'a>( if let Some(name) = field_name { items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields)); } - sidebar_assoc_items(cx, it, items, deref_id_map); + sidebar_assoc_items(cx, it.item_id.expect_def_id(), items, deref_id_map); } fn sidebar_trait<'a>( @@ -357,7 +357,7 @@ fn sidebar_trait<'a>( prov_assoc_fns, )); - sidebar_assoc_items(cx, it, blocks, deref_id_map); + sidebar_assoc_items(cx, it.item_id.expect_def_id(), blocks, deref_id_map); blocks.push(LinkBlock::forced( Link::new("dyn-compatibility", "Dyn Compatibility"), @@ -386,7 +386,7 @@ fn sidebar_primitive<'a>( deref_id_map: &'a DefIdMap, ) { if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { - sidebar_assoc_items(cx, it, items, deref_id_map); + sidebar_assoc_items(cx, it.item_id.expect_def_id(), items, deref_id_map); } else { let (concrete, synthetic, blanket_impl) = super::get_filtered_impls_for_reference(&cx.shared, it); @@ -423,7 +423,17 @@ fn sidebar_type_alias<'a>( } } } - sidebar_assoc_items(cx, it, items, deref_id_map); + let alias_def_id = it.item_id.expect_def_id(); + let aliased_type = t.inner_type.as_ref().and_then(|_| { + cx.tcx().type_of(alias_def_id).instantiate_identity().skip_norm_wip().ty_adt_def() + }); + sidebar_assoc_items_with_aliased_type( + cx, + alias_def_id, + items, + deref_id_map, + aliased_type.map(|adt| adt.did()), + ); } fn sidebar_union<'a>( @@ -435,106 +445,106 @@ fn sidebar_union<'a>( ) { let fields = get_struct_fields_name(&u.fields); items.push(LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)); - sidebar_assoc_items(cx, it, items, deref_id_map); + sidebar_assoc_items(cx, it.item_id.expect_def_id(), items, deref_id_map); } /// Adds trait implementations into the blocks of links fn sidebar_assoc_items<'a>( cx: &'a Context<'_>, - it: &'a clean::Item, + item_def_id: DefId, + links: &mut Vec>, + deref_id_map: &'a DefIdMap, +) { + sidebar_assoc_items_with_aliased_type(cx, item_def_id, links, deref_id_map, None) +} + +fn sidebar_assoc_items_with_aliased_type<'a>( + cx: &'a Context<'_>, + item_def_id: DefId, links: &mut Vec>, deref_id_map: &'a DefIdMap, + aliased_type: Option, ) { - let did = it.item_id.expect_def_id(); let cache = cx.cache(); + let v = match cache.impls.get(&item_def_id) { + Some(impls) => impls.as_slice(), + None if aliased_type.is_some() => &[], + None => return, + }; let mut assoc_consts = Vec::new(); let mut assoc_types = Vec::new(); let mut assoc_fns = Vec::new(); let mut methods = Vec::new(); - if let Some(v) = cache.impls.get(&did) { - let mut used_links = UsedLinks::default(); - let mut id_map = IdMap::new(); + let mut used_links = UsedLinks::default(); + let mut id_map = IdMap::new(); - { - let used_links_bor = &mut used_links; - for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) { - assoc_consts.extend(get_associated_constants(impl_, used_links_bor)); - assoc_types.extend(get_associated_types(impl_, used_links_bor)); - methods.extend(get_methods( - impl_, - GetMethodsMode::AlsoCollectAssocFns { assoc_fns: &mut assoc_fns }, - used_links_bor, - cx, - )); - } - // We want links' order to be reproducible so we don't use unstable sort. - assoc_consts.sort(); - assoc_types.sort(); - postprocess_method_list(&mut assoc_fns); - postprocess_method_list(&mut methods); + { + let used_links_bor = &mut used_links; + for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) { + assoc_consts.extend(get_associated_constants(impl_, used_links_bor)); + assoc_types.extend(get_associated_types(impl_, used_links_bor)); + methods.extend(get_methods( + impl_, + GetMethodsMode::AlsoCollectAssocFns { assoc_fns: &mut assoc_fns }, + used_links_bor, + cx, + )); } + // We want links' order to be reproducible so we don't use unstable sort. + assoc_consts.sort(); + assoc_types.sort(); + postprocess_method_list(&mut assoc_fns); + postprocess_method_list(&mut methods); + } - let mut blocks = vec![ - LinkBlock::new( - Link::new("implementations", "Associated Constants"), - "associatedconstant", - assoc_consts, - ), - LinkBlock::new( - Link::new("implementations", "Associated Types"), - "associatedtype", - assoc_types, - ), - LinkBlock::new( - Link::new("implementations", "Associated Functions"), - "method", - assoc_fns, - ), - LinkBlock::new(Link::new("implementations", "Methods"), "method", methods), - ]; - - if v.iter().any(|i| i.inner_impl().trait_.is_some()) { - if let Some(impl_) = v.iter().find(|i| { - i.trait_did() == cx.tcx().lang_items().deref_trait() && !i.is_negative_trait_impl() - }) { - let mut derefs = DefIdSet::default(); - derefs.insert(did); - sidebar_deref_methods( - cx, - &mut blocks, - impl_, - v, - &mut derefs, - &mut used_links, - deref_id_map, - ); - } - - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = - v.iter().partition::, _>(|i| i.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = - concrete.into_iter().partition::, _>(|i| i.inner_impl().kind.is_blanket()); + let mut blocks = vec![ + LinkBlock::new( + Link::new("implementations", "Associated Constants"), + "associatedconstant", + assoc_consts, + ), + LinkBlock::new( + Link::new("implementations", "Associated Types"), + "associatedtype", + assoc_types, + ), + LinkBlock::new(Link::new("implementations", "Associated Functions"), "method", assoc_fns), + LinkBlock::new(Link::new("implementations", "Methods"), "method", methods), + ]; + + if let Some((impl_, deref_mut)) = super::get_deref_impls(cx, v, item_def_id, aliased_type) { + let mut derefs = DefIdSet::default(); + derefs.insert(item_def_id); + derefs.extend(aliased_type); + sidebar_deref_methods( + cx, + &mut blocks, + impl_, + deref_mut, + &mut derefs, + &mut used_links, + deref_id_map, + ); + } - sidebar_render_assoc_items( - cx, - &mut id_map, - concrete, - synthetic, - blanket_impl, - &mut blocks, - ); - } + if v.iter().any(|i| i.inner_impl().trait_.is_some()) { + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = + v.iter().partition::, _>(|i| i.inner_impl().kind.is_auto()); + let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = + concrete.into_iter().partition::, _>(|i| i.inner_impl().kind.is_blanket()); - links.append(&mut blocks); + sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl, &mut blocks); } + + links.append(&mut blocks); } fn sidebar_deref_methods<'a>( cx: &'a Context<'_>, out: &mut Vec>, impl_: &Impl, - v: &[Impl], + deref_mut: bool, derefs: &mut DefIdSet, used_links: &mut UsedLinks, deref_id_map: &'a DefIdMap, @@ -551,6 +561,7 @@ fn sidebar_deref_methods<'a>( }) { debug!("found target, real_target: {target:?} {real_target:?}"); + if let Some(did) = target.def_id(c) && let Some(type_did) = impl_.inner_impl().for_.def_id(c) && // `impl Deref for S` @@ -560,7 +571,6 @@ fn sidebar_deref_methods<'a>( return; } let tcx = cx.tcx(); - let deref_mut = v.iter().any(|i| i.trait_did() == tcx.lang_items().deref_mut_trait()); let inner_impl = target .def_id(c) .or_else(|| { @@ -575,7 +585,7 @@ fn sidebar_deref_methods<'a>( .iter() .filter(|i| { i.inner_impl().trait_.is_none() - && real_target.is_doc_subtype_of(&i.inner_impl().for_, c) + && real_target.is_doc_subtype_of(&i.inner_impl().for_, cx) }) .flat_map(|i| { get_methods( @@ -613,11 +623,7 @@ fn sidebar_deref_methods<'a>( if let Some(target_did) = target.def_id(c) && let Some(target_impls) = c.impls.get(&target_did) && let Some(target_deref_impl) = target_impls.iter().find(|i| { - i.inner_impl() - .trait_ - .as_ref() - .map(|t| Some(t.def_id()) == tcx.lang_items().deref_trait()) - .unwrap_or(false) + i.inner_impl().trait_.as_ref().map(|t| t.is_deref_trait(tcx)).unwrap_or(false) && !i.is_negative_trait_impl() }) { @@ -625,7 +631,7 @@ fn sidebar_deref_methods<'a>( cx, out, target_deref_impl, - target_impls, + deref_mut, derefs, used_links, deref_id_map, @@ -649,7 +655,7 @@ fn sidebar_enum<'a>( variants.sort_unstable(); items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)); - sidebar_assoc_items(cx, it, items, deref_id_map); + sidebar_assoc_items(cx, it.item_id.expect_def_id(), items, deref_id_map); } pub(crate) fn sidebar_module_like( @@ -710,7 +716,7 @@ fn sidebar_foreign_type<'a>( items: &mut Vec>, deref_id_map: &'a DefIdMap, ) { - sidebar_assoc_items(cx, it, items, deref_id_map); + sidebar_assoc_items(cx, it.item_id.expect_def_id(), items, deref_id_map); } /// Renders the trait implementations for this type diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 17c636b351943..b9ddb227a8204 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -29,7 +29,6 @@ use indexmap::IndexMap; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_middle::ty::TyCtxt; -use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_session::Session; use rustc_span::Symbol; use rustc_span::def_id::DefId; @@ -38,6 +37,7 @@ use serde::ser::SerializeSeq; use serde::{Deserialize, Serialize, Serializer}; use super::{Context, RenderMode, collect_paths_for_type, ensure_trailing_slash}; +use crate::clean::types::impl_may_apply_to_type_alias; use crate::clean::{Crate, Item, ItemId, ItemKind}; use crate::config::{EmitType, PathToParts, RenderOptions, ShouldMerge}; use crate::docfs::PathError; @@ -1031,7 +1031,6 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> { let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else { return; }; - let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder(); // Exclude impls that are directly on this type. They're already in the HTML. // Some inlining scenarios can cause there to be two versions of the same // impl: one on the type alias and one on the underlying target type. @@ -1040,15 +1039,8 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> { for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ { // Only include this impl if it actually unifies with this alias. // Synthetic impls are not included; those are also included in the HTML. - // - // FIXME(checked_type_alias): Once the feature is complete or stable, rewrite this - // to use type unification. - // Be aware of `tests/rustdoc-html/type-alias/deeply-nested-112515.rs` which might - // regress. let Some(impl_did) = impl_item_id.as_def_id() else { continue }; - let for_ty = self.cx.tcx().type_of(impl_did).skip_binder(); - let reject_cx = DeepRejectCtxt::relate_infer_infer(self.cx.tcx()); - if !reject_cx.types_may_unify(aliased_ty, for_ty) { + if !impl_may_apply_to_type_alias(self.cx.tcx(), self_did, impl_did) { continue; } // Avoid duplicates 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/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-html/deref-to-alias.rs b/tests/rustdoc-html/deref-to-alias.rs new file mode 100644 index 0000000000000..696c7dcf7c29f --- /dev/null +++ b/tests/rustdoc-html/deref-to-alias.rs @@ -0,0 +1,32 @@ +// This test ensures that dereferencing to a type alias will still list +// matching methods of the type alias. + +#![crate_name = "foo"] + +use std::ops::Deref; + +pub struct Foo(T); + +impl Foo { + pub fn get_i32(&self) -> i32 { self.0 } +} + +impl Foo { + pub fn get_u32(&self) -> u32 { self.0 } +} + +pub type X = Foo; + +//@ has 'foo/struct.Bar.html' +// FIXME: Only `get_i32` should be listed, this is a bug. +//@ count - '//*[@id="deref-methods-X-1"]//h4' 2 +//@ has - '//*[@id="deref-methods-X-1"]//h4' 'get_i32' +//@ has - '//*[@id="deref-methods-X-1"]//h4' 'get_u32' +pub struct Bar; + +impl Deref for Bar { + type Target = X; + fn deref(&self) -> &Self::Target { + &X(0) + } +} diff --git a/tests/rustdoc-html/deref/deref-methods-aliased-type.rs b/tests/rustdoc-html/deref/deref-methods-aliased-type.rs new file mode 100644 index 0000000000000..35d3a7eb2433e --- /dev/null +++ b/tests/rustdoc-html/deref/deref-methods-aliased-type.rs @@ -0,0 +1,114 @@ +// Ensures that aliased types get the "Methods from Deref" but only when it matches +// the aliased type. +// Regression test for . + +// ignore-tidy-file-linelength + +#![crate_name = "foo"] + +use std::ops::{Deref, DerefMut}; + +pub struct Target; + +impl Target { + pub fn target_ref(&self) {} + pub fn target_mut(&mut self) {} +} + +pub struct Other; + +impl Other { + pub fn other_ref(&self) {} + pub fn other_mut(&mut self) {} +} + +pub struct Wrapper(T); + +impl Deref for Wrapper { + type Target = Target; + fn deref(&self) -> &Target { + &Target + } +} + +impl Deref for Wrapper { + type Target = Other; + fn deref(&self) -> &Other { + &Other + } +} + +impl DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Other { + &mut Other + } +} + +//@ has 'foo/type.Byte.html' +// Only one deref. +//@ count - '//h2[@id="deref-methods-Target"]' 1 +// Which makes two section headers. One for the "Aliased Type" and one for "Methods from Deref<>". +//@ count - '//*[@class="section-header"]' 2 +//@ has - '//*[@class="section-header"]' 'Aliased Type' +//@ has - '//*[@class="section-header"]' 'Methods from Deref' +// Only one method from the deref (not the one taking `&mut` since `DerefMut` is not +// implemented for `Wrapper`. +//@ count - '//*[@id="deref-methods-Target-1"]/*[@class="method"]' 1 +//@ has - '//*[@id="method.target_ref"]//h4' 'pub fn target_ref(&self)' + +// Same checks but in the sidebar. +//@ count - '//*[@class="sidebar-elems"]//*[@class="block deref-methods"]//a' 1 +//@ has - '//*[@class="sidebar-elems"]//*[@class="block deref-methods"]//a[@href="#method.target_ref"]' 'target_ref' +pub type Byte = Wrapper; + +//@ has 'foo/type.Word.html' +// There should be only one `Deref` block (for both `Deref` and `DerefMut`). +//@ count - '//h2[@id="deref-methods-Other"]' 1 +// Which makes two section headers. One for the "Aliased Type" and one for "Methods from Deref<>". +//@ count - '//*[@class="section-header"]' 2 +//@ has - '//*[@class="section-header"]' 'Aliased Type' +//@ has - '//*[@class="section-header"]' 'Methods from Deref' +// Since it implements both `Deref` and `DerefMut`, we should see both methods. +//@ count - '//*[@id="deref-methods-Other-1"]/*[@class="method"]' 2 +//@ has - '//*[@id="method.other_ref"]//h4' 'pub fn other_ref(&self)' +//@ has - '//*[@id="method.other_mut"]//h4' 'pub fn other_mut(&mut self)' + +// Same checks but in the sidebar. +//@ count - '//*[@class="sidebar-elems"]//*[@class="block deref-methods"]//a' 2 +//@ has - '//*[@class="sidebar-elems"]//*[@class="block deref-methods"]//a[@href="#method.other_ref"]' 'other_ref' +//@ has - '//*[@class="sidebar-elems"]//*[@class="block deref-methods"]//a[@href="#method.other_mut"]' 'other_mut' +pub type Word = Wrapper; + +//@ has 'foo/type.Dword.html' +// There is no `Deref` implementation for `Wrapper` so we confirm that. +// Which makes two section headers. One for the "Aliased Type" and one for "Methods from Deref<>". +//@ count - '//*[@class="section-header"]' 1 +//@ has - '//*[@class="section-header"]' 'Aliased Type' +pub type Dword = Wrapper; + +pub struct Foo(T); + +impl Foo { + pub fn tada(&self) {} +} + +impl Deref for Foo { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } +} + +// Checks that traversal `Deref` is working. +//@ has 'foo/struct.FooBar.html' +//@ has - '//*[@id="deref-methods-Foo%3CFoo%3CT%3E%3E-1"]//*[@class="code-header"]' \ +// 'pub fn tada(&self)' +//@ has - '//*[@id="deref-methods-Foo%3CFoo%3CT%3E%3E"]' 'Methods from Deref>>' +pub struct FooBar(Foo>); + +impl Deref for FooBar { + type Target = Foo>; + fn deref(&self) -> &Foo> { + &self.0 + } +} diff --git a/tests/rustdoc-html/deref/deref-on-aliased.rs b/tests/rustdoc-html/deref/deref-on-aliased.rs new file mode 100644 index 0000000000000..cf9f189ecc710 --- /dev/null +++ b/tests/rustdoc-html/deref/deref-on-aliased.rs @@ -0,0 +1,30 @@ +// Ensures that aliased types also get the "methods from deref" content. +// Regression test for . + +#![crate_name = "foo"] + +use std::ops::Deref; + +// First we check for the `Col` struct. +//@ has 'foo/struct.Col.html' +//@ has - '//*[@id="main-content"]//h2[@id="deref-methods-%5BT%5D"]' 'Methods from Deref' +//@ has - '//*[@id="rustdoc-toc"]//a[@href="#deref-methods-%5BT%5D"]' 'Methods from Deref' + +pub struct Col { + data: [T; N], +} + +// Then for the type alias. +//@ has 'foo/type.Mat.html' +//@ has - '//*[@id="main-content"]//h2[@id="deref-methods-%5BT%5D"]' 'Methods from Deref' +//@ has - '//*[@id="rustdoc-toc"]//a[@href="#deref-methods-%5BT%5D"]' 'Methods from Deref' + +pub type Mat = Col; + +impl Deref for Col { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + &self.data + } +} diff --git a/tests/rustdoc-html/deref/not-mut-deref.rs b/tests/rustdoc-html/deref/not-mut-deref.rs new file mode 100644 index 0000000000000..02aac49e46935 --- /dev/null +++ b/tests/rustdoc-html/deref/not-mut-deref.rs @@ -0,0 +1,35 @@ +// This test ensures that `get_u32` is not listed as the `Deref` impl +// doesn't match it. +// Regression test for . + +#![crate_name = "foo"] + +//@ has 'foo/struct.Bar.html' +//@ count - '//*[@id="deref-methods-Foo%3Ci32%3E-1"]/*[@class="method"]' 1 +//@ has - '//*[@id="deref-methods-Foo%3Ci32%3E-1"]/*[@class="method"]/h4' \ +// 'pub fn get_i32(&self) -> i32' + +// Same check for the sidebar. +//@ count - '//*[@id="rustdoc-toc"]/*[@class="block deref-methods"]//a' 1 +//@ has - '//*[@id="rustdoc-toc"]/*[@class="block deref-methods"]//a' 'get_i32' + +use std::ops::Deref; + +pub struct Foo(T); + +impl Foo { + pub fn get_i32(&self) -> i32 { self.0 } +} + +impl Foo { + pub fn get_u32(&self) -> u32 { self.0 } +} + +pub struct Bar(Foo); + +impl Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Foo { + &self.0 + } +} diff --git a/tests/rustdoc-html/typedef/deref-32077.rs b/tests/rustdoc-html/typedef/deref-32077.rs index faab4b6f522ba..5c6949936e43e 100644 --- a/tests/rustdoc-html/typedef/deref-32077.rs +++ b/tests/rustdoc-html/typedef/deref-32077.rs @@ -54,7 +54,7 @@ impl std::ops::Deref for GenericStruct { pub struct Wrap(GenericStruct); //@ has 'foo/type.Alias.html' -//@ !has - '//h2' 'Methods from Deref' +//@ has - '//h2' 'Methods from Deref' //@ !has - '//*[@id="impl-Deref-for-Wrap%3CT%3E"]/h3' 'impl Deref for Wrap' //@ hasraw 'type.impl/foo/struct.Wrap.js' 'impl-Deref-for-Wrap%3CT%3E' // Deref Methods aren't gathered for type aliases, though the actual impl is. 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 {