Auto merge of #72747 - Dylan-DPC:rollup-vvydkgl, r=Dylan-DPC
Rollup of 9 pull requests Successful merges: - #72310 (Add Peekable::next_if) - #72383 (Suggest using std::mem::drop function instead of explicit destructor call) - #72398 (SocketAddr and friends now correctly pad its content) - #72465 (Warn about unused captured variables) - #72568 (Implement total_cmp for f32, f64) - #72572 (Add some regression tests) - #72591 (librustc_middle: Rename upvar_list to closure_captures) - #72701 (Fix grammar in liballoc raw_vec) - #72731 (Add missing empty line in E0619 explanation) Failed merges: r? @ghost
This commit is contained in:
commit
4bd32c9804
@ -315,7 +315,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
|
||||
/// `used_capacity + needed_extra_capacity` elements. If it doesn't already,
|
||||
/// will reallocate the minimum possible amount of memory necessary.
|
||||
/// Generally this will be exactly the amount of memory necessary,
|
||||
/// but in principle the allocator is free to give back more than
|
||||
/// but in principle the allocator is free to give back more than what
|
||||
/// we asked for.
|
||||
///
|
||||
/// If `used_capacity` exceeds `self.capacity()`, this may fail to actually allocate
|
||||
|
@ -1619,6 +1619,69 @@ impl<I: Iterator> Peekable<I> {
|
||||
let iter = &mut self.iter;
|
||||
self.peeked.get_or_insert_with(|| iter.next()).as_ref()
|
||||
}
|
||||
|
||||
/// Consume the next value of this iterator if a condition is true.
|
||||
///
|
||||
/// If `func` returns `true` for the next value of this iterator, consume and return it.
|
||||
/// Otherwise, return `None`.
|
||||
///
|
||||
/// # Examples
|
||||
/// Consume a number if it's equal to 0.
|
||||
/// ```
|
||||
/// #![feature(peekable_next_if)]
|
||||
/// let mut iter = (0..5).peekable();
|
||||
/// // The first item of the iterator is 0; consume it.
|
||||
/// assert_eq!(iter.next_if(|&x| x == 0), Some(0));
|
||||
/// // The next item returned is now 1, so `consume` will return `false`.
|
||||
/// assert_eq!(iter.next_if(|&x| x == 0), None);
|
||||
/// // `next_if` saves the value of the next item if it was not equal to `expected`.
|
||||
/// assert_eq!(iter.next(), Some(1));
|
||||
/// ```
|
||||
///
|
||||
/// Consume any number less than 10.
|
||||
/// ```
|
||||
/// #![feature(peekable_next_if)]
|
||||
/// let mut iter = (1..20).peekable();
|
||||
/// // Consume all numbers less than 10
|
||||
/// while iter.next_if(|&x| x < 10).is_some() {}
|
||||
/// // The next value returned will be 10
|
||||
/// assert_eq!(iter.next(), Some(10));
|
||||
/// ```
|
||||
#[unstable(feature = "peekable_next_if", issue = "72480")]
|
||||
pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
|
||||
match self.next() {
|
||||
Some(matched) if func(&matched) => Some(matched),
|
||||
other => {
|
||||
// Since we called `self.next()`, we consumed `self.peeked`.
|
||||
assert!(self.peeked.is_none());
|
||||
self.peeked = Some(other);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume the next item if it is equal to `expected`.
|
||||
///
|
||||
/// # Example
|
||||
/// Consume a number if it's equal to 0.
|
||||
/// ```
|
||||
/// #![feature(peekable_next_if)]
|
||||
/// let mut iter = (0..5).peekable();
|
||||
/// // The first item of the iterator is 0; consume it.
|
||||
/// assert_eq!(iter.next_if_eq(&0), Some(0));
|
||||
/// // The next item returned is now 1, so `consume` will return `false`.
|
||||
/// assert_eq!(iter.next_if_eq(&0), None);
|
||||
/// // `next_if_eq` saves the value of the next item if it was not equal to `expected`.
|
||||
/// assert_eq!(iter.next(), Some(1));
|
||||
/// ```
|
||||
#[unstable(feature = "peekable_next_if", issue = "72480")]
|
||||
pub fn next_if_eq<R>(&mut self, expected: &R) -> Option<I::Item>
|
||||
where
|
||||
R: ?Sized,
|
||||
I::Item: PartialEq<R>,
|
||||
{
|
||||
self.next_if(|next| next == expected)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that rejects elements while `predicate` returns `true`.
|
||||
|
@ -810,4 +810,78 @@ impl f32 {
|
||||
pub fn from_ne_bytes(bytes: [u8; 4]) -> Self {
|
||||
Self::from_bits(u32::from_ne_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Returns an ordering between self and other values.
|
||||
/// Unlike the standard partial comparison between floating point numbers,
|
||||
/// this comparison always produces an ordering in accordance to
|
||||
/// the totalOrder predicate as defined in IEEE 754 (2008 revision)
|
||||
/// floating point standard. The values are ordered in following order:
|
||||
/// - Negative quiet NaN
|
||||
/// - Negative signaling NaN
|
||||
/// - Negative infinity
|
||||
/// - Negative numbers
|
||||
/// - Negative subnormal numbers
|
||||
/// - Negative zero
|
||||
/// - Positive zero
|
||||
/// - Positive subnormal numbers
|
||||
/// - Positive numbers
|
||||
/// - Positive infinity
|
||||
/// - Positive signaling NaN
|
||||
/// - Positive quiet NaN
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #![feature(total_cmp)]
|
||||
/// struct GoodBoy {
|
||||
/// name: String,
|
||||
/// weight: f32,
|
||||
/// }
|
||||
///
|
||||
/// let mut bois = vec![
|
||||
/// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 },
|
||||
/// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 },
|
||||
/// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 },
|
||||
/// GoodBoy { name: "Chonk".to_owned(), weight: f32::INFINITY },
|
||||
/// GoodBoy { name: "Abs. Unit".to_owned(), weight: f32::NAN },
|
||||
/// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 },
|
||||
/// ];
|
||||
///
|
||||
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
|
||||
/// # assert!(bois.into_iter().map(|b| b.weight)
|
||||
/// # .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter())
|
||||
/// # .all(|(a, b)| a.to_bits() == b.to_bits()))
|
||||
/// ```
|
||||
#[unstable(feature = "total_cmp", issue = "72599")]
|
||||
#[inline]
|
||||
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
|
||||
let mut left = self.to_bits() as i32;
|
||||
let mut right = other.to_bits() as i32;
|
||||
|
||||
// In case of negatives, flip all the bits except the sign
|
||||
// to achieve a similar layout as two's complement integers
|
||||
//
|
||||
// Why does this work? IEEE 754 floats consist of three fields:
|
||||
// Sign bit, exponent and mantissa. The set of exponent and mantissa
|
||||
// fields as a whole have the property that their bitwise order is
|
||||
// equal to the numeric magnitude where the magnitude is defined.
|
||||
// The magnitude is not normally defined on NaN values, but
|
||||
// IEEE 754 totalOrder defines the NaN values also to follow the
|
||||
// bitwise order. This leads to order explained in the doc comment.
|
||||
// However, the representation of magnitude is the same for negative
|
||||
// and positive numbers – only the sign bit is different.
|
||||
// To easily compare the floats as signed integers, we need to
|
||||
// flip the exponent and mantissa bits in case of negative numbers.
|
||||
// We effectively convert the numbers to "two's complement" form.
|
||||
//
|
||||
// To do the flipping, we construct a mask and XOR against it.
|
||||
// We branchlessly calculate an "all-ones except for the sign bit"
|
||||
// mask from negative-signed values: right shifting sign-extends
|
||||
// the integer, so we "fill" the mask with sign bits, and then
|
||||
// convert to unsigned to push one more zero bit.
|
||||
// On positive values, the mask is all zeros, so it's a no-op.
|
||||
left ^= (((left >> 31) as u32) >> 1) as i32;
|
||||
right ^= (((right >> 31) as u32) >> 1) as i32;
|
||||
|
||||
left.cmp(&right)
|
||||
}
|
||||
}
|
||||
|
@ -824,4 +824,78 @@ impl f64 {
|
||||
pub fn from_ne_bytes(bytes: [u8; 8]) -> Self {
|
||||
Self::from_bits(u64::from_ne_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Returns an ordering between self and other values.
|
||||
/// Unlike the standard partial comparison between floating point numbers,
|
||||
/// this comparison always produces an ordering in accordance to
|
||||
/// the totalOrder predicate as defined in IEEE 754 (2008 revision)
|
||||
/// floating point standard. The values are ordered in following order:
|
||||
/// - Negative quiet NaN
|
||||
/// - Negative signaling NaN
|
||||
/// - Negative infinity
|
||||
/// - Negative numbers
|
||||
/// - Negative subnormal numbers
|
||||
/// - Negative zero
|
||||
/// - Positive zero
|
||||
/// - Positive subnormal numbers
|
||||
/// - Positive numbers
|
||||
/// - Positive infinity
|
||||
/// - Positive signaling NaN
|
||||
/// - Positive quiet NaN
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #![feature(total_cmp)]
|
||||
/// struct GoodBoy {
|
||||
/// name: String,
|
||||
/// weight: f64,
|
||||
/// }
|
||||
///
|
||||
/// let mut bois = vec![
|
||||
/// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 },
|
||||
/// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 },
|
||||
/// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 },
|
||||
/// GoodBoy { name: "Chonk".to_owned(), weight: f64::INFINITY },
|
||||
/// GoodBoy { name: "Abs. Unit".to_owned(), weight: f64::NAN },
|
||||
/// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 },
|
||||
/// ];
|
||||
///
|
||||
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
|
||||
/// # assert!(bois.into_iter().map(|b| b.weight)
|
||||
/// # .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter())
|
||||
/// # .all(|(a, b)| a.to_bits() == b.to_bits()))
|
||||
/// ```
|
||||
#[unstable(feature = "total_cmp", issue = "72599")]
|
||||
#[inline]
|
||||
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
|
||||
let mut left = self.to_bits() as i64;
|
||||
let mut right = other.to_bits() as i64;
|
||||
|
||||
// In case of negatives, flip all the bits except the sign
|
||||
// to achieve a similar layout as two's complement integers
|
||||
//
|
||||
// Why does this work? IEEE 754 floats consist of three fields:
|
||||
// Sign bit, exponent and mantissa. The set of exponent and mantissa
|
||||
// fields as a whole have the property that their bitwise order is
|
||||
// equal to the numeric magnitude where the magnitude is defined.
|
||||
// The magnitude is not normally defined on NaN values, but
|
||||
// IEEE 754 totalOrder defines the NaN values also to follow the
|
||||
// bitwise order. This leads to order explained in the doc comment.
|
||||
// However, the representation of magnitude is the same for negative
|
||||
// and positive numbers – only the sign bit is different.
|
||||
// To easily compare the floats as signed integers, we need to
|
||||
// flip the exponent and mantissa bits in case of negative numbers.
|
||||
// We effectively convert the numbers to "two's complement" form.
|
||||
//
|
||||
// To do the flipping, we construct a mask and XOR against it.
|
||||
// We branchlessly calculate an "all-ones except for the sign bit"
|
||||
// mask from negative-signed values: right shifting sign-extends
|
||||
// the integer, so we "fill" the mask with sign bits, and then
|
||||
// convert to unsigned to push one more zero bit.
|
||||
// On positive values, the mask is all zeros, so it's a no-op.
|
||||
left ^= (((left >> 63) as u64) >> 1) as i64;
|
||||
right ^= (((right >> 63) as u64) >> 1) as i64;
|
||||
|
||||
left.cmp(&right)
|
||||
}
|
||||
}
|
||||
|
@ -813,6 +813,30 @@ fn test_iterator_peekable_rfold() {
|
||||
assert_eq!(i, xs.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_peekable_next_if_eq() {
|
||||
// first, try on references
|
||||
let xs = vec!["Heart", "of", "Gold"];
|
||||
let mut it = xs.into_iter().peekable();
|
||||
// try before `peek()`
|
||||
assert_eq!(it.next_if_eq(&"trillian"), None);
|
||||
assert_eq!(it.next_if_eq(&"Heart"), Some("Heart"));
|
||||
// try after peek()
|
||||
assert_eq!(it.peek(), Some(&"of"));
|
||||
assert_eq!(it.next_if_eq(&"of"), Some("of"));
|
||||
assert_eq!(it.next_if_eq(&"zaphod"), None);
|
||||
// make sure `next()` still behaves
|
||||
assert_eq!(it.next(), Some("Gold"));
|
||||
|
||||
// make sure comparison works for owned values
|
||||
let xs = vec![String::from("Ludicrous"), "speed".into()];
|
||||
let mut it = xs.into_iter().peekable();
|
||||
// make sure basic functionality works
|
||||
assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into()));
|
||||
assert_eq!(it.next_if_eq("speed"), Some("speed".into()));
|
||||
assert_eq!(it.next_if_eq(""), None);
|
||||
}
|
||||
|
||||
/// This is an iterator that follows the Iterator contract,
|
||||
/// but it is not fused. After having returned None once, it will start
|
||||
/// producing elements if .next() is called again.
|
||||
|
@ -43,6 +43,7 @@
|
||||
#![feature(leading_trailing_ones)]
|
||||
#![feature(const_forget)]
|
||||
#![feature(option_unwrap_none)]
|
||||
#![feature(peekable_next_if)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
The type-checker needed to know the type of an expression, but that type had not
|
||||
yet been inferred.
|
||||
|
||||
|
@ -419,7 +419,7 @@ pub struct TypeckTables<'tcx> {
|
||||
/// The upvarID contains the HIR node ID and it also contains the full path
|
||||
/// leading to the member of the struct or tuple that is used instead of the
|
||||
/// entire variable.
|
||||
pub upvar_list: ty::UpvarListMap,
|
||||
pub closure_captures: ty::UpvarListMap,
|
||||
|
||||
/// Stores the type, expression, span and optional scope span of all types
|
||||
/// that are live across the yield of this generator (if a generator).
|
||||
@ -447,7 +447,7 @@ impl<'tcx> TypeckTables<'tcx> {
|
||||
used_trait_imports: Lrc::new(Default::default()),
|
||||
tainted_by_errors: None,
|
||||
concrete_opaque_types: Default::default(),
|
||||
upvar_list: Default::default(),
|
||||
closure_captures: Default::default(),
|
||||
generator_interior_types: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -688,7 +688,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckTables<'tcx> {
|
||||
ref used_trait_imports,
|
||||
tainted_by_errors,
|
||||
ref concrete_opaque_types,
|
||||
ref upvar_list,
|
||||
ref closure_captures,
|
||||
ref generator_interior_types,
|
||||
} = *self;
|
||||
|
||||
@ -721,7 +721,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckTables<'tcx> {
|
||||
used_trait_imports.hash_stable(hcx, hasher);
|
||||
tainted_by_errors.hash_stable(hcx, hasher);
|
||||
concrete_opaque_types.hash_stable(hcx, hasher);
|
||||
upvar_list.hash_stable(hcx, hasher);
|
||||
closure_captures.hash_stable(hcx, hasher);
|
||||
generator_interior_types.hash_stable(hcx, hasher);
|
||||
})
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ fn do_mir_borrowck<'a, 'tcx>(
|
||||
infcx.set_tainted_by_errors();
|
||||
}
|
||||
let upvars: Vec<_> = tables
|
||||
.upvar_list
|
||||
.closure_captures
|
||||
.get(&def_id.to_def_id())
|
||||
.into_iter()
|
||||
.flat_map(|v| v.values())
|
||||
|
@ -227,7 +227,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||
let mut name = None;
|
||||
if let Some(def_id) = def_id.as_local() {
|
||||
let tables = self.ecx.tcx.typeck_tables_of(def_id);
|
||||
if let Some(upvars) = tables.upvar_list.get(&def_id.to_def_id()) {
|
||||
if let Some(upvars) = tables.closure_captures.get(&def_id.to_def_id()) {
|
||||
// Sometimes the index is beyond the number of upvars (seen
|
||||
// for a generator).
|
||||
if let Some((&var_hir_id, _)) = upvars.get_index(field) {
|
||||
|
@ -790,11 +790,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let hir_tables = self.hir.tables();
|
||||
|
||||
// In analyze_closure() in upvar.rs we gathered a list of upvars used by a
|
||||
// closure and we stored in a map called upvar_list in TypeckTables indexed
|
||||
// indexed closure and we stored in a map called closure_captures in TypeckTables
|
||||
// with the closure's DefId. Here, we run through that vec of UpvarIds for
|
||||
// the given closure and use the necessary information to create upvar
|
||||
// debuginfo and to fill `self.upvar_mutbls`.
|
||||
if let Some(upvars) = hir_tables.upvar_list.get(&fn_def_id) {
|
||||
if let Some(upvars) = hir_tables.closure_captures.get(&fn_def_id) {
|
||||
let closure_env_arg = Local::new(1);
|
||||
let mut closure_env_projs = vec![];
|
||||
let mut closure_ty = self.local_decls[closure_env_arg].ty;
|
||||
|
@ -884,7 +884,7 @@ fn convert_var<'tcx>(
|
||||
) -> ExprKind<'tcx> {
|
||||
let upvar_index = cx
|
||||
.tables()
|
||||
.upvar_list
|
||||
.closure_captures
|
||||
.get(&cx.body_owner)
|
||||
.and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i));
|
||||
|
||||
|
@ -76,22 +76,10 @@
|
||||
//! is not just used to generate a new value. For example, `x += 1` is
|
||||
//! a read but not a use. This is used to generate better warnings.
|
||||
//!
|
||||
//! ## Special Variables
|
||||
//! ## Special nodes and variables
|
||||
//!
|
||||
//! We generate various special variables for various, well, special purposes.
|
||||
//! These are described in the `specials` struct:
|
||||
//!
|
||||
//! - `exit_ln`: a live node that is generated to represent every 'exit' from
|
||||
//! the function, whether it be by explicit return, panic, or other means.
|
||||
//!
|
||||
//! - `fallthrough_ln`: a live node that represents a fallthrough
|
||||
//!
|
||||
//! - `clean_exit_var`: a synthetic variable that is only 'read' from the
|
||||
//! fallthrough node. It is only live if the function could converge
|
||||
//! via means other than an explicit `return` expression. That is, it is
|
||||
//! only dead if the end of the function's block can never be reached.
|
||||
//! It is the responsibility of typeck to ensure that there are no
|
||||
//! `return` expressions in a function declared as diverging.
|
||||
//! We generate various special nodes for various, well, special purposes.
|
||||
//! These are described in the `Specials` struct.
|
||||
|
||||
use self::LiveNodeKind::*;
|
||||
use self::VarKind::*;
|
||||
@ -140,6 +128,7 @@ enum LiveNodeKind {
|
||||
UpvarNode(Span),
|
||||
ExprNode(Span),
|
||||
VarDefNode(Span),
|
||||
ClosureNode,
|
||||
ExitNode,
|
||||
}
|
||||
|
||||
@ -149,6 +138,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
|
||||
UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_string(s)),
|
||||
ExprNode(s) => format!("Expr node [{}]", sm.span_to_string(s)),
|
||||
VarDefNode(s) => format!("Var def node [{}]", sm.span_to_string(s)),
|
||||
ClosureNode => "Closure node".to_owned(),
|
||||
ExitNode => "Exit node".to_owned(),
|
||||
}
|
||||
}
|
||||
@ -253,7 +243,7 @@ struct LocalInfo {
|
||||
enum VarKind {
|
||||
Param(HirId, Symbol),
|
||||
Local(LocalInfo),
|
||||
CleanExit,
|
||||
Upvar(HirId, Symbol),
|
||||
}
|
||||
|
||||
struct IrMaps<'tcx> {
|
||||
@ -306,10 +296,9 @@ impl IrMaps<'tcx> {
|
||||
self.num_vars += 1;
|
||||
|
||||
match vk {
|
||||
Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) => {
|
||||
Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) | Upvar(node_id, _) => {
|
||||
self.variable_map.insert(node_id, v);
|
||||
}
|
||||
CleanExit => {}
|
||||
}
|
||||
|
||||
debug!("{:?} is {:?}", v, vk);
|
||||
@ -328,15 +317,14 @@ impl IrMaps<'tcx> {
|
||||
|
||||
fn variable_name(&self, var: Variable) -> String {
|
||||
match self.var_kinds[var.get()] {
|
||||
Local(LocalInfo { name, .. }) | Param(_, name) => name.to_string(),
|
||||
CleanExit => "<clean-exit>".to_owned(),
|
||||
Local(LocalInfo { name, .. }) | Param(_, name) | Upvar(_, name) => name.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn variable_is_shorthand(&self, var: Variable) -> bool {
|
||||
match self.var_kinds[var.get()] {
|
||||
Local(LocalInfo { is_shorthand, .. }) => is_shorthand,
|
||||
Param(..) | CleanExit => false,
|
||||
Param(..) | Upvar(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,7 +345,7 @@ fn visit_fn<'tcx>(
|
||||
sp: Span,
|
||||
id: hir::HirId,
|
||||
) {
|
||||
debug!("visit_fn");
|
||||
debug!("visit_fn {:?}", id);
|
||||
|
||||
// swap in a new set of IR maps for this function body:
|
||||
let def_id = ir.tcx.hir().local_def_id(id);
|
||||
@ -377,6 +365,14 @@ fn visit_fn<'tcx>(
|
||||
|
||||
let body = ir.tcx.hir().body(body_id);
|
||||
|
||||
if let Some(upvars) = ir.tcx.upvars_mentioned(def_id) {
|
||||
for (&var_hir_id, _upvar) in upvars {
|
||||
debug!("adding upvar {:?}", var_hir_id);
|
||||
let var_name = ir.tcx.hir().name(var_hir_id);
|
||||
fn_maps.add_variable(Upvar(var_hir_id, var_name));
|
||||
}
|
||||
}
|
||||
|
||||
for param in body.params {
|
||||
let is_shorthand = match param.pat.kind {
|
||||
rustc_hir::PatKind::Struct(..) => true,
|
||||
@ -399,10 +395,12 @@ fn visit_fn<'tcx>(
|
||||
|
||||
// compute liveness
|
||||
let mut lsets = Liveness::new(&mut fn_maps, def_id);
|
||||
let entry_ln = lsets.compute(&body.value);
|
||||
let entry_ln = lsets.compute(fk, &body, sp, id);
|
||||
lsets.log_liveness(entry_ln, id);
|
||||
|
||||
// check for various error conditions
|
||||
lsets.visit_body(body);
|
||||
lsets.warn_about_unused_upvars(entry_ln);
|
||||
lsets.warn_about_unused_args(body, entry_ln);
|
||||
}
|
||||
|
||||
@ -462,11 +460,8 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
// live nodes required for uses or definitions of variables:
|
||||
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
|
||||
debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res);
|
||||
if let Res::Local(var_hir_id) = path.res {
|
||||
let upvars = ir.tcx.upvars_mentioned(ir.body_owner);
|
||||
if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) {
|
||||
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
|
||||
}
|
||||
if let Res::Local(_var_hir_id) = path.res {
|
||||
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
|
||||
}
|
||||
intravisit::walk_expr(ir, expr);
|
||||
}
|
||||
@ -482,16 +477,9 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
let mut call_caps = Vec::new();
|
||||
let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id);
|
||||
if let Some(upvars) = ir.tcx.upvars_mentioned(closure_def_id) {
|
||||
let parent_upvars = ir.tcx.upvars_mentioned(ir.body_owner);
|
||||
call_caps.extend(upvars.iter().filter_map(|(&var_id, upvar)| {
|
||||
let has_parent =
|
||||
parent_upvars.map_or(false, |upvars| upvars.contains_key(&var_id));
|
||||
if !has_parent {
|
||||
let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
|
||||
Some(CaptureInfo { ln: upvar_ln, var_hid: var_id })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
call_caps.extend(upvars.iter().map(|(&var_id, upvar)| {
|
||||
let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
|
||||
CaptureInfo { ln: upvar_ln, var_hid: var_id }
|
||||
}));
|
||||
}
|
||||
ir.set_captures(expr.hir_id, call_caps);
|
||||
@ -647,9 +635,13 @@ impl RWUTable {
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Specials {
|
||||
/// A live node representing a point of execution before closure entry &
|
||||
/// after closure exit. Used to calculate liveness of captured variables
|
||||
/// through calls to the same closure. Used for Fn & FnMut closures only.
|
||||
closure_ln: LiveNode,
|
||||
/// A live node representing every 'exit' from the function, whether it be
|
||||
/// by explicit return, panic, or other means.
|
||||
exit_ln: LiveNode,
|
||||
fallthrough_ln: LiveNode,
|
||||
clean_exit_var: Variable,
|
||||
}
|
||||
|
||||
const ACC_READ: u32 = 1;
|
||||
@ -673,14 +665,9 @@ struct Liveness<'a, 'tcx> {
|
||||
|
||||
impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
fn new(ir: &'a mut IrMaps<'tcx>, def_id: LocalDefId) -> Liveness<'a, 'tcx> {
|
||||
// Special nodes and variables:
|
||||
// - exit_ln represents the end of the fn, either by return or panic
|
||||
// - implicit_ret_var is a pseudo-variable that represents
|
||||
// an implicit return
|
||||
let specials = Specials {
|
||||
closure_ln: ir.add_live_node(ClosureNode),
|
||||
exit_ln: ir.add_live_node(ExitNode),
|
||||
fallthrough_ln: ir.add_live_node(ExitNode),
|
||||
clean_exit_var: ir.add_variable(CleanExit),
|
||||
};
|
||||
|
||||
let tables = ir.tcx.typeck_tables_of(def_id);
|
||||
@ -777,12 +764,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
|
||||
fn write_vars<F>(&self, wr: &mut dyn Write, ln: LiveNode, mut test: F) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(usize) -> LiveNode,
|
||||
F: FnMut(usize) -> bool,
|
||||
{
|
||||
let node_base_idx = self.idx(ln, Variable(0));
|
||||
for var_idx in 0..self.ir.num_vars {
|
||||
let idx = node_base_idx + var_idx;
|
||||
if test(idx).is_valid() {
|
||||
if test(idx) {
|
||||
write!(wr, " {:?}", Variable(var_idx as u32))?;
|
||||
}
|
||||
}
|
||||
@ -795,14 +782,31 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
{
|
||||
let wr = &mut wr as &mut dyn Write;
|
||||
write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
|
||||
self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx));
|
||||
self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_valid());
|
||||
write!(wr, " writes");
|
||||
self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx));
|
||||
self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_valid());
|
||||
write!(wr, " uses");
|
||||
self.write_vars(wr, ln, |idx| self.rwu_table.get_used(idx));
|
||||
|
||||
write!(wr, " precedes {:?}]", self.successors[ln.get()]);
|
||||
}
|
||||
String::from_utf8(wr).unwrap()
|
||||
}
|
||||
|
||||
fn log_liveness(&self, entry_ln: LiveNode, hir_id: hir::HirId) {
|
||||
// hack to skip the loop unless debug! is enabled:
|
||||
debug!(
|
||||
"^^ liveness computation results for body {} (entry={:?})",
|
||||
{
|
||||
for ln_idx in 0..self.ir.num_live_nodes {
|
||||
debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
|
||||
}
|
||||
hir_id
|
||||
},
|
||||
entry_ln
|
||||
);
|
||||
}
|
||||
|
||||
fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
|
||||
self.successors[ln.get()] = succ_ln;
|
||||
|
||||
@ -903,30 +907,87 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
self.rwu_table.assign_unpacked(idx, rwu);
|
||||
}
|
||||
|
||||
fn compute(&mut self, body: &hir::Expr<'_>) -> LiveNode {
|
||||
debug!("compute: using id for body, {:?}", body);
|
||||
fn compute(
|
||||
&mut self,
|
||||
fk: FnKind<'_>,
|
||||
body: &hir::Body<'_>,
|
||||
span: Span,
|
||||
id: hir::HirId,
|
||||
) -> LiveNode {
|
||||
debug!("compute: using id for body, {:?}", body.value);
|
||||
|
||||
// the fallthrough exit is only for those cases where we do not
|
||||
// explicitly return:
|
||||
let s = self.s;
|
||||
self.init_from_succ(s.fallthrough_ln, s.exit_ln);
|
||||
self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
|
||||
// # Liveness of captured variables
|
||||
//
|
||||
// When computing the liveness for captured variables we take into
|
||||
// account how variable is captured (ByRef vs ByValue) and what is the
|
||||
// closure kind (Generator / FnOnce vs Fn / FnMut).
|
||||
//
|
||||
// Variables captured by reference are assumed to be used on the exit
|
||||
// from the closure.
|
||||
//
|
||||
// In FnOnce closures, variables captured by value are known to be dead
|
||||
// on exit since it is impossible to call the closure again.
|
||||
//
|
||||
// In Fn / FnMut closures, variables captured by value are live on exit
|
||||
// if they are live on the entry to the closure, since only the closure
|
||||
// itself can access them on subsequent calls.
|
||||
|
||||
let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln);
|
||||
|
||||
// hack to skip the loop unless debug! is enabled:
|
||||
debug!(
|
||||
"^^ liveness computation results for body {} (entry={:?})",
|
||||
{
|
||||
for ln_idx in 0..self.ir.num_live_nodes {
|
||||
debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
|
||||
if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
|
||||
// Mark upvars captured by reference as used after closure exits.
|
||||
for (&var_hir_id, upvar) in upvars.iter().rev() {
|
||||
let upvar_id = ty::UpvarId {
|
||||
var_path: ty::UpvarPath { hir_id: var_hir_id },
|
||||
closure_expr_id: self.ir.body_owner.expect_local(),
|
||||
};
|
||||
match self.tables.upvar_capture(upvar_id) {
|
||||
ty::UpvarCapture::ByRef(_) => {
|
||||
let var = self.variable(var_hir_id, upvar.span);
|
||||
self.acc(self.s.exit_ln, var, ACC_READ | ACC_USE);
|
||||
}
|
||||
ty::UpvarCapture::ByValue => {}
|
||||
}
|
||||
body.hir_id
|
||||
},
|
||||
entry_ln
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
entry_ln
|
||||
let succ = self.propagate_through_expr(&body.value, self.s.exit_ln);
|
||||
|
||||
match fk {
|
||||
FnKind::Method(..) | FnKind::ItemFn(..) => return succ,
|
||||
FnKind::Closure(..) => {}
|
||||
}
|
||||
|
||||
let ty = self.tables.node_type(id);
|
||||
match ty.kind {
|
||||
ty::Closure(_def_id, substs) => match substs.as_closure().kind() {
|
||||
ty::ClosureKind::Fn => {}
|
||||
ty::ClosureKind::FnMut => {}
|
||||
ty::ClosureKind::FnOnce => return succ,
|
||||
},
|
||||
ty::Generator(..) => return succ,
|
||||
_ => {
|
||||
span_bug!(span, "type of closure expr {:?} is not a closure {:?}", id, ty,);
|
||||
}
|
||||
};
|
||||
|
||||
// Propagate through calls to the closure.
|
||||
let mut first_merge = true;
|
||||
loop {
|
||||
self.init_from_succ(self.s.closure_ln, succ);
|
||||
for param in body.params {
|
||||
param.pat.each_binding(|_bm, hir_id, _x, ident| {
|
||||
let var = self.variable(hir_id, ident.span);
|
||||
self.define(self.s.closure_ln, var);
|
||||
})
|
||||
}
|
||||
|
||||
if !self.merge_from_succ(self.s.exit_ln, self.s.closure_ln, first_merge) {
|
||||
break;
|
||||
}
|
||||
first_merge = false;
|
||||
assert_eq!(succ, self.propagate_through_expr(&body.value, self.s.exit_ln));
|
||||
}
|
||||
|
||||
succ
|
||||
}
|
||||
|
||||
fn propagate_through_block(&mut self, blk: &hir::Block<'_>, succ: LiveNode) -> LiveNode {
|
||||
@ -1363,14 +1424,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
acc: u32,
|
||||
) -> LiveNode {
|
||||
match path.res {
|
||||
Res::Local(hid) => {
|
||||
let upvars = self.ir.tcx.upvars_mentioned(self.ir.body_owner);
|
||||
if !upvars.map_or(false, |upvars| upvars.contains_key(&hid)) {
|
||||
self.access_var(hir_id, hid, succ, acc, path.span)
|
||||
} else {
|
||||
succ
|
||||
}
|
||||
}
|
||||
Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span),
|
||||
_ => succ,
|
||||
}
|
||||
}
|
||||
@ -1529,16 +1583,13 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
match expr.kind {
|
||||
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
|
||||
if let Res::Local(var_hid) = path.res {
|
||||
let upvars = self.ir.tcx.upvars_mentioned(self.ir.body_owner);
|
||||
if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hid)) {
|
||||
// Assignment to an immutable variable or argument: only legal
|
||||
// if there is no later assignment. If this local is actually
|
||||
// mutable, then check for a reassignment to flag the mutability
|
||||
// as being used.
|
||||
let ln = self.live_node(expr.hir_id, expr.span);
|
||||
let var = self.variable(var_hid, expr.span);
|
||||
self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
|
||||
}
|
||||
// Assignment to an immutable variable or argument: only legal
|
||||
// if there is no later assignment. If this local is actually
|
||||
// mutable, then check for a reassignment to flag the mutability
|
||||
// as being used.
|
||||
let ln = self.live_node(expr.hir_id, expr.span);
|
||||
let var = self.variable(var_hid, expr.span);
|
||||
self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@ -1554,11 +1605,60 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
if name.is_empty() || name.as_bytes()[0] == b'_' { None } else { Some(name) }
|
||||
}
|
||||
|
||||
fn warn_about_unused_upvars(&self, entry_ln: LiveNode) {
|
||||
let upvars = match self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
|
||||
None => return,
|
||||
Some(upvars) => upvars,
|
||||
};
|
||||
for (&var_hir_id, upvar) in upvars.iter() {
|
||||
let var = self.variable(var_hir_id, upvar.span);
|
||||
let upvar_id = ty::UpvarId {
|
||||
var_path: ty::UpvarPath { hir_id: var_hir_id },
|
||||
closure_expr_id: self.ir.body_owner.expect_local(),
|
||||
};
|
||||
match self.tables.upvar_capture(upvar_id) {
|
||||
ty::UpvarCapture::ByValue => {}
|
||||
ty::UpvarCapture::ByRef(..) => continue,
|
||||
};
|
||||
if self.used_on_entry(entry_ln, var) {
|
||||
if self.live_on_entry(entry_ln, var).is_none() {
|
||||
if let Some(name) = self.should_warn(var) {
|
||||
self.ir.tcx.struct_span_lint_hir(
|
||||
lint::builtin::UNUSED_ASSIGNMENTS,
|
||||
var_hir_id,
|
||||
vec![upvar.span],
|
||||
|lint| {
|
||||
lint.build(&format!("value captured by `{}` is never read", name))
|
||||
.help("did you mean to capture by reference instead?")
|
||||
.emit();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(name) = self.should_warn(var) {
|
||||
self.ir.tcx.struct_span_lint_hir(
|
||||
lint::builtin::UNUSED_VARIABLES,
|
||||
var_hir_id,
|
||||
vec![upvar.span],
|
||||
|lint| {
|
||||
lint.build(&format!("unused variable: `{}`", name))
|
||||
.help("did you mean to capture by reference instead?")
|
||||
.emit();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
|
||||
for p in body.params {
|
||||
self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
|
||||
if self.live_on_entry(ln, var).is_none() {
|
||||
self.report_dead_assign(hir_id, spans, var, true);
|
||||
self.report_unsed_assign(hir_id, spans, var, |name| {
|
||||
format!("value passed to `{}` is never read", name)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1672,35 +1772,30 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
|
||||
fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
|
||||
if self.live_on_exit(ln, var).is_none() {
|
||||
self.report_dead_assign(hir_id, spans, var, false);
|
||||
self.report_unsed_assign(hir_id, spans, var, |name| {
|
||||
format!("value assigned to `{}` is never read", name)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_param: bool) {
|
||||
fn report_unsed_assign(
|
||||
&self,
|
||||
hir_id: HirId,
|
||||
spans: Vec<Span>,
|
||||
var: Variable,
|
||||
message: impl Fn(&str) -> String,
|
||||
) {
|
||||
if let Some(name) = self.should_warn(var) {
|
||||
if is_param {
|
||||
self.ir.tcx.struct_span_lint_hir(
|
||||
lint::builtin::UNUSED_ASSIGNMENTS,
|
||||
hir_id,
|
||||
spans,
|
||||
|lint| {
|
||||
lint.build(&format!("value passed to `{}` is never read", name))
|
||||
.help("maybe it is overwritten before being read?")
|
||||
.emit();
|
||||
},
|
||||
)
|
||||
} else {
|
||||
self.ir.tcx.struct_span_lint_hir(
|
||||
lint::builtin::UNUSED_ASSIGNMENTS,
|
||||
hir_id,
|
||||
spans,
|
||||
|lint| {
|
||||
lint.build(&format!("value assigned to `{}` is never read", name))
|
||||
.help("maybe it is overwritten before being read?")
|
||||
.emit();
|
||||
},
|
||||
)
|
||||
}
|
||||
self.ir.tcx.struct_span_lint_hir(
|
||||
lint::builtin::UNUSED_ASSIGNMENTS,
|
||||
hir_id,
|
||||
spans,
|
||||
|lint| {
|
||||
lint.build(&message(&name))
|
||||
.help("maybe it is overwritten before being read?")
|
||||
.emit();
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,31 @@ use rustc_target::spec::abi;
|
||||
/// Checks that it is legal to call methods of the trait corresponding
|
||||
/// to `trait_id` (this only cares about the trait, not the specific
|
||||
/// method that is called).
|
||||
pub fn check_legal_trait_for_method_call(tcx: TyCtxt<'_>, span: Span, trait_id: DefId) {
|
||||
pub fn check_legal_trait_for_method_call(
|
||||
tcx: TyCtxt<'_>,
|
||||
span: Span,
|
||||
receiver: Option<Span>,
|
||||
trait_id: DefId,
|
||||
) {
|
||||
if tcx.lang_items().drop_trait() == Some(trait_id) {
|
||||
struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method")
|
||||
.span_label(span, "explicit destructor calls not allowed")
|
||||
.emit();
|
||||
let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method");
|
||||
err.span_label(span, "explicit destructor calls not allowed");
|
||||
|
||||
let snippet = receiver
|
||||
.and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
let suggestion =
|
||||
if snippet.is_empty() { "drop".to_string() } else { format!("drop({})", snippet) };
|
||||
|
||||
err.span_suggestion(
|
||||
span,
|
||||
&format!("consider using `drop` function: `{}`", suggestion),
|
||||
String::new(),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,9 +597,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) {
|
||||
// Disallow calls to the method `drop` defined in the `Drop` trait.
|
||||
match pick.item.container {
|
||||
ty::TraitContainer(trait_def_id) => {
|
||||
callee::check_legal_trait_for_method_call(self.tcx, self.span, trait_def_id)
|
||||
}
|
||||
ty::TraitContainer(trait_def_id) => callee::check_legal_trait_for_method_call(
|
||||
self.tcx,
|
||||
self.span,
|
||||
Some(self.self_expr.span),
|
||||
trait_def_id,
|
||||
),
|
||||
ty::ImplContainer(..) => {}
|
||||
}
|
||||
}
|
||||
|
@ -5433,7 +5433,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
|
||||
match container {
|
||||
ty::TraitContainer(trait_did) => {
|
||||
callee::check_legal_trait_for_method_call(tcx, span, trait_did)
|
||||
callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
|
||||
}
|
||||
ty::ImplContainer(impl_def_id) => {
|
||||
if segments.len() == 1 {
|
||||
|
@ -112,7 +112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
|
||||
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
|
||||
let mut upvar_list: FxIndexMap<hir::HirId, ty::UpvarId> =
|
||||
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> =
|
||||
FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default());
|
||||
for (&var_hir_id, _) in upvars.iter() {
|
||||
let upvar_id = ty::UpvarId {
|
||||
@ -122,7 +122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
debug!("seed upvar_id {:?}", upvar_id);
|
||||
// Adding the upvar Id to the list of Upvars, which will be added
|
||||
// to the map for the closure at the end of the for loop.
|
||||
upvar_list.insert(var_hir_id, upvar_id);
|
||||
closure_captures.insert(var_hir_id, upvar_id);
|
||||
|
||||
let capture_kind = match capture_clause {
|
||||
hir::CaptureBy::Value => ty::UpvarCapture::ByValue,
|
||||
@ -140,8 +140,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// Add the vector of upvars to the map keyed with the closure id.
|
||||
// This gives us an easier access to them without having to call
|
||||
// tcx.upvars again..
|
||||
if !upvar_list.is_empty() {
|
||||
self.tables.borrow_mut().upvar_list.insert(closure_def_id, upvar_list);
|
||||
if !closure_captures.is_empty() {
|
||||
self.tables.borrow_mut().closure_captures.insert(closure_def_id, closure_captures);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,8 +74,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);
|
||||
wbcx.tables.used_trait_imports = used_trait_imports;
|
||||
|
||||
wbcx.tables.upvar_list =
|
||||
mem::replace(&mut self.tables.borrow_mut().upvar_list, Default::default());
|
||||
wbcx.tables.closure_captures =
|
||||
mem::replace(&mut self.tables.borrow_mut().closure_captures, Default::default());
|
||||
|
||||
if self.is_tainted_by_errors() {
|
||||
// FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted.
|
||||
|
@ -1531,4 +1531,147 @@ mod tests {
|
||||
fn test_clamp_max_is_nan() {
|
||||
let _ = 1.0f32.clamp(3.0, NAN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_total_cmp() {
|
||||
use core::cmp::Ordering;
|
||||
|
||||
fn quiet_bit_mask() -> u32 {
|
||||
1 << (f32::MANTISSA_DIGITS - 2)
|
||||
}
|
||||
|
||||
fn min_subnorm() -> f32 {
|
||||
f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0)
|
||||
}
|
||||
|
||||
fn max_subnorm() -> f32 {
|
||||
f32::MIN_POSITIVE - min_subnorm()
|
||||
}
|
||||
|
||||
fn q_nan() -> f32 {
|
||||
f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask())
|
||||
}
|
||||
|
||||
fn s_nan() -> f32 {
|
||||
f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42)
|
||||
}
|
||||
|
||||
assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
|
||||
assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY));
|
||||
assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX));
|
||||
assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX));
|
||||
assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY));
|
||||
assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
|
||||
assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX));
|
||||
assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX));
|
||||
assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY));
|
||||
assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan()));
|
||||
assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
|
||||
|
||||
assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
|
||||
assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY));
|
||||
assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX));
|
||||
assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX));
|
||||
assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY));
|
||||
assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
|
||||
}
|
||||
}
|
||||
|
@ -1554,4 +1554,147 @@ mod tests {
|
||||
fn test_clamp_max_is_nan() {
|
||||
let _ = 1.0f64.clamp(3.0, NAN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_total_cmp() {
|
||||
use core::cmp::Ordering;
|
||||
|
||||
fn quiet_bit_mask() -> u64 {
|
||||
1 << (f64::MANTISSA_DIGITS - 2)
|
||||
}
|
||||
|
||||
fn min_subnorm() -> f64 {
|
||||
f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0)
|
||||
}
|
||||
|
||||
fn max_subnorm() -> f64 {
|
||||
f64::MIN_POSITIVE - min_subnorm()
|
||||
}
|
||||
|
||||
fn q_nan() -> f64 {
|
||||
f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask())
|
||||
}
|
||||
|
||||
fn s_nan() -> f64 {
|
||||
f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42)
|
||||
}
|
||||
|
||||
assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
|
||||
assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY));
|
||||
assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX));
|
||||
assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX));
|
||||
assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY));
|
||||
assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
|
||||
assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX));
|
||||
assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX));
|
||||
assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY));
|
||||
assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan()));
|
||||
assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
|
||||
|
||||
assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
|
||||
assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY));
|
||||
assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX));
|
||||
assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX));
|
||||
assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY));
|
||||
assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
|
||||
}
|
||||
}
|
||||
|
@ -311,6 +311,7 @@
|
||||
#![feature(test)]
|
||||
#![feature(thread_local)]
|
||||
#![feature(toowned_clone_into)]
|
||||
#![feature(total_cmp)]
|
||||
#![feature(trace_macros)]
|
||||
#![feature(track_caller)]
|
||||
#![feature(try_reserve)]
|
||||
|
@ -2,7 +2,7 @@ use crate::cmp::Ordering;
|
||||
use crate::convert::TryInto;
|
||||
use crate::fmt;
|
||||
use crate::hash;
|
||||
use crate::io;
|
||||
use crate::io::{self, Write};
|
||||
use crate::iter;
|
||||
use crate::mem;
|
||||
use crate::net::{htons, ntohs, IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
@ -600,7 +600,26 @@ impl fmt::Display for SocketAddr {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl fmt::Display for SocketAddrV4 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.ip(), self.port())
|
||||
// Fast path: if there's no alignment stuff, write to the output buffer
|
||||
// directly
|
||||
if f.precision().is_none() && f.width().is_none() {
|
||||
write!(f, "{}:{}", self.ip(), self.port())
|
||||
} else {
|
||||
const IPV4_SOCKET_BUF_LEN: usize = (3 * 4) // the segments
|
||||
+ 3 // the separators
|
||||
+ 1 + 5; // the port
|
||||
let mut buf = [0; IPV4_SOCKET_BUF_LEN];
|
||||
let mut buf_slice = &mut buf[..];
|
||||
|
||||
// Unwrap is fine because writing to a sufficiently-sized
|
||||
// buffer is infallible
|
||||
write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
|
||||
let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();
|
||||
|
||||
// This unsafe is OK because we know what is being written to the buffer
|
||||
let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
|
||||
f.pad(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -614,7 +633,28 @@ impl fmt::Debug for SocketAddrV4 {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl fmt::Display for SocketAddrV6 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[{}]:{}", self.ip(), self.port())
|
||||
// Fast path: if there's no alignment stuff, write to the output
|
||||
// buffer directly
|
||||
if f.precision().is_none() && f.width().is_none() {
|
||||
write!(f, "[{}]:{}", self.ip(), self.port())
|
||||
} else {
|
||||
const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address
|
||||
+ 7 // The colon separators
|
||||
+ 2 // The brackets
|
||||
+ 1 + 5; // The port
|
||||
|
||||
let mut buf = [0; IPV6_SOCKET_BUF_LEN];
|
||||
let mut buf_slice = &mut buf[..];
|
||||
|
||||
// Unwrap is fine because writing to a sufficiently-sized
|
||||
// buffer is infallible
|
||||
write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap();
|
||||
let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();
|
||||
|
||||
// This unsafe is OK because we know what is being written to the buffer
|
||||
let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
|
||||
f.pad(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1168,6 +1208,28 @@ mod tests {
|
||||
assert!(v6.is_ipv6());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn socket_v4_to_str() {
|
||||
let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080);
|
||||
|
||||
assert_eq!(format!("{}", socket), "192.168.0.1:8080");
|
||||
assert_eq!(format!("{:<20}", socket), "192.168.0.1:8080 ");
|
||||
assert_eq!(format!("{:>20}", socket), " 192.168.0.1:8080");
|
||||
assert_eq!(format!("{:^20}", socket), " 192.168.0.1:8080 ");
|
||||
assert_eq!(format!("{:.10}", socket), "192.168.0.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn socket_v6_to_str() {
|
||||
let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap();
|
||||
|
||||
assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53");
|
||||
assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53 ");
|
||||
assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1]:53");
|
||||
assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1]:53 ");
|
||||
assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compare() {
|
||||
let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap();
|
||||
|
@ -8,6 +8,6 @@ fn foo(mut f: Box<dyn FnMut()>) {
|
||||
|
||||
fn main() {
|
||||
let mut y = true;
|
||||
foo(Box::new(move || y = false) as Box<_>);
|
||||
foo(Box::new(move || y = !y) as Box<_>);
|
||||
//~^ ERROR cannot assign to `y`, as it is not declared as mutable
|
||||
}
|
||||
|
@ -8,6 +8,6 @@ fn foo(mut f: Box<dyn FnMut()>) {
|
||||
|
||||
fn main() {
|
||||
let y = true;
|
||||
foo(Box::new(move || y = false) as Box<_>);
|
||||
foo(Box::new(move || y = !y) as Box<_>);
|
||||
//~^ ERROR cannot assign to `y`, as it is not declared as mutable
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ error[E0594]: cannot assign to `y`, as it is not declared as mutable
|
||||
|
|
||||
LL | let y = true;
|
||||
| - help: consider changing this to be mutable: `mut y`
|
||||
LL | foo(Box::new(move || y = false) as Box<_>);
|
||||
| ^^^^^^^^^ cannot assign
|
||||
LL | foo(Box::new(move || y = !y) as Box<_>);
|
||||
| ^^^^^^ cannot assign
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
|
||||
--> $DIR/E0040.rs:13:7
|
||||
|
|
||||
LL | x.drop();
|
||||
| ^^^^ explicit destructor calls not allowed
|
||||
| ^^^^
|
||||
| |
|
||||
| explicit destructor calls not allowed
|
||||
| help: consider using `drop` function: `drop(x)`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
|
||||
--> $DIR/explicit-call-to-dtor.rs:13:7
|
||||
|
|
||||
LL | x.drop();
|
||||
| ^^^^ explicit destructor calls not allowed
|
||||
| ^^^^
|
||||
| |
|
||||
| explicit destructor calls not allowed
|
||||
| help: consider using `drop` function: `drop(x)`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
|
||||
--> $DIR/explicit-call-to-supertrait-dtor.rs:17:14
|
||||
|
|
||||
LL | self.drop();
|
||||
| ^^^^ explicit destructor calls not allowed
|
||||
| ^^^^
|
||||
| |
|
||||
| explicit destructor calls not allowed
|
||||
| help: consider using `drop` function: `drop(self)`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
|
||||
--> $DIR/illegal-ufcs-drop.rs:8:5
|
||||
|
|
||||
LL | Drop::drop(&mut Foo)
|
||||
| ^^^^^^^^^^ explicit destructor calls not allowed
|
||||
| ^^^^^^^^^^
|
||||
| |
|
||||
| explicit destructor calls not allowed
|
||||
| help: consider using `drop` function: `drop`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
26
src/test/ui/impl-trait/issue-56445.rs
Normal file
26
src/test/ui/impl-trait/issue-56445.rs
Normal file
@ -0,0 +1,26 @@
|
||||
// Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-629426939
|
||||
// check-pass
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct S<'a>
|
||||
{
|
||||
pub m1: PhantomData<&'a u8>,
|
||||
pub m2: [u8; S::size()],
|
||||
}
|
||||
|
||||
impl<'a> S<'a>
|
||||
{
|
||||
pub const fn size() -> usize { 1 }
|
||||
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
m1: PhantomData,
|
||||
m2: [0; Self::size()],
|
||||
}
|
||||
}
|
||||
}
|
13
src/test/ui/impl-trait/issue-68532.rs
Normal file
13
src/test/ui/impl-trait/issue-68532.rs
Normal file
@ -0,0 +1,13 @@
|
||||
// check-pass
|
||||
|
||||
pub struct A<'a>(&'a ());
|
||||
|
||||
impl<'a> A<'a> {
|
||||
const N: usize = 68;
|
||||
|
||||
pub fn foo(&self) {
|
||||
let _b = [0; Self::N];
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,5 +1,4 @@
|
||||
// run-pass
|
||||
#![forbid(warnings)]
|
||||
|
||||
// We shouldn't need to rebind a moved upvar as mut if it's already
|
||||
// marked as mut
|
||||
@ -7,4 +6,6 @@
|
||||
pub fn main() {
|
||||
let mut x = 1;
|
||||
let _thunk = Box::new(move|| { x = 2; });
|
||||
//~^ WARN value assigned to `x` is never read
|
||||
//~| WARN unused variable: `x`
|
||||
}
|
||||
|
20
src/test/ui/issues/issue-11958.stderr
Normal file
20
src/test/ui/issues/issue-11958.stderr
Normal file
@ -0,0 +1,20 @@
|
||||
warning: value assigned to `x` is never read
|
||||
--> $DIR/issue-11958.rs:8:36
|
||||
|
|
||||
LL | let _thunk = Box::new(move|| { x = 2; });
|
||||
| ^
|
||||
|
|
||||
= note: `#[warn(unused_assignments)]` on by default
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: unused variable: `x`
|
||||
--> $DIR/issue-11958.rs:8:36
|
||||
|
|
||||
LL | let _thunk = Box::new(move|| { x = 2; });
|
||||
| ^
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` on by default
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: 2 warnings emitted
|
||||
|
108
src/test/ui/liveness/liveness-upvars.rs
Normal file
108
src/test/ui/liveness/liveness-upvars.rs
Normal file
@ -0,0 +1,108 @@
|
||||
// edition:2018
|
||||
// check-pass
|
||||
#![warn(unused)]
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
pub fn unintentional_copy_one() {
|
||||
let mut last = None;
|
||||
let mut f = move |s| {
|
||||
last = Some(s); //~ WARN value assigned to `last` is never read
|
||||
//~| WARN unused variable: `last`
|
||||
};
|
||||
f("a");
|
||||
f("b");
|
||||
f("c");
|
||||
dbg!(last.unwrap());
|
||||
}
|
||||
|
||||
pub fn unintentional_copy_two() {
|
||||
let mut sum = 0;
|
||||
(1..10).for_each(move |x| {
|
||||
sum += x; //~ WARN unused variable: `sum`
|
||||
});
|
||||
dbg!(sum);
|
||||
}
|
||||
|
||||
pub fn f() {
|
||||
let mut c = 0;
|
||||
|
||||
// Captured by value, but variable is dead on entry.
|
||||
move || {
|
||||
c = 1; //~ WARN value captured by `c` is never read
|
||||
println!("{}", c);
|
||||
};
|
||||
let _ = async move {
|
||||
c = 1; //~ WARN value captured by `c` is never read
|
||||
println!("{}", c);
|
||||
};
|
||||
|
||||
// Read and written to, but never actually used.
|
||||
move || {
|
||||
c += 1; //~ WARN unused variable: `c`
|
||||
};
|
||||
let _ = async move {
|
||||
c += 1; //~ WARN value assigned to `c` is never read
|
||||
//~| WARN unused variable: `c`
|
||||
};
|
||||
|
||||
move || {
|
||||
println!("{}", c);
|
||||
// Value is read by closure itself on later invocations.
|
||||
c += 1;
|
||||
};
|
||||
let b = Box::new(42);
|
||||
move || {
|
||||
println!("{}", c);
|
||||
// Never read because this is FnOnce closure.
|
||||
c += 1; //~ WARN value assigned to `c` is never read
|
||||
drop(b);
|
||||
};
|
||||
let _ = async move {
|
||||
println!("{}", c);
|
||||
// Never read because this is a generator.
|
||||
c += 1; //~ WARN value assigned to `c` is never read
|
||||
};
|
||||
}
|
||||
|
||||
pub fn nested() {
|
||||
let mut d = None;
|
||||
let mut e = None;
|
||||
|| {
|
||||
|| {
|
||||
d = Some("d1"); //~ WARN value assigned to `d` is never read
|
||||
d = Some("d2");
|
||||
};
|
||||
move || {
|
||||
e = Some("e1"); //~ WARN value assigned to `e` is never read
|
||||
//~| WARN unused variable: `e`
|
||||
e = Some("e2"); //~ WARN value assigned to `e` is never read
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
pub fn g<T: Default>(mut v: T) {
|
||||
|r| {
|
||||
if r {
|
||||
v = T::default(); //~ WARN value assigned to `v` is never read
|
||||
} else {
|
||||
drop(v);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn h<T: Copy + Default + std::fmt::Debug>() {
|
||||
let mut z = T::default();
|
||||
move |b| {
|
||||
loop {
|
||||
if b {
|
||||
z = T::default(); //~ WARN value assigned to `z` is never read
|
||||
//~| WARN unused variable: `z`
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
dbg!(z);
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
150
src/test/ui/liveness/liveness-upvars.stderr
Normal file
150
src/test/ui/liveness/liveness-upvars.stderr
Normal file
@ -0,0 +1,150 @@
|
||||
warning: value assigned to `last` is never read
|
||||
--> $DIR/liveness-upvars.rs:9:9
|
||||
|
|
||||
LL | last = Some(s);
|
||||
| ^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/liveness-upvars.rs:3:9
|
||||
|
|
||||
LL | #![warn(unused)]
|
||||
| ^^^^^^
|
||||
= note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]`
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: unused variable: `last`
|
||||
--> $DIR/liveness-upvars.rs:9:9
|
||||
|
|
||||
LL | last = Some(s);
|
||||
| ^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/liveness-upvars.rs:3:9
|
||||
|
|
||||
LL | #![warn(unused)]
|
||||
| ^^^^^^
|
||||
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: unused variable: `sum`
|
||||
--> $DIR/liveness-upvars.rs:21:9
|
||||
|
|
||||
LL | sum += x;
|
||||
| ^^^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: value captured by `c` is never read
|
||||
--> $DIR/liveness-upvars.rs:31:9
|
||||
|
|
||||
LL | c = 1;
|
||||
| ^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: value captured by `c` is never read
|
||||
--> $DIR/liveness-upvars.rs:35:9
|
||||
|
|
||||
LL | c = 1;
|
||||
| ^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: unused variable: `c`
|
||||
--> $DIR/liveness-upvars.rs:41:9
|
||||
|
|
||||
LL | c += 1;
|
||||
| ^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: value assigned to `c` is never read
|
||||
--> $DIR/liveness-upvars.rs:44:9
|
||||
|
|
||||
LL | c += 1;
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: unused variable: `c`
|
||||
--> $DIR/liveness-upvars.rs:44:9
|
||||
|
|
||||
LL | c += 1;
|
||||
| ^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: value assigned to `c` is never read
|
||||
--> $DIR/liveness-upvars.rs:57:9
|
||||
|
|
||||
LL | c += 1;
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: value assigned to `c` is never read
|
||||
--> $DIR/liveness-upvars.rs:63:9
|
||||
|
|
||||
LL | c += 1;
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: value assigned to `d` is never read
|
||||
--> $DIR/liveness-upvars.rs:72:13
|
||||
|
|
||||
LL | d = Some("d1");
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: value assigned to `e` is never read
|
||||
--> $DIR/liveness-upvars.rs:76:13
|
||||
|
|
||||
LL | e = Some("e1");
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: value assigned to `e` is never read
|
||||
--> $DIR/liveness-upvars.rs:78:13
|
||||
|
|
||||
LL | e = Some("e2");
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: unused variable: `e`
|
||||
--> $DIR/liveness-upvars.rs:76:13
|
||||
|
|
||||
LL | e = Some("e1");
|
||||
| ^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: value assigned to `v` is never read
|
||||
--> $DIR/liveness-upvars.rs:86:13
|
||||
|
|
||||
LL | v = T::default();
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: value assigned to `z` is never read
|
||||
--> $DIR/liveness-upvars.rs:98:17
|
||||
|
|
||||
LL | z = T::default();
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: unused variable: `z`
|
||||
--> $DIR/liveness-upvars.rs:98:17
|
||||
|
|
||||
LL | z = T::default();
|
||||
| ^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: 17 warnings emitted
|
||||
|
@ -0,0 +1,9 @@
|
||||
#![feature(impl_trait_in_bindings)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
fn main() {
|
||||
const C: impl Copy = 0;
|
||||
match C {
|
||||
C | _ => {} //~ ERROR: opaque types cannot be used in patterns
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
error: opaque types cannot be used in patterns
|
||||
--> $DIR/issue-71042-opaquely-typed-constant-used-in-pattern.rs:7:9
|
||||
|
|
||||
LL | C | _ => {}
|
||||
| ^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
23
src/test/ui/type-alias-impl-trait/issue-70121.rs
Normal file
23
src/test/ui/type-alias-impl-trait/issue-70121.rs
Normal file
@ -0,0 +1,23 @@
|
||||
// check-pass
|
||||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
pub type Successors<'a> = impl Iterator<Item = &'a ()>;
|
||||
|
||||
pub fn f<'a>() -> Successors<'a> {
|
||||
None.into_iter()
|
||||
}
|
||||
|
||||
pub trait Tr {
|
||||
type Item;
|
||||
}
|
||||
|
||||
impl<'a> Tr for &'a () {
|
||||
type Item = Successors<'a>;
|
||||
}
|
||||
|
||||
pub fn kazusa<'a>() -> <&'a () as Tr>::Item {
|
||||
None.into_iter()
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,5 +1,4 @@
|
||||
// run-pass
|
||||
#![allow(unused_variables)]
|
||||
// Test that we mutate a counter on the stack only when we expect to.
|
||||
|
||||
fn call<F>(f: F) where F : FnOnce() {
|
||||
@ -13,7 +12,7 @@ fn main() {
|
||||
call(|| {
|
||||
// Move `y`, but do not move `counter`, even though it is read
|
||||
// by value (note that it is also mutated).
|
||||
for item in y {
|
||||
for item in y { //~ WARN unused variable: `item`
|
||||
let v = counter;
|
||||
counter += v;
|
||||
}
|
||||
@ -22,7 +21,8 @@ fn main() {
|
||||
|
||||
call(move || {
|
||||
// this mutates a moved copy, and hence doesn't affect original
|
||||
counter += 1;
|
||||
counter += 1; //~ WARN value assigned to `counter` is never read
|
||||
//~| WARN unused variable: `counter`
|
||||
});
|
||||
assert_eq!(counter, 88);
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
warning: unused variable: `item`
|
||||
--> $DIR/unboxed-closures-counter-not-moved.rs:15:13
|
||||
|
|
||||
LL | for item in y {
|
||||
| ^^^^ help: if this is intentional, prefix it with an underscore: `_item`
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` on by default
|
||||
|
||||
warning: value assigned to `counter` is never read
|
||||
--> $DIR/unboxed-closures-counter-not-moved.rs:24:9
|
||||
|
|
||||
LL | counter += 1;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_assignments)]` on by default
|
||||
= help: maybe it is overwritten before being read?
|
||||
|
||||
warning: unused variable: `counter`
|
||||
--> $DIR/unboxed-closures-counter-not-moved.rs:24:9
|
||||
|
|
||||
LL | counter += 1;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: 3 warnings emitted
|
||||
|
@ -13,11 +13,11 @@ fn set(x: &mut usize) { *x = 42; }
|
||||
fn main() {
|
||||
{
|
||||
let mut x = 0_usize;
|
||||
move || x += 1;
|
||||
move || x += 1; //~ WARN unused variable: `x`
|
||||
}
|
||||
{
|
||||
let mut x = 0_usize;
|
||||
move || x += 1;
|
||||
move || x += 1; //~ WARN unused variable: `x`
|
||||
}
|
||||
{
|
||||
let mut x = 0_usize;
|
||||
|
@ -0,0 +1,19 @@
|
||||
warning: unused variable: `x`
|
||||
--> $DIR/unboxed-closures-move-mutable.rs:16:17
|
||||
|
|
||||
LL | move || x += 1;
|
||||
| ^
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` on by default
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: unused variable: `x`
|
||||
--> $DIR/unboxed-closures-move-mutable.rs:20:17
|
||||
|
|
||||
LL | move || x += 1;
|
||||
| ^
|
||||
|
|
||||
= help: did you mean to capture by reference instead?
|
||||
|
||||
warning: 2 warnings emitted
|
||||
|
Loading…
x
Reference in New Issue
Block a user