Auto merge of #77247 - jonas-schievink:rollup-r6ehh8h, r=jonas-schievink
Rollup of 10 pull requests Successful merges: - #76917 (Add missing code examples on HashMap types) - #77107 (Enable const propagation into operands at mir_opt_level=2) - #77129 (Update cargo) - #77167 (Fix FIXME in core::num test: Check sign of zero in min/max tests.) - #77184 (Rust vec bench import specific rand::RngCore) - #77208 (Late link args order) - #77209 (Fix documentation highlighting in ty::BorrowKind) - #77231 (Move helper function for `missing_const_for_fn` out of rustc to clippy) - #77235 (pretty-print-reparse hack: Rename some variables for clarity) - #77243 (Test more attributes in test issue-75930-derive-cfg.rs) Failed merges: r? `@ghost`
This commit is contained in:
commit
1ec980d225
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 { .. } => {}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rand::prelude::*;
|
||||
use rand::RngCore;
|
||||
use std::iter::{repeat, FromIterator};
|
||||
use test::{black_box, Bencher};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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>,
|
||||
|
@ -1,29 +1,65 @@
|
||||
// check-pass
|
||||
// compile-flags: -Z span-debug
|
||||
// compile-flags: -Z span-debug --error-format human
|
||||
// aux-build:test-macros.rs
|
||||
|
||||
// Regression test for issue #75930
|
||||
// Tests that we cfg-strip all targets before invoking
|
||||
// a derive macro
|
||||
// We need '--error-format human' to stop compiletest from
|
||||
// trying to interpret proc-macro output as JSON messages
|
||||
// (a pretty-printed struct may cause a line to start with '{' )
|
||||
// FIXME: We currently lose spans here (see issue #43081)
|
||||
|
||||
#[macro_use]
|
||||
extern crate test_macros;
|
||||
|
||||
#[print_helper(a)]
|
||||
#[cfg_attr(not(FALSE), allow(dead_code))]
|
||||
#[print_attr]
|
||||
#[derive(Print)]
|
||||
#[print_helper(b)]
|
||||
struct Foo<#[cfg(FALSE)] A, B> {
|
||||
#[cfg(FALSE)] first: String,
|
||||
second: bool,
|
||||
#[cfg_attr(FALSE, deny(warnings))] second: bool,
|
||||
third: [u8; {
|
||||
#[cfg(FALSE)] struct Bar;
|
||||
#[cfg(not(FALSE))] struct Inner;
|
||||
#[cfg(FALSE)] let a = 25;
|
||||
match true {
|
||||
#[cfg(FALSE)] true => {},
|
||||
false => {},
|
||||
#[cfg_attr(not(FALSE), allow(warnings))] false => {},
|
||||
_ => {}
|
||||
};
|
||||
|
||||
#[print_helper(should_be_removed)]
|
||||
fn removed_fn() {
|
||||
#![cfg(FALSE)]
|
||||
}
|
||||
|
||||
#[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() {
|
||||
#![cfg(not(FALSE))]
|
||||
let my_val = true;
|
||||
}
|
||||
|
||||
enum TupleEnum {
|
||||
Foo(
|
||||
#[cfg(FALSE)] u8,
|
||||
#[cfg(FALSE)] bool,
|
||||
#[cfg(not(FALSE))] i32,
|
||||
#[cfg(FALSE)] String, u8
|
||||
)
|
||||
}
|
||||
|
||||
struct TupleStruct(
|
||||
#[cfg(FALSE)] String,
|
||||
#[cfg(not(FALSE))] i32,
|
||||
#[cfg(FALSE)] bool,
|
||||
u8
|
||||
);
|
||||
|
||||
0
|
||||
}],
|
||||
#[print_helper(d)]
|
||||
fourth: B
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
Subproject commit 8777a6b1e8834899f51b7e09cc9b8d85b2417110
|
||||
Subproject commit 05c611ae3c4255b7a2bcf4fcfa65b20286a07839
|
@ -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)]
|
||||
|
@ -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;
|
||||
|
@ -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::*;
|
||||
|
@ -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 { .. } => {
|
Loading…
x
Reference in New Issue
Block a user