diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index d562639a6588d..c187471fb5faa 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -4,6 +4,7 @@ use crate::fmt; use crate::mem::MaybeUninit; +use crate::num::flt2dec; use crate::ops::{Div, Rem, Sub}; use crate::ptr; use crate::slice; @@ -256,6 +257,161 @@ macro_rules! impl_Display { }; } +macro_rules! impl_Exp { + ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + fn $name( + mut n: $u, + is_nonnegative: bool, + upper: bool, + f: &mut fmt::Formatter<'_> + ) -> fmt::Result { + let (mut n, mut exponent, trailing_zeros, added_precision) = { + let mut exponent = 0; + // count and remove trailing decimal zeroes + while n % 10 == 0 && n >= 10 { + n /= 10; + exponent += 1; + } + let trailing_zeros = exponent; + + let (added_precision, subtracted_precision) = match f.precision() { + Some(fmt_prec) => { + // number of decimal digits minus 1 + let mut tmp = n; + let mut prec = 0; + while tmp >= 10 { + tmp /= 10; + prec += 1; + } + (fmt_prec.saturating_sub(prec), prec.saturating_sub(fmt_prec)) + } + None => (0,0) + }; + for _ in 1..subtracted_precision { + n/=10; + exponent += 1; + } + if subtracted_precision != 0 { + let rem = n % 10; + n /= 10; + exponent += 1; + // round up last digit + if rem >= 5 { + n += 1; + } + } + (n, exponent, trailing_zeros, added_precision) + }; + + // 39 digits (worst case u128) + . = 40 + let mut buf = [MaybeUninit::::uninit(); 40]; + let mut curr = buf.len() as isize; //index for buf + let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + + // decode 2 chars at a time + while n >= 100 { + let d1 = ((n % 100) as isize) << 1; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + n /= 100; + exponent += 2; + } + // n is <= 99, so at most 2 chars long + let mut n = n as isize; // possibly reduce 64bit math + // decode second-to-last character + if n >= 10 { + curr -= 1; + unsafe { + *buf_ptr.offset(curr) = (n as u8 % 10_u8) + b'0'; + } + n /= 10; + exponent += 1; + } + // add decimal point iff >1 mantissa digit will be printed + if exponent != trailing_zeros || added_precision != 0 { + curr -= 1; + unsafe { + *buf_ptr.offset(curr) = b'.'; + } + } + + let buf_slice = unsafe { + // decode last character + curr -= 1; + *buf_ptr.offset(curr) = (n as u8) + b'0'; + + let len = buf.len() - curr as usize; + slice::from_raw_parts(buf_ptr.offset(curr), len) + }; + + // stores 'e' (or 'E') and the up to 2-digit exponent + let mut exp_buf = [MaybeUninit::::uninit(); 3]; + let exp_ptr = MaybeUninit::first_ptr_mut(&mut exp_buf); + let exp_slice = unsafe { + *exp_ptr.offset(0) = if upper {b'E'} else {b'e'}; + let len = if exponent < 10 { + *exp_ptr.offset(1) = (exponent as u8) + b'0'; + 2 + } else { + let off = exponent << 1; + ptr::copy_nonoverlapping(lut_ptr.offset(off), exp_ptr.offset(1), 2); + 3 + }; + slice::from_raw_parts(exp_ptr, len) + }; + + let parts = &[ + flt2dec::Part::Copy(buf_slice), + flt2dec::Part::Zero(added_precision), + 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) + } + + $( + #[stable(feature = "integer_exp_format", since = "1.42.0")] + impl fmt::LowerExp for $t { + #[allow(unused_comparisons)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let is_nonnegative = *self >= 0; + let n = if is_nonnegative { + self.$conv_fn() + } else { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.$conv_fn()).wrapping_add(1) + }; + $name(n, is_nonnegative, false, f) + } + })* + $( + #[stable(feature = "integer_exp_format", since = "1.42.0")] + impl fmt::UpperExp for $t { + #[allow(unused_comparisons)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let is_nonnegative = *self >= 0; + let n = if is_nonnegative { + self.$conv_fn() + } else { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.$conv_fn()).wrapping_add(1) + }; + $name(n, is_nonnegative, true, f) + } + })* + }; +} + // Include wasm32 in here since it doesn't reflect the native pointer size, and // often cares strongly about getting a smaller code size. #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] @@ -265,6 +421,10 @@ mod imp { i8, u8, i16, u16, i32, u32, i64, u64, usize, isize as u64 via to_u64 named fmt_u64 ); + impl_Exp!( + i8, u8, i16, u16, i32, u32, i64, u64, usize, isize + as u64 via to_u64 named exp_u64 + ); } #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] @@ -272,6 +432,9 @@ mod imp { use super::*; impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32); impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64); + impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); + impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64); } impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128); +impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128); diff --git a/src/libcore/tests/fmt/num.rs b/src/libcore/tests/fmt/num.rs index 10fcf8b76ccb4..a50c2b46a919b 100644 --- a/src/libcore/tests/fmt/num.rs +++ b/src/libcore/tests/fmt/num.rs @@ -38,6 +38,16 @@ fn test_format_int() { assert_eq!(format!("{:o}", 1i16), "1"); assert_eq!(format!("{:o}", 1i32), "1"); assert_eq!(format!("{:o}", 1i64), "1"); + assert_eq!(format!("{:e}", 1isize), "1e0"); + assert_eq!(format!("{:e}", 1i8), "1e0"); + assert_eq!(format!("{:e}", 1i16), "1e0"); + assert_eq!(format!("{:e}", 1i32), "1e0"); + assert_eq!(format!("{:e}", 1i64), "1e0"); + assert_eq!(format!("{:E}", 1isize), "1E0"); + assert_eq!(format!("{:E}", 1i8), "1E0"); + assert_eq!(format!("{:E}", 1i16), "1E0"); + assert_eq!(format!("{:E}", 1i32), "1E0"); + assert_eq!(format!("{:E}", 1i64), "1E0"); assert_eq!(format!("{}", 1usize), "1"); assert_eq!(format!("{}", 1u8), "1"); @@ -69,6 +79,14 @@ fn test_format_int() { assert_eq!(format!("{:o}", 1u16), "1"); assert_eq!(format!("{:o}", 1u32), "1"); assert_eq!(format!("{:o}", 1u64), "1"); + assert_eq!(format!("{:e}", 1u8), "1e0"); + assert_eq!(format!("{:e}", 1u16), "1e0"); + assert_eq!(format!("{:e}", 1u32), "1e0"); + assert_eq!(format!("{:e}", 1u64), "1e0"); + assert_eq!(format!("{:E}", 1u8), "1E0"); + assert_eq!(format!("{:E}", 1u16), "1E0"); + assert_eq!(format!("{:E}", 1u32), "1E0"); + assert_eq!(format!("{:E}", 1u64), "1E0"); // Test a larger number assert_eq!(format!("{:b}", 55), "110111"); @@ -76,6 +94,64 @@ fn test_format_int() { assert_eq!(format!("{}", 55), "55"); assert_eq!(format!("{:x}", 55), "37"); assert_eq!(format!("{:X}", 55), "37"); + assert_eq!(format!("{:e}", 55), "5.5e1"); + assert_eq!(format!("{:E}", 55), "5.5E1"); + assert_eq!(format!("{:e}", 10000000000u64), "1e10"); + assert_eq!(format!("{:E}", 10000000000u64), "1E10"); + assert_eq!(format!("{:e}", 10000000001u64), "1.0000000001e10"); + assert_eq!(format!("{:E}", 10000000001u64), "1.0000000001E10"); +} + +#[test] +fn test_format_int_exp_limits() { + use core::{i128, i16, i32, i64, i8, u128, u16, u32, u64, u8}; + assert_eq!(format!("{:e}", i8::MIN), "-1.28e2"); + assert_eq!(format!("{:e}", i8::MAX), "1.27e2"); + assert_eq!(format!("{:e}", i16::MIN), "-3.2768e4"); + assert_eq!(format!("{:e}", i16::MAX), "3.2767e4"); + assert_eq!(format!("{:e}", i32::MIN), "-2.147483648e9"); + assert_eq!(format!("{:e}", i32::MAX), "2.147483647e9"); + assert_eq!(format!("{:e}", i64::MIN), "-9.223372036854775808e18"); + assert_eq!(format!("{:e}", i64::MAX), "9.223372036854775807e18"); + assert_eq!(format!("{:e}", i128::MIN), "-1.70141183460469231731687303715884105728e38"); + assert_eq!(format!("{:e}", i128::MAX), "1.70141183460469231731687303715884105727e38"); + + assert_eq!(format!("{:e}", u8::MAX), "2.55e2"); + assert_eq!(format!("{:e}", u16::MAX), "6.5535e4"); + assert_eq!(format!("{:e}", u32::MAX), "4.294967295e9"); + assert_eq!(format!("{:e}", u64::MAX), "1.8446744073709551615e19"); + assert_eq!(format!("{:e}", u128::MAX), "3.40282366920938463463374607431768211455e38"); +} + +#[test] +fn test_format_int_exp_precision() { + use core::{i128, i16, i32, i64, i8}; + + //test that float and integer match + let big_int: u32 = 314_159_265; + assert_eq!(format!("{:.1e}", big_int), format!("{:.1e}", f64::from(big_int))); + + //test adding precision + assert_eq!(format!("{:.10e}", i8::MIN), "-1.2800000000e2"); + assert_eq!(format!("{:.10e}", i16::MIN), "-3.2768000000e4"); + assert_eq!(format!("{:.10e}", i32::MIN), "-2.1474836480e9"); + assert_eq!(format!("{:.20e}", i64::MIN), "-9.22337203685477580800e18"); + assert_eq!(format!("{:.40e}", i128::MIN), "-1.7014118346046923173168730371588410572800e38"); + + //test rounding + assert_eq!(format!("{:.1e}", i8::MIN), "-1.3e2"); + assert_eq!(format!("{:.1e}", i16::MIN), "-3.3e4"); + assert_eq!(format!("{:.1e}", i32::MIN), "-2.1e9"); + assert_eq!(format!("{:.1e}", i64::MIN), "-9.2e18"); + assert_eq!(format!("{:.1e}", i128::MIN), "-1.7e38"); + + //test huge precision + assert_eq!(format!("{:.1000e}", 1), format!("1.{}e0", "0".repeat(1000))); + //test zero precision + assert_eq!(format!("{:.0e}", 1), format!("1e0",)); + + //test padding with precision (and sign) + assert_eq!(format!("{:+10.3e}", 1), " +1.000e0"); } #[test] @@ -86,6 +162,8 @@ fn test_format_int_zero() { assert_eq!(format!("{:o}", 0), "0"); assert_eq!(format!("{:x}", 0), "0"); assert_eq!(format!("{:X}", 0), "0"); + assert_eq!(format!("{:e}", 0), "0e0"); + assert_eq!(format!("{:E}", 0), "0E0"); assert_eq!(format!("{}", 0u32), "0"); assert_eq!(format!("{:?}", 0u32), "0"); @@ -93,6 +171,8 @@ fn test_format_int_zero() { assert_eq!(format!("{:o}", 0u32), "0"); assert_eq!(format!("{:x}", 0u32), "0"); assert_eq!(format!("{:X}", 0u32), "0"); + assert_eq!(format!("{:e}", 0u32), "0e0"); + assert_eq!(format!("{:E}", 0u32), "0E0"); } #[test] diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index 2e8e4dac237bc..4fdabed54b852 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/src/librustc_mir/const_eval/eval_queries.rs @@ -288,7 +288,10 @@ pub fn const_eval_raw_provider<'tcx>( let cid = key.value; let def_id = cid.instance.def.def_id(); - if def_id.is_local() && tcx.typeck_tables_of(def_id).tainted_by_errors { + if def_id.is_local() + && tcx.has_typeck_tables(def_id) + && tcx.typeck_tables_of(def_id).tainted_by_errors + { return Err(ErrorHandled::Reported); } diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 5a4225ece65aa..20b9df0a2d9b6 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -1832,10 +1832,16 @@ impl<'a> Parser<'a> { } } Err(mut e) => { + e.span_label(struct_sp, "while parsing this struct"); if let Some(f) = recovery_field { fields.push(f); + e.span_suggestion( + self.prev_span.shrink_to_hi(), + "try adding a comma", + ",".into(), + Applicability::MachineApplicable, + ); } - e.span_label(struct_sp, "while parsing this struct"); e.emit(); self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); self.eat(&token::Comma); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1a505a39e4637..80d914d8d0ae5 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -2280,7 +2280,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } else if attr.check_name(sym::thread_local) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL; } else if attr.check_name(sym::track_caller) { - if tcx.fn_sig(id).abi() != abi::Abi::Rust { + if tcx.is_closure(id) || tcx.fn_sig(id).abi() != abi::Abi::Rust { struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") .emit(); } @@ -2301,7 +2301,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.export_name = Some(s); } } else if attr.check_name(sym::target_feature) { - if tcx.fn_sig(id).unsafety() == Unsafety::Normal { + if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == Unsafety::Normal { let msg = "`#[target_feature(..)]` can only be applied to `unsafe` functions"; tcx.sess .struct_span_err(attr.span, msg) diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 700e015b08edc..04eaba515ff22 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -6,6 +6,7 @@ use crate::fmt::{self, Write}; use crate::io; use crate::mem; use crate::memchr; +use crate::num::NonZeroU8; use crate::ops; use crate::os::raw::c_char; use crate::ptr; @@ -741,6 +742,32 @@ impl From> for CString { } } +#[stable(feature = "cstring_from_vec_of_nonzerou8", since = "1.43.0")] +impl From> for CString { + /// Converts a [`Vec`]`<`[`NonZeroU8`]`>` into a [`CString`] without + /// copying nor checking for inner null bytes. + /// + /// [`CString`]: ../ffi/struct.CString.html + /// [`NonZeroU8`]: ../num/struct.NonZeroU8.html + /// [`Vec`]: ../vec/struct.Vec.html + #[inline] + fn from(v: Vec) -> CString { + unsafe { + // Transmute `Vec` to `Vec`. + let v: Vec = { + // Safety: + // - transmuting between `NonZeroU8` and `u8` is sound; + // - `alloc::Layout == alloc::Layout`. + let (ptr, len, cap): (*mut NonZeroU8, _, _) = Vec::into_raw_parts(v); + Vec::from_raw_parts(ptr.cast::(), len, cap) + }; + // Safety: `v` cannot contain null bytes, given the type-level + // invariant of `NonZeroU8`. + CString::from_vec_unchecked(v) + } + } +} + #[stable(feature = "more_box_slice_clone", since = "1.29.0")] impl Clone for Box { #[inline] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index f9c9f224730da..799585ffc0acf 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -309,6 +309,7 @@ #![feature(unboxed_closures)] #![feature(untagged_unions)] #![feature(unwind_attributes)] +#![feature(vec_into_raw_parts)] // NB: the above list is sorted to minimize merge conflicts. #![default_lib_allocator] diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs index 04bfdf67e12d2..a11f61fdd69fd 100644 --- a/src/libstd/sys/wasi/fs.rs +++ b/src/libstd/sys/wasi/fs.rs @@ -12,7 +12,6 @@ use crate::sys::time::SystemTime; use crate::sys::unsupported; use crate::sys_common::FromInner; -pub use crate::sys_common::fs::copy; pub use crate::sys_common::fs::remove_dir_all; pub struct File { @@ -647,3 +646,12 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { pub fn osstr2str(f: &OsStr) -> io::Result<&str> { f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8")) } + +pub fn copy(from: &Path, to: &Path) -> io::Result { + use crate::fs::File; + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + + io::copy(&mut reader, &mut writer) +} diff --git a/src/test/ui/consts/issue-68684.rs b/src/test/ui/consts/issue-68684.rs new file mode 100644 index 0000000000000..c98f199b60e49 --- /dev/null +++ b/src/test/ui/consts/issue-68684.rs @@ -0,0 +1,15 @@ +// check-pass + +enum _Enum { + A(), +} + +type _E = _Enum; + +const fn _a() -> _Enum { + _E::A() +} + +const _A: _Enum = _a(); + +fn main() {} diff --git a/src/test/ui/macros/issue-68060.rs b/src/test/ui/macros/issue-68060.rs new file mode 100644 index 0000000000000..85ebd66b66cb6 --- /dev/null +++ b/src/test/ui/macros/issue-68060.rs @@ -0,0 +1,16 @@ +// build-fail + +#![feature(track_caller)] + +fn main() { + (0..) + .map( + #[target_feature(enable = "")] + //~^ ERROR: the feature named `` is not valid for this target + //~| ERROR: `#[target_feature(..)]` can only be applied to `unsafe` functions + #[track_caller] + //~^ ERROR: `#[track_caller]` requires Rust ABI + |_| (), + ) + .next(); +} diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr new file mode 100644 index 0000000000000..230867410d966 --- /dev/null +++ b/src/test/ui/macros/issue-68060.stderr @@ -0,0 +1,24 @@ +error: `#[target_feature(..)]` can only be applied to `unsafe` functions + --> $DIR/issue-68060.rs:8:13 + | +LL | #[target_feature(enable = "")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions +... +LL | |_| (), + | ------ not an `unsafe` function + +error: the feature named `` is not valid for this target + --> $DIR/issue-68060.rs:8:30 + | +LL | #[target_feature(enable = "")] + | ^^^^^^^^^^^ `` is not valid for this target + +error[E0737]: `#[track_caller]` requires Rust ABI + --> $DIR/issue-68060.rs:11:13 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0737`. diff --git a/src/test/ui/parser/removed-syntax-with-1.stderr b/src/test/ui/parser/removed-syntax-with-1.stderr index 193138d74604d..c3f747b61b99f 100644 --- a/src/test/ui/parser/removed-syntax-with-1.stderr +++ b/src/test/ui/parser/removed-syntax-with-1.stderr @@ -2,8 +2,9 @@ error: expected one of `,`, `.`, `?`, `}`, or an operator, found `with` --> $DIR/removed-syntax-with-1.rs:8:25 | LL | let b = S { foo: () with a, bar: () }; - | - ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator - | | + | - -^^^^ expected one of `,`, `.`, `?`, `}`, or an operator + | | | + | | help: try adding a comma: `,` | while parsing this struct error: aborting due to previous error diff --git a/src/test/ui/suggestions/struct-initializer-comma.rs b/src/test/ui/suggestions/struct-initializer-comma.rs new file mode 100644 index 0000000000000..613b976848f70 --- /dev/null +++ b/src/test/ui/suggestions/struct-initializer-comma.rs @@ -0,0 +1,13 @@ +struct Foo { + first: bool, + second: u8, +} + +fn main() { + let a = Foo { + //~^ ERROR missing field + first: true + second: 25 + //~^ ERROR expected one of + }; +} diff --git a/src/test/ui/suggestions/struct-initializer-comma.stderr b/src/test/ui/suggestions/struct-initializer-comma.stderr new file mode 100644 index 0000000000000..731e8e10ab3ca --- /dev/null +++ b/src/test/ui/suggestions/struct-initializer-comma.stderr @@ -0,0 +1,23 @@ +error: expected one of `,`, `.`, `?`, `}`, or an operator, found `second` + --> $DIR/struct-initializer-comma.rs:10:9 + | +LL | let a = Foo { + | --- while parsing this struct +LL | +LL | first: true + | - + | | + | expected one of `,`, `.`, `?`, `}`, or an operator + | help: try adding a comma: `,` +LL | second: 25 + | ^^^^^^ unexpected token + +error[E0063]: missing field `second` in initializer of `Foo` + --> $DIR/struct-initializer-comma.rs:7:13 + | +LL | let a = Foo { + | ^^^ missing `second` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0063`.