diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 2bd4733db420b..56e284a12fa83 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -315,7 +315,7 @@ impl RawVec { /// `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 diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs index 195847ee98dc4..00529f0e2d54f 100644 --- a/src/libcore/iter/adapters/mod.rs +++ b/src/libcore/iter/adapters/mod.rs @@ -1619,6 +1619,69 @@ impl Peekable { 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 { + 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(&mut self, expected: &R) -> Option + where + R: ?Sized, + I::Item: PartialEq, + { + self.next_if(|next| next == expected) + } } /// An iterator that rejects elements while `predicate` returns `true`. diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 434569020d2a8..6313de31ce4d5 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -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) + } } diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 6476ddb4541ff..d42e5392c5863 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -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) + } } diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 52cf068f0a567..0265235a65a35 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -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. diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 6a8e908b9c618..37ebf4112808e 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -43,6 +43,7 @@ #![feature(leading_trailing_ones)] #![feature(const_forget)] #![feature(option_unwrap_none)] +#![feature(peekable_next_if)] extern crate test; diff --git a/src/librustc_error_codes/error_codes/E0619.md b/src/librustc_error_codes/error_codes/E0619.md index 8727692c0a5b8..f516de43095bd 100644 --- a/src/librustc_error_codes/error_codes/E0619.md +++ b/src/librustc_error_codes/error_codes/E0619.md @@ -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. diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs index 7a20014484190..0298f3d8442a6 100644 --- a/src/librustc_middle/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -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> 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> 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); }) } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index a0c1d96bb4743..65e62dbd9dd49 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -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()) diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index c83555d65faf0..e962dfb2b3e86 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -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) { diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs index b30b57ea9217a..3d821aa55a1f8 100644 --- a/src/librustc_mir_build/build/mod.rs +++ b/src/librustc_mir_build/build/mod.rs @@ -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; diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs index 114bf5710402f..fd3c48b38badb 100644 --- a/src/librustc_mir_build/hair/cx/expr.rs +++ b/src/librustc_mir_build/hair/cx/expr.rs @@ -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)); diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs index ece78d0251281..55978afc59437 100644 --- a/src/librustc_passes/liveness.rs +++ b/src/librustc_passes/liveness.rs @@ -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 => "".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(&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); + + // # 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. + + 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 => {} + } + } + } - // 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); + let succ = self.propagate_through_expr(&body.value, self.s.exit_ln); - let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln); + match fk { + FnKind::Method(..) | FnKind::ItemFn(..) => return succ, + FnKind::Closure(..) => {} + } - // 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))); - } - body.hir_id + 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, }, - entry_ln - ); + 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)); + } - entry_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, 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, var: Variable, is_param: bool) { + fn report_unsed_assign( + &self, + hir_id: HirId, + spans: Vec, + 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(); + }, + ) } } } diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 52ddacc1c4b1a..f4e46a0493151 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -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, + 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(); } } diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 410c5efdf37d4..7e4e1aa608ece 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -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(..) => {} } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index da711b9d48042..3cb6ce61ee8d7 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 { diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 8707e4fe84a22..19a23e5a59478 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -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 = + let mut closure_captures: FxIndexMap = 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); } } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 154ca391aa5fd..3473dc7a58d04 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -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. diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index deb9eb5b4b05c..8ff19557a3063 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -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())); + } } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index b79e550ed265e..d7845fd2c4ddc 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -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())); + } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index cc3e613fa3d60..72dfe2937f491 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -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)] diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index 08536de4d55c3..b780340884e1f 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -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::().unwrap(); diff --git a/src/test/ui/closures/closure-immutable-outer-variable.fixed b/src/test/ui/closures/closure-immutable-outer-variable.fixed index 102f1f94a36e1..1b0feede34ecf 100644 --- a/src/test/ui/closures/closure-immutable-outer-variable.fixed +++ b/src/test/ui/closures/closure-immutable-outer-variable.fixed @@ -8,6 +8,6 @@ fn foo(mut f: Box) { 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 } diff --git a/src/test/ui/closures/closure-immutable-outer-variable.rs b/src/test/ui/closures/closure-immutable-outer-variable.rs index 6eb43b372c96c..50ec1c6148a04 100644 --- a/src/test/ui/closures/closure-immutable-outer-variable.rs +++ b/src/test/ui/closures/closure-immutable-outer-variable.rs @@ -8,6 +8,6 @@ fn foo(mut f: Box) { 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 } diff --git a/src/test/ui/closures/closure-immutable-outer-variable.stderr b/src/test/ui/closures/closure-immutable-outer-variable.stderr index 7e60f3cd8ffa4..799097889cd30 100644 --- a/src/test/ui/closures/closure-immutable-outer-variable.stderr +++ b/src/test/ui/closures/closure-immutable-outer-variable.stderr @@ -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 diff --git a/src/test/ui/error-codes/E0040.stderr b/src/test/ui/error-codes/E0040.stderr index 966455902817d..69cf28b29704f 100644 --- a/src/test/ui/error-codes/E0040.stderr +++ b/src/test/ui/error-codes/E0040.stderr @@ -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 diff --git a/src/test/ui/explicit/explicit-call-to-dtor.stderr b/src/test/ui/explicit/explicit-call-to-dtor.stderr index cbbe967179ef3..5ebe4ee4b90f8 100644 --- a/src/test/ui/explicit/explicit-call-to-dtor.stderr +++ b/src/test/ui/explicit/explicit-call-to-dtor.stderr @@ -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 diff --git a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr index 0b302e30b64f3..cd3fb3119a5cf 100644 --- a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr +++ b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr @@ -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 diff --git a/src/test/ui/illegal-ufcs-drop.stderr b/src/test/ui/illegal-ufcs-drop.stderr index d35d376962c17..57c99739afd24 100644 --- a/src/test/ui/illegal-ufcs-drop.stderr +++ b/src/test/ui/illegal-ufcs-drop.stderr @@ -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 diff --git a/src/test/ui/impl-trait/issue-56445.rs b/src/test/ui/impl-trait/issue-56445.rs new file mode 100644 index 0000000000000..a34d7bae3a6ca --- /dev/null +++ b/src/test/ui/impl-trait/issue-56445.rs @@ -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()], + } + } +} diff --git a/src/test/ui/impl-trait/issue-68532.rs b/src/test/ui/impl-trait/issue-68532.rs new file mode 100644 index 0000000000000..01a7af0aee40e --- /dev/null +++ b/src/test/ui/impl-trait/issue-68532.rs @@ -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() {} diff --git a/src/test/ui/issues/issue-11958.rs b/src/test/ui/issues/issue-11958.rs index 8fe8a8c606189..a7af01e25b4e2 100644 --- a/src/test/ui/issues/issue-11958.rs +++ b/src/test/ui/issues/issue-11958.rs @@ -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` } diff --git a/src/test/ui/issues/issue-11958.stderr b/src/test/ui/issues/issue-11958.stderr new file mode 100644 index 0000000000000..25de6ff4c118c --- /dev/null +++ b/src/test/ui/issues/issue-11958.stderr @@ -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 + diff --git a/src/test/ui/liveness/liveness-upvars.rs b/src/test/ui/liveness/liveness-upvars.rs new file mode 100644 index 0000000000000..b2837e74b8c51 --- /dev/null +++ b/src/test/ui/liveness/liveness-upvars.rs @@ -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(mut v: T) { + |r| { + if r { + v = T::default(); //~ WARN value assigned to `v` is never read + } else { + drop(v); + } + }; +} + +pub fn h() { + 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() {} diff --git a/src/test/ui/liveness/liveness-upvars.stderr b/src/test/ui/liveness/liveness-upvars.stderr new file mode 100644 index 0000000000000..14fed91786436 --- /dev/null +++ b/src/test/ui/liveness/liveness-upvars.stderr @@ -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 + diff --git a/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs new file mode 100644 index 0000000000000..c5e4a72fb9ff1 --- /dev/null +++ b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs @@ -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 + } +} diff --git a/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr new file mode 100644 index 0000000000000..7695223f2cf98 --- /dev/null +++ b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr @@ -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 + diff --git a/src/test/ui/type-alias-impl-trait/issue-70121.rs b/src/test/ui/type-alias-impl-trait/issue-70121.rs new file mode 100644 index 0000000000000..dff0d89d465dd --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-70121.rs @@ -0,0 +1,23 @@ +// check-pass + +#![feature(type_alias_impl_trait)] + +pub type Successors<'a> = impl Iterator; + +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() {} diff --git a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs index fb24df3c24e87..390386e57fa72 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs @@ -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) 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); } diff --git a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr new file mode 100644 index 0000000000000..ba4b3dac6705e --- /dev/null +++ b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr @@ -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 + diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs index 9b519e63a95cc..e5b19db782231 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs @@ -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; diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr new file mode 100644 index 0000000000000..4dfd1bb307574 --- /dev/null +++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr @@ -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 +