diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index faeb727202cd6..87d539f748c6b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1333,9 +1333,6 @@ fn add_late_link_args( crate_type: CrateType, codegen_results: &CodegenResults, ) { - if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { - cmd.args(args); - } let any_dynamic_crate = crate_type == CrateType::Dylib || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| { *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) @@ -1349,6 +1346,9 @@ fn add_late_link_args( cmd.args(args); } } + if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { + cmd.args(args); + } } /// Add arbitrary "post-link" args defined by the target spec. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 492afa54445bc..d6cf68a900ce0 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -682,25 +682,31 @@ pub enum BorrowKind { /// implicit closure bindings. It is needed when the closure /// is borrowing or mutating a mutable referent, e.g.: /// - /// let x: &mut isize = ...; - /// let y = || *x += 5; + /// ``` + /// let x: &mut isize = ...; + /// let y = || *x += 5; + /// ``` /// /// If we were to try to translate this closure into a more explicit /// form, we'd encounter an error with the code as written: /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` /// /// This is then illegal because you cannot mutate a `&mut` found /// in an aliasable location. To solve, you'd have to translate with /// an `&mut` borrow: /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` /// /// Now the assignment to `**env.x` is legal, but creating a /// mutable pointer to `x` is not because `x` is not mutable. We diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index aa88719c26a49..0f04ead94dd33 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -1046,9 +1046,9 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { self.super_operand(operand, location); - // Only const prop copies and moves on `mir_opt_level=3` as doing so - // currently increases compile time. - if self.tcx.sess.opts.debugging_opts.mir_opt_level >= 3 { + // Only const prop copies and moves on `mir_opt_level=2` as doing so + // currently slightly increases compile time in some cases. + if self.tcx.sess.opts.debugging_opts.mir_opt_level >= 2 { self.propagate_operand(operand) } } @@ -1246,8 +1246,8 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { | TerminatorKind::InlineAsm { .. } => {} // Every argument in our function calls have already been propagated in `visit_operand`. // - // NOTE: because LLVM codegen gives performance regressions with it, so this is gated - // on `mir_opt_level=3`. + // NOTE: because LLVM codegen gives slight performance regressions with it, so this is + // gated on `mir_opt_level=2`. TerminatorKind::Call { .. } => {} } diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index a622de9a8af3e..850cafcf89811 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -36,7 +36,6 @@ pub mod match_branches; pub mod no_landing_pads; pub mod nrvo; pub mod promote_consts; -pub mod qualify_min_const_fn; pub mod remove_noop_landing_pads; pub mod remove_unneeded_drops; pub mod required_consts; diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 21bbdc9ba8dce..d59dd4016a9f9 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -7,7 +7,7 @@ #![feature(or_patterns)] use rustc_ast as ast; -use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{self, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{self, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; @@ -299,7 +299,7 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke // FIXME(#43081): Avoid this pretty-print + reparse hack let source = pprust::nonterminal_to_string(nt); let filename = FileName::macro_expansion_source_code(&source); - let tokens_for_real = parse_stream_from_source_str(filename, source, sess, Some(span)); + let reparsed_tokens = parse_stream_from_source_str(filename, source, sess, Some(span)); // During early phases of the compiler the AST could get modified // directly (e.g., attributes added or removed) and the internal cache @@ -325,7 +325,7 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke // modifications, including adding/removing typically non-semantic // tokens such as extra braces and commas, don't happen. if let Some(tokens) = tokens { - if tokenstream_probably_equal_for_proc_macro(&tokens, &tokens_for_real, sess) { + if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess) { return tokens; } info!( @@ -333,9 +333,9 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke going with stringified version" ); info!("cached tokens: {:?}", tokens); - info!("reparsed tokens: {:?}", tokens_for_real); + info!("reparsed tokens: {:?}", reparsed_tokens); } - tokens_for_real + reparsed_tokens } // See comments in `Nonterminal::to_tokenstream` for why we care about @@ -344,8 +344,8 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke // This is otherwise the same as `eq_unspanned`, only recursing with a // different method. pub fn tokenstream_probably_equal_for_proc_macro( - first: &TokenStream, - other: &TokenStream, + tokens: &TokenStream, + reparsed_tokens: &TokenStream, sess: &ParseSess, ) -> bool { // When checking for `probably_eq`, we ignore certain tokens that aren't @@ -359,9 +359,6 @@ pub fn tokenstream_probably_equal_for_proc_macro( // The pretty printer tends to add trailing commas to // everything, and in particular, after struct fields. | token::Comma - // The pretty printer emits `NoDelim` as whitespace. - | token::OpenDelim(DelimToken::NoDelim) - | token::CloseDelim(DelimToken::NoDelim) // The pretty printer collapses many semicolons into one. | token::Semi // We don't preserve leading `|` tokens in patterns, so @@ -460,10 +457,11 @@ pub fn tokenstream_probably_equal_for_proc_macro( // Break tokens after we expand any nonterminals, so that we break tokens // that are produced as a result of nonterminal expansion. - let t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); - let t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); + let tokens = tokens.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); + let reparsed_tokens = + reparsed_tokens.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); - t1.eq_by(t2, |t1, t2| tokentree_probably_equal_for_proc_macro(&t1, &t2, sess)) + tokens.eq_by(reparsed_tokens, |t, rt| tokentree_probably_equal_for_proc_macro(&t, &rt, sess)) } // See comments in `Nonterminal::to_tokenstream` for why we care about @@ -472,16 +470,20 @@ pub fn tokenstream_probably_equal_for_proc_macro( // This is otherwise the same as `eq_unspanned`, only recursing with a // different method. pub fn tokentree_probably_equal_for_proc_macro( - first: &TokenTree, - other: &TokenTree, + token: &TokenTree, + reparsed_token: &TokenTree, sess: &ParseSess, ) -> bool { - match (first, other) { - (TokenTree::Token(token), TokenTree::Token(token2)) => { - token_probably_equal_for_proc_macro(token, token2) + match (token, reparsed_token) { + (TokenTree::Token(token), TokenTree::Token(reparsed_token)) => { + token_probably_equal_for_proc_macro(token, reparsed_token) } - (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { - delim == delim2 && tokenstream_probably_equal_for_proc_macro(&tts, &tts2, sess) + ( + TokenTree::Delimited(_, delim, tokens), + TokenTree::Delimited(_, reparsed_delim, reparsed_tokens), + ) => { + delim == reparsed_delim + && tokenstream_probably_equal_for_proc_macro(tokens, reparsed_tokens, sess) } _ => false, } diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index a864918655fb8..0234ff55f0118 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -23,6 +23,7 @@ pub fn opts() -> TargetOptions { "-lmsvcrt".to_string(), "-lmingwex".to_string(), "-lmingw32".to_string(), + "-lgcc".to_string(), // alas, mingw* libraries above depend on libgcc // mingw's msvcrt is a weird hybrid import library and static library. // And it seems that the linker fails to use import symbols from msvcrt // that are required from functions in msvcrt in certain cases. For example @@ -41,8 +42,6 @@ pub fn opts() -> TargetOptions { // the shared libgcc_s-dw2-1.dll. This is required to support // unwinding across DLL boundaries. "-lgcc_s".to_string(), - "-lgcc".to_string(), - "-lkernel32".to_string(), ]; late_link_args_dynamic.insert(LinkerFlavor::Gcc, dynamic_unwind_libs.clone()); late_link_args_dynamic.insert(LinkerFlavor::Lld(LldFlavor::Ld), dynamic_unwind_libs); @@ -54,10 +53,6 @@ pub fn opts() -> TargetOptions { // boundaries when unwinding across FFI boundaries. "-lgcc_eh".to_string(), "-l:libpthread.a".to_string(), - "-lgcc".to_string(), - // libpthread depends on libmsvcrt, so we need to link it *again*. - "-lmsvcrt".to_string(), - "-lkernel32".to_string(), ]; late_link_args_static.insert(LinkerFlavor::Gcc, static_unwind_libs.clone()); late_link_args_static.insert(LinkerFlavor::Lld(LldFlavor::Ld), static_unwind_libs); diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs index 687efa8e9e777..789ae3a20eabf 100644 --- a/library/alloc/benches/vec.rs +++ b/library/alloc/benches/vec.rs @@ -1,4 +1,4 @@ -use rand::prelude::*; +use rand::RngCore; use std::iter::{repeat, FromIterator}; use test::{black_box, Bencher}; diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 27a358bddaf4f..ed26c59c17c43 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -268,12 +268,14 @@ where } impl SliceContains for u8 { + #[inline] fn slice_contains(&self, x: &[Self]) -> bool { memchr::memchr(*self, x).is_some() } } impl SliceContains for i8 { + #[inline] fn slice_contains(&self, x: &[Self]) -> bool { let byte = *self as u8; // SAFETY: `i8` and `u8` have the same memory layout, thus casting `x.as_ptr()` diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 3b13ed5fed396..ce9c1a6df5044 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -12,6 +12,7 @@ const HI_U64: u64 = 0x8080808080808080; // Use truncation. const LO_USIZE: usize = LO_U64 as usize; const HI_USIZE: usize = HI_U64 as usize; +const USIZE_BYTES: usize = mem::size_of::(); /// Returns `true` if `x` contains any zero byte. /// @@ -38,19 +39,30 @@ fn repeat_byte(b: u8) -> usize { } /// Returns the first index matching the byte `x` in `text`. +#[inline] pub fn memchr(x: u8, text: &[u8]) -> Option { + // Fast path for small slices + if text.len() < 2 * USIZE_BYTES { + return text.iter().position(|elt| *elt == x); + } + + memchr_general_case(x, text) +} + +#[inline(never)] +fn memchr_general_case(x: u8, text: &[u8]) -> Option { // Scan for a single byte value by reading two `usize` words at a time. // // Split `text` in three parts // - unaligned initial part, before the first word aligned address in text // - body, scan by 2 words at a time // - the last remaining part, < 2 word size + + // search up to an aligned boundary let len = text.len(); let ptr = text.as_ptr(); - let usize_bytes = mem::size_of::(); + let mut offset = ptr.align_offset(USIZE_BYTES); - // search up to an aligned boundary - let mut offset = ptr.align_offset(usize_bytes); if offset > 0 { offset = cmp::min(offset, len); if let Some(index) = text[..offset].iter().position(|elt| *elt == x) { @@ -60,22 +72,19 @@ pub fn memchr(x: u8, text: &[u8]) -> Option { // search the body of the text let repeated_x = repeat_byte(x); + while offset <= len - 2 * USIZE_BYTES { + unsafe { + let u = *(ptr.add(offset) as *const usize); + let v = *(ptr.add(offset + USIZE_BYTES) as *const usize); - if len >= 2 * usize_bytes { - while offset <= len - 2 * usize_bytes { - unsafe { - let u = *(ptr.add(offset) as *const usize); - let v = *(ptr.add(offset + usize_bytes) as *const usize); - - // break if there is a matching byte - let zu = contains_zero_byte(u ^ repeated_x); - let zv = contains_zero_byte(v ^ repeated_x); - if zu || zv { - break; - } + // break if there is a matching byte + let zu = contains_zero_byte(u ^ repeated_x); + let zv = contains_zero_byte(v ^ repeated_x); + if zu || zv { + break; } - offset += usize_bytes * 2; } + offset += USIZE_BYTES * 2; } // Find the byte after the point the body loop stopped. diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 12dcd6c6ba8d0..4192632fe5781 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1636,6 +1636,7 @@ impl [T] { /// assert!(!v.iter().any(|e| e == "hi")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn contains(&self, x: &T) -> bool where T: PartialEq, diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 939f1325c8499..378c8af344b59 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -634,14 +634,18 @@ assume_usize_width! { macro_rules! test_float { ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr) => { mod $modname { - // FIXME(nagisa): these tests should test for sign of -0.0 #[test] fn min() { assert_eq!((0.0 as $fty).min(0.0), 0.0); + assert!((0.0 as $fty).min(0.0).is_sign_positive()); assert_eq!((-0.0 as $fty).min(-0.0), -0.0); + assert!((-0.0 as $fty).min(-0.0).is_sign_negative()); assert_eq!((9.0 as $fty).min(9.0), 9.0); assert_eq!((-9.0 as $fty).min(0.0), -9.0); assert_eq!((0.0 as $fty).min(9.0), 0.0); + assert!((0.0 as $fty).min(9.0).is_sign_positive()); + assert_eq!((-0.0 as $fty).min(9.0), -0.0); + assert!((-0.0 as $fty).min(9.0).is_sign_negative()); assert_eq!((-0.0 as $fty).min(-9.0), -9.0); assert_eq!(($inf as $fty).min(9.0), 9.0); assert_eq!((9.0 as $fty).min($inf), 9.0); @@ -660,11 +664,19 @@ macro_rules! test_float { #[test] fn max() { assert_eq!((0.0 as $fty).max(0.0), 0.0); + assert!((0.0 as $fty).max(0.0).is_sign_positive()); assert_eq!((-0.0 as $fty).max(-0.0), -0.0); + assert!((-0.0 as $fty).max(-0.0).is_sign_negative()); assert_eq!((9.0 as $fty).max(9.0), 9.0); assert_eq!((-9.0 as $fty).max(0.0), 0.0); + assert!((-9.0 as $fty).max(0.0).is_sign_positive()); + assert_eq!((-9.0 as $fty).max(-0.0), -0.0); + assert!((-9.0 as $fty).max(-0.0).is_sign_negative()); assert_eq!((0.0 as $fty).max(9.0), 9.0); + assert_eq!((0.0 as $fty).max(-9.0), 0.0); + assert!((0.0 as $fty).max(-9.0).is_sign_positive()); assert_eq!((-0.0 as $fty).max(-9.0), -0.0); + assert!((-0.0 as $fty).max(-9.0).is_sign_negative()); assert_eq!(($inf as $fty).max(9.0), $inf); assert_eq!((9.0 as $fty).max($inf), $inf); assert_eq!(($inf as $fty).max(-9.0), $inf); diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 61d71d55d6593..1bb835e1eada1 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1102,6 +1102,16 @@ where /// documentation for more. /// /// [`iter`]: HashMap::iter +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.iter(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, K: 'a, V: 'a> { base: base::Iter<'a, K, V>, @@ -1129,6 +1139,16 @@ impl fmt::Debug for Iter<'_, K, V> { /// documentation for more. /// /// [`iter_mut`]: HashMap::iter_mut +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.iter_mut(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, K: 'a, V: 'a> { base: base::IterMut<'a, K, V>, @@ -1148,6 +1168,16 @@ impl<'a, K, V> IterMut<'a, K, V> { /// (provided by the `IntoIterator` trait). See its documentation for more. /// /// [`into_iter`]: IntoIterator::into_iter +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.into_iter(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter { base: base::IntoIter, @@ -1167,6 +1197,16 @@ impl IntoIter { /// documentation for more. /// /// [`keys`]: HashMap::keys +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_keys = map.keys(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Keys<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, @@ -1194,6 +1234,16 @@ impl fmt::Debug for Keys<'_, K, V> { /// documentation for more. /// /// [`values`]: HashMap::values +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_values = map.values(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Values<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, @@ -1221,6 +1271,16 @@ impl fmt::Debug for Values<'_, K, V> { /// documentation for more. /// /// [`drain`]: HashMap::drain +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.drain(); +/// ``` #[stable(feature = "drain", since = "1.6.0")] pub struct Drain<'a, K: 'a, V: 'a> { base: base::Drain<'a, K, V>, @@ -1239,6 +1299,18 @@ impl<'a, K, V> Drain<'a, K, V> { /// This `struct` is created by the [`drain_filter`] method on [`HashMap`]. /// /// [`drain_filter`]: HashMap::drain_filter +/// +/// # Example +/// +/// ``` +/// #![feature(hash_drain_filter)] +/// +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.drain_filter(|_k, v| *v % 2 == 0); +/// ``` #[unstable(feature = "hash_drain_filter", issue = "59618")] pub struct DrainFilter<'a, K, V, F> where @@ -1253,6 +1325,16 @@ where /// documentation for more. /// /// [`values_mut`]: HashMap::values_mut +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_values = map.values_mut(); +/// ``` #[stable(feature = "map_values_mut", since = "1.10.0")] pub struct ValuesMut<'a, K: 'a, V: 'a> { inner: IterMut<'a, K, V>, @@ -1264,6 +1346,18 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { /// See its documentation for more. /// /// [`into_keys`]: HashMap::into_keys +/// +/// # Example +/// +/// ``` +/// #![feature(map_into_keys_values)] +/// +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_keys = map.into_keys(); +/// ``` #[unstable(feature = "map_into_keys_values", issue = "75294")] pub struct IntoKeys { inner: IntoIter, @@ -1275,6 +1369,18 @@ pub struct IntoKeys { /// See its documentation for more. /// /// [`into_values`]: HashMap::into_values +/// +/// # Example +/// +/// ``` +/// #![feature(map_into_keys_values)] +/// +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_keys = map.into_values(); +/// ``` #[unstable(feature = "map_into_keys_values", issue = "75294")] pub struct IntoValues { inner: IntoIter, @@ -1285,7 +1391,6 @@ pub struct IntoValues { /// See the [`HashMap::raw_entry_mut`] docs for usage examples. /// /// [`HashMap::raw_entry_mut`]: HashMap::raw_entry_mut - #[unstable(feature = "hash_raw_entry", issue = "56167")] pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { map: &'a mut HashMap, diff --git a/src/test/codegen/issue-75659.rs b/src/test/codegen/issue-75659.rs new file mode 100644 index 0000000000000..8f0ce8ef9a81b --- /dev/null +++ b/src/test/codegen/issue-75659.rs @@ -0,0 +1,62 @@ +// This test checks that the call to memchr/slice_contains is optimized away +// when searching in small slices. + +// compile-flags: -O + +#![crate_type = "lib"] + +// CHECK-LABEL: @foo1 +#[no_mangle] +pub fn foo1(x: u8, data: &[u8; 1]) -> bool { + // CHECK-NOT: memchr + // CHECK-NOT: slice_contains + data.contains(&x) +} + +// CHECK-LABEL: @foo2 +#[no_mangle] +pub fn foo2(x: u8, data: &[u8; 2]) -> bool { + // CHECK-NOT: memchr + // CHECK-NOT: slice_contains + data.contains(&x) +} + +// CHECK-LABEL: @foo3 +#[no_mangle] +pub fn foo3(x: u8, data: &[u8; 3]) -> bool { + // CHECK-NOT: memchr + // CHECK-NOT: slice_contains + data.contains(&x) +} + +// CHECK-LABEL: @foo4 +#[no_mangle] +pub fn foo4(x: u8, data: &[u8; 4]) -> bool { + // CHECK-NOT: memchr + // CHECK-NOT: slice_contains + data.contains(&x) +} + +// CHECK-LABEL: @foo8 +#[no_mangle] +pub fn foo8(x: u8, data: &[u8; 8]) -> bool { + // CHECK-NOT: memchr + // CHECK-NOT: slice_contains + data.contains(&x) +} + +// CHECK-LABEL: @foo8_i8 +#[no_mangle] +pub fn foo8_i8(x: i8, data: &[i8; 8]) -> bool { + // CHECK-NOT: memchr + // CHECK-NOT: slice_contains + data.contains(&x) +} + +// Check that the general case isn't inlined +// CHECK-LABEL: @foo80 +#[no_mangle] +pub fn foo80(x: u8, data: &[u8; 80]) -> bool { + // CHECK: call core::slice::memchr + data.contains(&x) +} diff --git a/src/tools/cargo b/src/tools/cargo index 8777a6b1e8834..05c611ae3c425 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 8777a6b1e8834899f51b7e09cc9b8d85b2417110 +Subproject commit 05c611ae3c4255b7a2bcf4fcfa65b20286a07839 diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 58112ac8da5f1..c3ff34e6e1eed 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -6,6 +6,7 @@ #![feature(concat_idents)] #![feature(crate_visibility_modifier)] #![feature(drain_filter)] +#![feature(in_band_lifetimes)] #![feature(or_patterns)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 1ad184dfc460b..e5f7cc5111120 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -4,7 +4,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn; +use crate::utils::qualify_min_const_fn::is_min_const_fn; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index ea52741b7cc42..96d9905027b62 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -20,6 +20,7 @@ pub mod paths; pub mod ptr; pub mod sugg; pub mod usage; +pub mod qualify_min_const_fn; pub use self::attrs::*; pub use self::diagnostics::*; diff --git a/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs b/src/tools/clippy/clippy_lints/src/utils/qualify_min_const_fn.rs similarity index 71% rename from compiler/rustc_mir/src/transform/qualify_min_const_fn.rs rename to src/tools/clippy/clippy_lints/src/utils/qualify_min_const_fn.rs index f15a7f7c2c889..6809b1fa88d35 100644 --- a/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_lints/src/utils/qualify_min_const_fn.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir::*; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym}; use rustc_span::Span; use rustc_target::spec::abi::Abi::RustIntrinsic; use std::borrow::Cow; @@ -11,14 +11,6 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult { - // Prevent const trait methods from being annotated as `stable`. - if tcx.features().staged_api { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) { - return Err((body.span, "trait methods cannot be stable const fn".into())); - } - } - let mut current = def_id; loop { let predicates = tcx.predicates_of(current); @@ -32,27 +24,20 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - | ty::PredicateAtom::ConstEquate(..) | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, ty::PredicateAtom::ObjectSafe(_) => { - bug!("object safe predicate on function: {:#?}", predicate) + panic!("object safe predicate on function: {:#?}", predicate) } ty::PredicateAtom::ClosureKind(..) => { - bug!("closure kind predicate on function: {:#?}", predicate) + panic!("closure kind predicate on function: {:#?}", predicate) } ty::PredicateAtom::Subtype(_) => { - bug!("subtype predicate on function: {:#?}", predicate) + panic!("subtype predicate on function: {:#?}", predicate) } - ty::PredicateAtom::Trait(pred, constness) => { + ty::PredicateAtom::Trait(pred, _) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; } match pred.self_ty().kind() { ty::Param(ref p) => { - // Allow `T: ?const Trait` - if constness == hir::Constness::NotConst - && feature_allowed(tcx, def_id, sym::const_trait_bound_opt_out) - { - continue; - } - let generics = tcx.generics_of(current); let def = generics.type_param(p, tcx); let span = tcx.def_span(def.def_id); @@ -77,18 +62,17 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - } for local in &body.local_decls { - check_ty(tcx, local.ty, local.source_info.span, def_id)?; + check_ty(tcx, local.ty, local.source_info.span)?; } // impl trait is gone in MIR, so check the return type manually check_ty( tcx, tcx.fn_sig(def_id).output().skip_binder(), body.local_decls.iter().next().unwrap().source_info.span, - def_id, )?; for bb in body.basic_blocks() { - check_terminator(tcx, body, def_id, bb.terminator())?; + check_terminator(tcx, body, bb.terminator())?; for stmt in &bb.statements { check_statement(tcx, body, def_id, stmt)?; } @@ -96,7 +80,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - Ok(()) } -fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult { +fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { for arg in ty.walk() { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -108,15 +92,11 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> Mc match ty.kind() { ty::Ref(_, _, hir::Mutability::Mut) => { - if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) { return Err((span, "mutable references in const fn are unstable".into())); - } } ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::FnPtr(..) => { - if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) { return Err((span, "function pointers in const fn are unstable".into())); - } } ty::Dynamic(preds, _) => { for pred in preds.iter() { @@ -161,12 +141,12 @@ fn check_rvalue( Err((span, "cannot access thread local storage in const fn".into())) } Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => { - check_operand(tcx, operand, span, def_id, body) + check_operand(tcx, operand, span, body) } Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) - | Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, def_id, body), + | Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, body), Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { use rustc_middle::ty::cast::CastTy; let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); @@ -175,14 +155,14 @@ fn check_rvalue( (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { Err((span, "casting pointers to ints is unstable in const fn".into())) } - _ => check_operand(tcx, operand, span, def_id, body), + _ => check_operand(tcx, operand, span, body), } } Rvalue::Cast( CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _, - ) => check_operand(tcx, operand, span, def_id, body), + ) => check_operand(tcx, operand, span, body), Rvalue::Cast( CastKind::Pointer( PointerCast::UnsafeFnPointer @@ -204,7 +184,7 @@ fn check_rvalue( }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { - check_operand(tcx, op, span, def_id, body)?; + check_operand(tcx, op, span, body)?; // Casting/coercing things to slices is fine. Ok(()) } else { @@ -214,8 +194,8 @@ fn check_rvalue( } // binops are fine on integers Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { - check_operand(tcx, lhs, span, def_id, body)?; - check_operand(tcx, rhs, span, def_id, body)?; + check_operand(tcx, lhs, span, body)?; + check_operand(tcx, rhs, span, body)?; let ty = lhs.ty(body, tcx); if ty.is_integral() || ty.is_bool() || ty.is_char() { Ok(()) @@ -230,14 +210,14 @@ fn check_rvalue( Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { - check_operand(tcx, operand, span, def_id, body) + check_operand(tcx, operand, span, body) } else { Err((span, "only int and `bool` operations are stable in const fn".into())) } } Rvalue::Aggregate(_, operands) => { for operand in operands { - check_operand(tcx, operand, span, def_id, body)?; + check_operand(tcx, operand, span, body)?; } Ok(()) } @@ -253,15 +233,15 @@ fn check_statement( let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, *place, span, def_id, body)?; + check_place(tcx, *place, span, body)?; check_rvalue(tcx, body, def_id, rval, span) } - StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, def_id, body), + StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body), // just an assignment StatementKind::SetDiscriminant { place, .. } => { - check_place(tcx, **place, span, def_id, body) + check_place(tcx, **place, span, body) } StatementKind::LlvmInlineAsm { .. } => { @@ -282,11 +262,10 @@ fn check_operand( tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, - def_id: DefId, body: &Body<'tcx>, ) -> McfResult { match operand { - Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, def_id, body), + Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Constant(c) => match c.check_static_ptr(tcx) { Some(_) => Err((span, "cannot access `static` items in const fn".into())), None => Ok(()), @@ -298,7 +277,6 @@ fn check_place( tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, - def_id: DefId, body: &Body<'tcx>, ) -> McfResult { let mut cursor = place.projection.as_ref(); @@ -310,9 +288,7 @@ fn check_place( if let Some(def) = base_ty.ty_adt_def() { // No union field accesses in `const fn` if def.is_union() { - if !feature_allowed(tcx, def_id, sym::const_fn_union) { return Err((span, "accessing union fields is unstable".into())); - } } } } @@ -327,48 +303,9 @@ fn check_place( Ok(()) } -/// Returns `true` if the given feature gate is allowed within the function with the given `DefId`. -fn feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool { - // All features require that the corresponding gate be enabled, - // even if the function has `#[allow_internal_unstable(the_gate)]`. - if !tcx.features().enabled(feature_gate) { - return false; - } - - // If this crate is not using stability attributes, or this function is not claiming to be a - // stable `const fn`, that is all that is required. - if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) { - return true; - } - - // However, we cannot allow stable `const fn`s to use unstable features without an explicit - // opt-in via `allow_internal_unstable`. - super::check_consts::allow_internal_unstable(tcx, def_id, feature_gate) -} - -/// Returns `true` if the given library feature gate is allowed within the function with the given `DefId`. -pub fn lib_feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool { - // All features require that the corresponding gate be enabled, - // even if the function has `#[allow_internal_unstable(the_gate)]`. - if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature_gate) { - return false; - } - - // If this crate is not using stability attributes, or this function is not claiming to be a - // stable `const fn`, that is all that is required. - if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) { - return true; - } - - // However, we cannot allow stable `const fn`s to use unstable features without an explicit - // opt-in via `allow_internal_unstable`. - super::check_consts::allow_internal_unstable(tcx, def_id, feature_gate) -} - fn check_terminator( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, - def_id: DefId, terminator: &Terminator<'tcx>, ) -> McfResult { let span = terminator.source_info.span; @@ -380,14 +317,14 @@ fn check_terminator( | TerminatorKind::Resume | TerminatorKind::Unreachable => Ok(()), - TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, def_id, body), + TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), TerminatorKind::DropAndReplace { place, value, .. } => { - check_place(tcx, *place, span, def_id, body)?; - check_operand(tcx, value, span, def_id, body) + check_place(tcx, *place, span, body)?; + check_operand(tcx, value, span, body) } TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => { - check_operand(tcx, discr, span, def_id, body) + check_operand(tcx, discr, span, body) } TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), @@ -405,15 +342,7 @@ fn check_terminator( } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { - // Allow unstable const if we opt in by using #[allow_internal_unstable] - // on function or macro declaration. - if !crate::const_eval::is_min_const_fn(tcx, fn_def_id) - && !crate::const_eval::is_unstable_const_fn(tcx, fn_def_id) - .map(|feature| { - span.allows_unstable(feature) - || lib_feature_allowed(tcx, def_id, feature) - }) - .unwrap_or(false) + if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) { return Err(( span, @@ -432,7 +361,6 @@ fn check_terminator( // transmutes in const fn before we add more hacks to this. if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute - && !feature_allowed(tcx, def_id, sym::const_fn_transmute) { return Err(( span, @@ -440,10 +368,10 @@ fn check_terminator( )); } - check_operand(tcx, func, span, fn_def_id, body)?; + check_operand(tcx, func, span, body)?; for arg in args { - check_operand(tcx, arg, span, fn_def_id, body)?; + check_operand(tcx, arg, span, body)?; } Ok(()) } else { @@ -452,7 +380,7 @@ fn check_terminator( } TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => { - check_operand(tcx, cond, span, def_id, body) + check_operand(tcx, cond, span, body) } TerminatorKind::InlineAsm { .. } => {