diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 8ef6090c743a9..8cc6f04c0653a 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -865,6 +865,25 @@ impl From> for Box<[u8]> { } } +#[stable(feature = "box_from_array", since = "1.45.0")] +impl From<[T; N]> for Box<[T]> +where + [T; N]: LengthAtMost32, +{ + /// Converts a `[T; N]` into a `Box<[T]>` + /// + /// This conversion moves the array to newly heap-allocated memory. + /// + /// # Examples + /// ```rust + /// let boxed: Box<[u8]> = Box::from([4, 2]); + /// println!("{:?}", boxed); + /// ``` + fn from(array: [T; N]) -> Box<[T]> { + box array + } +} + #[stable(feature = "boxed_slice_try_from", since = "1.43.0")] impl TryFrom> for Box<[T; N]> where diff --git a/src/libcore/char/convert.rs b/src/libcore/char/convert.rs index 315020bac5850..87c56c4b0a105 100644 --- a/src/libcore/char/convert.rs +++ b/src/libcore/char/convert.rs @@ -99,7 +99,7 @@ pub fn from_u32(i: u32) -> Option { #[inline] #[stable(feature = "char_from_unchecked", since = "1.5.0")] pub unsafe fn from_u32_unchecked(i: u32) -> char { - transmute(i) + if cfg!(debug_assertions) { char::from_u32(i).unwrap() } else { transmute(i) } } #[stable(feature = "char_convert", since = "1.13.0")] @@ -218,7 +218,7 @@ impl TryFrom for char { Err(CharTryFromError(())) } else { // SAFETY: checked that it's a legal unicode value - Ok(unsafe { from_u32_unchecked(i) }) + Ok(unsafe { transmute(i) }) } } } 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/iter/range.rs b/src/libcore/iter/range.rs index 388a5548a31a5..bd7e6cfa5a750 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -1,3 +1,4 @@ +use crate::char; use crate::convert::TryFrom; use crate::mem; use crate::ops::{self, Add, Sub, Try}; @@ -400,6 +401,73 @@ step_integer_impls! { wider than usize: [u32 i32], [u64 i64], [u128 i128]; } +#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +unsafe impl Step for char { + #[inline] + fn steps_between(&start: &char, &end: &char) -> Option { + let start = start as u32; + let end = end as u32; + if start <= end { + let count = end - start; + if start < 0xD800 && 0xE000 <= end { + usize::try_from(count - 0x800).ok() + } else { + usize::try_from(count).ok() + } + } else { + None + } + } + + #[inline] + fn forward_checked(start: char, count: usize) -> Option { + let start = start as u32; + let mut res = Step::forward_checked(start, count)?; + if start < 0xD800 && 0xD800 <= res { + res = Step::forward_checked(res, 0x800)?; + } + if res <= char::MAX as u32 { + // SAFETY: res is a valid unicode scalar + // (below 0x110000 and not in 0xD800..0xE000) + Some(unsafe { char::from_u32_unchecked(res) }) + } else { + None + } + } + + #[inline] + fn backward_checked(start: char, count: usize) -> Option { + let start = start as u32; + let mut res = Step::backward_checked(start, count)?; + if start >= 0xE000 && 0xE000 > res { + res = Step::backward_checked(res, 0x800)?; + } + // SAFETY: res is a valid unicode scalar + // (below 0x110000 and not in 0xD800..0xE000) + Some(unsafe { char::from_u32_unchecked(res) }) + } + + #[inline] + unsafe fn forward_unchecked(start: char, count: usize) -> char { + let start = start as u32; + let mut res = Step::forward_unchecked(start, count); + if start < 0xD800 && 0xD800 <= res { + res = Step::forward_unchecked(res, 0x800); + } + char::from_u32_unchecked(res) + } + + #[inline] + unsafe fn backward_unchecked(start: char, count: usize) -> char { + let start = start as u32; + let mut res = Step::backward_unchecked(start, count); + if start >= 0xE000 && 0xE000 > res { + res = Step::backward_unchecked(res, 0x800); + } + char::from_u32_unchecked(res) + } +} + macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] @@ -551,15 +619,7 @@ impl Iterator for ops::RangeFrom { #[inline] fn nth(&mut self, n: usize) -> Option { - // If we would jump over the maximum value, panic immediately. - // This is consistent with behavior before the Step redesign, - // even though it's inconsistent with n `next` calls. - // To get consistent behavior, change it to use `forward` instead. - // This change should go through FCP separately to the redesign, so is for now left as a - // FIXME: make this consistent - let plus_n = - Step::forward_checked(self.start.clone(), n).expect("overflow in RangeFrom::nth"); - // The final step should always be debug-checked. + let plus_n = Step::forward(self.start.clone(), n); self.start = Step::forward(plus_n.clone(), 1); Some(plus_n) } @@ -582,7 +642,11 @@ impl Iterator for ops::RangeInclusive { } let is_iterating = self.start < self.end; Some(if is_iterating { - let n = Step::forward(self.start.clone(), 1); + // SAFETY: just checked precondition + // We use the unchecked version here, because + // otherwise `for _ in '\0'..=char::MAX` + // does not successfully remove panicking code. + let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; mem::replace(&mut self.start, n) } else { self.exhausted = 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/ops/range.rs b/src/libcore/ops/range.rs index d4e6048579a56..d86f39c4550c8 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -151,10 +151,16 @@ impl> Range { /// /// The `RangeFrom` `start..` contains all values with `x >= start`. /// -/// *Note*: Currently, no overflow checking is done for the [`Iterator`] -/// implementation; if you use an integer range and the integer overflows, it -/// might panic in debug mode or create an endless loop in release mode. **This -/// overflow behavior might change in the future.** +/// *Note*: Overflow in the [`Iterator`] implementation (when the contained +/// data type reaches its numerical limit) is allowed to panic, wrap, or +/// saturate. This behavior is defined by the implementation of the [`Step`] +/// trait. For primitive integers, this follows the normal rules, and respects +/// the overflow checks profile (panic in debug, wrap in release). Note also +/// that overflow happens earlier than you might assume: the overflow happens +/// in the call to `next` that yields the maximum value, as the range must be +/// set to a state to yield the next value. +/// +/// [`Step`]: crate::iter::Step /// /// # Examples /// diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 52cf068f0a567..ab4f4aa7c73c8 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. @@ -1932,6 +1956,18 @@ fn test_range() { ); } +#[test] +fn test_char_range() { + use std::char; + assert!(('\0'..=char::MAX).eq((0..=char::MAX as u32).filter_map(char::from_u32))); + assert!(('\0'..=char::MAX).rev().eq((0..=char::MAX as u32).filter_map(char::from_u32).rev())); + + assert_eq!(('\u{D7FF}'..='\u{E000}').count(), 2); + assert_eq!(('\u{D7FF}'..='\u{E000}').size_hint(), (2, Some(2))); + assert_eq!(('\u{D7FF}'..'\u{E000}').count(), 1); + assert_eq!(('\u{D7FF}'..'\u{E000}').size_hint(), (1, Some(1))); +} + #[test] fn test_range_exhaustion() { let mut r = 10..10; 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_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_session/lint.rs b/src/librustc_session/lint.rs index b16d513d9239f..8a66fac1e3634 100644 --- a/src/librustc_session/lint.rs +++ b/src/librustc_session/lint.rs @@ -347,14 +347,14 @@ pub trait LintPass { fn name(&self) -> &'static str; } -/// Implements `LintPass for $name` with the given list of `Lint` statics. +/// Implements `LintPass for $ty` with the given list of `Lint` statics. #[macro_export] macro_rules! impl_lint_pass { - ($name:ident => [$($lint:expr),* $(,)?]) => { - impl $crate::lint::LintPass for $name { - fn name(&self) -> &'static str { stringify!($name) } + ($ty:ty => [$($lint:expr),* $(,)?]) => { + impl $crate::lint::LintPass for $ty { + fn name(&self) -> &'static str { stringify!($ty) } } - impl $name { + impl $ty { pub fn get_lints() -> $crate::lint::LintArray { $crate::lint_array!($($lint),*) } } }; 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/test/ui-fulldeps/lint-pass-macros.rs b/src/test/ui-fulldeps/lint-pass-macros.rs new file mode 100644 index 0000000000000..b3c2a542792f0 --- /dev/null +++ b/src/test/ui-fulldeps/lint-pass-macros.rs @@ -0,0 +1,26 @@ +// compile-flags: -Z unstable-options +// check-pass + +#![feature(rustc_private)] + +extern crate rustc_session; + +use rustc_session::lint::{LintArray, LintPass}; +use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; + +declare_lint! { + pub TEST_LINT, + Allow, + "test" +} + +struct Foo; + +struct Bar<'a>(&'a u32); + +impl_lint_pass!(Foo => [TEST_LINT]); +impl_lint_pass!(Bar<'_> => [TEST_LINT]); + +declare_lint_pass!(Baz => [TEST_LINT]); + +fn main() {} diff --git a/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs index 0d0765e971d50..b4a083636b64f 100644 --- a/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs +++ b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs @@ -18,6 +18,10 @@ pub fn yes_array_into_vec() -> Vec { [].into() } +pub fn yes_array_into_box() -> Box<[T]> { + [].into() +} + use std::collections::VecDeque; pub fn yes_vecdeque_partial_eq_array() -> impl PartialEq<[B; 32]> diff --git a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs index 4b195f3a06edc..48cf21d489ada 100644 --- a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs +++ b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs @@ -12,6 +12,8 @@ pub fn no_box() { let boxed_array = >::try_from(boxed_slice); //~^ ERROR the trait bound `std::boxed::Box<[i32; 33]>: std::convert::From>` is not satisfied //~^^ ERROR the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied + let boxed_slice = >::from([0; 33]); + //~^ 15:42: 15:49: arrays only have std trait implementations for lengths 0..=32 [E0277] } pub fn no_rc() { diff --git a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr index ce1c9ae551ea5..5c01603ab881c 100644 --- a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr +++ b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr @@ -18,10 +18,23 @@ LL | let boxed_array = >::try_from(boxed_slice); as std::convert::From<&str>> as std::convert::From>> as std::convert::From> - and 21 others + and 22 others = note: required because of the requirements on the impl of `std::convert::Into>` for `std::boxed::Box<[i32]>` = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::boxed::Box<[i32; 33]>` +error[E0277]: arrays only have std trait implementations for lengths 0..=32 + --> $DIR/alloc-types-no-impls-length-33.rs:15:42 + | +LL | let boxed_slice = >::from([0; 33]); + | ^^^^^^^ + | | + | expected an implementor of trait `std::convert::From<[{integer}; 33]>` + | help: consider borrowing here: `&[0; 33]` + | + = note: the trait bound `[i32; 33]: std::convert::From<[{integer}; 33]>` is not satisfied + = note: required because of the requirements on the impl of `std::convert::From<[i32; 33]>` for `std::boxed::Box<[i32]>` + = note: required by `std::convert::From::from` + error[E0277]: the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied --> $DIR/alloc-types-no-impls-length-33.rs:12:23 | @@ -32,7 +45,7 @@ LL | let boxed_array = >::try_from(boxed_slice); as std::convert::TryFrom>> error[E0277]: the trait bound `std::rc::Rc<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:19:23 + --> $DIR/alloc-types-no-impls-length-33.rs:21:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::rc::Rc<[i32; 33]>` @@ -47,7 +60,7 @@ LL | let boxed_array = >::try_from(boxed_slice); = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::rc::Rc<[i32; 33]>` error[E0277]: the trait bound `std::rc::Rc<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:19:23 + --> $DIR/alloc-types-no-impls-length-33.rs:21:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::rc::Rc<[i32; 33]>` @@ -56,7 +69,7 @@ LL | let boxed_array = >::try_from(boxed_slice); as std::convert::TryFrom>> error[E0277]: the trait bound `std::sync::Arc<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:26:23 + --> $DIR/alloc-types-no-impls-length-33.rs:28:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::sync::Arc<[i32; 33]>` @@ -71,7 +84,7 @@ LL | let boxed_array = >::try_from(boxed_slice); = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::sync::Arc<[i32; 33]>` error[E0277]: the trait bound `std::sync::Arc<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:26:23 + --> $DIR/alloc-types-no-impls-length-33.rs:28:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::sync::Arc<[i32; 33]>` @@ -79,6 +92,6 @@ LL | let boxed_array = >::try_from(boxed_slice); = help: the following implementations were found: as std::convert::TryFrom>> -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0277`. 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/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/unused-crate-deps/test-use-ok.rs b/src/test/ui/unused-crate-deps/test-use-ok.rs new file mode 100644 index 0000000000000..66d6440c9cb9f --- /dev/null +++ b/src/test/ui/unused-crate-deps/test-use-ok.rs @@ -0,0 +1,15 @@ +// Test-only use OK + +// edition:2018 +// check-pass +// aux-crate:bar=bar.rs +// compile-flags:--test + +#![deny(unused_crate_dependencies)] + +fn main() {} + +#[test] +fn test_bar() { + assert_eq!(bar::BAR, "bar"); +} diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 63fd052a5560d..a6995eb820a78 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1584,29 +1584,35 @@ impl<'test> TestCx<'test> { // // into // - // remote-test-client run program:support-lib.so arg1 arg2 + // remote-test-client run program 2 support-lib.so support-lib2.so arg1 arg2 // // The test-client program will upload `program` to the emulator // along with all other support libraries listed (in this case - // `support-lib.so`. It will then execute the program on the - // emulator with the arguments specified (in the environment we give - // the process) and then report back the same result. + // `support-lib.so` and `support-lib2.so`. It will then execute + // the program on the emulator with the arguments specified + // (in the environment we give the process) and then report back + // the same result. _ if self.config.remote_test_client.is_some() => { let aux_dir = self.aux_output_dir_name(); - let ProcArgs { mut prog, args } = self.make_run_args(); + let ProcArgs { prog, args } = self.make_run_args(); + let mut support_libs = Vec::new(); if let Ok(entries) = aux_dir.read_dir() { for entry in entries { let entry = entry.unwrap(); if !entry.path().is_file() { continue; } - prog.push_str(":"); - prog.push_str(entry.path().to_str().unwrap()); + support_libs.push(entry.path()); } } let mut test_client = Command::new(self.config.remote_test_client.as_ref().unwrap()); - test_client.args(&["run", &prog]).args(args).envs(env.clone()); + test_client + .args(&["run", &prog]) + .arg(support_libs.len().to_string()) + .args(support_libs) + .args(args) + .envs(env.clone()); self.compose_and_run( test_client, self.config.run_lib_path.to_str().unwrap(), diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs index 3379d82eda829..dea8bb933c9d9 100644 --- a/src/tools/remote-test-client/src/main.rs +++ b/src/tools/remote-test-client/src/main.rs @@ -44,7 +44,11 @@ fn main() { args.next().map(|s| s.into()), ), "push" => push(Path::new(&args.next().unwrap())), - "run" => run(args.next().unwrap(), args.collect()), + "run" => run( + args.next().unwrap(), + args.next().and_then(|count| count.parse().ok()).unwrap(), + args.collect(), + ), "help" | "-h" | "--help" => help(), cmd => { println!("unknown command: {}", cmd); @@ -197,12 +201,14 @@ fn push(path: &Path) { println!("done pushing {:?}", path); } -fn run(files: String, args: Vec) { +fn run(exe: String, support_lib_count: usize, all_args: Vec) { let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string()); let client = t!(TcpStream::connect(device_address)); let mut client = BufWriter::new(client); t!(client.write_all(b"run ")); + let (support_libs, args) = all_args.split_at(support_lib_count); + // Send over the args for arg in args { t!(client.write_all(arg.as_bytes())); @@ -227,9 +233,7 @@ fn run(files: String, args: Vec) { t!(client.write_all(&[0])); // Send over support libraries - let mut files = files.split(':'); - let exe = files.next().unwrap(); - for file in files.map(Path::new) { + for file in support_libs.iter().map(Path::new) { send(&file, &mut client); } t!(client.write_all(&[0])); diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 826e3d05111ae..e7eff35e55725 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -12,15 +12,19 @@ #![deny(warnings)] +#[cfg(not(windows))] +use std::fs::Permissions; +#[cfg(not(windows))] +use std::os::unix::prelude::*; + use std::cmp; use std::env; -use std::fs::{self, File, Permissions}; +use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufReader}; use std::net::{TcpListener, TcpStream}; -use std::os::unix::prelude::*; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::{Command, ExitStatus, Stdio}; use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; @@ -72,21 +76,23 @@ fn main() { let config = Config::parse_args(); - let bind_addr = if cfg!(target_os = "android") || config.remote { + let bind_addr = if cfg!(target_os = "android") || cfg!(windows) || config.remote { "0.0.0.0:12345" } else { "10.0.2.15:12345" }; - let (listener, work) = if cfg!(target_os = "android") { - (t!(TcpListener::bind(bind_addr)), "/data/tmp/work") + let listener = t!(TcpListener::bind(bind_addr)); + let work: PathBuf = if cfg!(target_os = "android") { + "/data/tmp/work".into() } else { - (t!(TcpListener::bind(bind_addr)), "/tmp/work") + let mut temp_dir = env::temp_dir(); + temp_dir.push("work"); + temp_dir }; println!("listening!"); - let work = Path::new(work); - t!(fs::create_dir_all(work)); + t!(fs::create_dir_all(&work)); let lock = Arc::new(Mutex::new(())); @@ -99,10 +105,11 @@ fn main() { if &buf[..] == b"ping" { t!(socket.write_all(b"pong")); } else if &buf[..] == b"push" { - handle_push(socket, work); + handle_push(socket, &work); } else if &buf[..] == b"run " { let lock = lock.clone(); - thread::spawn(move || handle_run(socket, work, &lock)); + let work = work.clone(); + thread::spawn(move || handle_run(socket, &work, &lock)); } else { panic!("unknown command {:?}", buf); } @@ -196,17 +203,28 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { let exe = recv(&path, &mut reader); let mut cmd = Command::new(&exe); - for arg in args { - cmd.arg(arg); - } - for (k, v) in env { - cmd.env(k, v); - } + cmd.args(args); + cmd.envs(env); // Support libraries were uploaded to `work` earlier, so make sure that's // in `LD_LIBRARY_PATH`. Also include our own current dir which may have // had some libs uploaded. - cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display())); + if cfg!(windows) { + // On windows, libraries are just searched in the executable directory, + // system directories, PWD, and PATH, in that order. PATH is the only one + // we can change for this. + cmd.env( + "PATH", + env::join_paths( + std::iter::once(work.to_owned()) + .chain(std::iter::once(path.clone())) + .chain(env::split_paths(&env::var_os("PATH").unwrap())), + ) + .unwrap(), + ); + } else { + cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display())); + } // Spawn the child and ferry over stdout/stderr to the socket in a framed // fashion (poor man's style) @@ -223,10 +241,9 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { // Finally send over the exit status. let status = t!(child.wait()); - let (which, code) = match status.code() { - Some(n) => (0, n), - None => (1, status.signal().unwrap()), - }; + + let (which, code) = get_status_code(&status); + t!(socket.lock().unwrap().write_all(&[ which, (code >> 24) as u8, @@ -236,6 +253,19 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { ])); } +#[cfg(not(windows))] +fn get_status_code(status: &ExitStatus) -> (u8, i32) { + match status.code() { + Some(n) => (0, n), + None => (1, status.signal().unwrap()), + } +} + +#[cfg(windows)] +fn get_status_code(status: &ExitStatus) -> (u8, i32) { + (0, status.code().unwrap()) +} + fn recv(dir: &Path, io: &mut B) -> PathBuf { let mut filename = Vec::new(); t!(io.read_until(0, &mut filename)); @@ -253,10 +283,17 @@ fn recv(dir: &Path, io: &mut B) -> PathBuf { let dst = dir.join(t!(str::from_utf8(&filename[..len]))); let amt = read_u32(io) as u64; t!(io::copy(&mut io.take(amt), &mut t!(File::create(&dst)))); - t!(fs::set_permissions(&dst, Permissions::from_mode(0o755))); + set_permissions(&dst); dst } +#[cfg(not(windows))] +fn set_permissions(path: &Path) { + t!(fs::set_permissions(&path, Permissions::from_mode(0o755))); +} +#[cfg(windows)] +fn set_permissions(_path: &Path) {} + fn my_copy(src: &mut dyn Read, which: u8, dst: &Mutex) { let mut b = [0; 1024]; loop {