diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 4ec3e39facc..bf6d3322176 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -400,7 +400,7 @@ impl<'a> AstValidator<'a> { if let Defaultness::Default(def_span) = defaultness { let span = self.session.source_map().guess_head_span(span); self.err_handler() - .struct_span_err(span, "`default` is only allowed on items in `impl` definitions") + .struct_span_err(span, "`default` is only allowed on items in trait impls") .span_label(def_span, "`default` because of this") .emit(); } diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 636c37142ad..fef6602b9cc 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -111,6 +111,7 @@ E0206: include_str!("./error_codes/E0206.md"), E0207: include_str!("./error_codes/E0207.md"), E0210: include_str!("./error_codes/E0210.md"), E0211: include_str!("./error_codes/E0211.md"), +E0212: include_str!("./error_codes/E0212.md"), E0214: include_str!("./error_codes/E0214.md"), E0220: include_str!("./error_codes/E0220.md"), E0221: include_str!("./error_codes/E0221.md"), @@ -503,7 +504,6 @@ E0779: include_str!("./error_codes/E0779.md"), // E0196, // cannot determine a type for this closure E0208, // E0209, // builtin traits can only be implemented on structs or enums - E0212, // cannot extract an associated type from a higher-ranked trait bound // E0213, // associated types are not accepted in this context // E0215, // angle-bracket notation is not stable with `Fn` // E0216, // parenthetical notation is only stable with `Fn` diff --git a/compiler/rustc_error_codes/src/error_codes/E0212.md b/compiler/rustc_error_codes/src/error_codes/E0212.md new file mode 100644 index 00000000000..17465414650 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0212.md @@ -0,0 +1,35 @@ +Cannot use the associated type of +a trait with uninferred generic parameters. + +Erroneous code example: + +```compile_fail,E0212 +pub trait Foo { + type A; + + fn get(&self, t: T) -> Self::A; +} + +fn foo2 Foo<&'x isize>>( + field: I::A) {} // error! +``` + +In this example, we have to instantiate `'x`, and +we don't know what lifetime to instantiate it with. +To fix this, spell out the precise lifetimes involved. +Example: + +``` +pub trait Foo { + type A; + + fn get(&self, t: T) -> Self::A; +} + +fn foo3 Foo<&'x isize>>( + x: >::A) {} // ok! + + +fn foo4<'a, I : for<'x> Foo<&'x isize>>( + x: >::A) {} // ok! +``` diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs index 2ed115b1297..e6d822086f5 100644 --- a/compiler/rustc_mir/src/lib.rs +++ b/compiler/rustc_mir/src/lib.rs @@ -28,6 +28,7 @@ Rust MIR: a lowered representation of Rust. #![feature(or_patterns)] #![feature(once_cell)] #![feature(control_flow_enum)] +#![feature(str_split_once)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index e9528557b33..af81d9af0e2 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -148,40 +148,46 @@ impl DebugOptions { if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(',') { - let mut setting = setting_str.splitn(2, '='); - match setting.next() { - Some(option) if option == "allow_unused_expressions" => { - allow_unused_expressions = bool_option_val(option, setting.next()); + let (option, value) = match setting_str.split_once('=') { + None => (setting_str, None), + Some((k, v)) => (k, Some(v)), + }; + match option { + "allow_unused_expressions" => { + allow_unused_expressions = bool_option_val(option, value); debug!( "{} env option `allow_unused_expressions` is set to {}", RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions ); } - Some(option) if option == "counter_format" => { - if let Some(strval) = setting.next() { - counter_format = counter_format_option_val(strval); - debug!( - "{} env option `counter_format` is set to {:?}", - RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format - ); - } else { - bug!( - "`{}` option in environment variable {} requires one or more \ - plus-separated choices (a non-empty subset of \ - `id+block+operation`)", - option, - RUSTC_COVERAGE_DEBUG_OPTIONS - ); - } + "counter_format" => { + match value { + None => { + bug!( + "`{}` option in environment variable {} requires one or more \ + plus-separated choices (a non-empty subset of \ + `id+block+operation`)", + option, + RUSTC_COVERAGE_DEBUG_OPTIONS + ); + } + Some(val) => { + counter_format = counter_format_option_val(val); + debug!( + "{} env option `counter_format` is set to {:?}", + RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format + ); + } + }; } - Some("") => {} - Some(invalid) => bug!( - "Unsupported setting `{}` in environment variable {}", - invalid, - RUSTC_COVERAGE_DEBUG_OPTIONS - ), - None => {} - } + _ => { + bug!( + "Unsupported setting `{}` in environment variable {}", + option, + RUSTC_COVERAGE_DEBUG_OPTIONS + ) + } + }; } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 6a0c98805cb..38b46e19cf2 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1343,8 +1343,10 @@ fn parse_output_types( if !debugging_opts.parse_only { for list in matches.opt_strs("emit") { for output_type in list.split(',') { - let mut parts = output_type.splitn(2, '='); - let shorthand = parts.next().unwrap(); + let (shorthand, path) = match output_type.split_once('=') { + None => (output_type, None), + Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))), + }; let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { early_error( error_format, @@ -1355,7 +1357,6 @@ fn parse_output_types( ), ) }); - let path = parts.next().map(PathBuf::from); output_types.insert(output_type, path); } } @@ -1499,11 +1500,10 @@ fn parse_opt_level( let max_c = matches .opt_strs_pos("C") .into_iter() - .flat_map( - |(i, s)| { - if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } - }, - ) + .flat_map(|(i, s)| { + // NB: This can match a string without `=`. + if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } + }) .max(); if max_o > max_c { OptLevel::Default @@ -1538,11 +1538,10 @@ fn select_debuginfo( let max_c = matches .opt_strs_pos("C") .into_iter() - .flat_map( - |(i, s)| { - if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } - }, - ) + .flat_map(|(i, s)| { + // NB: This can match a string without `=`. + if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } + }) .max(); if max_g > max_c || cg.tracer == TracerMode::Hardware { DebugInfo::Full @@ -1575,23 +1574,26 @@ fn parse_libs( .map(|s| { // Parse string of the form "[KIND=]lib[:new_name]", // where KIND is one of "dylib", "framework", "static". - let mut parts = s.splitn(2, '='); - let kind = parts.next().unwrap(); - let (name, kind) = match (parts.next(), kind) { - (None, name) => (name, NativeLibKind::Unspecified), - (Some(name), "dylib") => (name, NativeLibKind::Dylib), - (Some(name), "framework") => (name, NativeLibKind::Framework), - (Some(name), "static") => (name, NativeLibKind::StaticBundle), - (Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle), - (_, s) => { - early_error( - error_format, - &format!( - "unknown library kind `{}`, expected \ - one of dylib, framework, or static", - s - ), - ); + let (name, kind) = match s.split_once('=') { + None => (s, NativeLibKind::Unspecified), + Some((kind, name)) => { + let kind = match kind { + "dylib" => NativeLibKind::Dylib, + "framework" => NativeLibKind::Framework, + "static" => NativeLibKind::StaticBundle, + "static-nobundle" => NativeLibKind::StaticNoBundle, + s => { + early_error( + error_format, + &format!( + "unknown library kind `{}`, expected \ + one of dylib, framework, or static", + s + ), + ); + } + }; + (name.to_string(), kind) } }; if kind == NativeLibKind::StaticNoBundle @@ -1603,10 +1605,11 @@ fn parse_libs( accepted on the nightly compiler", ); } - let mut name_parts = name.splitn(2, ':'); - let name = name_parts.next().unwrap(); - let new_name = name_parts.next(); - (name.to_owned(), new_name.map(|n| n.to_owned()), kind) + let (name, new_name) = match name.split_once(':') { + None => (name, None), + Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())), + }; + (name, new_name, kind) }) .collect() } @@ -1627,20 +1630,13 @@ pub fn parse_externs( let is_unstable_enabled = debugging_opts.unstable_options; let mut externs: BTreeMap = BTreeMap::new(); for arg in matches.opt_strs("extern") { - let mut parts = arg.splitn(2, '='); - let name = parts - .next() - .unwrap_or_else(|| early_error(error_format, "--extern value must not be empty")); - let path = parts.next().map(|s| s.to_string()); - - let mut name_parts = name.splitn(2, ':'); - let first_part = name_parts.next(); - let second_part = name_parts.next(); - let (options, name) = match (first_part, second_part) { - (Some(opts), Some(name)) => (Some(opts), name), - (Some(name), None) => (None, name), - (None, None) => early_error(error_format, "--extern name must not be empty"), - _ => unreachable!(), + let (name, path) = match arg.split_once('=') { + None => (arg, None), + Some((name, path)) => (name.to_string(), Some(path.to_string())), + }; + let (options, name) = match name.split_once(':') { + None => (None, name), + Some((opts, name)) => (Some(opts), name.to_string()), }; let entry = externs.entry(name.to_owned()); @@ -1729,17 +1725,12 @@ fn parse_remap_path_prefix( matches .opt_strs("remap-path-prefix") .into_iter() - .map(|remap| { - let mut parts = remap.rsplitn(2, '='); // reverse iterator - let to = parts.next(); - let from = parts.next(); - match (from, to) { - (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), - _ => early_error( - error_format, - "--remap-path-prefix must contain '=' between FROM and TO", - ), - } + .map(|remap| match remap.rsplit_once('=') { + None => early_error( + error_format, + "--remap-path-prefix must contain '=' between FROM and TO", + ), + Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), }) .collect() } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index d002f597391..36bf8634c6e 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,6 +1,7 @@ #![feature(crate_visibility_modifier)] #![feature(once_cell)] #![feature(or_patterns)] +#![feature(str_split_once)] #[macro_use] extern crate bitflags; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 74f0e7c7c90..22372cb0431 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -179,9 +179,10 @@ macro_rules! options { { let mut op = $defaultfn(); for option in matches.opt_strs($prefix) { - let mut iter = option.splitn(2, '='); - let key = iter.next().unwrap(); - let value = iter.next(); + let (key, value) = match option.split_once('=') { + None => (option, None), + Some((k, v)) => (k.to_string(), Some(v)), + }; let option_to_lookup = key.replace("-", "_"); let mut found = false; for &(candidate, setter, type_desc, _) in $stat { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 06538ad04e7..a91f6acb896 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -461,6 +461,9 @@ symbols! { document_private_items, dotdot_in_tuple_patterns, dotdoteq_in_patterns, + dreg, + dreg_low16, + dreg_low8, drop, drop_in_place, drop_types_in_const, @@ -545,6 +548,7 @@ symbols! { format_args_capture, format_args_nl, freeze, + freg, frem_fast, from, from_desugaring, @@ -629,6 +633,7 @@ symbols! { iter, keyword, kind, + kreg, label, label_break_value, lang, @@ -654,6 +659,7 @@ symbols! { lint_reasons, literal, llvm_asm, + local, local_inner_macros, log10f32, log10f64, @@ -856,6 +862,9 @@ symbols! { pub_restricted, pure, pushpop_unsafe, + qreg, + qreg_low4, + qreg_low8, quad_precision_float, question_mark, quote, @@ -877,6 +886,13 @@ symbols! { reexport_test_harness_main, reference, reflect, + reg, + reg16, + reg32, + reg64, + reg_abcd, + reg_byte, + reg_thumb, register_attr, register_tool, relaxed_adts, @@ -1062,6 +1078,8 @@ symbols! { spotlight, sqrtf32, sqrtf64, + sreg, + sreg_low16, sse4a_target_feature, stable, staged_api, @@ -1218,6 +1236,8 @@ symbols! { volatile_load, volatile_set_memory, volatile_store, + vreg, + vreg_low16, warn, wasm_import_module, wasm_target_feature, @@ -1229,6 +1249,9 @@ symbols! { wrapping_mul, wrapping_sub, write_bytes, + xmm_reg, + ymm_reg, + zmm_reg, } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index aff1ff92d31..3c65c84b0de 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -20,16 +20,16 @@ macro_rules! def_reg_class { } impl $arch_regclass { - pub fn name(self) -> &'static str { + pub fn name(self) -> rustc_span::Symbol { match self { - $(Self::$class => stringify!($class),)* + $(Self::$class => rustc_span::symbol::sym::$class,)* } } - pub fn parse(_arch: super::InlineAsmArch, name: &str) -> Result { + pub fn parse(_arch: super::InlineAsmArch, name: rustc_span::Symbol) -> Result { match name { $( - stringify!($class) => Ok(Self::$class), + rustc_span::sym::$class => Ok(Self::$class), )* _ => Err("unknown register class"), } @@ -327,7 +327,7 @@ pub enum InlineAsmRegClass { } impl InlineAsmRegClass { - pub fn name(self) -> &'static str { + pub fn name(self) -> Symbol { match self { Self::X86(r) => r.name(), Self::Arm(r) => r.name(), @@ -422,29 +422,22 @@ impl InlineAsmRegClass { } pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { - // FIXME: use direct symbol comparison for register class names - name.with(|name| { - Ok(match arch { - InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - Self::X86(X86InlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::AArch64 => { - Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { - Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::Hexagon => { - Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::Mips | InlineAsmArch::Mips64 => { - Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::Wasm32 => Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?), - }) + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { + Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Wasm32 => Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?), }) } @@ -484,7 +477,7 @@ impl fmt::Display for InlineAsmRegOrRegClass { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Reg(r) => write!(f, "\"{}\"", r.name()), - Self::RegClass(r) => f.write_str(r.name()), + Self::RegClass(r) => write!(f, "{}", r.name()), } } } diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index fb747dfcbd3..1ad57582eba 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -15,6 +15,7 @@ #![feature(never_type)] #![feature(associated_type_bounds)] #![feature(exhaustive_patterns)] +#![feature(str_split_once)] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index e271a6dec40..88422395216 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -54,10 +54,7 @@ fn macos_deployment_target() -> (u32, u32) { let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok(); let version = deployment_target .as_ref() - .and_then(|s| { - let mut i = s.splitn(2, '.'); - i.next().and_then(|a| i.next().map(|b| (a, b))) - }) + .and_then(|s| s.split_once('.')) .and_then(|(a, b)| a.parse::().and_then(|a| b.parse::().map(|b| (a, b))).ok()); version.unwrap_or((10, 7)) diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 0ff10abb60a..38da1e5ea03 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -359,8 +359,8 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { self.tcx().sess, span, E0212, - "cannot extract an associated type from a higher-ranked trait bound \ - in this context" + "cannot use the associated type of a trait \ + with uninferred generic parameters" ); match self.node() { diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 85c809e0d18..57807bc5453 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1469,6 +1469,8 @@ impl VecDeque { #[inline] fn is_contiguous(&self) -> bool { + // FIXME: Should we consider `head == 0` to mean + // that `self` is contiguous? self.tail <= self.head } @@ -2198,7 +2200,7 @@ impl VecDeque { if self.is_contiguous() { let tail = self.tail; let head = self.head; - return unsafe { &mut self.buffer_as_mut_slice()[tail..head] }; + return unsafe { RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0 }; } let buf = self.buf.ptr(); @@ -2224,7 +2226,13 @@ impl VecDeque { self.tail = 0; self.head = len; } - } else if free >= self.head { + } else if free > self.head { + // FIXME: We currently do not consider ....ABCDEFGH + // to be contiguous because `head` would be `0` in this + // case. While we probably want to change this it + // isn't trivial as a few places expect `is_contiguous` + // to mean that we can just slice using `buf[tail..head]`. + // there is enough free space to copy the head in one go, // this means that we first shift the tail forwards, and then // copy the head to the correct position. @@ -2238,7 +2246,7 @@ impl VecDeque { // ...ABCDEFGH. self.tail = self.head; - self.head = self.tail + len; + self.head = self.wrap_add(self.tail, len); } } else { // free is smaller than both head and tail, @@ -2278,7 +2286,7 @@ impl VecDeque { let tail = self.tail; let head = self.head; - unsafe { &mut self.buffer_as_mut_slice()[tail..head] } + unsafe { RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0 } } /// Rotates the double-ended queue `mid` places to the left. @@ -2839,7 +2847,7 @@ impl From> for Vec { let len = other.len(); let cap = other.cap(); - if other.head != 0 { + if other.tail != 0 { ptr::copy(buf.add(other.tail), buf, len); } Vec::from_raw_parts(buf, len, cap) diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index d74f91c752c..21f52af056b 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -210,6 +210,20 @@ fn make_contiguous_small_free() { ); } +#[test] +fn make_contiguous_head_to_end() { + let mut dq = VecDeque::with_capacity(3); + dq.push_front('B'); + dq.push_front('A'); + dq.push_back('C'); + dq.make_contiguous(); + let expected_tail = 0; + let expected_head = 3; + assert_eq!(expected_tail, dq.tail); + assert_eq!(expected_head, dq.head); + assert_eq!((&['A', 'B', 'C'] as &[_], &[] as &[_]), dq.as_slices()); +} + #[test] fn test_remove() { // This test checks that every single combination of tail position, length, and diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index ce216e5336e..27b32b69502 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1413,7 +1413,7 @@ impl String { self.len() == 0 } - /// Splits the string into two at the given index. + /// Splits the string into two at the given byte index. /// /// Returns a newly allocated `String`. `self` contains bytes `[0, at)`, and /// the returned `String` contains bytes `[at, len)`. `at` must be on the diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 47ae1a64190..f752472c3ba 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -325,6 +325,120 @@ pub enum Ordering { } impl Ordering { + /// Returns `true` if the ordering is the `Equal` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_eq(), false); + /// assert_eq!(Ordering::Equal.is_eq(), true); + /// assert_eq!(Ordering::Greater.is_eq(), false); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_eq(self) -> bool { + matches!(self, Equal) + } + + /// Returns `true` if the ordering is not the `Equal` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_ne(), true); + /// assert_eq!(Ordering::Equal.is_ne(), false); + /// assert_eq!(Ordering::Greater.is_ne(), true); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_ne(self) -> bool { + !matches!(self, Equal) + } + + /// Returns `true` if the ordering is the `Less` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_lt(), true); + /// assert_eq!(Ordering::Equal.is_lt(), false); + /// assert_eq!(Ordering::Greater.is_lt(), false); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_lt(self) -> bool { + matches!(self, Less) + } + + /// Returns `true` if the ordering is the `Greater` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_gt(), false); + /// assert_eq!(Ordering::Equal.is_gt(), false); + /// assert_eq!(Ordering::Greater.is_gt(), true); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_gt(self) -> bool { + matches!(self, Greater) + } + + /// Returns `true` if the ordering is either the `Less` or `Equal` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_le(), true); + /// assert_eq!(Ordering::Equal.is_le(), true); + /// assert_eq!(Ordering::Greater.is_le(), false); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_le(self) -> bool { + !matches!(self, Greater) + } + + /// Returns `true` if the ordering is either the `Greater` or `Equal` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_ge(), false); + /// assert_eq!(Ordering::Equal.is_ge(), true); + /// assert_eq!(Ordering::Greater.is_ge(), true); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_ge(self) -> bool { + !matches!(self, Less) + } + /// Reverses the `Ordering`. /// /// * `Less` becomes `Greater`. diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 33eefe5d265..4fa48427ec6 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1211,7 +1211,7 @@ any high-order bits of `rhs` that would cause the shift to exceed the bitwidth o Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to the range of the type, rather than the bits shifted out of the LHS being returned to the other end. -The primitive integer types all implement a `[`rotate_left`](#method.rotate_left) function, +The primitive integer types all implement a [`rotate_left`](#method.rotate_left) function, which may be what you want instead. # Examples diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 09a9b184e3a..48c77bd13e0 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -206,8 +206,10 @@ impl f32 { /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error, yielding a more accurate result than an unfused multiply-add. /// - /// Using `mul_add` can be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. /// /// # Examples /// diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 64bb7cd9fd1..e823ac43fdd 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -206,8 +206,10 @@ impl f64 { /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error, yielding a more accurate result than an unfused multiply-add. /// - /// Using `mul_add` can be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. /// /// # Examples /// diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 6ea7704d422..1160011f352 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -9,6 +9,7 @@ use crate::cell::{Cell, RefCell}; use crate::fmt; use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter}; use crate::lazy::SyncOnceCell; +use crate::pin::Pin; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard}; use crate::sys::stdio; @@ -490,7 +491,7 @@ pub struct Stdout { // FIXME: this should be LineWriter or BufWriter depending on the state of // stdout (tty or not). Note that if this is not line buffered it // should also flush-on-panic or some form of flush-on-abort. - inner: &'static ReentrantMutex>>, + inner: Pin<&'static ReentrantMutex>>>, } /// A locked reference to the `Stdout` handle. @@ -550,25 +551,29 @@ pub struct StdoutLock<'a> { pub fn stdout() -> Stdout { static INSTANCE: SyncOnceCell>>> = SyncOnceCell::new(); + + fn cleanup() { + if let Some(instance) = INSTANCE.get() { + // Flush the data and disable buffering during shutdown + // by replacing the line writer by one with zero + // buffering capacity. + // We use try_lock() instead of lock(), because someone + // might have leaked a StdoutLock, which would + // otherwise cause a deadlock here. + if let Some(lock) = Pin::static_ref(instance).try_lock() { + *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); + } + } + } + Stdout { - inner: INSTANCE.get_or_init(|| unsafe { - let _ = sys_common::at_exit(|| { - if let Some(instance) = INSTANCE.get() { - // Flush the data and disable buffering during shutdown - // by replacing the line writer by one with zero - // buffering capacity. - // We use try_lock() instead of lock(), because someone - // might have leaked a StdoutLock, which would - // otherwise cause a deadlock here. - if let Some(lock) = instance.try_lock() { - *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); - } - } - }); - let r = ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))); - r.init(); - r - }), + inner: Pin::static_ref(&INSTANCE).get_or_init_pin( + || unsafe { + let _ = sys_common::at_exit(cleanup); + ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) + }, + |mutex| unsafe { mutex.init() }, + ), } } @@ -700,7 +705,7 @@ impl fmt::Debug for StdoutLock<'_> { /// an error. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { - inner: &'static ReentrantMutex>, + inner: Pin<&'static ReentrantMutex>>, } /// A locked reference to the `Stderr` handle. @@ -756,21 +761,16 @@ pub struct StderrLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stderr() -> Stderr { - // Note that unlike `stdout()` we don't use `Lazy` here which registers a - // destructor. Stderr is not buffered nor does the `stderr_raw` type consume - // any owned resources, so there's no need to run any destructors at some - // point in the future. - // - // This has the added benefit of allowing `stderr` to be usable during - // process shutdown as well! + // Note that unlike `stdout()` we don't use `at_exit` here to register a + // destructor. Stderr is not buffered , so there's no need to run a + // destructor for flushing the buffer static INSTANCE: SyncOnceCell>> = SyncOnceCell::new(); Stderr { - inner: INSTANCE.get_or_init(|| unsafe { - let r = ReentrantMutex::new(RefCell::new(stderr_raw())); - r.init(); - r - }), + inner: Pin::static_ref(&INSTANCE).get_or_init_pin( + || unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) }, + |mutex| unsafe { mutex.init() }, + ), } } diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs index e0095e64faf..68f57958bb2 100644 --- a/library/std/src/lazy.rs +++ b/library/std/src/lazy.rs @@ -10,6 +10,7 @@ use crate::{ mem::MaybeUninit, ops::{Deref, Drop}, panic::{RefUnwindSafe, UnwindSafe}, + pin::Pin, sync::Once, }; @@ -297,6 +298,60 @@ impl SyncOnceCell { Ok(unsafe { self.get_unchecked() }) } + /// Internal-only API that gets the contents of the cell, initializing it + /// in two steps with `f` and `g` if the cell was empty. + /// + /// `f` is called to construct the value, which is then moved into the cell + /// and given as a (pinned) mutable reference to `g` to finish + /// initialization. + /// + /// This allows `g` to inspect an manipulate the value after it has been + /// moved into its final place in the cell, but before the cell is + /// considered initialized. + /// + /// # Panics + /// + /// If `f` or `g` panics, the panic is propagated to the caller, and the + /// cell remains uninitialized. + /// + /// With the current implementation, if `g` panics, the value from `f` will + /// not be dropped. This should probably be fixed if this is ever used for + /// a type where this matters. + /// + /// It is an error to reentrantly initialize the cell from `f`. The exact + /// outcome is unspecified. Current implementation deadlocks, but this may + /// be changed to a panic in the future. + pub(crate) fn get_or_init_pin(self: Pin<&Self>, f: F, g: G) -> Pin<&T> + where + F: FnOnce() -> T, + G: FnOnce(Pin<&mut T>), + { + if let Some(value) = self.get_ref().get() { + // SAFETY: The inner value was already initialized, and will not be + // moved anymore. + return unsafe { Pin::new_unchecked(value) }; + } + + let slot = &self.value; + + // Ignore poisoning from other threads + // If another thread panics, then we'll be able to run our closure + self.once.call_once_force(|_| { + let value = f(); + // SAFETY: We use the Once (self.once) to guarantee unique access + // to the UnsafeCell (slot). + let value: &mut T = unsafe { (&mut *slot.get()).write(value) }; + // SAFETY: The value has been written to its final place in + // self.value. We do not to move it anymore, which we promise here + // with a Pin<&mut T>. + g(unsafe { Pin::new_unchecked(value) }); + }); + + // SAFETY: The inner value has been initialized, and will not be moved + // anymore. + unsafe { Pin::new_unchecked(self.get_ref().get_unchecked()) } + } + /// Consumes the `SyncOnceCell`, returning the wrapped value. Returns /// `None` if the cell was empty. /// diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6c240cb4c3e..fc48997f1bb 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -266,6 +266,7 @@ #![feature(format_args_nl)] #![feature(gen_future)] #![feature(generator_trait)] +#![feature(get_mut_unchecked)] #![feature(global_asm)] #![feature(hashmap_internals)] #![feature(int_error_internals)] @@ -293,6 +294,7 @@ #![feature(panic_info_message)] #![feature(panic_internals)] #![feature(panic_unwind)] +#![feature(pin_static_ref)] #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] @@ -312,6 +314,7 @@ #![feature(stdsimd)] #![feature(stmt_expr_attributes)] #![feature(str_internals)] +#![feature(str_split_once)] #![feature(test)] #![feature(thread_local)] #![feature(thread_local_internals)] diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs index 3937a1ffa38..77369cdd35f 100644 --- a/library/std/src/sys/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/unix/kernel_copy/tests.rs @@ -1,18 +1,18 @@ -use crate::env::temp_dir; use crate::fs::OpenOptions; use crate::io; use crate::io::Result; use crate::io::SeekFrom; use crate::io::{BufRead, Read, Seek, Write}; use crate::os::unix::io::AsRawFd; +use crate::sys_common::io::test::tmpdir; #[test] fn copy_specialization() -> Result<()> { use crate::io::{BufReader, BufWriter}; - let path = crate::env::temp_dir(); - let source_path = path.join("copy-spec.source"); - let sink_path = path.join("copy-spec.sink"); + let tmp_path = tmpdir(); + let source_path = tmp_path.join("copy-spec.source"); + let sink_path = tmp_path.join("copy-spec.sink"); let result: Result<()> = try { let mut source = crate::fs::OpenOptions::new() @@ -68,7 +68,8 @@ fn copy_specialization() -> Result<()> { #[bench] fn bench_file_to_file_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; - let src_path = temp_dir().join("file-copy-bench-src"); + let temp_path = tmpdir(); + let src_path = temp_path.join("file-copy-bench-src"); let mut src = crate::fs::OpenOptions::new() .create(true) .truncate(true) @@ -78,7 +79,7 @@ fn bench_file_to_file_copy(b: &mut test::Bencher) { .unwrap(); src.write(&vec![0u8; BYTES]).unwrap(); - let sink_path = temp_dir().join("file-copy-bench-sink"); + let sink_path = temp_path.join("file-copy-bench-sink"); let mut sink = crate::fs::OpenOptions::new() .create(true) .truncate(true) @@ -97,7 +98,8 @@ fn bench_file_to_file_copy(b: &mut test::Bencher) { #[bench] fn bench_file_to_socket_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; - let src_path = temp_dir().join("pipe-copy-bench-src"); + let temp_path = tmpdir(); + let src_path = temp_path.join("pipe-copy-bench-src"); let mut src = OpenOptions::new() .create(true) .truncate(true) @@ -128,7 +130,8 @@ fn bench_file_to_socket_copy(b: &mut test::Bencher) { #[bench] fn bench_file_to_uds_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; - let src_path = temp_dir().join("uds-copy-bench-src"); + let temp_path = tmpdir(); + let src_path = temp_path.join("uds-copy-bench-src"); let mut src = OpenOptions::new() .create(true) .truncate(true) diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs index fb2bb0613ee..065365e5572 100644 --- a/library/std/src/sys/windows/thread_local_key.rs +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -1,4 +1,4 @@ -use crate::mem; +use crate::mem::ManuallyDrop; use crate::ptr; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::SeqCst; @@ -111,16 +111,13 @@ struct Node { } unsafe fn register_dtor(key: Key, dtor: Dtor) { - let mut node = Box::new(Node { key, dtor, next: ptr::null_mut() }); + let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() })); let mut head = DTORS.load(SeqCst); loop { node.next = head; - match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) { - Ok(_) => { - mem::forget(node); - return; - } + match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) { + Ok(_) => return, // nothing to drop, we successfully added the node to the list Err(cur) => head = cur, } } diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 48ba4ddfc0b..38ba0d2fbdb 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -177,11 +177,8 @@ impl TryFrom<&str> for LookupHost { } // split the string by ':' and convert the second part to u16 - let mut parts_iter = s.rsplitn(2, ':'); - let port_str = try_opt!(parts_iter.next(), "invalid socket address"); - let host = try_opt!(parts_iter.next(), "invalid socket address"); + let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() } } diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs index 162eab2388d..475bfca9b6d 100644 --- a/library/std/src/sys_common/remutex.rs +++ b/library/std/src/sys_common/remutex.rs @@ -1,10 +1,10 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::fmt; -use crate::marker; +use crate::marker::PhantomPinned; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::pin::Pin; use crate::sys::mutex as sys; /// A re-entrant mutual exclusion @@ -15,6 +15,7 @@ use crate::sys::mutex as sys; pub struct ReentrantMutex { inner: sys::ReentrantMutex, data: T, + _pinned: PhantomPinned, } unsafe impl Send for ReentrantMutex {} @@ -37,10 +38,10 @@ impl RefUnwindSafe for ReentrantMutex {} /// guarded data. #[must_use = "if unused the ReentrantMutex will immediately unlock"] pub struct ReentrantMutexGuard<'a, T: 'a> { - lock: &'a ReentrantMutex, + lock: Pin<&'a ReentrantMutex>, } -impl !marker::Send for ReentrantMutexGuard<'_, T> {} +impl !Send for ReentrantMutexGuard<'_, T> {} impl ReentrantMutex { /// Creates a new reentrant mutex in an unlocked state. @@ -51,7 +52,11 @@ impl ReentrantMutex { /// once this mutex is in its final resting place, and only then are the /// lock/unlock methods safe. pub const unsafe fn new(t: T) -> ReentrantMutex { - ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t } + ReentrantMutex { + inner: sys::ReentrantMutex::uninitialized(), + data: t, + _pinned: PhantomPinned, + } } /// Initializes this mutex so it's ready for use. @@ -60,8 +65,8 @@ impl ReentrantMutex { /// /// Unsafe to call more than once, and must be called after this will no /// longer move in memory. - pub unsafe fn init(&self) { - self.inner.init(); + pub unsafe fn init(self: Pin<&mut Self>) { + self.get_unchecked_mut().inner.init() } /// Acquires a mutex, blocking the current thread until it is able to do so. @@ -76,9 +81,9 @@ impl ReentrantMutex { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn lock(&self) -> ReentrantMutexGuard<'_, T> { + pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> { unsafe { self.inner.lock() } - ReentrantMutexGuard::new(&self) + ReentrantMutexGuard { lock: self } } /// Attempts to acquire this lock. @@ -93,8 +98,12 @@ impl ReentrantMutex { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn try_lock(&self) -> Option> { - if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None } + pub fn try_lock(self: Pin<&Self>) -> Option> { + if unsafe { self.inner.try_lock() } { + Some(ReentrantMutexGuard { lock: self }) + } else { + None + } } } @@ -107,30 +116,6 @@ impl Drop for ReentrantMutex { } } -impl fmt::Debug for ReentrantMutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_lock() { - Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(), - None => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - - f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish() - } - } - } -} - -impl<'mutex, T> ReentrantMutexGuard<'mutex, T> { - fn new(lock: &'mutex ReentrantMutex) -> ReentrantMutexGuard<'mutex, T> { - ReentrantMutexGuard { lock } - } -} - impl Deref for ReentrantMutexGuard<'_, T> { type Target = T; diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs index 9c686e579d7..88453ded2f9 100644 --- a/library/std/src/sys_common/remutex/tests.rs +++ b/library/std/src/sys_common/remutex/tests.rs @@ -1,4 +1,6 @@ +use crate::boxed::Box; use crate::cell::RefCell; +use crate::pin::Pin; use crate::sync::Arc; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::thread; @@ -6,10 +8,11 @@ use crate::thread; #[test] fn smoke() { let m = unsafe { - let m = ReentrantMutex::new(()); - m.init(); + let mut m = Box::pin(ReentrantMutex::new(())); + m.as_mut().init(); m }; + let m = m.as_ref(); { let a = m.lock(); { @@ -27,18 +30,19 @@ fn smoke() { #[test] fn is_mutex() { let m = unsafe { - let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); - m.init(); - m + // FIXME: Simplify this if Arc gets a Arc::get_pin_mut. + let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0))); + Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init(); + Pin::new_unchecked(m) }; let m2 = m.clone(); - let lock = m.lock(); + let lock = m.as_ref().lock(); let child = thread::spawn(move || { - let lock = m2.lock(); + let lock = m2.as_ref().lock(); assert_eq!(*lock.borrow(), 4950); }); for i in 0..100 { - let lock = m.lock(); + let lock = m.as_ref().lock(); *lock.borrow_mut() += i; } drop(lock); @@ -48,20 +52,21 @@ fn is_mutex() { #[test] fn trylock_works() { let m = unsafe { - let m = Arc::new(ReentrantMutex::new(())); - m.init(); - m + // FIXME: Simplify this if Arc gets a Arc::get_pin_mut. + let mut m = Arc::new(ReentrantMutex::new(())); + Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init(); + Pin::new_unchecked(m) }; let m2 = m.clone(); - let _lock = m.try_lock(); - let _lock2 = m.try_lock(); + let _lock = m.as_ref().try_lock(); + let _lock2 = m.as_ref().try_lock(); thread::spawn(move || { - let lock = m2.try_lock(); + let lock = m2.as_ref().try_lock(); assert!(lock.is_none()); }) .join() .unwrap(); - let _lock3 = m.try_lock(); + let _lock3 = m.as_ref().try_lock(); } pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell>); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 5c12a54eef1..656d9669e81 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -30,6 +30,7 @@ #![feature(termination_trait_lib)] #![feature(test)] #![feature(total_cmp)] +#![feature(str_split_once)] // Public reexports pub use self::bench::{black_box, Bencher}; diff --git a/library/test/src/time.rs b/library/test/src/time.rs index 130792fa5d7..e0b6eadffa1 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -105,30 +105,24 @@ impl TimeThreshold { /// value. pub fn from_env_var(env_var_name: &str) -> Option { let durations_str = env::var(env_var_name).ok()?; + let (warn_str, critical_str) = durations_str.split_once(',').unwrap_or_else(|| { + panic!( + "Duration variable {} expected to have 2 numbers separated by comma, but got {}", + env_var_name, durations_str + ) + }); - // Split string into 2 substrings by comma and try to parse numbers. - let mut durations = durations_str.splitn(2, ',').map(|v| { + let parse_u64 = |v| { u64::from_str(v).unwrap_or_else(|_| { panic!( "Duration value in variable {} is expected to be a number, but got {}", env_var_name, v ) }) - }); - - // Callback to be called if the environment variable has unexpected structure. - let panic_on_incorrect_value = || { - panic!( - "Duration variable {} expected to have 2 numbers separated by comma, but got {}", - env_var_name, durations_str - ); }; - let (warn, critical) = ( - durations.next().unwrap_or_else(panic_on_incorrect_value), - durations.next().unwrap_or_else(panic_on_incorrect_value), - ); - + let warn = parse_u64(warn_str); + let critical = parse_u64(critical_str); if warn > critical { panic!("Test execution warn time should be less or equal to the critical time"); } diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile index f4071961f8e..77510d7ac62 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile @@ -1,6 +1,7 @@ -FROM ubuntu:16.04 +FROM ubuntu:20.04 -RUN apt-get update && apt-get install -y --no-install-recommends \ +# Avoid interactive prompts while installing `tzdata` dependency with `DEBIAN_FRONTEND`. +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ g++ \ make \ ninja-build \ diff --git a/src/doc/unstable-book/src/language-features/const-fn.md b/src/doc/unstable-book/src/language-features/const-fn.md index 50dbbaf5674..bcf7f78b8fe 100644 --- a/src/doc/unstable-book/src/language-features/const-fn.md +++ b/src/doc/unstable-book/src/language-features/const-fn.md @@ -6,24 +6,5 @@ The tracking issue for this feature is: [#57563] ------------------------ -The `const_fn` feature allows marking free functions and inherent methods as -`const`, enabling them to be called in constants contexts, with constant -arguments. - -## Examples - -```rust -#![feature(const_fn)] - -const fn double(x: i32) -> i32 { - x * 2 -} - -const FIVE: i32 = 5; -const TEN: i32 = double(FIVE); - -fn main() { - assert_eq!(5, FIVE); - assert_eq!(10, TEN); -} -``` +The `const_fn` feature enables additional functionality not stabilized in the +[minimal subset of `const_fn`](https://github.com/rust-lang/rust/issues/53555) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index e60970af0d3..2d58614b139 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -397,12 +397,9 @@ impl Options { matches .opt_strs("default-setting") .iter() - .map(|s| { - let mut kv = s.splitn(2, '='); - // never panics because `splitn` always returns at least one element - let k = kv.next().unwrap().to_string(); - let v = kv.next().unwrap_or("true").to_string(); - (k, v) + .map(|s| match s.split_once('=') { + None => (s.clone(), "true".to_string()), + Some((k, v)) => (k.to_string(), v.to_string()), }) .collect(), ]; @@ -707,11 +704,9 @@ fn parse_extern_html_roots( ) -> Result, &'static str> { let mut externs = BTreeMap::new(); for arg in &matches.opt_strs("extern-html-root-url") { - let mut parts = arg.splitn(2, '='); - let name = parts.next().ok_or("--extern-html-root-url must not be empty")?; - let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?; + let (name, url) = + arg.split_once('=').ok_or("--extern-html-root-url must be of the form name=url")?; externs.insert(name.to_string(), url.to_string()); } - Ok(externs) } diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index e8039942f4f..f2c74c46d7d 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -98,7 +98,7 @@ crate fn render( placeholder=\"Click or press ‘S’ to search, ‘?’ for more options…\" \ type=\"search\">\ \ - ? + \ " + text + - "
(" + nbElems + ")
"; + return ""; } - return "
" + text + "
(" + nbElems + ")
"; + return ""; } function showResults(results) { diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 8eef65a231d..61905b8eca8 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -1176,21 +1176,22 @@ pre.rust { height: 35px; } -#titles > div { +#titles > button { float: left; width: 33.3%; text-align: center; font-size: 18px; cursor: pointer; + border: 0; border-top: 2px solid; } -#titles > div:not(:last-child) { +#titles > button:not(:last-child) { margin-right: 1px; width: calc(33.3% - 1px); } -#titles > div > div.count { +#titles > button > div.count { display: inline-block; font-size: 16px; } @@ -1459,7 +1460,7 @@ h4 > .notable-traits { top: 24px; } - #titles > div > div.count { + #titles > button > div.count { float: left; width: 100%; } @@ -1565,7 +1566,7 @@ h4 > .notable-traits { } @media (max-width: 416px) { - #titles, #titles > div { + #titles, #titles > button { height: 73px; } diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css index c1f796f09e8..76bbe4f6201 100644 --- a/src/librustdoc/html/static/themes/ayu.css +++ b/src/librustdoc/html/static/themes/ayu.css @@ -403,22 +403,22 @@ pre.ignore:hover, .information:hover + pre.ignore { border-color: #5c6773; } -#titles > div.selected { +#titles > button.selected { background-color: #141920 !important; border-bottom: 1px solid #ffb44c !important; border-top: none; } -#titles > div:not(.selected) { +#titles > button:not(.selected) { background-color: transparent !important; border: none; } -#titles > div:hover { +#titles > button:hover { border-bottom: 1px solid rgba(242, 151, 24, 0.3); } -#titles > div > div.count { +#titles > button > div.count { color: #888; } @@ -434,7 +434,7 @@ above the `@media (max-width: 700px)` rules due to a bug in the css checker */ .block a.current.derive,.content span.macro,.content a.macro,.block a.current.macro {} .content .highlighted.trait {} .content span.struct,.content a.struct,.block a.current.struct {} -#titles>div:hover,#titles>div.selected {} +#titles>button:hover,#titles>button.selected {} .content .highlighted.traitalias {} .content span.type,.content a.type,.block a.current.type {} .content span.union,.content a.union,.block a.current.union {} diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index 946ca0a40c9..86ce99284eb 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -352,16 +352,17 @@ pre.ignore:hover, .information:hover + pre.ignore { border-color: #777; } -#titles > div:not(.selected) { +#titles > button:not(.selected) { background-color: #252525; border-top-color: #252525; } -#titles > div:hover, #titles > div.selected { +#titles > button:hover, #titles > button.selected { border-top-color: #0089ff; + background-color: #353535; } -#titles > div > div.count { +#titles > button > div.count { color: #888; } diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index e0b9a04921a..52cfdf6f7a3 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -343,16 +343,17 @@ pre.ignore:hover, .information:hover + pre.ignore { border-color: #999; } -#titles > div:not(.selected) { +#titles > button:not(.selected) { background-color: #e6e6e6; border-top-color: #e6e6e6; } -#titles > div:hover, #titles > div.selected { +#titles > button:hover, #titles > button.selected { border-top-color: #0089ff; + background-color: #353535; } -#titles > div > div.count { +#titles > button > div.count { color: #888; } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 26bf4b569ff..f851d1a2372 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -16,6 +16,7 @@ #![feature(once_cell)] #![feature(type_ascription)] #![feature(split_inclusive)] +#![feature(str_split_once)] #![recursion_limit = "256"] #[macro_use] diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 551c086a8d4..5ce64c4cd83 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -435,8 +435,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // Try looking for methods and associated items. let mut split = path_str.rsplitn(2, "::"); - // this can be an `unwrap()` because we ensure the link is never empty - let (item_str, item_name) = split.next().map(|i| (i, Symbol::intern(i))).unwrap(); + // NB: `split`'s first element is always defined, even if the delimiter was not present. + let item_str = split.next().unwrap(); + assert!(!item_str.is_empty()); + let item_name = Symbol::intern(item_str); let path_root = split .next() .map(|f| f.to_owned()) diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed index 760d2b433c8..bca69a97677 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed @@ -11,7 +11,7 @@ pub trait Foo { fn foo2 Foo<&'x isize>>( x: >::A) - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters { // This case is illegal because we have to instantiate `'x`, and // we don't know what region to instantiate it with. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs index 6eb584ea645..1e23dd8890b 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs @@ -11,7 +11,7 @@ pub trait Foo { fn foo2 Foo<&'x isize>>( x: I::A) - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters { // This case is illegal because we have to instantiate `'x`, and // we don't know what region to instantiate it with. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr index f2137f68665..989624bdd93 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr @@ -1,4 +1,4 @@ -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-fn.rs:13:8 | LL | x: I::A) @@ -6,3 +6,4 @@ LL | x: I::A) error: aborting due to previous error +For more information about this error, try `rustc --explain E0212`. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs index 58f186d7775..ed30d86cb5b 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs @@ -9,14 +9,14 @@ pub trait Foo { struct SomeStruct Foo<&'x isize>> { field: I::A - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters } enum SomeEnum<'b, I: for<'a> Foo<&'a isize>> { TupleVariant(I::A), - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters StructVariant { field: I::A }, - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters OkVariant(&'b usize), } @@ -33,7 +33,7 @@ struct YetAnotherStruct<'a, I: for<'x> Foo<&'x isize>> { struct Why<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, 'y, 'z, 'aa, I: for<'l, 'm> Foo<&'l &'m isize>> { field: I::A, - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters } pub fn main() {} diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr index e3fd2860ebc..cadc3e9eab1 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr @@ -1,4 +1,4 @@ -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-struct.rs:11:12 | LL | field: I::A @@ -10,7 +10,7 @@ LL | struct SomeStruct<'a, I: for<'x> Foo<&'x isize>> { LL | field: >::A | -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-struct.rs:16:18 | LL | TupleVariant(I::A), @@ -22,7 +22,7 @@ LL | enum SomeEnum<'c, 'b, I: for<'a> Foo<&'a isize>> { LL | TupleVariant(>::A), | -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-struct.rs:18:28 | LL | StructVariant { field: I::A }, @@ -36,7 +36,7 @@ LL | LL | StructVariant { field: >::A }, | -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-struct.rs:35:12 | LL | field: I::A, @@ -51,3 +51,4 @@ LL | field: >::A, error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0212`. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed index acf32bccbec..66d8613f184 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed @@ -11,7 +11,7 @@ pub trait Foo { trait SomeTrait Foo<&'x isize>> { fn some_method(&self, arg: >::A); - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters } trait AnotherTrait Foo<&'x isize>> { @@ -30,7 +30,7 @@ struct Peach(std::marker::PhantomData); impl Banana<'a>> Peach { fn mango(&self) -> >::Assoc { - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters Default::default() } } diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs index a249f89685e..0a1b29de19e 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs @@ -11,7 +11,7 @@ pub trait Foo { trait SomeTrait Foo<&'x isize>> { fn some_method(&self, arg: I::A); - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters } trait AnotherTrait Foo<&'x isize>> { @@ -30,7 +30,7 @@ struct Peach(std::marker::PhantomData); impl Banana<'a>> Peach { fn mango(&self) -> X::Assoc { - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters Default::default() } } diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr index a37fec24493..d457f9f8468 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr @@ -1,10 +1,10 @@ -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:13:32 | LL | fn some_method(&self, arg: I::A); | ^^^^ help: use a fully qualified path with inferred lifetimes: `>::A` -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:32:24 | LL | fn mango(&self) -> X::Assoc { @@ -12,3 +12,4 @@ LL | fn mango(&self) -> X::Assoc { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0212`. diff --git a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs index 34aee7f6935..f2d97b7bac3 100644 --- a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs +++ b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs @@ -3,10 +3,10 @@ fn main() {} trait X { - default const A: u8; //~ ERROR `default` is only allowed on items in `impl` definitions - default const B: u8 = 0; //~ ERROR `default` is only allowed on items in `impl` definitions - default type D; //~ ERROR `default` is only allowed on items in `impl` definitions - default type C: Ord; //~ ERROR `default` is only allowed on items in `impl` definitions - default fn f1(); //~ ERROR `default` is only allowed on items in `impl` definitions - default fn f2() {} //~ ERROR `default` is only allowed on items in `impl` definitions + default const A: u8; //~ ERROR `default` is only allowed on items in trait impls + default const B: u8 = 0; //~ ERROR `default` is only allowed on items in trait impls + default type D; //~ ERROR `default` is only allowed on items in trait impls + default type C: Ord; //~ ERROR `default` is only allowed on items in trait impls + default fn f1(); //~ ERROR `default` is only allowed on items in trait impls + default fn f2() {} //~ ERROR `default` is only allowed on items in trait impls } diff --git a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr index cdc9ee8d9e7..76fa860334d 100644 --- a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr +++ b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr @@ -1,4 +1,4 @@ -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:6:5 | LL | default const A: u8; @@ -6,7 +6,7 @@ LL | default const A: u8; | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:7:5 | LL | default const B: u8 = 0; @@ -14,7 +14,7 @@ LL | default const B: u8 = 0; | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:8:5 | LL | default type D; @@ -22,7 +22,7 @@ LL | default type D; | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:9:5 | LL | default type C: Ord; @@ -30,7 +30,7 @@ LL | default type C: Ord; | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:10:5 | LL | default fn f1(); @@ -38,7 +38,7 @@ LL | default fn f1(); | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:11:5 | LL | default fn f2() {} diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index f213944e0ab..dcfe1bb803f 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -14,6 +14,8 @@ //! A few exceptions are allowed as there's known bugs in rustdoc, but this //! should catch the majority of "broken link" cases. +#![feature(str_split_once)] + use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::env; @@ -232,11 +234,12 @@ fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Opti { return; } - let mut parts = url.splitn(2, '#'); - let url = parts.next().unwrap(); - let fragment = parts.next(); - let mut parts = url.splitn(2, '?'); - let url = parts.next().unwrap(); + let (url, fragment) = match url.split_once('#') { + None => (url, None), + Some((url, fragment)) => (url, Some(fragment)), + }; + // NB: the `splitn` always succeeds, even if the delimiter is not present. + let url = url.splitn(2, '?').next().unwrap(); // Once we've plucked out the URL, parse it using our base url and // then try to extract a file path. diff --git a/src/tools/tidy/src/cargo.rs b/src/tools/tidy/src/cargo.rs index 7bdd78a91e7..e06616a59f3 100644 --- a/src/tools/tidy/src/cargo.rs +++ b/src/tools/tidy/src/cargo.rs @@ -59,11 +59,10 @@ fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) { break; } - let mut parts = line.splitn(2, '='); - let krate = parts.next().unwrap().trim(); - if parts.next().is_none() { - continue; - } + let krate = match line.split_once('=') { + None => continue, + Some((krate, _)) => krate.trim(), + }; // Don't worry about depending on core/std while not writing `extern crate // core/std` -- that's intentional. diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs index 82a5234ac5b..a7199fdfce6 100644 --- a/src/tools/tidy/src/error_codes_check.rs +++ b/src/tools/tidy/src/error_codes_check.rs @@ -85,47 +85,61 @@ fn extract_error_codes( for line in f.lines() { let s = line.trim(); if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") { - if let Some(err_code) = s.splitn(2, ':').next() { - let err_code = err_code.to_owned(); - if !error_codes.contains_key(&err_code) { - error_codes.insert(err_code.clone(), false); - } - // Now we extract the tests from the markdown file! - let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1)); - let md_file_name = some_or_continue!(md.splitn(2, "\")").next()); - let path = some_or_continue!(path.parent()) - .join(md_file_name) - .canonicalize() - .expect("failed to canonicalize error explanation file path"); - match read_to_string(&path) { - Ok(content) => { - if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) - && !check_if_error_code_is_test_in_explanation(&content, &err_code) - { - errors.push(format!( - "`{}` doesn't use its own error code in compile_fail example", - path.display(), - )); - } - if check_error_code_explanation(&content, error_codes, err_code) { - errors.push(format!( - "`{}` uses invalid tag `compile-fail` instead of `compile_fail`", - path.display(), - )); - } + let err_code = s + .split_once(':') + .expect( + format!( + "Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} without a `:` delimiter", + s, + ).as_str() + ) + .0 + .to_owned(); + if !error_codes.contains_key(&err_code) { + error_codes.insert(err_code.clone(), false); + } + // Now we extract the tests from the markdown file! + let md_file_name = match s.split_once("include_str!(\"") { + None => continue, + Some((_, md)) => match md.split_once("\")") { + None => continue, + Some((file_name, _)) => file_name, + }, + }; + let path = some_or_continue!(path.parent()) + .join(md_file_name) + .canonicalize() + .expect("failed to canonicalize error explanation file path"); + match read_to_string(&path) { + Ok(content) => { + if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) + && !check_if_error_code_is_test_in_explanation(&content, &err_code) + { + errors.push(format!( + "`{}` doesn't use its own error code in compile_fail example", + path.display(), + )); } - Err(e) => { - eprintln!("Couldn't read `{}`: {}", path.display(), e); + if check_error_code_explanation(&content, error_codes, err_code) { + errors.push(format!( + "`{}` uses invalid tag `compile-fail` instead of `compile_fail`", + path.display(), + )); } } + Err(e) => { + eprintln!("Couldn't read `{}`: {}", path.display(), e); + } } } else if reached_no_explanation && s.starts_with('E') { - if let Some(err_code) = s.splitn(2, ',').next() { - let err_code = err_code.to_owned(); - if !error_codes.contains_key(&err_code) { - // this check should *never* fail! - error_codes.insert(err_code, false); - } + let err_code = match s.split_once(',') { + None => s, + Some((err_code, _)) => err_code, + } + .to_string(); + if !error_codes.contains_key(&err_code) { + // this check should *never* fail! + error_codes.insert(err_code, false); } } else if s == ";" { reached_no_explanation = true; @@ -137,12 +151,15 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap continue, + Some((err_code, _)) => match err_code.split_once('[') { + None => continue, + Some((_, err_code)) => err_code, + }, + }; + let nb = error_codes.entry(err_code.to_owned()).or_insert(false); + *nb = true; } } } diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 6b0ecde7de4..55440fdcf3f 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -28,7 +28,7 @@ pub fn check(root: &Path, bad: &mut bool) { } // Extract source value. - let source = line.splitn(2, '=').nth(1).unwrap().trim(); + let source = line.split_once('=').unwrap().1.trim(); // Allow all soft-dev repos. // We also allow our personal forks for scenarios where we are breaking a CI cycle and need diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index d8029ea04f0..3c2880d0d5e 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -112,6 +112,7 @@ pub fn check( let gate_test_str = "gate-test-"; let feature_name = match line.find(gate_test_str) { + // NB: the `splitn` always succeeds, even if the delimiter is not present. Some(i) => line[i + gate_test_str.len()..].splitn(2, ' ').next().unwrap(), None => continue, }; diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index cc4c43f0468..e11d293210b 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -3,6 +3,8 @@ //! This library contains the tidy lints and exposes it //! to be used by tools. +#![feature(str_split_once)] + use std::fs::File; use std::io::Read; use walkdir::{DirEntry, WalkDir}; diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 47b328dae47..03f4efea983 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -19,14 +19,11 @@ pub fn check(path: &Path, bad: &mut bool) { // // For now, just make sure that there is a corresponding // `$testname.rs` file. - let testname = file_path - .file_name() - .unwrap() - .to_str() - .unwrap() - .splitn(2, '.') - .next() - .unwrap(); + // + // NB: We do not use file_stem() as some file names have multiple `.`s and we + // must strip all of them. + let testname = + file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0; if !file_path.with_file_name(testname).with_extension("rs").exists() { println!("Stray file with UI testing output: {:?}", file_path); *bad = true;