Skip to content

Rollup of 10 pull requests #77241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 22 commits into from
Closed
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
be3d8e5
Add missing code examples on HashMap types
GuillaumeGomez Sep 19, 2020
90c7731
Enable const prop into operands at mir_opt_level=2
bugadani Sep 23, 2020
50d9663
Update cargo
ehuss Sep 24, 2020
74952b9
Fix FIXME in core::num test: Check sign of zero in min/max tests.
m-ou-se Sep 24, 2020
bfdb790
Link dynamic and static late_link_args before generic ones
mati865 Sep 25, 2020
939fd37
Rust vec bench import specific rand::RngCore
pickfire Sep 25, 2020
d023436
Refactor memchr to allow optimization
bugadani Sep 20, 2020
58d57f3
Fix documentation highlighting in ty::BorrowKind
jyn514 Sep 25, 2020
1b84389
Move `qualify_min_const_fn` out of rustc into clippy
oli-obk Sep 26, 2020
5d359db
Remove all unstable feature support in the `missing_const_for_fn` lint
oli-obk Sep 26, 2020
275bf62
pretty-print-reparse hack: Rename some variables for clarity
petrochenkov Sep 26, 2020
fe3e5aa
pretty-print-reparse hack: Remove an impossible case
petrochenkov Sep 26, 2020
88b2577
Rollup merge of #76917 - GuillaumeGomez:map-missing-code-examples, r=…
jonas-schievink Sep 26, 2020
d7d35af
Rollup merge of #76971 - bugadani:issue-75659, r=Amanieu
jonas-schievink Sep 26, 2020
7c6524e
Rollup merge of #77107 - bugadani:perf, r=oli-obk
jonas-schievink Sep 26, 2020
cd1af38
Rollup merge of #77129 - ehuss:update-cargo, r=ehuss
jonas-schievink Sep 26, 2020
de81508
Rollup merge of #77167 - fusion-engineering-forks:fix-fixme-min-max-s…
jonas-schievink Sep 26, 2020
ea72c11
Rollup merge of #77184 - pickfire:patch-4, r=kennytm
jonas-schievink Sep 26, 2020
2230b32
Rollup merge of #77208 - mati865:late-link-args-order, r=petrochenkov
jonas-schievink Sep 26, 2020
53b95de
Rollup merge of #77209 - jyn514:fix-docs, r=petrochenkov
jonas-schievink Sep 26, 2020
96c5848
Rollup merge of #77231 - oli-obk:clippy_const_fn, r=Manishearth
jonas-schievink Sep 26, 2020
b12225f
Rollup merge of #77235 - petrochenkov:reparse, r=Aaron1011
jonas-schievink Sep 26, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
@@ -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.
26 changes: 16 additions & 10 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
@@ -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
10 changes: 5 additions & 5 deletions compiler/rustc_mir/src/transform/const_prop.rs
Original file line number Diff line number Diff line change
@@ -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 { .. } => {}
}

1 change: 0 additions & 1 deletion compiler/rustc_mir/src/transform/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
42 changes: 22 additions & 20 deletions compiler/rustc_parse/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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,17 +325,17 @@ 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!(
"cached tokens found, but they're not \"probably equal\", \
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,
}
7 changes: 1 addition & 6 deletions compiler/rustc_target/src/spec/windows_gnu_base.rs
Original file line number Diff line number Diff line change
@@ -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);
2 changes: 1 addition & 1 deletion library/alloc/benches/vec.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rand::prelude::*;
use rand::RngCore;
use std::iter::{repeat, FromIterator};
use test::{black_box, Bencher};

2 changes: 2 additions & 0 deletions library/core/src/slice/cmp.rs
Original file line number Diff line number Diff line change
@@ -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()`
41 changes: 25 additions & 16 deletions library/core/src/slice/memchr.rs
Original file line number Diff line number Diff line change
@@ -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::<usize>();

/// 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<usize> {
// 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<usize> {
// 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::<usize>();
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<usize> {

// 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.
1 change: 1 addition & 0 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
@@ -1636,6 +1636,7 @@ impl<T> [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,
14 changes: 13 additions & 1 deletion library/core/tests/num/mod.rs
Original file line number Diff line number Diff line change
@@ -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);
107 changes: 106 additions & 1 deletion library/std/src/collections/hash/map.rs
Original file line number Diff line number Diff line change
@@ -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<K: Debug, V: Debug> 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<K, V> {
base: base::IntoIter<K, V>,
@@ -1167,6 +1197,16 @@ impl<K, V> IntoIter<K, V> {
/// 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<K: Debug, V> 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<K, V: Debug> 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<K, V> {
inner: IntoIter<K, V>,
@@ -1275,6 +1369,18 @@ pub struct IntoKeys<K, V> {
/// 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<K, V> {
inner: IntoIter<K, V>,
@@ -1285,7 +1391,6 @@ pub struct IntoValues<K, V> {
/// 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<K, V, S>,
62 changes: 62 additions & 0 deletions src/test/codegen/issue-75659.rs
Original file line number Diff line number Diff line change
@@ -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)
}
1 change: 1 addition & 0 deletions src/tools/clippy/clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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)]
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions src/tools/clippy/clippy_lints/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Original file line number Diff line number Diff line change
@@ -3,22 +3,14 @@ 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;

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,26 +62,25 @@ 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)?;
}
}
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,18 +361,17 @@ 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,
"can only call `transmute` from const items, not `const fn`".into(),
));
}

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 { .. } => {