From 14999dd74b38ca79b80772f4f33425574faff89a Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Wed, 29 Jan 2020 21:26:16 +0100 Subject: [PATCH 01/14] Add methods to leak RefCell borrows to references Usually, references to the interior are only created by the `Deref` and `DerefMut` impl of the guards `Ref` and `RefMut`. Note that `RefCell` already has to cope with leaks of such guards which, when it occurs, effectively makes it impossible to ever acquire a mutable guard or any guard for `Ref` and `RefMut` respectively. It is already safe to use this to create a reference to the inner of the ref cell that lives as long as the reference to the `RefCell` itself, e.g. ```rust fn leak(r: &RefCell) -> Option<&usize> { let guard = r.try_borrow().ok()?; let leaked = Box::leak(Box::new(guard)); Some(&*leaked) } ``` The newly added methods allow the same reference conversion without an indirection over a leaked allocation and composing with both borrow and try_borrow without additional method combinations. --- src/libcore/cell.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index e7eecf7540ad7..b1d799953e710 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -1245,6 +1245,38 @@ impl<'b, T: ?Sized> Ref<'b, T> { let borrow = orig.borrow.clone(); (Ref { value: a, borrow }, Ref { value: b, borrow: orig.borrow }) } + + /// Convert into a reference to the underlying data. + /// + /// The underlying `RefCell` can never be mutably borrowed from again and will always appear + /// already immutably borrowed. It can still be immutably borrowed until more than `isize::MAX` + /// `Ref`s of this `RefCell` have been leaked, through this function or another leak, in total. + /// + /// This is an associated function that needs to be used as + /// `Ref::leak(...)`. A method would interfere with methods of the + /// same name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_leak)] + /// use std::cell::{RefCell, Ref}; + /// let cell = RefCell::new(0); + /// + /// let value = Ref::leak(cell.borrow()); + /// assert_eq!(*value, 0); + /// + /// assert!(cell.try_borrow().is_ok()); + /// assert!(cell.try_borrow_mut().is_err()); + /// ``` + #[unstable(feature = "cell_leak", issue = "none")] + pub fn leak(orig: Ref<'b, T>) -> &'b T { + // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell never + // goes back to UNUSED again. No further references can be created from the original cell, + // making the current borrow the only reference for the remaining lifetime. + mem::forget(orig.borrow); + orig.value + } } #[unstable(feature = "coerce_unsized", issue = "27732")] @@ -1330,6 +1362,37 @@ impl<'b, T: ?Sized> RefMut<'b, T> { let borrow = orig.borrow.clone(); (RefMut { value: a, borrow }, RefMut { value: b, borrow: orig.borrow }) } + + /// Convert into a mutable reference to the underlying data. + /// + /// The underlying `RefCell` can not be borrowed from again and will always appear already + /// mutably borrowed, making the returned reference the only to the interior. + /// + /// This is an associated function that needs to be used as + /// `RefMut::leak(...)`. A method would interfere with methods of the + /// same name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_leak)] + /// use std::cell::{RefCell, RefMut}; + /// let cell = RefCell::new(0); + /// + /// let value = RefMut::leak(cell.borrow_mut()); + /// assert_eq!(*value, 0); + /// *value = 1; + /// + /// assert!(cell.try_borrow_mut().is_err()); + /// ``` + #[unstable(feature = "cell_leak", issue = "none")] + pub fn leak(orig: RefMut<'b, T>) -> &'b mut T { + // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell never + // goes back to UNUSED again. No further references can be created from the original cell, + // making the current borrow the only reference for the remaining lifetime. + mem::forget(orig.borrow); + orig.value + } } struct BorrowRefMut<'b> { From 99b4357f1763f7d98b9b78221207e09d075513b1 Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Wed, 12 Feb 2020 16:56:09 +0100 Subject: [PATCH 02/14] Add tracking number, adjust documentation wording --- src/libcore/cell.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index b1d799953e710..17222b27b2d70 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -1249,8 +1249,9 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// Convert into a reference to the underlying data. /// /// The underlying `RefCell` can never be mutably borrowed from again and will always appear - /// already immutably borrowed. It can still be immutably borrowed until more than `isize::MAX` - /// `Ref`s of this `RefCell` have been leaked, through this function or another leak, in total. + /// already immutably borrowed. It is not a good idea to leak more than a constant number of + /// references. The `RefCell` can be immutably borrowed again if only a smaller number of leaks + /// have occurred in total. /// /// This is an associated function that needs to be used as /// `Ref::leak(...)`. A method would interfere with methods of the @@ -1269,7 +1270,7 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// assert!(cell.try_borrow().is_ok()); /// assert!(cell.try_borrow_mut().is_err()); /// ``` - #[unstable(feature = "cell_leak", issue = "none")] + #[unstable(feature = "cell_leak", issue = "69099")] pub fn leak(orig: Ref<'b, T>) -> &'b T { // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell never // goes back to UNUSED again. No further references can be created from the original cell, @@ -1385,7 +1386,7 @@ impl<'b, T: ?Sized> RefMut<'b, T> { /// /// assert!(cell.try_borrow_mut().is_err()); /// ``` - #[unstable(feature = "cell_leak", issue = "none")] + #[unstable(feature = "cell_leak", issue = "69099")] pub fn leak(orig: RefMut<'b, T>) -> &'b mut T { // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell never // goes back to UNUSED again. No further references can be created from the original cell, From bd12cd3d2fe2fd69cff05d4b710c8020dea2cdf7 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 15 Feb 2020 18:06:39 -0500 Subject: [PATCH 03/14] Formatter::sign is &'static str The contents were always UTF-8 anyway, and &str has an equivalent representation to &[u8], so this should not affect performance while removing unsafety at edges. It may be worth exploring a further adjustment that stores a single byte (instead of 16) as the contents are always "", "-", or "+". --- src/libcore/fmt/mod.rs | 6 +++--- src/libcore/fmt/num.rs | 6 +++--- src/libcore/num/flt2dec/mod.rs | 30 +++++++++++++++--------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 973c2f2b91555..3f2c965470652 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1356,11 +1356,11 @@ impl<'a> Formatter<'a> { let mut align = old_align; if self.sign_aware_zero_pad() { // a sign always goes first - let sign = unsafe { str::from_utf8_unchecked(formatted.sign) }; + let sign = formatted.sign; self.buf.write_str(sign)?; // remove the sign from the formatted parts - formatted.sign = b""; + formatted.sign = ""; width = width.saturating_sub(sign.len()); align = rt::v1::Alignment::Right; self.fill = '0'; @@ -1392,7 +1392,7 @@ impl<'a> Formatter<'a> { } if !formatted.sign.is_empty() { - write_bytes(self.buf, formatted.sign)?; + self.buf.write_str(formatted.sign)?; } for part in formatted.parts { match *part { diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index c187471fb5faa..5dfd3a8ecdbd6 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -369,11 +369,11 @@ macro_rules! impl_Exp { flt2dec::Part::Copy(exp_slice) ]; let sign = if !is_nonnegative { - &b"-"[..] + "-" } else if f.sign_plus() { - &b"+"[..] + "+" } else { - &b""[..] + "" }; let formatted = flt2dec::Formatted{sign, parts}; f.pad_formatted_parts(&formatted) diff --git a/src/libcore/num/flt2dec/mod.rs b/src/libcore/num/flt2dec/mod.rs index 9e760c13c0cf0..93a2348447e15 100644 --- a/src/libcore/num/flt2dec/mod.rs +++ b/src/libcore/num/flt2dec/mod.rs @@ -237,7 +237,7 @@ impl<'a> Part<'a> { #[derive(Clone)] pub struct Formatted<'a> { /// A byte slice representing a sign, either `""`, `"-"` or `"+"`. - pub sign: &'static [u8], + pub sign: &'static str, /// Formatted parts to be rendered after a sign and optional zero padding. pub parts: &'a [Part<'a>], } @@ -259,7 +259,7 @@ impl<'a> Formatted<'a> { if out.len() < self.sign.len() { return None; } - out[..self.sign.len()].copy_from_slice(self.sign); + out[..self.sign.len()].copy_from_slice(self.sign.as_bytes()); let mut written = self.sign.len(); for part in self.parts { @@ -402,38 +402,38 @@ pub enum Sign { } /// Returns the static byte string corresponding to the sign to be formatted. -/// It can be either `b""`, `b"+"` or `b"-"`. -fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static [u8] { +/// It can be either `""`, `"+"` or `"-"`. +fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static str { match (*decoded, sign) { - (FullDecoded::Nan, _) => b"", - (FullDecoded::Zero, Sign::Minus) => b"", + (FullDecoded::Nan, _) => "", + (FullDecoded::Zero, Sign::Minus) => "", (FullDecoded::Zero, Sign::MinusRaw) => { if negative { - b"-" + "-" } else { - b"" + "" } } - (FullDecoded::Zero, Sign::MinusPlus) => b"+", + (FullDecoded::Zero, Sign::MinusPlus) => "+", (FullDecoded::Zero, Sign::MinusPlusRaw) => { if negative { - b"-" + "-" } else { - b"+" + "+" } } (_, Sign::Minus) | (_, Sign::MinusRaw) => { if negative { - b"-" + "-" } else { - b"" + "" } } (_, Sign::MinusPlus) | (_, Sign::MinusPlusRaw) => { if negative { - b"-" + "-" } else { - b"+" + "+" } } } From 6c45e4540be20b7027ee6364eefc444b432b82ba Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 15 Feb 2020 18:18:13 -0500 Subject: [PATCH 04/14] Drop unused argument to float functions --- src/libcore/fmt/float.rs | 2 - src/libcore/num/flt2dec/mod.rs | 2 - src/libcore/tests/num/flt2dec/mod.rs | 460 +++++++++++++-------------- 3 files changed, 224 insertions(+), 240 deletions(-) diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs index 284e94926dc85..5ef673009bb6d 100644 --- a/src/libcore/fmt/float.rs +++ b/src/libcore/fmt/float.rs @@ -29,7 +29,6 @@ where *num, sign, precision, - false, buf.get_mut(), parts.get_mut(), ); @@ -59,7 +58,6 @@ where *num, sign, precision, - false, buf.get_mut(), parts.get_mut(), ); diff --git a/src/libcore/num/flt2dec/mod.rs b/src/libcore/num/flt2dec/mod.rs index 93a2348447e15..f5cd26a1852d6 100644 --- a/src/libcore/num/flt2dec/mod.rs +++ b/src/libcore/num/flt2dec/mod.rs @@ -462,7 +462,6 @@ pub fn to_shortest_str<'a, T, F>( v: T, sign: Sign, frac_digits: usize, - _upper: bool, buf: &'a mut [u8], parts: &'a mut [Part<'a>], ) -> Formatted<'a> @@ -679,7 +678,6 @@ pub fn to_exact_fixed_str<'a, T, F>( v: T, sign: Sign, frac_digits: usize, - _upper: bool, buf: &'a mut [u8], parts: &'a mut [Part<'a>], ) -> Formatted<'a> diff --git a/src/libcore/tests/num/flt2dec/mod.rs b/src/libcore/tests/num/flt2dec/mod.rs index f693504824641..e945d9c4a54ce 100644 --- a/src/libcore/tests/num/flt2dec/mod.rs +++ b/src/libcore/tests/num/flt2dec/mod.rs @@ -500,94 +500,91 @@ where { use core::num::flt2dec::Sign::*; - fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize, upper: bool) -> String + fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), { to_string_with_parts(|buf, parts| { - to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, upper, buf, parts) + to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, buf, parts) }) } let f = &mut f_; - assert_eq!(to_string(f, 0.0, Minus, 0, false), "0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 0, false), "0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 0, false), "+0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0, false), "+0"); - assert_eq!(to_string(f, -0.0, Minus, 0, false), "0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 0, false), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 0, false), "+0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0, false), "-0"); - assert_eq!(to_string(f, 0.0, Minus, 1, true), "0.0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 1, true), "0.0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1, true), "+0.0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1, true), "+0.0"); - assert_eq!(to_string(f, -0.0, Minus, 8, true), "0.00000000"); - assert_eq!(to_string(f, -0.0, MinusRaw, 8, true), "-0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8, true), "+0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, true), "-0.00000000"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0, false), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 0, true), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 0, false), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 0, true), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1, true), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64, true), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1, true), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64, true), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, 0, false), "3.14"); - assert_eq!(to_string(f, 3.14, MinusRaw, 0, false), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 0, false), "+3.14"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0, false), "+3.14"); - assert_eq!(to_string(f, -3.14, Minus, 0, false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusRaw, 0, false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlus, 0, false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0, false), "-3.14"); - assert_eq!(to_string(f, 3.14, Minus, 1, true), "3.14"); - assert_eq!(to_string(f, 3.14, MinusRaw, 2, true), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3, true), "+3.140"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4, true), "+3.1400"); - assert_eq!(to_string(f, -3.14, Minus, 8, true), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusRaw, 8, true), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlus, 8, true), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8, true), "-3.14000000"); - - assert_eq!(to_string(f, 7.5e-11, Minus, 0, false), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 3, false), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 12, false), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 13, false), "0.0000000000750"); - - assert_eq!(to_string(f, 1.9971e20, Minus, 0, false), "199710000000000000000"); - assert_eq!(to_string(f, 1.9971e20, Minus, 1, false), "199710000000000000000.0"); - assert_eq!(to_string(f, 1.9971e20, Minus, 8, false), "199710000000000000000.00000000"); - - assert_eq!(to_string(f, f32::MAX, Minus, 0, false), format!("34028235{:0>31}", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 1, false), format!("34028235{:0>31}.0", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 8, false), format!("34028235{:0>31}.00000000", "")); + assert_eq!(to_string(f, 0.0, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0), "+0"); + assert_eq!(to_string(f, -0.0, Minus, 0), "0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1), "+0.0"); + assert_eq!(to_string(f, -0.0, Minus, 8), "0.00000000"); + assert_eq!(to_string(f, -0.0, MinusRaw, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8), "+0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8), "-0.00000000"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 0), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 0), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); + assert_eq!(to_string(f, 3.14, MinusRaw, 0), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3.14"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0), "+3.14"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusRaw, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0), "-3.14"); + assert_eq!(to_string(f, 3.14, Minus, 1), "3.14"); + assert_eq!(to_string(f, 3.14, MinusRaw, 2), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3), "+3.140"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusRaw, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8), "-3.14000000"); + + assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); + + assert_eq!(to_string(f, 1.9971e20, Minus, 0), "199710000000000000000"); + assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0"); + assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000"); + + assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0, false), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 45, false), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 46, false), format!("0.{:0>44}10", "")); + assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 0, false), format!("17976931348623157{:0>292}", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 1, false), format!("17976931348623157{:0>292}.0", "")); - assert_eq!( - to_string(f, f64::MAX, Minus, 8, false), - format!("17976931348623157{:0>292}.00000000", "") - ); + assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", "")); let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0, false), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 324, false), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 325, false), format!("0.{:0>323}50", "")); + assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); if cfg!(miri) { // Miri is too slow @@ -595,7 +592,7 @@ where } // very large output - assert_eq!(to_string(f, 1.1, Minus, 80000, false), format!("1.1{:0>79999}", "")); + assert_eq!(to_string(f, 1.1, Minus, 80000), format!("1.1{:0>79999}", "")); } pub fn to_shortest_exp_str_test(mut f_: F) @@ -996,166 +993,157 @@ where { use core::num::flt2dec::Sign::*; - fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize, upper: bool) -> String + fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), { to_string_with_parts(|buf, parts| { - to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, upper, buf, parts) + to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, buf, parts) }) } let f = &mut f_; - assert_eq!(to_string(f, 0.0, Minus, 0, false), "0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 0, false), "0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 0, false), "+0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0, false), "+0"); - assert_eq!(to_string(f, -0.0, Minus, 0, false), "0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 0, false), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 0, false), "+0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0, false), "-0"); - assert_eq!(to_string(f, 0.0, Minus, 1, true), "0.0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 1, true), "0.0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1, true), "+0.0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1, true), "+0.0"); - assert_eq!(to_string(f, -0.0, Minus, 8, true), "0.00000000"); - assert_eq!(to_string(f, -0.0, MinusRaw, 8, true), "-0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8, true), "+0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, true), "-0.00000000"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0, false), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 1, true), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 8, false), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 64, true), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1, true), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64, true), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1, true), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64, true), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, 0, false), "3"); - assert_eq!(to_string(f, 3.14, MinusRaw, 0, false), "3"); - assert_eq!(to_string(f, 3.14, MinusPlus, 0, false), "+3"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0, false), "+3"); - assert_eq!(to_string(f, -3.14, Minus, 0, false), "-3"); - assert_eq!(to_string(f, -3.14, MinusRaw, 0, false), "-3"); - assert_eq!(to_string(f, -3.14, MinusPlus, 0, false), "-3"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0, false), "-3"); - assert_eq!(to_string(f, 3.14, Minus, 1, true), "3.1"); - assert_eq!(to_string(f, 3.14, MinusRaw, 2, true), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3, true), "+3.140"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4, true), "+3.1400"); - assert_eq!(to_string(f, -3.14, Minus, 8, true), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusRaw, 8, true), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlus, 8, true), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8, true), "-3.14000000"); - - assert_eq!(to_string(f, 0.195, Minus, 0, false), "0"); - assert_eq!(to_string(f, 0.195, MinusRaw, 0, false), "0"); - assert_eq!(to_string(f, 0.195, MinusPlus, 0, false), "+0"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 0, false), "+0"); - assert_eq!(to_string(f, -0.195, Minus, 0, false), "-0"); - assert_eq!(to_string(f, -0.195, MinusRaw, 0, false), "-0"); - assert_eq!(to_string(f, -0.195, MinusPlus, 0, false), "-0"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 0, false), "-0"); - assert_eq!(to_string(f, 0.195, Minus, 1, true), "0.2"); - assert_eq!(to_string(f, 0.195, MinusRaw, 2, true), "0.20"); - assert_eq!(to_string(f, 0.195, MinusPlus, 3, true), "+0.195"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 4, true), "+0.1950"); - assert_eq!(to_string(f, -0.195, Minus, 5, true), "-0.19500"); - assert_eq!(to_string(f, -0.195, MinusRaw, 6, true), "-0.195000"); - assert_eq!(to_string(f, -0.195, MinusPlus, 7, true), "-0.1950000"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 8, true), "-0.19500000"); - - assert_eq!(to_string(f, 999.5, Minus, 0, false), "1000"); - assert_eq!(to_string(f, 999.5, Minus, 1, false), "999.5"); - assert_eq!(to_string(f, 999.5, Minus, 2, false), "999.50"); - assert_eq!(to_string(f, 999.5, Minus, 3, false), "999.500"); - assert_eq!(to_string(f, 999.5, Minus, 30, false), "999.500000000000000000000000000000"); - - assert_eq!(to_string(f, 0.5, Minus, 0, false), "1"); - assert_eq!(to_string(f, 0.5, Minus, 1, false), "0.5"); - assert_eq!(to_string(f, 0.5, Minus, 2, false), "0.50"); - assert_eq!(to_string(f, 0.5, Minus, 3, false), "0.500"); - - assert_eq!(to_string(f, 0.95, Minus, 0, false), "1"); - assert_eq!(to_string(f, 0.95, Minus, 1, false), "0.9"); // because it really is less than 0.95 - assert_eq!(to_string(f, 0.95, Minus, 2, false), "0.95"); - assert_eq!(to_string(f, 0.95, Minus, 3, false), "0.950"); - assert_eq!(to_string(f, 0.95, Minus, 10, false), "0.9500000000"); - assert_eq!(to_string(f, 0.95, Minus, 30, false), "0.949999999999999955591079014994"); - - assert_eq!(to_string(f, 0.095, Minus, 0, false), "0"); - assert_eq!(to_string(f, 0.095, Minus, 1, false), "0.1"); - assert_eq!(to_string(f, 0.095, Minus, 2, false), "0.10"); - assert_eq!(to_string(f, 0.095, Minus, 3, false), "0.095"); - assert_eq!(to_string(f, 0.095, Minus, 4, false), "0.0950"); - assert_eq!(to_string(f, 0.095, Minus, 10, false), "0.0950000000"); - assert_eq!(to_string(f, 0.095, Minus, 30, false), "0.095000000000000001110223024625"); - - assert_eq!(to_string(f, 0.0095, Minus, 0, false), "0"); - assert_eq!(to_string(f, 0.0095, Minus, 1, false), "0.0"); - assert_eq!(to_string(f, 0.0095, Minus, 2, false), "0.01"); - assert_eq!(to_string(f, 0.0095, Minus, 3, false), "0.009"); // really is less than 0.0095 - assert_eq!(to_string(f, 0.0095, Minus, 4, false), "0.0095"); - assert_eq!(to_string(f, 0.0095, Minus, 5, false), "0.00950"); - assert_eq!(to_string(f, 0.0095, Minus, 10, false), "0.0095000000"); - assert_eq!(to_string(f, 0.0095, Minus, 30, false), "0.009499999999999999764077607267"); - - assert_eq!(to_string(f, 7.5e-11, Minus, 0, false), "0"); - assert_eq!(to_string(f, 7.5e-11, Minus, 3, false), "0.000"); - assert_eq!(to_string(f, 7.5e-11, Minus, 10, false), "0.0000000001"); - assert_eq!(to_string(f, 7.5e-11, Minus, 11, false), "0.00000000007"); // ditto - assert_eq!(to_string(f, 7.5e-11, Minus, 12, false), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 13, false), "0.0000000000750"); - assert_eq!(to_string(f, 7.5e-11, Minus, 20, false), "0.00000000007500000000"); - assert_eq!(to_string(f, 7.5e-11, Minus, 30, false), "0.000000000074999999999999999501"); - - assert_eq!(to_string(f, 1.0e25, Minus, 0, false), "10000000000000000905969664"); - assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "10000000000000000905969664.0"); - assert_eq!(to_string(f, 1.0e25, Minus, 3, false), "10000000000000000905969664.000"); - - assert_eq!(to_string(f, 1.0e-6, Minus, 0, false), "0"); - assert_eq!(to_string(f, 1.0e-6, Minus, 3, false), "0.000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 6, false), "0.000001"); - assert_eq!(to_string(f, 1.0e-6, Minus, 9, false), "0.000001000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 12, false), "0.000001000000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 22, false), "0.0000010000000000000000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 23, false), "0.00000099999999999999995"); - assert_eq!(to_string(f, 1.0e-6, Minus, 24, false), "0.000000999999999999999955"); - assert_eq!(to_string(f, 1.0e-6, Minus, 25, false), "0.0000009999999999999999547"); - assert_eq!(to_string(f, 1.0e-6, Minus, 35, false), "0.00000099999999999999995474811182589"); + assert_eq!(to_string(f, 0.0, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0), "+0"); + assert_eq!(to_string(f, -0.0, Minus, 0), "0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1), "+0.0"); + assert_eq!(to_string(f, -0.0, Minus, 8), "0.00000000"); + assert_eq!(to_string(f, -0.0, MinusRaw, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8), "+0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8), "-0.00000000"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 1), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 8), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 64), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, 0), "3"); + assert_eq!(to_string(f, 3.14, MinusRaw, 0), "3"); + assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0), "+3"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); + assert_eq!(to_string(f, -3.14, MinusRaw, 0), "-3"); + assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0), "-3"); + assert_eq!(to_string(f, 3.14, Minus, 1), "3.1"); + assert_eq!(to_string(f, 3.14, MinusRaw, 2), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3), "+3.140"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusRaw, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8), "-3.14000000"); + + assert_eq!(to_string(f, 0.195, Minus, 0), "0"); + assert_eq!(to_string(f, 0.195, MinusRaw, 0), "0"); + assert_eq!(to_string(f, 0.195, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 0), "+0"); + assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); + assert_eq!(to_string(f, -0.195, MinusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.195, MinusPlus, 0), "-0"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 0), "-0"); + assert_eq!(to_string(f, 0.195, Minus, 1), "0.2"); + assert_eq!(to_string(f, 0.195, MinusRaw, 2), "0.20"); + assert_eq!(to_string(f, 0.195, MinusPlus, 3), "+0.195"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 4), "+0.1950"); + assert_eq!(to_string(f, -0.195, Minus, 5), "-0.19500"); + assert_eq!(to_string(f, -0.195, MinusRaw, 6), "-0.195000"); + assert_eq!(to_string(f, -0.195, MinusPlus, 7), "-0.1950000"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 8), "-0.19500000"); + + assert_eq!(to_string(f, 999.5, Minus, 0), "1000"); + assert_eq!(to_string(f, 999.5, Minus, 1), "999.5"); + assert_eq!(to_string(f, 999.5, Minus, 2), "999.50"); + assert_eq!(to_string(f, 999.5, Minus, 3), "999.500"); + assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000"); + + assert_eq!(to_string(f, 0.5, Minus, 0), "1"); + assert_eq!(to_string(f, 0.5, Minus, 1), "0.5"); + assert_eq!(to_string(f, 0.5, Minus, 2), "0.50"); + assert_eq!(to_string(f, 0.5, Minus, 3), "0.500"); + + assert_eq!(to_string(f, 0.95, Minus, 0), "1"); + assert_eq!(to_string(f, 0.95, Minus, 1), "0.9"); // because it really is less than 0.95 + assert_eq!(to_string(f, 0.95, Minus, 2), "0.95"); + assert_eq!(to_string(f, 0.95, Minus, 3), "0.950"); + assert_eq!(to_string(f, 0.95, Minus, 10), "0.9500000000"); + assert_eq!(to_string(f, 0.95, Minus, 30), "0.949999999999999955591079014994"); + + assert_eq!(to_string(f, 0.095, Minus, 0), "0"); + assert_eq!(to_string(f, 0.095, Minus, 1), "0.1"); + assert_eq!(to_string(f, 0.095, Minus, 2), "0.10"); + assert_eq!(to_string(f, 0.095, Minus, 3), "0.095"); + assert_eq!(to_string(f, 0.095, Minus, 4), "0.0950"); + assert_eq!(to_string(f, 0.095, Minus, 10), "0.0950000000"); + assert_eq!(to_string(f, 0.095, Minus, 30), "0.095000000000000001110223024625"); + + assert_eq!(to_string(f, 0.0095, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0095, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0095, Minus, 2), "0.01"); + assert_eq!(to_string(f, 0.0095, Minus, 3), "0.009"); // really is less than 0.0095 + assert_eq!(to_string(f, 0.0095, Minus, 4), "0.0095"); + assert_eq!(to_string(f, 0.0095, Minus, 5), "0.00950"); + assert_eq!(to_string(f, 0.0095, Minus, 10), "0.0095000000"); + assert_eq!(to_string(f, 0.0095, Minus, 30), "0.009499999999999999764077607267"); + + assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0"); + assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000"); + assert_eq!(to_string(f, 7.5e-11, Minus, 10), "0.0000000001"); + assert_eq!(to_string(f, 7.5e-11, Minus, 11), "0.00000000007"); // ditto + assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); + assert_eq!(to_string(f, 7.5e-11, Minus, 20), "0.00000000007500000000"); + assert_eq!(to_string(f, 7.5e-11, Minus, 30), "0.000000000074999999999999999501"); + + assert_eq!(to_string(f, 1.0e25, Minus, 0), "10000000000000000905969664"); + assert_eq!(to_string(f, 1.0e25, Minus, 1), "10000000000000000905969664.0"); + assert_eq!(to_string(f, 1.0e25, Minus, 3), "10000000000000000905969664.000"); + + assert_eq!(to_string(f, 1.0e-6, Minus, 0), "0"); + assert_eq!(to_string(f, 1.0e-6, Minus, 3), "0.000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 6), "0.000001"); + assert_eq!(to_string(f, 1.0e-6, Minus, 9), "0.000001000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 12), "0.000001000000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 22), "0.0000010000000000000000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 23), "0.00000099999999999999995"); + assert_eq!(to_string(f, 1.0e-6, Minus, 24), "0.000000999999999999999955"); + assert_eq!(to_string(f, 1.0e-6, Minus, 25), "0.0000009999999999999999547"); + assert_eq!(to_string(f, 1.0e-6, Minus, 35), "0.00000099999999999999995474811182589"); + assert_eq!(to_string(f, 1.0e-6, Minus, 45), "0.000000999999999999999954748111825886258685614"); assert_eq!( - to_string(f, 1.0e-6, Minus, 45, false), - "0.000000999999999999999954748111825886258685614" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 55, false), + to_string(f, 1.0e-6, Minus, 55), "0.0000009999999999999999547481118258862586856139387236908" ); assert_eq!( - to_string(f, 1.0e-6, Minus, 65, false), + to_string(f, 1.0e-6, Minus, 65), "0.00000099999999999999995474811182588625868561393872369080781936646" ); assert_eq!( - to_string(f, 1.0e-6, Minus, 75, false), + to_string(f, 1.0e-6, Minus, 75), "0.000000999999999999999954748111825886258685613938723690807819366455078125000" ); - assert_eq!(to_string(f, f32::MAX, Minus, 0, false), "340282346638528859811704183484516925440"); - assert_eq!( - to_string(f, f32::MAX, Minus, 1, false), - "340282346638528859811704183484516925440.0" - ); - assert_eq!( - to_string(f, f32::MAX, Minus, 2, false), - "340282346638528859811704183484516925440.00" - ); + assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440"); + assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0"); + assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00"); if cfg!(miri) { // Miri is too slow @@ -1163,24 +1151,24 @@ where } let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0, false), "0"); - assert_eq!(to_string(f, minf32, Minus, 1, false), "0.0"); - assert_eq!(to_string(f, minf32, Minus, 2, false), "0.00"); - assert_eq!(to_string(f, minf32, Minus, 4, false), "0.0000"); - assert_eq!(to_string(f, minf32, Minus, 8, false), "0.00000000"); - assert_eq!(to_string(f, minf32, Minus, 16, false), "0.0000000000000000"); - assert_eq!(to_string(f, minf32, Minus, 32, false), "0.00000000000000000000000000000000"); + assert_eq!(to_string(f, minf32, Minus, 0), "0"); + assert_eq!(to_string(f, minf32, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf32, Minus, 2), "0.00"); + assert_eq!(to_string(f, minf32, Minus, 4), "0.0000"); + assert_eq!(to_string(f, minf32, Minus, 8), "0.00000000"); + assert_eq!(to_string(f, minf32, Minus, 16), "0.0000000000000000"); + assert_eq!(to_string(f, minf32, Minus, 32), "0.00000000000000000000000000000000"); assert_eq!( - to_string(f, minf32, Minus, 64, false), + to_string(f, minf32, Minus, 64), "0.0000000000000000000000000000000000000000000014012984643248170709" ); assert_eq!( - to_string(f, minf32, Minus, 128, false), + to_string(f, minf32, Minus, 128), "0.0000000000000000000000000000000000000000000014012984643248170709\ 2372958328991613128026194187651577175706828388979108268586060149" ); assert_eq!( - to_string(f, minf32, Minus, 256, false), + to_string(f, minf32, Minus, 256), "0.0000000000000000000000000000000000000000000014012984643248170709\ 2372958328991613128026194187651577175706828388979108268586060148\ 6638188362121582031250000000000000000000000000000000000000000000\ @@ -1188,7 +1176,7 @@ where ); assert_eq!( - to_string(f, f64::MAX, Minus, 0, false), + to_string(f, f64::MAX, Minus, 0), "1797693134862315708145274237317043567980705675258449965989174768\ 0315726078002853876058955863276687817154045895351438246423432132\ 6889464182768467546703537516986049910576551282076245490090389328\ @@ -1196,7 +1184,7 @@ where 26204144723168738177180919299881250404026184124858368" ); assert_eq!( - to_string(f, f64::MAX, Minus, 10, false), + to_string(f, f64::MAX, Minus, 10), "1797693134862315708145274237317043567980705675258449965989174768\ 0315726078002853876058955863276687817154045895351438246423432132\ 6889464182768467546703537516986049910576551282076245490090389328\ @@ -1205,16 +1193,16 @@ where ); let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0, false), "0"); - assert_eq!(to_string(f, minf64, Minus, 1, false), "0.0"); - assert_eq!(to_string(f, minf64, Minus, 10, false), "0.0000000000"); + assert_eq!(to_string(f, minf64, Minus, 0), "0"); + assert_eq!(to_string(f, minf64, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf64, Minus, 10), "0.0000000000"); assert_eq!( - to_string(f, minf64, Minus, 100, false), + to_string(f, minf64, Minus, 100), "0.0000000000000000000000000000000000000000000000000000000000000000\ 000000000000000000000000000000000000" ); assert_eq!( - to_string(f, minf64, Minus, 1000, false), + to_string(f, minf64, Minus, 1000), "0.0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000\ @@ -1234,15 +1222,15 @@ where ); // very large output - assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>80000}", "")); - assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("10.{:0>80000}", "")); - assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>80000}", "")); + assert_eq!(to_string(f, 0.0, Minus, 80000), format!("0.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 80000), format!("10.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 80000), format!("1.{:0>80000}", "")); assert_eq!( - to_string(f, 1.0e-1, Minus, 80000, false), + to_string(f, 1.0e-1, Minus, 80000), format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "") ); assert_eq!( - to_string(f, 1.0e-20, Minus, 80000, false), + to_string(f, 1.0e-20, Minus, 80000), format!( "0.0000000000000000000099999999999999994515327145420957165172950370\ 2787392447107715776066783064379706047475337982177734375{:0>79881}", From 34ef8f5441d5335c4177abd622383ed34a6e9315 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 15 Feb 2020 18:47:34 -0500 Subject: [PATCH 05/14] Move to using an extern type for opaqueness This prevents accidental dereferences and so forth of the Void type, as well as cleaning up the error message to reference Opaque rather than the more complicated PhantomData type. --- src/libcore/fmt/mod.rs | 16 ++++------------ src/test/ui/fmt/send-sync.stderr | 20 ++++++++------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 3f2c965470652..0c51a802faba3 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -238,16 +238,8 @@ pub struct Formatter<'a> { // NB. Argument is essentially an optimized partially applied formatting function, // equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. -struct Void { - _priv: (), - /// Erases all oibits, because `Void` erases the type of the object that - /// will be used to produce formatted output. Since we do not know what - /// oibits the real types have (and they can have any or none), we need to - /// take the most conservative approach and forbid all oibits. - /// - /// It was added after #45197 showed that one could share a `!Sync` - /// object across threads by passing it into `format_args!`. - _oibit_remover: PhantomData<*mut dyn Fn()>, +extern "C" { + type Opaque; } /// This struct represents the generic "argument" which is taken by the Xprintf @@ -259,8 +251,8 @@ struct Void { #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] #[doc(hidden)] pub struct ArgumentV1<'a> { - value: &'a Void, - formatter: fn(&Void, &mut Formatter<'_>) -> Result, + value: &'a Opaque, + formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, } impl<'a> ArgumentV1<'a> { diff --git a/src/test/ui/fmt/send-sync.stderr b/src/test/ui/fmt/send-sync.stderr index be6e41afaf811..c8439764effc3 100644 --- a/src/test/ui/fmt/send-sync.stderr +++ b/src/test/ui/fmt/send-sync.stderr @@ -1,34 +1,30 @@ -error[E0277]: `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely +error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely --> $DIR/send-sync.rs:8:5 | LL | fn send(_: T) {} | ---- ---- required by this bound in `send` ... LL | send(format_args!("{:?}", c)); - | ^^^^ `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely + | ^^^^ `core::fmt::Opaque` cannot be shared between threads safely | - = help: within `[std::fmt::ArgumentV1<'_>]`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)` - = note: required because it appears within the type `std::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>` - = note: required because it appears within the type `core::fmt::Void` - = note: required because it appears within the type `&core::fmt::Void` + = help: within `[std::fmt::ArgumentV1<'_>]`, the trait `std::marker::Sync` is not implemented for `core::fmt::Opaque` + = note: required because it appears within the type `&core::fmt::Opaque` = note: required because it appears within the type `std::fmt::ArgumentV1<'_>` = note: required because it appears within the type `[std::fmt::ArgumentV1<'_>]` = note: required because of the requirements on the impl of `std::marker::Send` for `&[std::fmt::ArgumentV1<'_>]` = note: required because it appears within the type `std::fmt::Arguments<'_>` -error[E0277]: `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely +error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely --> $DIR/send-sync.rs:9:5 | LL | fn sync(_: T) {} | ---- ---- required by this bound in `sync` ... LL | sync(format_args!("{:?}", c)); - | ^^^^ `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely + | ^^^^ `core::fmt::Opaque` cannot be shared between threads safely | - = help: within `std::fmt::Arguments<'_>`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)` - = note: required because it appears within the type `std::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>` - = note: required because it appears within the type `core::fmt::Void` - = note: required because it appears within the type `&core::fmt::Void` + = help: within `std::fmt::Arguments<'_>`, the trait `std::marker::Sync` is not implemented for `core::fmt::Opaque` + = note: required because it appears within the type `&core::fmt::Opaque` = note: required because it appears within the type `std::fmt::ArgumentV1<'_>` = note: required because it appears within the type `[std::fmt::ArgumentV1<'_>]` = note: required because it appears within the type `&[std::fmt::ArgumentV1<'_>]` From f6bfdc95445180aee579dcacc6e6bdc4e6ecf56f Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 16 Feb 2020 10:17:01 -0500 Subject: [PATCH 06/14] Move the show_usize marker function to a static Currently, function items are always tagged unnamed_addr, which means that casting a function to a function pointer is not guaranteed to produce a deterministic address. However, once a function pointer is created, we do expect that to remain stable. So, this changes the show_usize function to a static containing a function pointer and uses that for comparisons. Notably, a *static* may have 'unstable' address, but the function pointer within it must be constant. Resolves issue 58320. --- src/libcore/fmt/mod.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 0c51a802faba3..993b1073493e9 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -255,12 +255,19 @@ pub struct ArgumentV1<'a> { formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, } -impl<'a> ArgumentV1<'a> { - #[inline(never)] - fn show_usize(x: &usize, f: &mut Formatter<'_>) -> Result { - Display::fmt(x, f) - } +// This gurantees a single stable value for the function pointer associated with +// indices/counts in the formatting infrastructure. +// +// Note that a function defined as such would not be correct as functions are +// always tagged unnamed_addr with the current lowering to LLVM IR, so their +// address is not considered important to LLVM and as such the as_usize cast +// could have been miscompiled. In practice, we never call as_usize on non-usize +// containing data (as a matter of static generation of the formatting +// arguments), so this is merely an additional check. +#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] +static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |_, _| loop {}; +impl<'a> ArgumentV1<'a> { #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { @@ -270,11 +277,13 @@ impl<'a> ArgumentV1<'a> { #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] pub fn from_usize(x: &usize) -> ArgumentV1<'_> { - ArgumentV1::new(x, ArgumentV1::show_usize) + ArgumentV1::new(x, USIZE_MARKER) } fn as_usize(&self) -> Option { - if self.formatter as usize == ArgumentV1::show_usize as usize { + if self.formatter as usize == USIZE_MARKER as usize { + // SAFETY: The `formatter` field is only set to USIZE_MARKER if + // the value is a usize, so this is safe Some(unsafe { *(self.value as *const _ as *const usize) }) } else { None From 03ca0e270613592f35ce654e2ef1e68cdfb154e3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 22 Feb 2020 14:38:38 -0500 Subject: [PATCH 07/14] Allow getting `no_std` from the config file Currently, it is only set correctly in the sanity checking implicit default fallback code. Having a config file at all will for force `no_std = false`. --- src/bootstrap/config.rs | 3 +++ src/bootstrap/sanity.rs | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 214d572329ec6..b474d896bd212 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -353,6 +353,7 @@ struct TomlTarget { musl_root: Option, wasi_root: Option, qemu_rootfs: Option, + no_std: Option, } impl Config { @@ -615,6 +616,8 @@ impl Config { target.musl_root = cfg.musl_root.clone().map(PathBuf::from); target.wasi_root = cfg.wasi_root.clone().map(PathBuf::from); target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from); + target.no_std = + cfg.no_std.unwrap_or(triple.contains("-none-") || triple.contains("nvptx")); config.target_config.insert(INTERNER.intern_string(triple.clone()), target); } diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 8ff7056e628f3..76e721ed8e3ed 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -194,9 +194,7 @@ pub fn check(build: &mut Build) { if target.contains("-none-") || target.contains("nvptx") { if build.no_std(*target).is_none() { - let target = build.config.target_config.entry(target.clone()).or_default(); - - target.no_std = true; + build.config.target_config.entry(target.clone()).or_default(); } if build.no_std(*target) == Some(false) { From 329022dfad7199053cbe225e8d7d13ebbd5eb230 Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Mon, 24 Feb 2020 11:23:47 +0100 Subject: [PATCH 08/14] Address method comments --- src/libcore/cell.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 17222b27b2d70..b6d5c6ae27db7 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -1272,9 +1272,8 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// ``` #[unstable(feature = "cell_leak", issue = "69099")] pub fn leak(orig: Ref<'b, T>) -> &'b T { - // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell never - // goes back to UNUSED again. No further references can be created from the original cell, - // making the current borrow the only reference for the remaining lifetime. + // By forgetting this Ref we ensure that the borrow counter in the RefCell never goes back + // to UNUSED again. No further mutable references can be created from the original cell. mem::forget(orig.borrow); orig.value } From 4dbdadf94dbfa7eaf89e54d849ae6287de6b89c5 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 24 Feb 2020 19:03:15 +0300 Subject: [PATCH 09/14] rustc_metadata: Use binary search from standard library instead of a hand rolled one. --- src/librustc_metadata/rmeta/decoder.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 01fd637b20e66..3dacd2cbe95c8 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -408,20 +408,12 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { { last_source_file } else { - let mut a = 0; - let mut b = imported_source_files.len(); - - while b - a > 1 { - let m = (a + b) / 2; - if imported_source_files[m].original_start_pos > lo { - b = m; - } else { - a = m; - } - } + let index = imported_source_files + .binary_search_by_key(&lo, |source_file| source_file.original_start_pos) + .unwrap_or_else(|index| index - 1); - self.last_source_file_index = a; - &imported_source_files[a] + self.last_source_file_index = index; + &imported_source_files[index] } }; From 245e15b4ac07e1e780921e3512ae2b9fb2a4a718 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 23 Feb 2020 13:49:19 +0100 Subject: [PATCH 10/14] parse: extract `parse_stmt_item` & `parse_stmt_path_start`. --- src/librustc_parse/parser/stmt.rs | 65 ++++++++++++++++--------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index bbfbe9c20df94..4d86836ff900a 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -58,33 +58,11 @@ impl<'a> Parser<'a> { // (1 token), but it fact not a path. Also, we avoid stealing syntax from `parse_item_`. if self.token.is_path_start() && !self.token.is_qpath_start() && !self.is_path_start_item() { - let path = self.parse_path(PathStyle::Expr)?; - - if self.eat(&token::Not) { - return self.parse_stmt_mac(lo, attrs.into(), path); - } - - let expr = if self.check(&token::OpenDelim(token::Brace)) { - self.parse_struct_expr(lo, path, AttrVec::new())? - } else { - let hi = self.prev_span; - self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) - }; - - let expr = self.with_res(Restrictions::STMT_EXPR, |this| { - let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?; - this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) - })?; - return Ok(Some(self.mk_stmt(lo.to(self.prev_span), StmtKind::Expr(expr)))); + return self.parse_stmt_path_start(lo, attrs).map(Some); } - // FIXME: Bad copy of attrs - let old_directory_ownership = - mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock); - let item = self.parse_item_common(attrs.clone(), false, true, |_| true)?; - self.directory.ownership = old_directory_ownership; - - if let Some(item) = item { + if let Some(item) = self.parse_stmt_item(attrs.clone())? { + // FIXME: Bad copy of attrs return Ok(Some(self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))))); } @@ -117,14 +95,37 @@ impl<'a> Parser<'a> { Ok(Some(self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)))) } + fn parse_stmt_item(&mut self, attrs: Vec) -> PResult<'a, Option> { + let old = mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock); + let item = self.parse_item_common(attrs.clone(), false, true, |_| true)?; + self.directory.ownership = old; + Ok(item) + } + + fn parse_stmt_path_start(&mut self, lo: Span, attrs: Vec) -> PResult<'a, Stmt> { + let path = self.parse_path(PathStyle::Expr)?; + + if self.eat(&token::Not) { + return self.parse_stmt_mac(lo, attrs.into(), path); + } + + let expr = if self.check(&token::OpenDelim(token::Brace)) { + self.parse_struct_expr(lo, path, AttrVec::new())? + } else { + let hi = self.prev_span; + self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) + }; + + let expr = self.with_res(Restrictions::STMT_EXPR, |this| { + let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?; + this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) + })?; + Ok(self.mk_stmt(lo.to(self.prev_span), StmtKind::Expr(expr))) + } + /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. /// At this point, the `!` token after the path has already been eaten. - fn parse_stmt_mac( - &mut self, - lo: Span, - attrs: AttrVec, - path: ast::Path, - ) -> PResult<'a, Option> { + fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> { let args = self.parse_mac_args()?; let delim = args.delim(); let hi = self.prev_span; @@ -145,7 +146,7 @@ impl<'a> Parser<'a> { let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; StmtKind::Expr(e) }; - Ok(Some(self.mk_stmt(lo.to(hi), kind))) + Ok(self.mk_stmt(lo.to(hi), kind)) } /// Error on outer attributes in this context. From 32295aee6ef45e1602d8df6033be47e06225a319 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 23 Feb 2020 14:10:03 +0100 Subject: [PATCH 11/14] parse: simplify `parse_stmt_without_recovery`. --- src/librustc_parse/parser/stmt.rs | 65 ++++++++++++++----------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index 4d86836ff900a..a1078a59239c7 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -35,39 +35,33 @@ impl<'a> Parser<'a> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - if self.eat_keyword(kw::Let) { - return self.parse_local_mk(lo, attrs.into()).map(Some); - } - if self.is_kw_followed_by_ident(kw::Mut) { - return self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut"); - } - if self.is_kw_followed_by_ident(kw::Auto) { + let stmt = if self.eat_keyword(kw::Let) { + self.parse_local_mk(lo, attrs.into())? + } else if self.is_kw_followed_by_ident(kw::Mut) { + self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")? + } else if self.is_kw_followed_by_ident(kw::Auto) { self.bump(); // `auto` let msg = "write `let` instead of `auto` to introduce a new variable"; - return self.recover_stmt_local(lo, attrs.into(), msg, "let"); - } - if self.is_kw_followed_by_ident(sym::var) { + self.recover_stmt_local(lo, attrs.into(), msg, "let")? + } else if self.is_kw_followed_by_ident(sym::var) { self.bump(); // `var` let msg = "write `let` instead of `var` to introduce a new variable"; - return self.recover_stmt_local(lo, attrs.into(), msg, "let"); - } - - // Starts like a simple path, being careful to avoid contextual keywords, - // e.g., `union`, items with `crate` visibility, or `auto trait` items. - // We aim to parse an arbitrary path `a::b` but not something that starts like a path - // (1 token), but it fact not a path. Also, we avoid stealing syntax from `parse_item_`. - if self.token.is_path_start() && !self.token.is_qpath_start() && !self.is_path_start_item() + self.recover_stmt_local(lo, attrs.into(), msg, "let")? + } else if self.token.is_path_start() + && !self.token.is_qpath_start() + && !self.is_path_start_item() { - return self.parse_stmt_path_start(lo, attrs).map(Some); - } - - if let Some(item) = self.parse_stmt_item(attrs.clone())? { + // We have avoided contextual keywords like `union`, items with `crate` visibility, + // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something + // that starts like a path (1 token), but it fact not a path. + // Also, we avoid stealing syntax from `parse_item_`. + self.parse_stmt_path_start(lo, attrs)? + } else if let Some(item) = self.parse_stmt_item(attrs.clone())? { // FIXME: Bad copy of attrs - return Ok(Some(self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))))); + self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) } - // Do not attempt to parse an expression if we're done here. - if self.token == token::Semi { + else if self.token == token::Semi { self.error_outer_attrs(&attrs); self.bump(); let mut last_semi = lo; @@ -82,17 +76,16 @@ impl<'a> Parser<'a> { ExprKind::Tup(Vec::new()), AttrVec::new(), )); - return Ok(Some(self.mk_stmt(lo.to(last_semi), kind))); - } - - if self.token == token::CloseDelim(token::Brace) { + self.mk_stmt(lo.to(last_semi), kind) + } else if self.token != token::CloseDelim(token::Brace) { + // Remainder are line-expr stmts. + let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?; + self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) + } else { self.error_outer_attrs(&attrs); return Ok(None); - } - - // Remainder are line-expr stmts. - let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?; - Ok(Some(self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)))) + }; + Ok(Some(stmt)) } fn parse_stmt_item(&mut self, attrs: Vec) -> PResult<'a, Option> { @@ -168,12 +161,12 @@ impl<'a> Parser<'a> { attrs: AttrVec, msg: &str, sugg: &str, - ) -> PResult<'a, Option> { + ) -> PResult<'a, Stmt> { let stmt = self.parse_local_mk(lo, attrs)?; self.struct_span_err(lo, "invalid variable declaration") .span_suggestion(lo, msg, sugg.to_string(), Applicability::MachineApplicable) .emit(); - Ok(Some(stmt)) + Ok(stmt) } fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { From 1eb0844f5d4321a7769238af740e6d93d3e966e4 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 25 Feb 2020 00:59:39 +0100 Subject: [PATCH 12/14] parse: move condition into guard --- src/librustc_parse/parser/stmt.rs | 56 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index a1078a59239c7..b7bf99e6e1603 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -366,36 +366,36 @@ impl<'a> Parser<'a> { let mut eat_semi = true; match stmt.kind { - StmtKind::Expr(ref expr) if self.token != token::Eof => { - // expression without semicolon - if classify::expr_requires_semi_to_be_stmt(expr) { - // Just check for errors and recover; do not eat semicolon yet. - if let Err(mut e) = - self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)]) - { - if let TokenKind::DocComment(..) = self.token.kind { - if let Ok(snippet) = self.span_to_snippet(self.token.span) { - let sp = self.token.span; - let marker = &snippet[..3]; - let (comment_marker, doc_comment_marker) = marker.split_at(2); - - e.span_suggestion( - sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), - &format!( - "add a space before `{}` to use a regular comment", - doc_comment_marker, - ), - format!("{} {}", comment_marker, doc_comment_marker), - Applicability::MaybeIncorrect, - ); - } + // Expression without semicolon. + StmtKind::Expr(ref expr) + if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => + { + // Just check for errors and recover; do not eat semicolon yet. + if let Err(mut e) = + self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)]) + { + if let TokenKind::DocComment(..) = self.token.kind { + if let Ok(snippet) = self.span_to_snippet(self.token.span) { + let sp = self.token.span; + let marker = &snippet[..3]; + let (comment_marker, doc_comment_marker) = marker.split_at(2); + + e.span_suggestion( + sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), + &format!( + "add a space before `{}` to use a regular comment", + doc_comment_marker, + ), + format!("{} {}", comment_marker, doc_comment_marker), + Applicability::MaybeIncorrect, + ); } - e.emit(); - self.recover_stmt(); - // Don't complain about type errors in body tail after parse error (#57383). - let sp = expr.span.to(self.prev_span); - stmt.kind = StmtKind::Expr(self.mk_expr_err(sp)); } + e.emit(); + self.recover_stmt(); + // Don't complain about type errors in body tail after parse error (#57383). + let sp = expr.span.to(self.prev_span); + stmt.kind = StmtKind::Expr(self.mk_expr_err(sp)); } } StmtKind::Local(..) => { From 4f15867faf2797257cbeb9e4a38ae8dc87dcf2e9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Feb 2020 21:59:36 -0500 Subject: [PATCH 13/14] bootstrap: Further centralize target defaulting logic. Background: targets can be specied with or without config files; unneccessarily differences in the logic between those cases has caused a) the bug I tried to fix in the previous commit, b) the bug I introduced in the previous commit. The solution is to make the code paths the same as much as possible. 1. Targets are now not created from the `default` method. (I would both remove the impl if this was a public library, but just wrap it for convience becaues it's not.) Instead, there is a `from_triple` method which does the defaulting. 2. Besides the sanity checking, use the new method in the code reading config files. Now `no_std` is overriden iff set explicitly just like the other fields which are optional in the TOML AST type. 3. In sanity checking, just populate the map for all targets no matter what. That way do don't duplicate logic trying to be clever and remember which targets have "non standard" overrides. Sanity checking is back to just sanity checking, and out of the game of trying to default too. --- src/bootstrap/config.rs | 16 +++++++++++++--- src/bootstrap/sanity.rs | 7 +++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index b474d896bd212..746cddbabd639 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -177,6 +177,15 @@ pub struct Target { pub no_std: bool, } +impl Target { + pub fn from_triple(triple: &str) -> Self { + let mut target: Self = Default::default(); + if triple.contains("-none-") || triple.contains("nvptx") { + target.no_std = true; + } + target + } +} /// Structure of the `config.toml` file that configuration is read from. /// /// This structure uses `Decodable` to automatically decode a TOML configuration @@ -596,7 +605,7 @@ impl Config { if let Some(ref t) = toml.target { for (triple, cfg) in t { - let mut target = Target::default(); + let mut target = Target::from_triple(triple); if let Some(ref s) = cfg.llvm_config { target.llvm_config = Some(config.src.join(s)); @@ -607,6 +616,9 @@ impl Config { if let Some(ref s) = cfg.android_ndk { target.ndk = Some(config.src.join(s)); } + if let Some(s) = cfg.no_std { + target.no_std = s; + } target.cc = cfg.cc.clone().map(PathBuf::from); target.cxx = cfg.cxx.clone().map(PathBuf::from); target.ar = cfg.ar.clone().map(PathBuf::from); @@ -616,8 +628,6 @@ impl Config { target.musl_root = cfg.musl_root.clone().map(PathBuf::from); target.wasi_root = cfg.wasi_root.clone().map(PathBuf::from); target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from); - target.no_std = - cfg.no_std.unwrap_or(triple.contains("-none-") || triple.contains("nvptx")); config.target_config.insert(INTERNER.intern_string(triple.clone()), target); } diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 76e721ed8e3ed..530e74da8cac0 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -17,6 +17,7 @@ use std::process::Command; use build_helper::{output, t}; +use crate::config::Target; use crate::Build; struct Finder { @@ -192,11 +193,9 @@ pub fn check(build: &mut Build) { panic!("the iOS target is only supported on macOS"); } - if target.contains("-none-") || target.contains("nvptx") { - if build.no_std(*target).is_none() { - build.config.target_config.entry(target.clone()).or_default(); - } + build.config.target_config.entry(target.clone()).or_insert(Target::from_triple(target)); + if target.contains("-none-") || target.contains("nvptx") { if build.no_std(*target) == Some(false) { panic!("All the *-none-* and nvptx* targets are no-std targets") } From 7876711b9be1673631c4aaea63c289a984afbb0b Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 25 Feb 2020 06:00:47 +0100 Subject: [PATCH 14/14] parse: address nitpick --- src/librustc_parse/parser/stmt.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index b7bf99e6e1603..d2a6f0b7fcf0c 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -59,9 +59,8 @@ impl<'a> Parser<'a> { } else if let Some(item) = self.parse_stmt_item(attrs.clone())? { // FIXME: Bad copy of attrs self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) - } - // Do not attempt to parse an expression if we're done here. - else if self.token == token::Semi { + } else if self.token == token::Semi { + // Do not attempt to parse an expression if we're done here. self.error_outer_attrs(&attrs); self.bump(); let mut last_semi = lo;