From 1d0378c454de72ddcfc08bcc105744923ef2d4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 18 Jun 2020 01:36:43 +0200 Subject: [PATCH 01/33] impl From for String This allows us to write fn char_to_string() -> String { 'a'.into() } which was not possible before. --- src/liballoc/string.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 0378ff5362a8b..cf86ba055b8af 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2508,3 +2508,11 @@ impl DoubleEndedIterator for Drain<'_> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Drain<'_> {} + +#[stable(feature = "from_char_for_string", since = "1.46.0")] +impl From for String { + #[inline] + fn from(c: char) -> Self { + c.to_string() + } +} From 2cde4932c7e8bd6000378af41029299ccf6eea69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 18 Jun 2020 11:19:03 +0200 Subject: [PATCH 02/33] add test for char into string --- src/liballoc/tests/string.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index 9ea020d2d19f4..d38655af78cb7 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -714,3 +714,10 @@ fn test_try_reserve_exact() { } } } + +#[test] +fn test_from_char() { + assert_eq!(String::from('a'), 'a'.to_string()); + let s: String = 'x'.into(); + assert_eq!(s, 'x'.to_string()); +} From 886f81e1d0883f67fbc81b8ece61b1c33f6923d2 Mon Sep 17 00:00:00 2001 From: Anirban Date: Tue, 23 Jun 2020 16:11:13 +0530 Subject: [PATCH 03/33] Fix sentence structure Fixed grammar and sentence structure on appropriate instances. --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 42fc0a63c0ffb..e9273cb6db8d9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# The Rust Programming Language +# The Rust Programming Language This is the main source code repository for [Rust]. It contains the compiler, -standard library, and documentation. +standard library and documentation. [Rust]: https://www.rust-lang.org @@ -17,8 +17,8 @@ Read ["Installation"] from [The Book]. _Note: If you wish to contribute to the compiler, you should read [this chapter][rustcguidebuild] of the rustc-dev-guide instead of this section._ -The Rust build system has a Python script called `x.py` to bootstrap building -the compiler. More information about it may be found by running `./x.py --help` +The Rust build system has a Python script called `x.py` to bootstrap when building +the compiler. More information about it can be found by running `./x.py --help` or reading the [rustc dev guide][rustcguidebuild]. [rustcguidebuild]: https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html @@ -54,9 +54,9 @@ or reading the [rustc dev guide][rustcguidebuild]. $ cp config.toml.example config.toml ``` - It is recommended that if you plan to use the Rust build system to create - an installation (using `./x.py install`) that you set the `prefix` value - in the `[install]` section to a directory that you have write permissions. + It is recommended that you set the `prefix` value in the `[install]` + section to a directory that you have write permissions if you plan to use + the Rust build system to create an installation. (using `./x.py install`) Create install directory if you are not installing in default directory @@ -143,8 +143,8 @@ shell with: ``` Currently, building Rust only works with some known versions of Visual Studio. If -you have a more recent version installed the build system doesn't understand -then you may need to force rustbuild to use an older version. This can be done +you have a more recent version installed and the build system doesn't understand, +you may need to force rustbuild to use an older version. This can be done by manually calling the appropriate vcvars file before running the bootstrap. ```batch From 0c88dd663a7095ccc405a2036047a90981137a51 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 23 Jun 2020 22:42:35 -0400 Subject: [PATCH 04/33] Update Box::from_raw example to generalize better I know very little about rust, so I saw this example and tried to generalize it by writing, ``` let layout = Layout::new::(); let new_obj = unsafe { let ptr = alloc(layout) as *mut T; *ptr = obj; Box::from_raw(ptr) }; ``` for some more complicated `T`, which ended up crashing with SIGSEGV, because it tried to `drop_in_place` the previous object in `ptr` which is of course garbage. I also added a comment that explains why `.write` is used, but I think adding that comment is optional and may be too verbose here. I do however think that changing this example is a good idea to suggest the correct generalization. `.write` is also used in most of the rest of the documentation here, even if the example is `i32`, so it would additionally be more consistent. --- src/liballoc/boxed.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index d10cbf1afab30..02634825c2108 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -382,7 +382,10 @@ impl Box { /// /// unsafe { /// let ptr = alloc(Layout::new::()) as *mut i32; - /// *ptr = 5; + /// // In general .write is required to avoid attempting to destruct + /// // the (uninitialized) previous contents of `ptr`, though for this + /// // simple example `*ptr = 5` would have worked as well. + /// ptr.write(5); /// let x = Box::from_raw(ptr); /// } /// ``` From 3b5d7f8cf66c6704d36b021f7173241d6351ad32 Mon Sep 17 00:00:00 2001 From: Anirban Date: Wed, 24 Jun 2020 10:29:35 +0530 Subject: [PATCH 05/33] Minor correction to sentence structure --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e9273cb6db8d9..6258356fd4672 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # The Rust Programming Language This is the main source code repository for [Rust]. It contains the compiler, -standard library and documentation. +standard library, and documentation. [Rust]: https://www.rust-lang.org @@ -54,9 +54,8 @@ or reading the [rustc dev guide][rustcguidebuild]. $ cp config.toml.example config.toml ``` - It is recommended that you set the `prefix` value in the `[install]` - section to a directory that you have write permissions if you plan to use - the Rust build system to create an installation. (using `./x.py install`) + If you plan to use `x.py install` to create an installation, it is recommended + that you set the `prefix` value in the `[install]` section to a directory. Create install directory if you are not installing in default directory From d6cf8fc63ee24269bd4df20ad9fe19a219b5c9f5 Mon Sep 17 00:00:00 2001 From: Anirban Date: Wed, 24 Jun 2020 21:09:08 +0530 Subject: [PATCH 06/33] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6258356fd4672..f423d7527cc59 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# The Rust Programming Language + This is the main source code repository for [Rust]. It contains the compiler, standard library, and documentation. @@ -17,9 +17,9 @@ Read ["Installation"] from [The Book]. _Note: If you wish to contribute to the compiler, you should read [this chapter][rustcguidebuild] of the rustc-dev-guide instead of this section._ -The Rust build system has a Python script called `x.py` to bootstrap when building -the compiler. More information about it can be found by running `./x.py --help` -or reading the [rustc dev guide][rustcguidebuild]. +The Rust build system uses a Python script called x.py to build the compiler, +which manages the bootstrapping process. More information about it can be found +by running `./x.py --help` or reading the [rustc dev guide][rustcguidebuild]. [rustcguidebuild]: https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html From 2bbc2b3de42f3b14ccc8b62c2acffc8840177777 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Thu, 25 Jun 2020 11:18:53 +0200 Subject: [PATCH 07/33] Document the static keyword --- src/libstd/keyword_docs.rs | 80 +++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index a4996d9eee810..746165beab8f5 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -1030,9 +1030,85 @@ mod self_upper_keyword {} // /// A place that is valid for the duration of a program. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// A `static` item is similar to a [`const`] item in that it lives for the +/// entire duration of the program and need to have its type explicited, with a +/// `static` lifetime, outliving any other lifetime. Added to that, `static` +/// items represent a precise memory location. /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +/// Static items do not call [`drop`] at the end of the program. +/// +/// There are two types of `static` items: those declared in association with +/// the [`mut`] keyword and those without. +/// +/// # Simple `static`s +/// +/// Non-[`mut`] `static` items that contain a type that is not interior mutable +/// may be placed in read-only memory. All access to a `static` item are +/// considered safe but some restrictions apply. See the [Reference] for more +/// information. +/// +/// ```rust +/// static FOO: [i32; 5] = [1, 2, 3, 4, 5]; +/// +/// let r1 = &FOO as *const _; +/// let r2 = &FOO as *const _; +/// // With a strictly read-only static, references will have the same adress +/// assert_eq!(r1, r2); +/// ``` +/// +/// # Mutable `static`s +/// +/// If a `static` item is declared with the [`mut`] keyword, then it is allowed +/// to be modified by the program. To make concurrency bugs hard to run into, +/// all access to a `static mut` require an [`unsafe`] block. Care should be +/// taken to ensure access (both read and write) are thread-safe. +/// +/// Despite their unsafety, mutable `static`s are very useful: they can be used +/// to represent global state shared by the whole program or be used in +/// [`extern`] blocks to bind to variables from C libraries. +/// +/// As global state: +/// +/// ```rust +/// # #![allow(unused_variables)] +/// # fn main() {} +/// # fn atomic_add(_: &mut u32, _: u32) -> u32 { 2 } +/// static mut LEVELS: u32 = 0; +/// +/// // This violates the idea of no shared state, and this doesn't internally +/// // protect against races, so this function is `unsafe` +/// unsafe fn bump_levels_unsafe1() -> u32 { +/// let ret = LEVELS; +/// LEVELS += 1; +/// return ret; +/// } +/// +/// // Assuming that we have an atomic_add function which returns the old value, +/// // this function is "safe" but the meaning of the return value may not be +/// // what callers expect, so it's still marked as `unsafe` +/// unsafe fn bump_levels_unsafe2() -> u32 { +/// return atomic_add(&mut LEVELS, 1); +/// } +/// ``` +/// +/// In an [`extern`] block: +/// +/// ```rust,no_run +/// # #![allow(dead_code)] +/// extern "C" { +/// static mut ERROR_MESSAGE: *mut std::os::raw::c_char; +/// } +/// ``` +/// +/// Mutable `static`s, just like simple `static`s, have some restrictions that +/// apply to them. See the [Reference] for more information. +/// +/// [`const`]: keyword.const.html +/// [`extern`]: keyword.extern.html +/// [`mut`]: keyword.mut.html +/// [`unsafe`]: keyword.unsafe.html +/// [`drop`]: mem/fn.drop.html +/// [Reference]: ../reference/items/static-items.html#static-items mod static_keyword {} #[doc(keyword = "struct")] From 22fc18f1776ab05688d2f57ed92e14830a44d1ee Mon Sep 17 00:00:00 2001 From: Anirban Date: Thu, 25 Jun 2020 16:29:37 +0530 Subject: [PATCH 08/33] Commit suggestion Co-authored-by: Niko Matsakis --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f423d7527cc59..18fb19b85afaf 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Read ["Installation"] from [The Book]. _Note: If you wish to contribute to the compiler, you should read [this chapter][rustcguidebuild] of the rustc-dev-guide instead of this section._ -The Rust build system uses a Python script called x.py to build the compiler, +The Rust build system uses a Python script called `x.py` to build the compiler, which manages the bootstrapping process. More information about it can be found by running `./x.py --help` or reading the [rustc dev guide][rustcguidebuild]. From 8edcc6d00d329c4cbe51b483bb82a1b365a073b4 Mon Sep 17 00:00:00 2001 From: Anirban Date: Thu, 25 Jun 2020 16:32:54 +0530 Subject: [PATCH 09/33] Add alternate text for rust logo image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18fb19b85afaf..cad82f9c72ec9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - +The Rust Programming Language This is the main source code repository for [Rust]. It contains the compiler, standard library, and documentation. From 3a1ac2823167e6254066758a7487ad384d32d7da Mon Sep 17 00:00:00 2001 From: Anirban Date: Thu, 25 Jun 2020 16:43:27 +0530 Subject: [PATCH 10/33] Added clickable-link Linked the logo/svg to https://www.rust-lang.org/ (change if required) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cad82f9c72ec9..58b10f4e19b92 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ + The Rust Programming Language + This is the main source code repository for [Rust]. It contains the compiler, standard library, and documentation. From 4c33b7c8d69648edcc7f4f3d2edbf96c00060d1e Mon Sep 17 00:00:00 2001 From: Anirban Date: Thu, 25 Jun 2020 19:33:19 +0530 Subject: [PATCH 11/33] Add responsiveness to logo Added responsiveness to image logo based on device width (set at 90% of device width, which can be changed as per requirement) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 58b10f4e19b92..c2ded10f05a36 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ - -The Rust Programming Language + +The Rust Programming Language This is the main source code repository for [Rust]. It contains the compiler, From 9fb14b5890457672f3148f2c65b83c45cb17599e Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Thu, 25 Jun 2020 17:05:12 +0200 Subject: [PATCH 12/33] Use WASM's saturating casts if they are available WebAssembly supports saturating floating point to integer casts behind a target feature. The feature is already available on many browsers. Beginning with 1.45 Rust will start defining the behavior of floating point to integer casts to be saturating as well. For this Rust constructs additional checks on top of the `fptoui` / `fptosi` instructions it emits. Here we introduce the possibility for the codegen backend to construct saturating casts itself and only fall back to constructing the checks ourselves if that is not possible. --- src/librustc_codegen_llvm/builder.rs | 50 ++++++++++++++++++++++ src/librustc_codegen_llvm/context.rs | 9 ++++ src/librustc_codegen_llvm/llvm_util.rs | 7 ++- src/librustc_codegen_ssa/mir/rvalue.rs | 11 +++-- src/librustc_codegen_ssa/traits/builder.rs | 2 + 5 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index ba285b5ef38d1..a6b55fee87026 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -652,6 +652,56 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty, UNNAMED) } } + fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { + if self.sess().target.target.arch == "wasm32" + && self + .sess() + .target_features + .contains(&rustc_span::symbol::Symbol::intern("nontrapping-fptoint")) + { + let src_ty = self.cx.val_ty(val); + let float_width = self.cx.float_width(src_ty); + let int_width = self.cx.int_width(dest_ty); + let name = match (int_width, float_width) { + (32, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f32"), + (32, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f64"), + (64, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f32"), + (64, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f64"), + _ => None, + }; + if let Some(name) = name { + let intrinsic = self.get_intrinsic(name); + return Some(self.call(intrinsic, &[val], None)); + } + } + None + } + + fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { + if self.sess().target.target.arch == "wasm32" + && self + .sess() + .target_features + .contains(&rustc_span::symbol::Symbol::intern("nontrapping-fptoint")) + { + let src_ty = self.cx.val_ty(val); + let float_width = self.cx.float_width(src_ty); + let int_width = self.cx.int_width(dest_ty); + let name = match (int_width, float_width) { + (32, 32) => Some("llvm.wasm.trunc.saturate.signed.i32.f32"), + (32, 64) => Some("llvm.wasm.trunc.saturate.signed.i32.f64"), + (64, 32) => Some("llvm.wasm.trunc.saturate.signed.i64.f32"), + (64, 64) => Some("llvm.wasm.trunc.saturate.signed.i64.f64"), + _ => None, + }; + if let Some(name) = name { + let intrinsic = self.get_intrinsic(name); + return Some(self.call(intrinsic, &[val], None)); + } + } + None + } + fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty, UNNAMED) } } diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 7ff5ac5cbdc10..896d3c1de62d5 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -482,6 +482,15 @@ impl CodegenCx<'b, 'tcx> { t_v8f64: t_f64, 8; } + ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f32", fn(t_f32) -> t_i32); + ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f64", fn(t_f64) -> t_i32); + ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f32", fn(t_f32) -> t_i64); + ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.wasm.trunc.saturate.signed.i32.f32", fn(t_f32) -> t_i32); + ifn!("llvm.wasm.trunc.saturate.signed.i32.f64", fn(t_f64) -> t_i32); + ifn!("llvm.wasm.trunc.saturate.signed.i64.f32", fn(t_f32) -> t_i64); + ifn!("llvm.wasm.trunc.saturate.signed.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.trap", fn() -> void); ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> i8p); diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs index 67a2251e8593e..807ea5cba624b 100644 --- a/src/librustc_codegen_llvm/llvm_util.rs +++ b/src/librustc_codegen_llvm/llvm_util.rs @@ -246,8 +246,11 @@ const RISCV_WHITELIST: &[(&str, Option)] = &[ ("e", Some(sym::riscv_target_feature)), ]; -const WASM_WHITELIST: &[(&str, Option)] = - &[("simd128", Some(sym::wasm_target_feature)), ("atomics", Some(sym::wasm_target_feature))]; +const WASM_WHITELIST: &[(&str, Option)] = &[ + ("simd128", Some(sym::wasm_target_feature)), + ("atomics", Some(sym::wasm_target_feature)), + ("nontrapping-fptoint", Some(sym::wasm_target_feature)), // TODO: Maybe None? +]; /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 57f72b1065d05..4b2be7b5321ff 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -774,12 +774,17 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( float_ty: Bx::Type, int_ty: Bx::Type, ) -> Bx::Value { - let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { - return fptosui_result; + return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; + } + + let try_sat_result = if signed { bx.fptosi_sat(x, int_ty) } else { bx.fptoui_sat(x, int_ty) }; + if let Some(try_sat_result) = try_sat_result { + return try_sat_result; } + let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; + let int_width = bx.cx().int_width(int_ty); let float_width = bx.cx().float_width(float_ty); // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index 7ffc9f15bffdc..d33d6857bc0ef 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs @@ -156,6 +156,8 @@ pub trait BuilderMethods<'a, 'tcx>: fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option; + fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option; fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; From 7f459ceffa778ceef104a6f946d99c14ebf04e8d Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Fri, 26 Jun 2020 17:53:52 +0200 Subject: [PATCH 13/33] Check for feature with pre-interned symbol --- src/librustc_codegen_llvm/builder.rs | 17 ++++++++--------- src/librustc_codegen_llvm/llvm_util.rs | 2 +- src/librustc_span/symbol.rs | 1 + 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index a6b55fee87026..83cd0dbfcd47e 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -18,6 +18,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::sym; use rustc_target::abi::{self, Align, Size}; use rustc_target::spec::{HasTargetSpec, Target}; use std::borrow::Cow; @@ -478,7 +479,11 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let llptr = self.struct_gep(place.llval, i as u64); let load = self.load(llptr, align); scalar_load_metadata(self, load, scalar); - if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load } + if scalar.is_bool() { + self.trunc(load, self.type_i1()) + } else { + load + } }; OperandValue::Pair( @@ -654,10 +659,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { if self.sess().target.target.arch == "wasm32" - && self - .sess() - .target_features - .contains(&rustc_span::symbol::Symbol::intern("nontrapping-fptoint")) + && self.sess().target_features.contains(&sym::wasm_nontrapping_fptoint) { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); @@ -679,10 +681,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { if self.sess().target.target.arch == "wasm32" - && self - .sess() - .target_features - .contains(&rustc_span::symbol::Symbol::intern("nontrapping-fptoint")) + && self.sess().target_features.contains(&sym::wasm_nontrapping_fptoint) { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs index 807ea5cba624b..16351b76dcad4 100644 --- a/src/librustc_codegen_llvm/llvm_util.rs +++ b/src/librustc_codegen_llvm/llvm_util.rs @@ -249,7 +249,7 @@ const RISCV_WHITELIST: &[(&str, Option)] = &[ const WASM_WHITELIST: &[(&str, Option)] = &[ ("simd128", Some(sym::wasm_target_feature)), ("atomics", Some(sym::wasm_target_feature)), - ("nontrapping-fptoint", Some(sym::wasm_target_feature)), // TODO: Maybe None? + ("nontrapping-fptoint", Some(sym::wasm_target_feature)), ]; /// When rustdoc is running, provide a list of all known features so that all their respective diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 970a26325926c..246112704acbd 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -837,6 +837,7 @@ symbols! { warn, wasm_import_module, wasm_target_feature, + wasm_nontrapping_fptoint: "nontrapping-fptoint", while_let, windows, windows_subsystem, From d1e6e17a8f46de1b2d96dcc85a94f1145017d61e Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Sat, 27 Jun 2020 13:26:30 +0200 Subject: [PATCH 14/33] Add codegen tests --- src/test/codegen/wasm_casts_nontrapping.rs | 162 ++++++++++++++++++++ src/test/codegen/wasm_casts_trapping.rs | 169 +++++++++++++++++++++ 2 files changed, 331 insertions(+) create mode 100644 src/test/codegen/wasm_casts_nontrapping.rs create mode 100644 src/test/codegen/wasm_casts_trapping.rs diff --git a/src/test/codegen/wasm_casts_nontrapping.rs b/src/test/codegen/wasm_casts_nontrapping.rs new file mode 100644 index 0000000000000..bd6073d8c204a --- /dev/null +++ b/src/test/codegen/wasm_casts_nontrapping.rs @@ -0,0 +1,162 @@ +// only-wasm32 +// compile-flags: -C target-feature=+nontrapping-fptoint +#![crate_type = "lib"] + +// CHECK-LABEL: @cast_f64_i64 +#[no_mangle] +pub fn cast_f64_i64(a: f64) -> i64 { + // CHECK: tail call i64 @llvm.wasm.trunc.saturate.signed.i64.f64(double {{.*}}) + // CHECK-NEXT: ret i64 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f64_i32 +#[no_mangle] +pub fn cast_f64_i32(a: f64) -> i32 { + // CHECK: tail call i32 @llvm.wasm.trunc.saturate.signed.i32.f64(double {{.*}}) + // CHECK-NEXT: ret i32 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_i64 +#[no_mangle] +pub fn cast_f32_i64(a: f32) -> i64 { + // CHECK: tail call i64 @llvm.wasm.trunc.saturate.signed.i64.f32(float {{.*}}) + // CHECK-NEXT: ret i64 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_i32 +#[no_mangle] +pub fn cast_f32_i32(a: f32) -> i32 { + // CHECK: tail call i32 @llvm.wasm.trunc.saturate.signed.i32.f32(float {{.*}}) + // CHECK-NEXT: ret i32 {{.*}} + a as _ +} + + +// CHECK-LABEL: @cast_f64_u64 +#[no_mangle] +pub fn cast_f64_u64(a: f64) -> u64 { + // CHECK: tail call i64 @llvm.wasm.trunc.saturate.unsigned.i64.f64(double {{.*}}) + // CHECK-NEXT: ret i64 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f64_u32 +#[no_mangle] +pub fn cast_f64_u32(a: f64) -> u32 { + // CHECK: tail call i32 @llvm.wasm.trunc.saturate.unsigned.i32.f64(double {{.*}}) + // CHECK-NEXT: ret i32 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_u64 +#[no_mangle] +pub fn cast_f32_u64(a: f32) -> u64 { + // CHECK: tail call i64 @llvm.wasm.trunc.saturate.unsigned.i64.f32(float {{.*}}) + // CHECK-NEXT: ret i64 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_u32 +#[no_mangle] +pub fn cast_f32_u32(a: f32) -> u32 { + // CHECK: tail call i32 @llvm.wasm.trunc.saturate.unsigned.i32.f32(float {{.*}}) + // CHECK-NEXT: ret i32 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_u8 +#[no_mangle] +pub fn cast_f32_u8(a: f32) -> u8 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i8 + // CHECK-NEXT: select i1 {{.*}}, i8 {{.*}}, i8 {{.*}} + // CHECK-NEXT: ret i8 {{.*}} + a as _ +} + + + +// CHECK-LABEL: @cast_unchecked_f64_i64 +#[no_mangle] +pub unsafe fn cast_unchecked_f64_i64(a: f64) -> i64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi double {{.*}} to i64 + // CHECK-NEXT: ret i64 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f64_i32 +#[no_mangle] +pub unsafe fn cast_unchecked_f64_i32(a: f64) -> i32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi double {{.*}} to i32 + // CHECK-NEXT: ret i32 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_i64 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_i64(a: f32) -> i64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi float {{.*}} to i64 + // CHECK-NEXT: ret i64 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_i32 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_i32(a: f32) -> i32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi float {{.*}} to i32 + // CHECK-NEXT: ret i32 {{.*}} + a.to_int_unchecked() +} + + +// CHECK-LABEL: @cast_unchecked_f64_u64 +#[no_mangle] +pub unsafe fn cast_unchecked_f64_u64(a: f64) -> u64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui double {{.*}} to i64 + // CHECK-NEXT: ret i64 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f64_u32 +#[no_mangle] +pub unsafe fn cast_unchecked_f64_u32(a: f64) -> u32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui double {{.*}} to i32 + // CHECK-NEXT: ret i32 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_u64 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_u64(a: f32) -> u64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i64 + // CHECK-NEXT: ret i64 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_u32 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_u32(a: f32) -> u32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i32 + // CHECK-NEXT: ret i32 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_u8 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_u8(a: f32) -> u8 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i8 + // CHECK-NEXT: ret i8 {{.*}} + a.to_int_unchecked() +} diff --git a/src/test/codegen/wasm_casts_trapping.rs b/src/test/codegen/wasm_casts_trapping.rs new file mode 100644 index 0000000000000..45b905190493a --- /dev/null +++ b/src/test/codegen/wasm_casts_trapping.rs @@ -0,0 +1,169 @@ +// only-wasm32 +// compile-flags: -C target-feature=-nontrapping-fptoint +#![crate_type = "lib"] + +// CHECK-LABEL: @cast_f64_i64 +#[no_mangle] +pub fn cast_f64_i64(a: f64) -> i64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi double {{.*}} to i64 + // CHECK-NEXT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f64_i32 +#[no_mangle] +pub fn cast_f64_i32(a: f64) -> i32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi double {{.*}} to i32 + // CHECK-NEXT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_i64 +#[no_mangle] +pub fn cast_f32_i64(a: f32) -> i64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi float {{.*}} to i64 + // CHECK-NEXT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_i32 +#[no_mangle] +pub fn cast_f32_i32(a: f32) -> i32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi float {{.*}} to i32 + // CHECK-NEXT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + a as _ +} + + +// CHECK-LABEL: @cast_f64_u64 +#[no_mangle] +pub fn cast_f64_u64(a: f64) -> u64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui double {{.*}} to i64 + // CHECK-NEXT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f64_u32 +#[no_mangle] +pub fn cast_f64_u32(a: f64) -> u32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui double {{.*}} to i32 + // CHECK-NEXT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_u64 +#[no_mangle] +pub fn cast_f32_u64(a: f32) -> u64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i64 + // CHECK-NEXT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_u32 +#[no_mangle] +pub fn cast_f32_u32(a: f32) -> u32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i32 + // CHECK-NEXT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + a as _ +} + +// CHECK-LABEL: @cast_f32_u8 +#[no_mangle] +pub fn cast_f32_u8(a: f32) -> u8 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i8 + // CHECK-NEXT: select i1 {{.*}}, i8 {{.*}}, i8 {{.*}} + a as _ +} + + + +// CHECK-LABEL: @cast_unchecked_f64_i64 +#[no_mangle] +pub unsafe fn cast_unchecked_f64_i64(a: f64) -> i64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi double {{.*}} to i64 + // CHECK-NEXT: ret i64 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f64_i32 +#[no_mangle] +pub unsafe fn cast_unchecked_f64_i32(a: f64) -> i32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi double {{.*}} to i32 + // CHECK-NEXT: ret i32 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_i64 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_i64(a: f32) -> i64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi float {{.*}} to i64 + // CHECK-NEXT: ret i64 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_i32 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_i32(a: f32) -> i32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptosi float {{.*}} to i32 + // CHECK-NEXT: ret i32 {{.*}} + a.to_int_unchecked() +} + + +// CHECK-LABEL: @cast_unchecked_f64_u64 +#[no_mangle] +pub unsafe fn cast_unchecked_f64_u64(a: f64) -> u64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui double {{.*}} to i64 + // CHECK-NEXT: ret i64 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f64_u32 +#[no_mangle] +pub unsafe fn cast_unchecked_f64_u32(a: f64) -> u32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui double {{.*}} to i32 + // CHECK-NEXT: ret i32 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_u64 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_u64(a: f32) -> u64 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i64 + // CHECK-NEXT: ret i64 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_u32 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_u32(a: f32) -> u32 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i32 + // CHECK-NEXT: ret i32 {{.*}} + a.to_int_unchecked() +} + +// CHECK-LABEL: @cast_unchecked_f32_u8 +#[no_mangle] +pub unsafe fn cast_unchecked_f32_u8(a: f32) -> u8 { + // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} + // CHECK: fptoui float {{.*}} to i8 + // CHECK-NEXT: ret i8 {{.*}} + a.to_int_unchecked() +} From 6aea81476531fb7ff15f8ac578a9ea12b0908e79 Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Sat, 27 Jun 2020 14:03:32 +0200 Subject: [PATCH 15/33] Add comments and format the code --- src/librustc_codegen_llvm/builder.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 83cd0dbfcd47e..eed327d0bc156 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -479,11 +479,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let llptr = self.struct_gep(place.llval, i as u64); let load = self.load(llptr, align); scalar_load_metadata(self, load, scalar); - if scalar.is_bool() { - self.trunc(load, self.type_i1()) - } else { - load - } + if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load } }; OperandValue::Pair( @@ -658,6 +654,9 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { + // WebAssembly has saturating floating point to integer casts if the + // `nontrapping-fptoint` target feature is activated. We'll use those if + // they are available. if self.sess().target.target.arch == "wasm32" && self.sess().target_features.contains(&sym::wasm_nontrapping_fptoint) { @@ -680,6 +679,9 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { + // WebAssembly has saturating floating point to integer casts if the + // `nontrapping-fptoint` target feature is activated. We'll use those if + // they are available. if self.sess().target.target.arch == "wasm32" && self.sess().target_features.contains(&sym::wasm_nontrapping_fptoint) { From 765bd47fa0f0e0d5d893283a94c76e2b1009d680 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 27 Jun 2020 11:35:12 -0400 Subject: [PATCH 16/33] Recover extra trailing angle brackets in struct definition This commit applies the existing 'extra angle bracket recovery' logic when parsing fields in struct definitions. This allows us to continue parsing the struct's fields, avoiding spurious 'missing field' errors in code that tries to use the struct. --- src/librustc_parse/parser/diagnostics.rs | 19 ++++++++---- src/librustc_parse/parser/expr.rs | 2 +- src/librustc_parse/parser/item.rs | 29 ++++++++++++++++++- src/librustc_parse/parser/path.rs | 2 +- .../recover-field-extra-angle-brackets.rs | 14 +++++++++ .../recover-field-extra-angle-brackets.stderr | 8 +++++ 6 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/parser/recover-field-extra-angle-brackets.rs create mode 100644 src/test/ui/parser/recover-field-extra-angle-brackets.stderr diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index fc9ffc3092447..4b060bf108f4e 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -376,7 +376,14 @@ impl<'a> Parser<'a> { /// let _ = vec![1, 2, 3].into_iter().collect::>>>(); /// ^^ help: remove extra angle brackets /// ``` - pub(super) fn check_trailing_angle_brackets(&mut self, segment: &PathSegment, end: TokenKind) { + /// + /// If `true` is returned, then trailing brackets were recovered, tokens were consumed + /// up until one of the tokens in 'end' was encountered, and an error was emitted. + pub(super) fn check_trailing_angle_brackets( + &mut self, + segment: &PathSegment, + end: &[&TokenKind], + ) -> bool { // This function is intended to be invoked after parsing a path segment where there are two // cases: // @@ -409,7 +416,7 @@ impl<'a> Parser<'a> { parsed_angle_bracket_args, ); if !parsed_angle_bracket_args { - return; + return false; } // Keep the span at the start so we can highlight the sequence of `>` characters to be @@ -447,18 +454,18 @@ impl<'a> Parser<'a> { number_of_gt, number_of_shr, ); if number_of_gt < 1 && number_of_shr < 1 { - return; + return false; } // Finally, double check that we have our end token as otherwise this is the // second case. if self.look_ahead(position, |t| { trace!("check_trailing_angle_brackets: t={:?}", t); - *t == end + end.contains(&&t.kind) }) { // Eat from where we started until the end token so that parsing can continue // as if we didn't have those extra angle brackets. - self.eat_to_tokens(&[&end]); + self.eat_to_tokens(end); let span = lo.until(self.token.span); let total_num_of_gt = number_of_gt + number_of_shr * 2; @@ -473,7 +480,9 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ) .emit(); + return true; } + false } /// Check to see if a pair of chained operators looks like an attempt at chained comparison, diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 2745b18a8cd51..fb38fdc26c782 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -867,7 +867,7 @@ impl<'a> Parser<'a> { let fn_span_lo = self.token.span; let segment = self.parse_path_segment(PathStyle::Expr)?; - self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren)); + self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]); if self.check(&token::OpenDelim(token::Paren)) { // Method call `expr.f()` diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 10df16964da08..fa6264c98e4f0 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind, use rustc_ast::ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::ast::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind}; use rustc_ast::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData}; -use rustc_ast::ast::{FnHeader, ForeignItem, PathSegment, Visibility, VisibilityKind}; +use rustc_ast::ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind}; use rustc_ast::ast::{MacArgs, MacCall, MacDelimiter}; use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; @@ -1262,6 +1262,25 @@ impl<'a> Parser<'a> { sp, &format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)), ); + + // Try to recover extra trailing angle brackets + let mut recovered = false; + if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind { + if let Some(last_segment) = segments.last() { + recovered = self.check_trailing_angle_brackets( + last_segment, + &[&token::Comma, &token::CloseDelim(token::Brace)], + ); + if recovered { + // Handle a case like `Vec>,` where we can continue parsing fields + // after the comma + self.eat(&token::Comma); + // `check_trailing_angle_brackets` already emitted a nicer error + err.cancel(); + } + } + } + if self.token.is_ident() { // This is likely another field; emit the diagnostic and keep going err.span_suggestion( @@ -1271,6 +1290,14 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ); err.emit(); + recovered = true; + } + + if recovered { + // Make sure an error was emitted (either by recovering an angle bracket, + // or by finding an identifier as the next token), since we're + // going to continue parsing + assert!(self.sess.span_diagnostic.has_errors()); } else { return Err(err); } diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index 5210614548da3..67e9b3af4a8cf 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -169,7 +169,7 @@ impl<'a> Parser<'a> { // `PathStyle::Expr` is only provided at the root invocation and never in // `parse_path_segment` to recurse and therefore can be checked to maintain // this invariant. - self.check_trailing_angle_brackets(&segment, token::ModSep); + self.check_trailing_angle_brackets(&segment, &[&token::ModSep]); } segments.push(segment); diff --git a/src/test/ui/parser/recover-field-extra-angle-brackets.rs b/src/test/ui/parser/recover-field-extra-angle-brackets.rs new file mode 100644 index 0000000000000..5e0e00bcb5e8d --- /dev/null +++ b/src/test/ui/parser/recover-field-extra-angle-brackets.rs @@ -0,0 +1,14 @@ +// Tests that we recover from extra trailing angle brackets +// in a struct field + +struct BadStruct { + first: Vec>, //~ ERROR unmatched angle bracket + second: bool +} + +fn bar(val: BadStruct) { + val.first; + val.second; +} + +fn main() {} diff --git a/src/test/ui/parser/recover-field-extra-angle-brackets.stderr b/src/test/ui/parser/recover-field-extra-angle-brackets.stderr new file mode 100644 index 0000000000000..318e55f6e99ac --- /dev/null +++ b/src/test/ui/parser/recover-field-extra-angle-brackets.stderr @@ -0,0 +1,8 @@ +error: unmatched angle bracket + --> $DIR/recover-field-extra-angle-brackets.rs:5:19 + | +LL | first: Vec>, + | ^ help: remove extra angle bracket + +error: aborting due to previous error + From 3fc5593ea80819f940f6edef3108d15ef2ad7956 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Sat, 27 Jun 2020 18:33:15 +0200 Subject: [PATCH 17/33] Document the type keyword --- src/libstd/keyword_docs.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index d972cf6db18cf..3b493c4244de6 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -1463,9 +1463,33 @@ mod true_keyword {} // /// Define an alias for an existing type. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// The syntax is `type Name = ExistingType;`. /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +/// # Examples +/// +/// `type` does **not** create a new type: +/// +/// ```rust +/// type Meters = u32; +/// type Kilograms = u32; +/// +/// let m: Meters = 3; +/// let k: Kilograms = 3; +/// +/// assert_eq!(m, k); +/// ``` +/// +/// In traits, using `type` allows the usage of an associated type without +/// knowing about it when declaring the [`trait`]: +/// +/// ```rust +/// trait Iterator { +/// type Item; +/// fn next(&mut self) -> Option; +/// } +/// ``` +/// +/// [`trait`]: keyword.trait.html mod type_keyword {} #[doc(keyword = "unsafe")] From 7055c23d2cb3baabbae6af7ab196e43035260856 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 24 Jun 2020 17:45:08 +0300 Subject: [PATCH 18/33] ast_pretty: Pass some token streams and trees by reference --- src/librustc_ast_pretty/pprust.rs | 38 +++++++++++----------- src/librustc_builtin_macros/log_syntax.rs | 2 +- src/librustc_builtin_macros/source_util.rs | 2 +- src/librustc_expand/mbe/macro_rules.rs | 4 +-- src/librustc_expand/proc_macro_server.rs | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs index 86faa1f086ce2..501cd3748282b 100644 --- a/src/librustc_ast_pretty/pprust.rs +++ b/src/librustc_ast_pretty/pprust.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_ast::attr; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, DelimToken, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::util::parser::{self, AssocOp, Fixity}; use rustc_ast::util::{classify, comments}; use rustc_span::edition::Edition; @@ -293,7 +293,7 @@ pub fn nonterminal_to_string(nt: &Nonterminal) -> String { token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(), token::NtLifetime(e) => e.to_string(), token::NtLiteral(ref e) => expr_to_string(e), - token::NtTT(ref tree) => tt_to_string(tree.clone()), + token::NtTT(ref tree) => tt_to_string(tree), token::NtVis(ref e) => vis_to_string(e), } } @@ -314,11 +314,11 @@ pub fn expr_to_string(e: &ast::Expr) -> String { to_string(|s| s.print_expr(e)) } -pub fn tt_to_string(tt: tokenstream::TokenTree) -> String { +pub fn tt_to_string(tt: &TokenTree) -> String { to_string(|s| s.print_tt(tt, false)) } -pub fn tts_to_string(tokens: TokenStream) -> String { +pub fn tts_to_string(tokens: &TokenStream) -> String { to_string(|s| s.print_tts(tokens, false)) } @@ -585,7 +585,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere false, None, delim.to_token(), - tokens.clone(), + tokens, true, span, ), @@ -594,7 +594,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere if let MacArgs::Eq(_, tokens) = &item.args { self.space(); self.word_space("="); - self.print_tts(tokens.clone(), true); + self.print_tts(tokens, true); } } } @@ -635,9 +635,9 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere /// appropriate macro, transcribe back into the grammar we just parsed from, /// and then pretty-print the resulting AST nodes (so, e.g., we print /// expression arguments as expressions). It can be done! I think. - fn print_tt(&mut self, tt: tokenstream::TokenTree, convert_dollar_crate: bool) { + fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) { match tt { - TokenTree::Token(ref token) => { + TokenTree::Token(token) => { self.word(token_to_string_ext(&token, convert_dollar_crate)); if let token::DocComment(..) = token.kind { self.hardbreak() @@ -648,7 +648,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere None, false, None, - delim, + *delim, tts, convert_dollar_crate, dspan.entire(), @@ -657,14 +657,14 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } } - fn print_tts(&mut self, tts: tokenstream::TokenStream, convert_dollar_crate: bool) { - let mut iter = tts.into_trees().peekable(); + fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) { + let mut iter = tts.trees().peekable(); while let Some(tt) = iter.next() { - let show_space = - if let Some(next) = iter.peek() { tt_prepend_space(next, &tt) } else { false }; - self.print_tt(tt, convert_dollar_crate); - if show_space { - self.space(); + self.print_tt(&tt, convert_dollar_crate); + if let Some(next) = iter.peek() { + if tt_prepend_space(next, &tt) { + self.space(); + } } } } @@ -675,7 +675,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere has_bang: bool, ident: Option, delim: DelimToken, - tts: TokenStream, + tts: &TokenStream, convert_dollar_crate: bool, span: Span, ) { @@ -1253,7 +1253,7 @@ impl<'a> State<'a> { has_bang, Some(item.ident), macro_def.body.delim(), - macro_def.body.inner_tokens(), + ¯o_def.body.inner_tokens(), true, item.span, ); @@ -1577,7 +1577,7 @@ impl<'a> State<'a> { true, None, m.args.delim(), - m.args.inner_tokens(), + &m.args.inner_tokens(), true, m.span(), ); diff --git a/src/librustc_builtin_macros/log_syntax.rs b/src/librustc_builtin_macros/log_syntax.rs index ae3a889428ae4..ede34a7612589 100644 --- a/src/librustc_builtin_macros/log_syntax.rs +++ b/src/librustc_builtin_macros/log_syntax.rs @@ -7,7 +7,7 @@ pub fn expand_log_syntax<'cx>( sp: rustc_span::Span, tts: TokenStream, ) -> Box { - println!("{}", pprust::tts_to_string(tts)); + println!("{}", pprust::tts_to_string(&tts)); // any so that `log_syntax` can be invoked as an expression and item. base::DummyResult::any_valid(sp) diff --git a/src/librustc_builtin_macros/source_util.rs b/src/librustc_builtin_macros/source_util.rs index 1b164eae5a345..e46cf67e64d66 100644 --- a/src/librustc_builtin_macros/source_util.rs +++ b/src/librustc_builtin_macros/source_util.rs @@ -71,7 +71,7 @@ pub fn expand_stringify( tts: TokenStream, ) -> Box { let sp = cx.with_def_site_ctxt(sp); - let s = pprust::tts_to_string(tts); + let s = pprust::tts_to_string(&tts); base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&s))) } diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs index 8cdb5b09c9e8b..28a3970918ee6 100644 --- a/src/librustc_expand/mbe/macro_rules.rs +++ b/src/librustc_expand/mbe/macro_rules.rs @@ -224,7 +224,7 @@ fn generic_extension<'cx>( let sess = cx.parse_sess; if cx.trace_macros() { - let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(arg.clone())); + let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg)); trace_macros_note(&mut cx.expansions, sp, msg); } @@ -300,7 +300,7 @@ fn generic_extension<'cx>( } if cx.trace_macros() { - let msg = format!("to `{}`", pprust::tts_to_string(tts.clone())); + let msg = format!("to `{}`", pprust::tts_to_string(&tts)); trace_macros_note(&mut cx.expansions, sp, msg); } diff --git a/src/librustc_expand/proc_macro_server.rs b/src/librustc_expand/proc_macro_server.rs index c88b5a37f718a..e5e530227e43a 100644 --- a/src/librustc_expand/proc_macro_server.rs +++ b/src/librustc_expand/proc_macro_server.rs @@ -413,7 +413,7 @@ impl server::TokenStream for Rustc<'_> { ) } fn to_string(&mut self, stream: &Self::TokenStream) -> String { - pprust::tts_to_string(stream.clone()) + pprust::tts_to_string(stream) } fn from_token_tree( &mut self, From 7231e575461d1246c247b2ac3e97c1adcab49737 Mon Sep 17 00:00:00 2001 From: James Box Date: Sat, 27 Jun 2020 22:55:42 -0500 Subject: [PATCH 19/33] Fix wording for anonymous parameter name help --- src/librustc_parse/parser/diagnostics.rs | 2 +- src/test/ui/anon-params/anon-params-denied-2018.stderr | 8 ++++---- src/test/ui/parser/inverted-parameters.rs | 2 +- src/test/ui/parser/inverted-parameters.stderr | 2 +- src/test/ui/parser/omitted-arg-in-item-fn.stderr | 2 +- src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr | 2 +- src/test/ui/span/issue-34264.stderr | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index fc9ffc3092447..e27bbc532cfc4 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -1415,7 +1415,7 @@ impl<'a> Parser<'a> { if self.token != token::Lt { err.span_suggestion( pat.span, - "if this was a parameter name, give it a type", + "if this is a parameter name, give it a type", format!("{}: TypeName", ident), Applicability::HasPlaceholders, ); diff --git a/src/test/ui/anon-params/anon-params-denied-2018.stderr b/src/test/ui/anon-params/anon-params-denied-2018.stderr index e7a806a846820..840294db0830a 100644 --- a/src/test/ui/anon-params/anon-params-denied-2018.stderr +++ b/src/test/ui/anon-params/anon-params-denied-2018.stderr @@ -9,7 +9,7 @@ help: if this is a `self` type, give it a parameter name | LL | fn foo(self: i32); | ^^^^^^^^^ -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | fn foo(i32: TypeName); | ^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ help: if this is a `self` type, give it a parameter name | LL | fn bar_with_default_impl(self: String, String) {} | ^^^^^^^^^^^^ -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | fn bar_with_default_impl(String: TypeName, String) {} | ^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | fn bar_with_default_impl(String, String) {} | ^ expected one of `:`, `@`, or `|` | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | fn bar_with_default_impl(String, String: TypeName) {} | ^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL | fn baz(a:usize, b, c: usize) -> usize { | ^ expected one of `:`, `@`, or `|` | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | fn baz(a:usize, b: TypeName, c: usize) -> usize { | ^^^^^^^^^^^ diff --git a/src/test/ui/parser/inverted-parameters.rs b/src/test/ui/parser/inverted-parameters.rs index 6f19ee9c7dc0d..5c4272504e061 100644 --- a/src/test/ui/parser/inverted-parameters.rs +++ b/src/test/ui/parser/inverted-parameters.rs @@ -20,7 +20,7 @@ fn pattern((i32, i32) (a, b)) {} fn fizz(i32) {} //~^ ERROR expected one of `:`, `@` -//~| HELP if this was a parameter name, give it a type +//~| HELP if this is a parameter name, give it a type //~| HELP if this is a `self` type, give it a parameter name //~| HELP if this is a type, explicitly ignore the parameter name diff --git a/src/test/ui/parser/inverted-parameters.stderr b/src/test/ui/parser/inverted-parameters.stderr index 043ff65f74e1a..ae180af93e373 100644 --- a/src/test/ui/parser/inverted-parameters.stderr +++ b/src/test/ui/parser/inverted-parameters.stderr @@ -39,7 +39,7 @@ help: if this is a `self` type, give it a parameter name | LL | fn fizz(self: i32) {} | ^^^^^^^^^ -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | fn fizz(i32: TypeName) {} | ^^^^^^^^^^^^^ diff --git a/src/test/ui/parser/omitted-arg-in-item-fn.stderr b/src/test/ui/parser/omitted-arg-in-item-fn.stderr index 9f138bf84ce19..bc3329dcbc23d 100644 --- a/src/test/ui/parser/omitted-arg-in-item-fn.stderr +++ b/src/test/ui/parser/omitted-arg-in-item-fn.stderr @@ -9,7 +9,7 @@ help: if this is a `self` type, give it a parameter name | LL | fn foo(self: x) { | ^^^^^^^ -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | fn foo(x: TypeName) { | ^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr b/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr index 1e51567a9b1c4..5516d4a4c1c1c 100644 --- a/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr +++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr @@ -9,7 +9,7 @@ help: if this is a `self` type, give it a parameter name | LL | trait Trait2015 { fn foo(#[allow(C)] self: i32); } | ^^^^^^^^^ -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | trait Trait2015 { fn foo(#[allow(C)] i32: TypeName); } | ^^^^^^^^^^^^^ diff --git a/src/test/ui/span/issue-34264.stderr b/src/test/ui/span/issue-34264.stderr index 116f5ddd5b4b2..40c3219bf27b0 100644 --- a/src/test/ui/span/issue-34264.stderr +++ b/src/test/ui/span/issue-34264.stderr @@ -21,7 +21,7 @@ LL | fn foo(Option, String) {} | ^ expected one of `:`, `@`, or `|` | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | fn foo(Option, String: TypeName) {} | ^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ help: if this is a `self` type, give it a parameter name | LL | fn bar(self: x, y: usize) {} | ^^^^^^^ -help: if this was a parameter name, give it a type +help: if this is a parameter name, give it a type | LL | fn bar(x: TypeName, y: usize) {} | ^^^^^^^^^^^ From e611a3fb8423f178e856813fc1a1f2397980bd8a Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Sun, 28 Jun 2020 17:20:27 +0200 Subject: [PATCH 20/33] Apply suggestions from code review --- src/libstd/keyword_docs.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index 3b493c4244de6..089056d68f803 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -1479,17 +1479,28 @@ mod true_keyword {} /// assert_eq!(m, k); /// ``` /// -/// In traits, using `type` allows the usage of an associated type without -/// knowing about it when declaring the [`trait`]: +/// In traits, `type` is used to declare an [associated type]: /// /// ```rust /// trait Iterator { +/// // associated type declaration /// type Item; /// fn next(&mut self) -> Option; /// } +/// +/// struct Once(Option); +/// +/// impl Iterator for Once { +/// // associated type definition +/// type Item = T; +/// fn next(&mut self) -> Option { +/// self.0.take() +/// } +/// } /// ``` /// /// [`trait`]: keyword.trait.html +/// [associated type]: ../reference/items/associated-items.html#associated-types mod type_keyword {} #[doc(keyword = "unsafe")] From dfd454bd3843c4f4dee2e943297bf3d208252dc6 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Sun, 28 Jun 2020 20:20:32 +0200 Subject: [PATCH 21/33] Apply suggestions, reformulating some paragraphs and improving some examples --- src/libstd/keyword_docs.rs | 74 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index 746165beab8f5..d84ed1c93dee4 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -1030,22 +1030,39 @@ mod self_upper_keyword {} // /// A place that is valid for the duration of a program. /// -/// A `static` item is similar to a [`const`] item in that it lives for the -/// entire duration of the program and need to have its type explicited, with a -/// `static` lifetime, outliving any other lifetime. Added to that, `static` -/// items represent a precise memory location. +/// A static item is a value which is valid for the entire duration of your +/// program (a `'static` lifetime). +/// +/// On the surface, `static` items seem very similar to [`const`]s: both contain +/// a value, both require type annotations and both can only be initialized with +/// constant functions and values. However, `static`s are notably different in +/// that they represent a location in memory. That means that you can have +/// references to `static` items and potentially even modify them, making them +/// essentially global variables. /// /// Static items do not call [`drop`] at the end of the program. /// /// There are two types of `static` items: those declared in association with /// the [`mut`] keyword and those without. /// +/// Items that are both static and owned cannot be moved: +/// +/// ```rust,compile_fail,E0507 +/// static VEC: Vec = vec![]; +/// +/// fn move_vec(v: Vec) -> Vec { +/// v +/// } +/// +/// move_vec(VEC); +/// ``` +/// /// # Simple `static`s /// -/// Non-[`mut`] `static` items that contain a type that is not interior mutable -/// may be placed in read-only memory. All access to a `static` item are -/// considered safe but some restrictions apply. See the [Reference] for more -/// information. +/// Accessing non-[`mut`] `static` items is considered safe, but some +/// restrictions apply. Most notably, the type of a `static` value needs to +/// implement the [`Sync`] trait, ruling out interior mutability containers +/// like [`RefCell`]. See the [Reference] for more information. /// /// ```rust /// static FOO: [i32; 5] = [1, 2, 3, 4, 5]; @@ -1054,43 +1071,22 @@ mod self_upper_keyword {} /// let r2 = &FOO as *const _; /// // With a strictly read-only static, references will have the same adress /// assert_eq!(r1, r2); +/// // A static item is used just like a variable +/// println!("{:?}", FOO); /// ``` /// /// # Mutable `static`s /// /// If a `static` item is declared with the [`mut`] keyword, then it is allowed -/// to be modified by the program. To make concurrency bugs hard to run into, -/// all access to a `static mut` require an [`unsafe`] block. Care should be -/// taken to ensure access (both read and write) are thread-safe. +/// to be modified by the program. However, accessing mutable `static`s can +/// cause undefined behavior in a number of ways, for example due to data races +/// in a multithreaded context. As such, all accesses to mutable `static`s +/// require an [`unsafe`] block. /// -/// Despite their unsafety, mutable `static`s are very useful: they can be used -/// to represent global state shared by the whole program or be used in +/// Despite their unsafety, mutable `static`s are necessary in many contexts: +/// they can be used to represent global state shared by the whole program or in /// [`extern`] blocks to bind to variables from C libraries. /// -/// As global state: -/// -/// ```rust -/// # #![allow(unused_variables)] -/// # fn main() {} -/// # fn atomic_add(_: &mut u32, _: u32) -> u32 { 2 } -/// static mut LEVELS: u32 = 0; -/// -/// // This violates the idea of no shared state, and this doesn't internally -/// // protect against races, so this function is `unsafe` -/// unsafe fn bump_levels_unsafe1() -> u32 { -/// let ret = LEVELS; -/// LEVELS += 1; -/// return ret; -/// } -/// -/// // Assuming that we have an atomic_add function which returns the old value, -/// // this function is "safe" but the meaning of the return value may not be -/// // what callers expect, so it's still marked as `unsafe` -/// unsafe fn bump_levels_unsafe2() -> u32 { -/// return atomic_add(&mut LEVELS, 1); -/// } -/// ``` -/// /// In an [`extern`] block: /// /// ```rust,no_run @@ -1108,7 +1104,9 @@ mod self_upper_keyword {} /// [`mut`]: keyword.mut.html /// [`unsafe`]: keyword.unsafe.html /// [`drop`]: mem/fn.drop.html -/// [Reference]: ../reference/items/static-items.html#static-items +/// [`Sync`]: marker/trait.Sync.html +/// [`RefCell`]: cell/struct.RefCell.html +/// [Reference]: ../reference/items/static-items.html mod static_keyword {} #[doc(keyword = "struct")] From 4224313e2bc3fc08e5eee0519d7b5813c3cad580 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Sun, 28 Jun 2020 20:48:53 +0200 Subject: [PATCH 22/33] Fix small nits --- src/libstd/keyword_docs.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index d84ed1c93dee4..3bae720270eba 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -1028,8 +1028,6 @@ mod self_upper_keyword {} #[doc(keyword = "static")] // -/// A place that is valid for the duration of a program. -/// /// A static item is a value which is valid for the entire duration of your /// program (a `'static` lifetime). /// @@ -1045,7 +1043,7 @@ mod self_upper_keyword {} /// There are two types of `static` items: those declared in association with /// the [`mut`] keyword and those without. /// -/// Items that are both static and owned cannot be moved: +/// Static items cannot be moved: /// /// ```rust,compile_fail,E0507 /// static VEC: Vec = vec![]; @@ -1054,6 +1052,7 @@ mod self_upper_keyword {} /// v /// } /// +/// // This line causes an error /// move_vec(VEC); /// ``` /// @@ -1071,7 +1070,7 @@ mod self_upper_keyword {} /// let r2 = &FOO as *const _; /// // With a strictly read-only static, references will have the same adress /// assert_eq!(r1, r2); -/// // A static item is used just like a variable +/// // A static item can be used just like a variable in many cases /// println!("{:?}", FOO); /// ``` /// From f3645ca6ca39bd891af3f0bd82b81f9a13718f6c Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Tue, 30 Jun 2020 10:56:10 +0200 Subject: [PATCH 23/33] remove rustdoc warnings --- src/librustc_mir_build/hair/pattern/_match.rs | 197 +++++++++--------- 1 file changed, 99 insertions(+), 98 deletions(-) diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs index 6ac5d41ec6135..18b92bf29bf1b 100644 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ b/src/librustc_mir_build/hair/pattern/_match.rs @@ -4,8 +4,8 @@ //! This file includes the logic for exhaustiveness and usefulness checking for //! pattern-matching. Specifically, given a list of patterns for a type, we can //! tell whether: -//! (a) the patterns cover every possible constructor for the type [exhaustiveness] -//! (b) each pattern is necessary [usefulness] +//! (a) the patterns cover every possible constructor for the type (exhaustiveness) +//! (b) each pattern is necessary (usefulness) //! //! The algorithm implemented here is a modified version of the one described in: //! http://moscova.inria.fr/~maranget/papers/warn/index.html @@ -101,53 +101,54 @@ //! To match the paper, the top of the stack is at the beginning / on the left. //! //! There are two important operations on pattern-stacks necessary to understand the algorithm: -//! 1. We can pop a given constructor off the top of a stack. This operation is called -//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or -//! `None`) and `p` a pattern-stack. -//! If the pattern on top of the stack can cover `c`, this removes the constructor and -//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns. -//! Otherwise the pattern-stack is discarded. -//! This essentially filters those pattern-stacks whose top covers the constructor `c` and -//! discards the others. //! -//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we -//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the -//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get -//! nothing back. +//! 1. We can pop a given constructor off the top of a stack. This operation is called +//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or +//! `None`) and `p` a pattern-stack. +//! If the pattern on top of the stack can cover `c`, this removes the constructor and +//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns. +//! Otherwise the pattern-stack is discarded. +//! This essentially filters those pattern-stacks whose top covers the constructor `c` and +//! discards the others. //! -//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` -//! on top of the stack, and we have four cases: -//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We -//! push onto the stack the arguments of this constructor, and return the result: -//! r_1, .., r_a, p_2, .., p_n -//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and -//! return nothing. -//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has -//! arguments (its arity), and return the resulting stack: -//! _, .., _, p_2, .., p_n -//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting -//! stack: -//! S(c, (r_1, p_2, .., p_n)) -//! S(c, (r_2, p_2, .., p_n)) +//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we +//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the +//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get +//! nothing back. //! -//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is -//! a pattern-stack. -//! This is used when we know there are missing constructor cases, but there might be -//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check -//! all its *other* components. +//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` +//! on top of the stack, and we have four cases: +//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We +//! push onto the stack the arguments of this constructor, and return the result: +//! r_1, .., r_a, p_2, .., p_n +//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and +//! return nothing. +//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has +//! arguments (its arity), and return the resulting stack: +//! _, .., _, p_2, .., p_n +//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +//! stack: +//! S(c, (r_1, p_2, .., p_n)) +//! S(c, (r_2, p_2, .., p_n)) //! -//! It is computed as follows. We look at the pattern `p_1` on top of the stack, -//! and we have three cases: -//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. -//! 1.2. `p_1 = _`. We return the rest of the stack: -//! p_2, .., p_n -//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting -//! stack. -//! D((r_1, p_2, .., p_n)) -//! D((r_2, p_2, .., p_n)) +//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is +//! a pattern-stack. +//! This is used when we know there are missing constructor cases, but there might be +//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check +//! all its *other* components. //! -//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the -//! exhaustive integer matching rules, so they're written here for posterity. +//! It is computed as follows. We look at the pattern `p_1` on top of the stack, +//! and we have three cases: +//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. +//! 1.2. `p_1 = _`. We return the rest of the stack: +//! p_2, .., p_n +//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +//! stack. +//! D((r_1, p_2, .., p_n)) +//! D((r_2, p_2, .., p_n)) +//! +//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the +//! exhaustive integer matching rules, so they're written here for posterity. //! //! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by //! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with @@ -168,66 +169,66 @@ //! //! Inductive step. (`n > 0`, i.e., whether there's at least one column //! [which may then be expanded into further columns later]) -//! We're going to match on the top of the new pattern-stack, `p_1`. -//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. -//! Then, the usefulness of `p_1` can be reduced to whether it is useful when -//! we ignore all the patterns in the first column of `P` that involve other constructors. -//! This is where `S(c, P)` comes in: -//! `U(P, p) := U(S(c, P), S(c, p))` -//! This special case is handled in `is_useful_specialized`. +//! We're going to match on the top of the new pattern-stack, `p_1`. +//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. +//! Then, the usefulness of `p_1` can be reduced to whether it is useful when +//! we ignore all the patterns in the first column of `P` that involve other constructors. +//! This is where `S(c, P)` comes in: +//! `U(P, p) := U(S(c, P), S(c, p))` +//! This special case is handled in `is_useful_specialized`. //! -//! For example, if `P` is: -//! [ -//! [Some(true), _], -//! [None, 0], -//! ] -//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only -//! matches values that row 2 doesn't. For row 1 however, we need to dig into the -//! arguments of `Some` to know whether some new value is covered. So we compute -//! `U([[true, _]], [false, 0])`. +//! For example, if `P` is: +//! [ +//! [Some(true), _], +//! [None, 0], +//! ] +//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only +//! matches values that row 2 doesn't. For row 1 however, we need to dig into the +//! arguments of `Some` to know whether some new value is covered. So we compute +//! `U([[true, _]], [false, 0])`. //! -//! - If `p_1 == _`, then we look at the list of constructors that appear in the first -//! component of the rows of `P`: -//! + If there are some constructors that aren't present, then we might think that the -//! wildcard `_` is useful, since it covers those constructors that weren't covered -//! before. -//! That's almost correct, but only works if there were no wildcards in those first -//! components. So we need to check that `p` is useful with respect to the rows that -//! start with a wildcard, if there are any. This is where `D` comes in: -//! `U(P, p) := U(D(P), D(p))` +//! - If `p_1 == _`, then we look at the list of constructors that appear in the first +//! component of the rows of `P`: +//! + If there are some constructors that aren't present, then we might think that the +//! wildcard `_` is useful, since it covers those constructors that weren't covered +//! before. +//! That's almost correct, but only works if there were no wildcards in those first +//! components. So we need to check that `p` is useful with respect to the rows that +//! start with a wildcard, if there are any. This is where `D` comes in: +//! `U(P, p) := U(D(P), D(p))` //! -//! For example, if `P` is: -//! [ -//! [_, true, _], -//! [None, false, 1], -//! ] -//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we -//! only had row 2, we'd know that `p` is useful. However row 1 starts with a -//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`. +//! For example, if `P` is: +//! [ +//! [_, true, _], +//! [None, false, 1], +//! ] +//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we +//! only had row 2, we'd know that `p` is useful. However row 1 starts with a +//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`. //! -//! + Otherwise, all possible constructors (for the relevant type) are present. In this -//! case we must check whether the wildcard pattern covers any unmatched value. For -//! that, we can think of the `_` pattern as a big OR-pattern that covers all -//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for -//! example. The wildcard pattern is useful in this case if it is useful when -//! specialized to one of the possible constructors. So we compute: -//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` +//! + Otherwise, all possible constructors (for the relevant type) are present. In this +//! case we must check whether the wildcard pattern covers any unmatched value. For +//! that, we can think of the `_` pattern as a big OR-pattern that covers all +//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for +//! example. The wildcard pattern is useful in this case if it is useful when +//! specialized to one of the possible constructors. So we compute: +//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` //! -//! For example, if `P` is: -//! [ -//! [Some(true), _], -//! [None, false], -//! ] -//! and `p` is [_, false], both `None` and `Some` constructors appear in the first -//! components of `P`. We will therefore try popping both constructors in turn: we -//! compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]], -//! [false]) for the `None` constructor. The first case returns true, so we know that -//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched -//! before. +//! For example, if `P` is: +//! [ +//! [Some(true), _], +//! [None, false], +//! ] +//! and `p` is [_, false], both `None` and `Some` constructors appear in the first +//! components of `P`. We will therefore try popping both constructors in turn: we +//! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]], +//! [false])` for the `None` constructor. The first case returns true, so we know that +//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched +//! before. //! -//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: -//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) -//! || U(P, (r_2, p_2, .., p_n))` +//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: +//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) +//! || U(P, (r_2, p_2, .., p_n))` //! //! Modifications to the algorithm //! ------------------------------ From 8afebc053709b3b49d4de9e61e268f0a047ed85e Mon Sep 17 00:00:00 2001 From: Adam Perry Date: Thu, 21 May 2020 16:43:39 -0700 Subject: [PATCH 24/33] Stabilize `#[track_caller]`. Does not yet make its constness stable, though. Use of `Location::caller` in const contexts is still gated by `#![feature(const_caller_location)]`. --- .../src/language-features/track-caller.md | 5 ----- src/libcore/lib.rs | 2 +- src/libcore/macros/mod.rs | 2 +- src/libcore/panic.rs | 14 ++++---------- src/librustc_error_codes/error_codes/E0736.md | 2 -- src/librustc_error_codes/error_codes/E0737.md | 2 -- src/librustc_error_codes/error_codes/E0739.md | 1 - src/librustc_errors/lib.rs | 2 +- src/librustc_feature/accepted.rs | 3 +++ src/librustc_feature/active.rs | 4 ---- src/librustc_feature/builtin_attrs.rs | 2 +- src/librustc_middle/lib.rs | 2 +- src/libstd/lib.rs | 2 +- src/test/ui/asm/sym.rs | 2 +- .../ui/feature-gates/feature-gate-track_caller.rs | 5 ----- .../feature-gates/feature-gate-track_caller.stderr | 12 ------------ src/test/ui/macros/issue-68060.rs | 2 -- src/test/ui/macros/issue-68060.stderr | 6 +++--- .../numbers-arithmetic/saturating-float-casts.rs | 1 - src/test/ui/rfc-2091-track-caller/call-chain.rs | 2 -- .../caller-location-fnptr-rt-ctfe-equiv.rs | 2 +- .../caller-location-intrinsic.rs | 6 ++---- .../rfc-2091-track-caller/const-caller-location.rs | 2 +- .../diverging-caller-location.rs | 2 -- .../ui/rfc-2091-track-caller/error-odd-syntax.rs | 2 -- .../rfc-2091-track-caller/error-odd-syntax.stderr | 2 +- .../error-with-invalid-abi.rs | 2 -- .../error-with-invalid-abi.stderr | 4 ++-- .../ui/rfc-2091-track-caller/error-with-naked.rs | 2 +- .../ui/rfc-2091-track-caller/intrinsic-wrapper.rs | 6 ++---- src/test/ui/rfc-2091-track-caller/only-for-fns.rs | 2 -- .../ui/rfc-2091-track-caller/only-for-fns.stderr | 2 +- src/test/ui/rfc-2091-track-caller/pass.rs | 2 -- .../track-caller-attribute.rs | 10 ++++------ .../ui/rfc-2091-track-caller/track-caller-ffi.rs | 10 ++++------ .../tracked-fn-ptr-with-arg.rs | 2 -- .../ui/rfc-2091-track-caller/tracked-fn-ptr.rs | 2 -- .../rfc-2091-track-caller/tracked-trait-impls.rs | 2 -- 38 files changed, 36 insertions(+), 99 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/track-caller.md delete mode 100644 src/test/ui/feature-gates/feature-gate-track_caller.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-track_caller.stderr diff --git a/src/doc/unstable-book/src/language-features/track-caller.md b/src/doc/unstable-book/src/language-features/track-caller.md deleted file mode 100644 index afc11a2b9492c..0000000000000 --- a/src/doc/unstable-book/src/language-features/track-caller.md +++ /dev/null @@ -1,5 +0,0 @@ -# `track_caller` - -The tracking issue for this feature is: [#47809](https://github.com/rust-lang/rust/issues/47809). - ------------------------- diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index aeb52bffbf24c..b732dacae1c5b 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -118,7 +118,7 @@ #![feature(staged_api)] #![feature(std_internals)] #![feature(stmt_expr_attributes)] -#![feature(track_caller)] +#![cfg_attr(bootstrap, feature(track_caller))] #![feature(transparent_unions)] #![feature(unboxed_closures)] #![feature(unsized_locals)] diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs index 3cfdde60135b7..13c0e8daf740f 100644 --- a/src/libcore/macros/mod.rs +++ b/src/libcore/macros/mod.rs @@ -1,6 +1,6 @@ #[doc(include = "panic.md")] #[macro_export] -#[allow_internal_unstable(core_panic, track_caller)] +#[allow_internal_unstable(core_panic, const_caller_location)] #[stable(feature = "core", since = "1.6.0")] macro_rules! panic { () => ( diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index c7009b76e8148..543aa969330ae 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -190,7 +190,6 @@ impl<'a> Location<'a> { /// # Examples /// /// ``` - /// #![feature(track_caller)] /// use core::panic::Location; /// /// /// Returns the [`Location`] at which it is called. @@ -206,7 +205,7 @@ impl<'a> Location<'a> { /// /// let fixed_location = get_just_one_location(); /// assert_eq!(fixed_location.file(), file!()); - /// assert_eq!(fixed_location.line(), 15); + /// assert_eq!(fixed_location.line(), 14); /// assert_eq!(fixed_location.column(), 5); /// /// // running the same untracked function in a different location gives us the same result @@ -217,7 +216,7 @@ impl<'a> Location<'a> { /// /// let this_location = get_caller_location(); /// assert_eq!(this_location.file(), file!()); - /// assert_eq!(this_location.line(), 29); + /// assert_eq!(this_location.line(), 28); /// assert_eq!(this_location.column(), 21); /// /// // running the tracked function in a different location produces a different value @@ -226,13 +225,8 @@ impl<'a> Location<'a> { /// assert_ne!(this_location.line(), another_location.line()); /// assert_ne!(this_location.column(), another_location.column()); /// ``` - // FIXME: When stabilizing this method, please also update the documentation - // of `intrinsics::caller_location`. - #[unstable( - feature = "track_caller", - reason = "uses #[track_caller] which is not yet stable", - issue = "47809" - )] + #[stable(feature = "track_caller", since = "1.46.0")] + #[rustc_const_unstable(feature = "const_caller_location", issue = "47809")] #[track_caller] pub const fn caller() -> &'static Location<'static> { crate::intrinsics::caller_location() diff --git a/src/librustc_error_codes/error_codes/E0736.md b/src/librustc_error_codes/error_codes/E0736.md index 8a60dc320599b..0f3d41ba66dc4 100644 --- a/src/librustc_error_codes/error_codes/E0736.md +++ b/src/librustc_error_codes/error_codes/E0736.md @@ -3,8 +3,6 @@ Erroneous code example: ```compile_fail,E0736 -#![feature(track_caller)] - #[naked] #[track_caller] fn foo() {} diff --git a/src/librustc_error_codes/error_codes/E0737.md b/src/librustc_error_codes/error_codes/E0737.md index c6553e97b7e91..ab5e60692b4da 100644 --- a/src/librustc_error_codes/error_codes/E0737.md +++ b/src/librustc_error_codes/error_codes/E0737.md @@ -5,8 +5,6 @@ restrictions. Erroneous code example: ```compile_fail,E0737 -#![feature(track_caller)] - #[track_caller] extern "C" fn foo() {} ``` diff --git a/src/librustc_error_codes/error_codes/E0739.md b/src/librustc_error_codes/error_codes/E0739.md index 707751066edbc..8d9039bef93f6 100644 --- a/src/librustc_error_codes/error_codes/E0739.md +++ b/src/librustc_error_codes/error_codes/E0739.md @@ -3,7 +3,6 @@ Erroneous code example: ```compile_fail,E0739 -#![feature(track_caller)] #[track_caller] struct Bar { a: u8, diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 0c1418d3cad27..362913ceadf18 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -5,7 +5,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(crate_visibility_modifier)] #![feature(nll)] -#![feature(track_caller)] +#![cfg_attr(bootstrap, feature(track_caller))] pub use emitter::ColorConfig; diff --git a/src/librustc_feature/accepted.rs b/src/librustc_feature/accepted.rs index b164b21913d6e..d93c17b05b498 100644 --- a/src/librustc_feature/accepted.rs +++ b/src/librustc_feature/accepted.rs @@ -265,6 +265,9 @@ declare_features! ( (accepted, const_if_match, "1.45.0", Some(49146), None), /// Allows the use of `loop` and `while` in constants. (accepted, const_loop, "1.45.0", Some(52000), None), + /// Allows `#[track_caller]` to be used which provides + /// accurate caller location reporting during panic (RFC 2091). + (accepted, track_caller, "1.46.0", Some(47809), None), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index 8660d6a8d6410..b9a55377949f2 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -494,10 +494,6 @@ declare_features! ( /// Allows the use of raw-dylibs (RFC 2627). (active, raw_dylib, "1.40.0", Some(58713), None), - /// Allows `#[track_caller]` to be used which provides - /// accurate caller location reporting during panic (RFC 2091). - (active, track_caller, "1.40.0", Some(47809), None), - /// Allows making `dyn Trait` well-formed even if `Trait` is not object safe. /// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden. diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs index 524a357971029..c9a34f033758b 100644 --- a/src/librustc_feature/builtin_attrs.rs +++ b/src/librustc_feature/builtin_attrs.rs @@ -260,6 +260,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(cold, Whitelisted, template!(Word)), ungated!(no_builtins, Whitelisted, template!(Word)), ungated!(target_feature, Whitelisted, template!(List: r#"enable = "name""#)), + ungated!(track_caller, Whitelisted, template!(Word)), gated!( no_sanitize, Whitelisted, template!(List: "address, memory, thread"), @@ -333,7 +334,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ gated!(ffi_returns_twice, Whitelisted, template!(Word), experimental!(ffi_returns_twice)), gated!(ffi_pure, Whitelisted, template!(Word), experimental!(ffi_pure)), gated!(ffi_const, Whitelisted, template!(Word), experimental!(ffi_const)), - gated!(track_caller, Whitelisted, template!(Word), experimental!(track_caller)), gated!( register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), experimental!(register_attr), diff --git a/src/librustc_middle/lib.rs b/src/librustc_middle/lib.rs index 676346fbebdd1..96b8ca27183ed 100644 --- a/src/librustc_middle/lib.rs +++ b/src/librustc_middle/lib.rs @@ -42,7 +42,7 @@ #![feature(or_patterns)] #![feature(range_is_empty)] #![feature(min_specialization)] -#![feature(track_caller)] +#![cfg_attr(bootstrap, feature(track_caller))] #![feature(trusted_len)] #![feature(stmt_expr_attributes)] #![feature(test)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index ef699ede2a140..372038df54f2e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -316,7 +316,7 @@ #![feature(toowned_clone_into)] #![feature(total_cmp)] #![feature(trace_macros)] -#![feature(track_caller)] +#![cfg_attr(bootstrap, feature(track_caller))] #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(untagged_unions)] diff --git a/src/test/ui/asm/sym.rs b/src/test/ui/asm/sym.rs index 8cff16aa75f69..9931697e4129c 100644 --- a/src/test/ui/asm/sym.rs +++ b/src/test/ui/asm/sym.rs @@ -3,7 +3,7 @@ // only-linux // run-pass -#![feature(asm, track_caller, thread_local)] +#![feature(asm, thread_local)] extern "C" fn f1() -> i32 { 111 diff --git a/src/test/ui/feature-gates/feature-gate-track_caller.rs b/src/test/ui/feature-gates/feature-gate-track_caller.rs deleted file mode 100644 index 5865cf0a4f754..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-track_caller.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[track_caller] -fn f() {} -//~^^ ERROR the `#[track_caller]` attribute is an experimental feature - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-track_caller.stderr b/src/test/ui/feature-gates/feature-gate-track_caller.stderr deleted file mode 100644 index 8ceab501617ee..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-track_caller.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: the `#[track_caller]` attribute is an experimental feature - --> $DIR/feature-gate-track_caller.rs:1:1 - | -LL | #[track_caller] - | ^^^^^^^^^^^^^^^ - | - = note: see issue #47809 for more information - = help: add `#![feature(track_caller)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/macros/issue-68060.rs b/src/test/ui/macros/issue-68060.rs index bc70f8ffec2b2..8772e98b6e9bb 100644 --- a/src/test/ui/macros/issue-68060.rs +++ b/src/test/ui/macros/issue-68060.rs @@ -1,5 +1,3 @@ -#![feature(track_caller)] - fn main() { (0..) .map( diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr index 22187c4a4098a..b9b2f946c5967 100644 --- a/src/test/ui/macros/issue-68060.stderr +++ b/src/test/ui/macros/issue-68060.stderr @@ -1,5 +1,5 @@ error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/issue-68060.rs:6:13 + --> $DIR/issue-68060.rs:4:13 | LL | #[target_feature(enable = "")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,13 +11,13 @@ LL | |_| (), = help: add `#![feature(target_feature_11)]` to the crate attributes to enable error: the feature named `` is not valid for this target - --> $DIR/issue-68060.rs:6:30 + --> $DIR/issue-68060.rs:4:30 | LL | #[target_feature(enable = "")] | ^^^^^^^^^^^ `` is not valid for this target error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/issue-68060.rs:9:13 + --> $DIR/issue-68060.rs:7:13 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs index e6d0c94a02fac..825a11972aeeb 100644 --- a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs +++ b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs @@ -9,7 +9,6 @@ // merged. #![feature(test, stmt_expr_attributes)] -#![feature(track_caller)] #![deny(overflowing_literals)] extern crate test; diff --git a/src/test/ui/rfc-2091-track-caller/call-chain.rs b/src/test/ui/rfc-2091-track-caller/call-chain.rs index 3f1c8f7abe8b8..fefb84de729fe 100644 --- a/src/test/ui/rfc-2091-track-caller/call-chain.rs +++ b/src/test/ui/rfc-2091-track-caller/call-chain.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(track_caller)] - use std::panic::Location; struct Foo; diff --git a/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs b/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs index edf9ba2c41a15..05240908917bc 100644 --- a/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs +++ b/src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs @@ -6,7 +6,7 @@ // run-pass // compile-flags: -Z unleash-the-miri-inside-of-you -#![feature(core_intrinsics, const_caller_location, track_caller, const_fn)] +#![feature(core_intrinsics, const_caller_location, const_fn)] type L = &'static std::panic::Location<'static>; diff --git a/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs b/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs index 090e912c1d0ba..f244b74e391ff 100644 --- a/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs +++ b/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(track_caller)] - #[inline(never)] #[track_caller] fn codegen_caller_loc() -> &'static core::panic::Location<'static> { @@ -15,13 +13,13 @@ macro_rules! caller_location_from_macro { fn main() { let loc = codegen_caller_loc(); assert_eq!(loc.file(), file!()); - assert_eq!(loc.line(), 16); + assert_eq!(loc.line(), 14); assert_eq!(loc.column(), 15); // `Location::caller()` in a macro should behave similarly to `file!` and `line!`, // i.e. point to where the macro was invoked, instead of the macro itself. let loc2 = caller_location_from_macro!(); assert_eq!(loc2.file(), file!()); - assert_eq!(loc2.line(), 23); + assert_eq!(loc2.line(), 21); assert_eq!(loc2.column(), 16); } diff --git a/src/test/ui/rfc-2091-track-caller/const-caller-location.rs b/src/test/ui/rfc-2091-track-caller/const-caller-location.rs index 0614c52c66036..8030a4d967a67 100644 --- a/src/test/ui/rfc-2091-track-caller/const-caller-location.rs +++ b/src/test/ui/rfc-2091-track-caller/const-caller-location.rs @@ -1,6 +1,6 @@ // run-pass -#![feature(const_fn, track_caller)] +#![feature(const_caller_location, const_fn)] use std::panic::Location; diff --git a/src/test/ui/rfc-2091-track-caller/diverging-caller-location.rs b/src/test/ui/rfc-2091-track-caller/diverging-caller-location.rs index 1fb75ef35ffc1..6681119557d79 100644 --- a/src/test/ui/rfc-2091-track-caller/diverging-caller-location.rs +++ b/src/test/ui/rfc-2091-track-caller/diverging-caller-location.rs @@ -6,8 +6,6 @@ //! we don't inspect the location returned -- it would be difficult to distinguish between the //! explicit panic and a failed assertion. That it compiles and runs is enough for this one. -#![feature(track_caller)] - #[track_caller] fn doesnt_return() -> ! { let _location = core::panic::Location::caller(); diff --git a/src/test/ui/rfc-2091-track-caller/error-odd-syntax.rs b/src/test/ui/rfc-2091-track-caller/error-odd-syntax.rs index d6560231871c9..6f4290e2a5ee9 100644 --- a/src/test/ui/rfc-2091-track-caller/error-odd-syntax.rs +++ b/src/test/ui/rfc-2091-track-caller/error-odd-syntax.rs @@ -1,5 +1,3 @@ -#![feature(track_caller)] - #[track_caller(1)] fn f() {} //~^^ ERROR malformed `track_caller` attribute input diff --git a/src/test/ui/rfc-2091-track-caller/error-odd-syntax.stderr b/src/test/ui/rfc-2091-track-caller/error-odd-syntax.stderr index 8906fa59506a7..e7ddf8df4ab53 100644 --- a/src/test/ui/rfc-2091-track-caller/error-odd-syntax.stderr +++ b/src/test/ui/rfc-2091-track-caller/error-odd-syntax.stderr @@ -1,5 +1,5 @@ error: malformed `track_caller` attribute input - --> $DIR/error-odd-syntax.rs:3:1 + --> $DIR/error-odd-syntax.rs:1:1 | LL | #[track_caller(1)] | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[track_caller]` diff --git a/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs b/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs index 1145f7786c78b..074e1ceb791ce 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs +++ b/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.rs @@ -1,5 +1,3 @@ -#![feature(track_caller)] - #[track_caller] extern "C" fn f() {} //~^^ ERROR `#[track_caller]` requires Rust ABI diff --git a/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr b/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr index e08c52fabd6b7..bcc0c8170e655 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr +++ b/src/test/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr @@ -1,11 +1,11 @@ error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/error-with-invalid-abi.rs:3:1 + --> $DIR/error-with-invalid-abi.rs:1:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/error-with-invalid-abi.rs:8:5 + --> $DIR/error-with-invalid-abi.rs:6:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs index f457384833335..2b110c9a32515 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs @@ -1,4 +1,4 @@ -#![feature(naked_functions, track_caller)] +#![feature(naked_functions)] #[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]` #[naked] diff --git a/src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs b/src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs index 76e62b89ab818..74217f47084a3 100644 --- a/src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs +++ b/src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(track_caller)] - macro_rules! caller_location_from_macro { () => (core::panic::Location::caller()); } @@ -9,13 +7,13 @@ macro_rules! caller_location_from_macro { fn main() { let loc = core::panic::Location::caller(); assert_eq!(loc.file(), file!()); - assert_eq!(loc.line(), 10); + assert_eq!(loc.line(), 8); assert_eq!(loc.column(), 15); // `Location::caller()` in a macro should behave similarly to `file!` and `line!`, // i.e. point to where the macro was invoked, instead of the macro itself. let loc2 = caller_location_from_macro!(); assert_eq!(loc2.file(), file!()); - assert_eq!(loc2.line(), 17); + assert_eq!(loc2.line(), 15); assert_eq!(loc2.column(), 16); } diff --git a/src/test/ui/rfc-2091-track-caller/only-for-fns.rs b/src/test/ui/rfc-2091-track-caller/only-for-fns.rs index 0fd59b4bf4918..bc0ca9552806f 100644 --- a/src/test/ui/rfc-2091-track-caller/only-for-fns.rs +++ b/src/test/ui/rfc-2091-track-caller/only-for-fns.rs @@ -1,5 +1,3 @@ -#![feature(track_caller)] - #[track_caller] struct S; //~^^ ERROR attribute should be applied to function diff --git a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr index c2fb8fa1eb6ca..6666dcfa6e599 100644 --- a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr +++ b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr @@ -1,5 +1,5 @@ error[E0739]: attribute should be applied to function - --> $DIR/only-for-fns.rs:3:1 + --> $DIR/only-for-fns.rs:1:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2091-track-caller/pass.rs b/src/test/ui/rfc-2091-track-caller/pass.rs index eef83b3d68f97..ada150b25cf2c 100644 --- a/src/test/ui/rfc-2091-track-caller/pass.rs +++ b/src/test/ui/rfc-2091-track-caller/pass.rs @@ -1,6 +1,4 @@ // run-pass -#![feature(track_caller)] - #[track_caller] fn f() {} diff --git a/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs b/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs index 76a380ed3e30d..efcc1f6942de1 100644 --- a/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs +++ b/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(track_caller)] - use std::panic::Location; #[track_caller] @@ -20,21 +18,21 @@ fn nested_tracked() -> &'static Location<'static> { fn main() { let location = Location::caller(); assert_eq!(location.file(), file!()); - assert_eq!(location.line(), 21); + assert_eq!(location.line(), 19); assert_eq!(location.column(), 20); let tracked = tracked(); assert_eq!(tracked.file(), file!()); - assert_eq!(tracked.line(), 26); + assert_eq!(tracked.line(), 24); assert_eq!(tracked.column(), 19); let nested = nested_intrinsic(); assert_eq!(nested.file(), file!()); - assert_eq!(nested.line(), 13); + assert_eq!(nested.line(), 11); assert_eq!(nested.column(), 5); let contained = nested_tracked(); assert_eq!(contained.file(), file!()); - assert_eq!(contained.line(), 17); + assert_eq!(contained.line(), 15); assert_eq!(contained.column(), 5); } diff --git a/src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs b/src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs index 23c17d743c43c..5115f687c2632 100644 --- a/src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs +++ b/src/test/ui/rfc-2091-track-caller/track-caller-ffi.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(track_caller)] - use std::panic::Location; extern "Rust" { @@ -30,21 +28,21 @@ mod provides { fn main() { let location = Location::caller(); assert_eq!(location.file(), file!()); - assert_eq!(location.line(), 31); + assert_eq!(location.line(), 29); assert_eq!(location.column(), 20); let tracked = unsafe { rust_track_caller_ffi_test_tracked() }; assert_eq!(tracked.file(), file!()); - assert_eq!(tracked.line(), 36); + assert_eq!(tracked.line(), 34); assert_eq!(tracked.column(), 28); let untracked = unsafe { rust_track_caller_ffi_test_untracked() }; assert_eq!(untracked.file(), file!()); - assert_eq!(untracked.line(), 26); + assert_eq!(untracked.line(), 24); assert_eq!(untracked.column(), 9); let contained = rust_track_caller_ffi_test_nested_tracked(); assert_eq!(contained.file(), file!()); - assert_eq!(contained.line(), 14); + assert_eq!(contained.line(), 12); assert_eq!(contained.column(), 14); } diff --git a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs index b17c1efb3d38c..5fcfea96d547a 100644 --- a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs +++ b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(track_caller)] - fn pass_to_ptr_call(f: fn(T), x: T) { f(x); } diff --git a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs index 8ee4d4fa16871..4415d850c241c 100644 --- a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs +++ b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(track_caller)] - fn ptr_call(f: fn()) { f(); } diff --git a/src/test/ui/rfc-2091-track-caller/tracked-trait-impls.rs b/src/test/ui/rfc-2091-track-caller/tracked-trait-impls.rs index 0a5f92bb635e5..4db4c29e53d58 100644 --- a/src/test/ui/rfc-2091-track-caller/tracked-trait-impls.rs +++ b/src/test/ui/rfc-2091-track-caller/tracked-trait-impls.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(track_caller)] - macro_rules! assert_expansion_site_is_tracked { () => {{ let location = std::panic::Location::caller(); From 9f7eec32bd8fc99bf03475511b036fba22cbecdf Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 25 Jun 2020 20:17:45 +0300 Subject: [PATCH 25/33] rustc_driver/pretty: avoid using TypeckTables::empty for TypedAnnotation. --- src/librustc_driver/lib.rs | 1 + src/librustc_driver/pretty.rs | 29 +++++++++++++++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index b45ab0f80ffac..afe5342877462 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -6,6 +6,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(nll)] +#![feature(track_caller)] #![recursion_limit = "256"] #[macro_use] diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 0a21eb8de059c..6a34a310f735a 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -80,8 +80,7 @@ where PpmTyped => { abort_on_err(tcx.analysis(LOCAL_CRATE), tcx.sess); - let empty_tables = ty::TypeckTables::empty(None); - let annotation = TypedAnnotation { tcx, tables: Cell::new(&empty_tables) }; + let annotation = TypedAnnotation { tcx, maybe_typeck_tables: Cell::new(None) }; tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir().krate())) } _ => panic!("Should use call_with_pp_support"), @@ -304,12 +303,22 @@ impl<'a> pprust::PpAnn for HygieneAnnotation<'a> { } } -struct TypedAnnotation<'a, 'tcx> { +struct TypedAnnotation<'tcx> { tcx: TyCtxt<'tcx>, - tables: Cell<&'a ty::TypeckTables<'tcx>>, + maybe_typeck_tables: Cell>>, } -impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> { +impl<'tcx> TypedAnnotation<'tcx> { + /// Gets the type-checking side-tables for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> { + self.maybe_typeck_tables.get().expect("`TypedAnnotation::tables` called outside of body") + } +} + +impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> { fn sess(&self) -> &Session { &self.tcx.sess } @@ -327,15 +336,15 @@ impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> { } } -impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> { +impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> { fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { - let old_tables = self.tables.get(); + let old_maybe_typeck_tables = self.maybe_typeck_tables.get(); if let pprust_hir::Nested::Body(id) = nested { - self.tables.set(self.tcx.body_tables(id)); + self.maybe_typeck_tables.set(Some(self.tcx.body_tables(id))); } let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>); pprust_hir::PpAnn::nested(pp_ann, state, nested); - self.tables.set(old_tables); + self.maybe_typeck_tables.set(old_maybe_typeck_tables); } fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { if let pprust_hir::AnnNode::Expr(_) = node { @@ -347,7 +356,7 @@ impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> { s.s.space(); s.s.word("as"); s.s.space(); - s.s.word(self.tables.get().expr_ty(expr).to_string()); + s.s.word(self.tables().expr_ty(expr).to_string()); s.pclose(); } } From 5498671bbe86eb73e00c98a5aa16f2f4cdf818d0 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 25 Jun 2020 20:25:46 +0300 Subject: [PATCH 26/33] rustc_passes/reachable: avoid using TypeckTables::empty for ReachableContext. --- src/librustc_passes/lib.rs | 1 + src/librustc_passes/reachable.rs | 27 +++++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index b55c5ec47eef1..58c3b999b1871 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -8,6 +8,7 @@ #![feature(in_band_lifetimes)] #![feature(nll)] #![feature(or_patterns)] +#![feature(track_caller)] #![recursion_limit = "256"] #[macro_use] diff --git a/src/librustc_passes/reachable.rs b/src/librustc_passes/reachable.rs index c9a4428c007aa..50fcefa3569ac 100644 --- a/src/librustc_passes/reachable.rs +++ b/src/librustc_passes/reachable.rs @@ -60,10 +60,10 @@ fn method_might_be_inlined( } // Information needed while computing reachability. -struct ReachableContext<'a, 'tcx> { +struct ReachableContext<'tcx> { // The type context. tcx: TyCtxt<'tcx>, - tables: &'a ty::TypeckTables<'tcx>, + maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>, // The set of items which must be exported in the linkage sense. reachable_symbols: HirIdSet, // A worklist of item IDs. Each item ID in this worklist will be inlined @@ -73,7 +73,7 @@ struct ReachableContext<'a, 'tcx> { any_library: bool, } -impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -81,18 +81,17 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { } fn visit_nested_body(&mut self, body: hir::BodyId) { - let old_tables = self.tables; - self.tables = self.tcx.body_tables(body); + let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body)); let body = self.tcx.hir().body(body); self.visit_body(body); - self.tables = old_tables; + self.maybe_typeck_tables = old_maybe_typeck_tables; } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { let res = match expr.kind { - hir::ExprKind::Path(ref qpath) => Some(self.tables.qpath_res(qpath, expr.hir_id)), + hir::ExprKind::Path(ref qpath) => Some(self.tables().qpath_res(qpath, expr.hir_id)), hir::ExprKind::MethodCall(..) => self - .tables + .tables() .type_dependent_def(expr.hir_id) .map(|(kind, def_id)| Res::Def(kind, def_id)), _ => None, @@ -133,7 +132,15 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { } } -impl<'a, 'tcx> ReachableContext<'a, 'tcx> { +impl<'tcx> ReachableContext<'tcx> { + /// Gets the type-checking side-tables for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> { + self.maybe_typeck_tables.expect("`ReachableContext::tables` called outside of body") + } + // Returns true if the given def ID represents a local item that is // eligible for inlining and false otherwise. fn def_id_represents_local_inlined_item(&self, def_id: DefId) -> bool { @@ -381,7 +388,7 @@ fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, crate_num: CrateNum) -> &'tcx HirIdSet }); let mut reachable_context = ReachableContext { tcx, - tables: &ty::TypeckTables::empty(None), + maybe_typeck_tables: None, reachable_symbols: Default::default(), worklist: Vec::new(), any_library, From fbb8a124183fcb430c62c7edeb8928a308dfa880 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 25 Jun 2020 20:58:33 +0300 Subject: [PATCH 27/33] rustc_passes/dead: avoid using TypeckTables::empty for MarkSymbolVisitor. --- src/librustc_passes/dead.rs | 45 +++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/librustc_passes/dead.rs b/src/librustc_passes/dead.rs index 503fbb64db83d..87348894a5ad9 100644 --- a/src/librustc_passes/dead.rs +++ b/src/librustc_passes/dead.rs @@ -37,10 +37,10 @@ fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { } } -struct MarkSymbolVisitor<'a, 'tcx> { +struct MarkSymbolVisitor<'tcx> { worklist: Vec, tcx: TyCtxt<'tcx>, - tables: &'a ty::TypeckTables<'tcx>, + maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>, live_symbols: FxHashSet, repr_has_repr_c: bool, in_pat: bool, @@ -50,7 +50,15 @@ struct MarkSymbolVisitor<'a, 'tcx> { struct_constructors: FxHashMap, } -impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { +impl<'tcx> MarkSymbolVisitor<'tcx> { + /// Gets the type-checking side-tables for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> { + self.maybe_typeck_tables.expect("`MarkSymbolVisitor::tables` called outside of body") + } + fn check_def_id(&mut self, def_id: DefId) { if let Some(def_id) = def_id.as_local() { let hir_id = self.tcx.hir().as_local_hir_id(def_id); @@ -107,7 +115,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } fn lookup_and_handle_method(&mut self, id: hir::HirId) { - if let Some(def_id) = self.tables.type_dependent_def_id(id) { + if let Some(def_id) = self.tables().type_dependent_def_id(id) { self.check_def_id(def_id); } else { bug!("no type-dependent def for method"); @@ -115,9 +123,9 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } fn handle_field_access(&mut self, lhs: &hir::Expr<'_>, hir_id: hir::HirId) { - match self.tables.expr_ty_adjusted(lhs).kind { + match self.tables().expr_ty_adjusted(lhs).kind { ty::Adt(def, _) => { - let index = self.tcx.field_index(hir_id, self.tables); + let index = self.tcx.field_index(hir_id, self.tables()); self.insert_def_id(def.non_enum_variant().fields[index].did); } ty::Tuple(..) => {} @@ -131,7 +139,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { res: Res, pats: &[hir::FieldPat<'_>], ) { - let variant = match self.tables.node_type(lhs.hir_id).kind { + let variant = match self.tables().node_type(lhs.hir_id).kind { ty::Adt(adt, _) => adt.variant_of_res(res), _ => span_bug!(lhs.span, "non-ADT in struct pattern"), }; @@ -139,7 +147,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { if let PatKind::Wild = pat.pat.kind { continue; } - let index = self.tcx.field_index(pat.hir_id, self.tables); + let index = self.tcx.field_index(pat.hir_id, self.tables()); self.insert_def_id(variant.fields[index].did); } } @@ -204,14 +212,14 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &[hir::Field<'_>]) { if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() { for field in fields { - let index = self.tcx.field_index(field.hir_id, self.tables); + let index = self.tcx.field_index(field.hir_id, self.tables()); self.insert_def_id(adt.non_enum_variant().fields[index].did); } } } } -impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { @@ -219,11 +227,10 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { } fn visit_nested_body(&mut self, body: hir::BodyId) { - let old_tables = self.tables; - self.tables = self.tcx.body_tables(body); + let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body)); let body = self.tcx.hir().body(body); self.visit_body(body); - self.tables = old_tables; + self.maybe_typeck_tables = old_maybe_typeck_tables; } fn visit_variant_data( @@ -248,7 +255,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { - let res = self.tables.qpath_res(qpath, expr.hir_id); + let res = self.tables().qpath_res(qpath, expr.hir_id); self.handle_res(res); } hir::ExprKind::MethodCall(..) => { @@ -258,9 +265,9 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { self.handle_field_access(&lhs, expr.hir_id); } hir::ExprKind::Struct(ref qpath, ref fields, _) => { - let res = self.tables.qpath_res(qpath, expr.hir_id); + let res = self.tables().qpath_res(qpath, expr.hir_id); self.handle_res(res); - if let ty::Adt(ref adt, _) = self.tables.expr_ty(expr).kind { + if let ty::Adt(ref adt, _) = self.tables().expr_ty(expr).kind { self.mark_as_used_if_union(adt, fields); } } @@ -283,11 +290,11 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { match pat.kind { PatKind::Struct(ref path, ref fields, _) => { - let res = self.tables.qpath_res(path, pat.hir_id); + let res = self.tables().qpath_res(path, pat.hir_id); self.handle_field_pattern_match(pat, res, fields); } PatKind::Path(ref qpath) => { - let res = self.tables.qpath_res(qpath, pat.hir_id); + let res = self.tables().qpath_res(qpath, pat.hir_id); self.handle_res(res); } _ => (), @@ -473,7 +480,7 @@ fn find_live<'tcx>( let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, - tables: &ty::TypeckTables::empty(None), + maybe_typeck_tables: None, live_symbols: Default::default(), repr_has_repr_c: false, in_pat: false, From e3d02f1229184d4382a0ec0b324266d807584e9f Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 25 Jun 2020 22:23:41 +0300 Subject: [PATCH 28/33] rustc_privacy: avoid using TypeckTables::empty for {Name,Type}PrivacyVisitor. --- src/librustc_privacy/lib.rs | 156 ++++++++++++++---------------------- 1 file changed, 58 insertions(+), 98 deletions(-) diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 9e6e7ea962bc3..bff45ecaed7eb 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -2,6 +2,7 @@ #![feature(in_band_lifetimes)] #![feature(nll)] #![feature(or_patterns)] +#![feature(track_caller)] #![recursion_limit = "256"] use rustc_attr as attr; @@ -345,17 +346,6 @@ fn def_id_visibility<'tcx>( } } -// Set the correct `TypeckTables` for the given `item_id` (or an empty table if -// there is no `TypeckTables` for the item). -fn item_tables<'a, 'tcx>( - tcx: TyCtxt<'tcx>, - hir_id: hir::HirId, - empty_tables: &'a ty::TypeckTables<'tcx>, -) -> &'a ty::TypeckTables<'tcx> { - let def_id = tcx.hir().local_def_id(hir_id); - if tcx.has_typeck_tables(def_id) { tcx.typeck_tables_of(def_id) } else { empty_tables } -} - fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visibility { if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 } } @@ -1029,14 +1019,21 @@ impl DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { /// This pass performs remaining checks for fields in struct expressions and patterns. ////////////////////////////////////////////////////////////////////////////////////// -struct NamePrivacyVisitor<'a, 'tcx> { +struct NamePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, - tables: &'a ty::TypeckTables<'tcx>, + maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>, current_item: Option, - empty_tables: &'a ty::TypeckTables<'tcx>, } -impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { +impl<'tcx> NamePrivacyVisitor<'tcx> { + /// Gets the type-checking side-tables for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> { + self.maybe_typeck_tables.expect("`NamePrivacyVisitor::tables` called outside of body") + } + // Checks that a field in a struct constructor (expression or pattern) is accessible. fn check_field( &mut self, @@ -1072,7 +1069,7 @@ impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { type Map = Map<'tcx>; /// We want to visit items in the context of their containing @@ -1087,39 +1084,22 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { } fn visit_nested_body(&mut self, body: hir::BodyId) { - let orig_tables = mem::replace(&mut self.tables, self.tcx.body_tables(body)); + let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body)); let body = self.tcx.hir().body(body); self.visit_body(body); - self.tables = orig_tables; + self.maybe_typeck_tables = old_maybe_typeck_tables; } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - let orig_current_item = mem::replace(&mut self.current_item, Some(item.hir_id)); - let orig_tables = - mem::replace(&mut self.tables, item_tables(self.tcx, item.hir_id, self.empty_tables)); + let orig_current_item = self.current_item.replace(item.hir_id); intravisit::walk_item(self, item); self.current_item = orig_current_item; - self.tables = orig_tables; - } - - fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { - let orig_tables = - mem::replace(&mut self.tables, item_tables(self.tcx, ti.hir_id, self.empty_tables)); - intravisit::walk_trait_item(self, ti); - self.tables = orig_tables; - } - - fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { - let orig_tables = - mem::replace(&mut self.tables, item_tables(self.tcx, ii.hir_id, self.empty_tables)); - intravisit::walk_impl_item(self, ii); - self.tables = orig_tables; } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Struct(ref qpath, fields, ref base) = expr.kind { - let res = self.tables.qpath_res(qpath, expr.hir_id); - let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap(); + let res = self.tables().qpath_res(qpath, expr.hir_id); + let adt = self.tables().expr_ty(expr).ty_adt_def().unwrap(); let variant = adt.variant_of_res(res); if let Some(ref base) = *base { // If the expression uses FRU we need to make sure all the unmentioned fields @@ -1128,7 +1108,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { for (vf_index, variant_field) in variant.fields.iter().enumerate() { let field = fields .iter() - .find(|f| self.tcx.field_index(f.hir_id, self.tables) == vf_index); + .find(|f| self.tcx.field_index(f.hir_id, self.tables()) == vf_index); let (use_ctxt, span) = match field { Some(field) => (field.ident.span, field.span), None => (base.span, base.span), @@ -1138,7 +1118,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { } else { for field in fields { let use_ctxt = field.ident.span; - let index = self.tcx.field_index(field.hir_id, self.tables); + let index = self.tcx.field_index(field.hir_id, self.tables()); self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false); } } @@ -1149,12 +1129,12 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { if let PatKind::Struct(ref qpath, fields, _) = pat.kind { - let res = self.tables.qpath_res(qpath, pat.hir_id); - let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap(); + let res = self.tables().qpath_res(qpath, pat.hir_id); + let adt = self.tables().pat_ty(pat).ty_adt_def().unwrap(); let variant = adt.variant_of_res(res); for field in fields { let use_ctxt = field.ident.span; - let index = self.tcx.field_index(field.hir_id, self.tables); + let index = self.tcx.field_index(field.hir_id, self.tables()); self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false); } } @@ -1169,16 +1149,22 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { /// Checks are performed on "semantic" types regardless of names and their hygiene. //////////////////////////////////////////////////////////////////////////////////////////// -struct TypePrivacyVisitor<'a, 'tcx> { +struct TypePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, - tables: &'a ty::TypeckTables<'tcx>, + maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>, current_item: LocalDefId, - in_body: bool, span: Span, - empty_tables: &'a ty::TypeckTables<'tcx>, } -impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { +impl<'tcx> TypePrivacyVisitor<'tcx> { + /// Gets the type-checking side-tables for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> { + self.maybe_typeck_tables.expect("`TypePrivacyVisitor::tables` called outside of body") + } + fn item_is_accessible(&self, did: DefId) -> bool { def_id_visibility(self.tcx, did) .0 @@ -1188,10 +1174,11 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { // Take node-id of an expression or pattern and check its type for privacy. fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool { self.span = span; - if self.visit(self.tables.node_type(id)) || self.visit(self.tables.node_substs(id)) { + let tables = self.tables(); + if self.visit(tables.node_type(id)) || self.visit(tables.node_substs(id)) { return true; } - if let Some(adjustments) = self.tables.adjustments().get(id) { + if let Some(adjustments) = tables.adjustments().get(id) { for adjustment in adjustments { if self.visit(adjustment.target) { return true; @@ -1214,7 +1201,7 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { type Map = Map<'tcx>; /// We want to visit items in the context of their containing @@ -1229,19 +1216,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { } fn visit_nested_body(&mut self, body: hir::BodyId) { - let orig_tables = mem::replace(&mut self.tables, self.tcx.body_tables(body)); - let orig_in_body = mem::replace(&mut self.in_body, true); + let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body)); let body = self.tcx.hir().body(body); self.visit_body(body); - self.tables = orig_tables; - self.in_body = orig_in_body; + self.maybe_typeck_tables = old_maybe_typeck_tables; } fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { self.span = hir_ty.span; - if self.in_body { + if let Some(tables) = self.maybe_typeck_tables { // Types in bodies. - if self.visit(self.tables.node_type(hir_ty.hir_id)) { + if self.visit(tables.node_type(hir_ty.hir_id)) { return; } } else { @@ -1258,7 +1243,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef<'tcx>) { self.span = trait_ref.path.span; - if !self.in_body { + if self.maybe_typeck_tables.is_none() { // Avoid calling `hir_trait_to_predicates` in bodies, it will ICE. // The traits' privacy in bodies is already checked as a part of trait object types. let bounds = rustc_typeck::hir_trait_to_predicates( @@ -1304,7 +1289,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { hir::ExprKind::MethodCall(_, span, _, _) => { // Method calls have to be checked specially. self.span = span; - if let Some(def_id) = self.tables.type_dependent_def_id(expr.hir_id) { + if let Some(def_id) = self.tables().type_dependent_def_id(expr.hir_id) { if self.visit(self.tcx.type_of(def_id)) { return; } @@ -1327,9 +1312,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { // more code internal visibility at link time. (Access to private functions // is already prohibited by type privacy for function types.) fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: hir::HirId, span: Span) { - let def = match self.tables.qpath_res(qpath, id) { - Res::Def(kind, def_id) => Some((kind, def_id)), - _ => None, + let def = match qpath { + hir::QPath::Resolved(_, path) => match path.res { + Res::Def(kind, def_id) => Some((kind, def_id)), + _ => None, + }, + hir::QPath::TypeRelative(..) => { + self.maybe_typeck_tables.and_then(|tables| tables.type_dependent_def(id)) + } }; let def = def.filter(|(kind, _)| match kind { DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static => true, @@ -1385,31 +1375,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { let orig_current_item = mem::replace(&mut self.current_item, self.tcx.hir().local_def_id(item.hir_id)); - let orig_in_body = mem::replace(&mut self.in_body, false); - let orig_tables = - mem::replace(&mut self.tables, item_tables(self.tcx, item.hir_id, self.empty_tables)); + let old_maybe_typeck_tables = self.maybe_typeck_tables.take(); intravisit::walk_item(self, item); - self.tables = orig_tables; - self.in_body = orig_in_body; + self.maybe_typeck_tables = old_maybe_typeck_tables; self.current_item = orig_current_item; } - - fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { - let orig_tables = - mem::replace(&mut self.tables, item_tables(self.tcx, ti.hir_id, self.empty_tables)); - intravisit::walk_trait_item(self, ti); - self.tables = orig_tables; - } - - fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { - let orig_tables = - mem::replace(&mut self.tables, item_tables(self.tcx, ii.hir_id, self.empty_tables)); - intravisit::walk_impl_item(self, ii); - self.tables = orig_tables; - } } -impl DefIdVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { +impl DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -2066,29 +2039,16 @@ pub fn provide(providers: &mut Providers<'_>) { } fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - let empty_tables = ty::TypeckTables::empty(None); - // Check privacy of names not checked in previous compilation stages. - let mut visitor = NamePrivacyVisitor { - tcx, - tables: &empty_tables, - current_item: None, - empty_tables: &empty_tables, - }; + let mut visitor = NamePrivacyVisitor { tcx, maybe_typeck_tables: None, current_item: None }; let (module, span, hir_id) = tcx.hir().get_module(module_def_id); intravisit::walk_mod(&mut visitor, module, hir_id); // Check privacy of explicitly written types and traits as well as // inferred types of expressions and patterns. - let mut visitor = TypePrivacyVisitor { - tcx, - tables: &empty_tables, - current_item: module_def_id, - in_body: false, - span, - empty_tables: &empty_tables, - }; + let mut visitor = + TypePrivacyVisitor { tcx, maybe_typeck_tables: None, current_item: module_def_id, span }; intravisit::walk_mod(&mut visitor, module, hir_id); } From a610b7fe97d4ff857847e9dc511f4db8ef8c8b59 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 25 Jun 2020 22:53:34 +0300 Subject: [PATCH 29/33] rustc_save_analysis: avoid using TypeckTables::empty for SaveContext. --- src/librustc_save_analysis/dump_visitor.rs | 18 +++++------ src/librustc_save_analysis/lib.rs | 36 +++++++++++++--------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index e63e31e03c9f0..38be97fc8d9eb 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -109,15 +109,15 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { F: FnOnce(&mut Self), { let tables = if self.tcx.has_typeck_tables(item_def_id) { - self.tcx.typeck_tables_of(item_def_id) + Some(self.tcx.typeck_tables_of(item_def_id)) } else { - self.save_ctxt.empty_tables + None }; - let old_tables = self.save_ctxt.tables; - self.save_ctxt.tables = tables; + let old_maybe_typeck_tables = self.save_ctxt.maybe_typeck_tables; + self.save_ctxt.maybe_typeck_tables = tables; f(self); - self.save_ctxt.tables = old_tables; + self.save_ctxt.maybe_typeck_tables = old_maybe_typeck_tables; } fn span_from_span(&self, span: Span) -> SpanData { @@ -226,7 +226,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { collector.visit_pat(&arg.pat); for (hir_id, ident, ..) in collector.collected_idents { - let typ = match self.save_ctxt.tables.node_type_opt(hir_id) { + let typ = match self.save_ctxt.tables().node_type_opt(hir_id) { Some(s) => s.to_string(), None => continue, }; @@ -859,7 +859,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { match p.kind { hir::PatKind::Struct(ref _path, fields, _) => { // FIXME do something with _path? - let adt = match self.save_ctxt.tables.node_type_opt(p.hir_id) { + let adt = match self.save_ctxt.tables().node_type_opt(p.hir_id) { Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(), _ => { intravisit::walk_pat(self, p); @@ -900,7 +900,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> { Res::Local(hir_id) => { let typ = self .save_ctxt - .tables + .tables() .node_type_opt(hir_id) .map(|t| t.to_string()) .unwrap_or_default(); @@ -1393,7 +1393,7 @@ impl<'l, 'tcx> Visitor<'tcx> for DumpVisitor<'l, 'tcx> { match ex.kind { hir::ExprKind::Struct(ref path, ref fields, ref base) => { let hir_expr = self.save_ctxt.tcx.hir().expect_expr(ex.hir_id); - let adt = match self.save_ctxt.tables.expr_ty_opt(&hir_expr) { + let adt = match self.save_ctxt.tables().expr_ty_opt(&hir_expr) { Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(), _ => { intravisit::walk_expr(self, ex); diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index f5c3e84c62426..18883ff822197 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -1,6 +1,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(nll)] #![feature(or_patterns)] +#![feature(track_caller)] #![recursion_limit = "256"] mod dump_visitor; @@ -47,12 +48,9 @@ use rls_data::{ use log::{debug, error, info}; -pub struct SaveContext<'l, 'tcx> { +pub struct SaveContext<'l, 'tcx: 'l> { tcx: TyCtxt<'tcx>, - tables: &'l ty::TypeckTables<'tcx>, - /// Used as a fallback when nesting the typeck tables during item processing - /// (if these are not available for that item, e.g. don't own a body) - empty_tables: &'l ty::TypeckTables<'tcx>, + maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>, access_levels: &'l AccessLevels, span_utils: SpanUtils<'tcx>, config: Config, @@ -67,6 +65,14 @@ pub enum Data { } impl<'l, 'tcx> SaveContext<'l, 'tcx> { + /// Gets the type-checking side-tables for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> { + self.maybe_typeck_tables.expect("`SaveContext::tables` called outside of body") + } + fn span_from_span(&self, span: Span) -> SpanData { use rls_span::{Column, Row}; @@ -518,13 +524,13 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { } pub fn get_expr_data(&self, expr: &hir::Expr<'_>) -> Option { - let ty = self.tables.expr_ty_adjusted_opt(expr)?; + let ty = self.tables().expr_ty_adjusted_opt(expr)?; if matches!(ty.kind, ty::Error(_)) { return None; } match expr.kind { hir::ExprKind::Field(ref sub_ex, ident) => { - match self.tables.expr_ty_adjusted(&sub_ex).kind { + match self.tables().expr_ty_adjusted(&sub_ex).kind { ty::Adt(def, _) if !def.is_enum() => { let variant = &def.non_enum_variant(); filter!(self.span_utils, ident.span); @@ -569,7 +575,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { } } hir::ExprKind::MethodCall(ref seg, ..) => { - let method_id = match self.tables.type_dependent_def_id(expr.hir_id) { + let method_id = match self.tables().type_dependent_def_id(expr.hir_id) { Some(id) => id, None => { debug!("could not resolve method id for {:?}", expr); @@ -618,7 +624,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { }, Node::Expr(&hir::Expr { kind: hir::ExprKind::Struct(ref qpath, ..), .. }) => { - self.tables.qpath_res(qpath, hir_id) + self.tables().qpath_res(qpath, hir_id) } Node::Expr(&hir::Expr { kind: hir::ExprKind::Path(ref qpath), .. }) @@ -629,9 +635,12 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { | hir::PatKind::TupleStruct(ref qpath, ..), .. }) - | Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => { - self.tables.qpath_res(qpath, hir_id) - } + | Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => match qpath { + hir::QPath::Resolved(_, path) => path.res, + hir::QPath::TypeRelative(..) => self + .maybe_typeck_tables + .map_or(Res::Err, |tables| tables.qpath_res(qpath, hir_id)), + }, Node::Binding(&hir::Pat { kind: hir::PatKind::Binding(_, canonical_id, ..), .. @@ -1001,8 +1010,7 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>( let save_ctxt = SaveContext { tcx, - tables: &ty::TypeckTables::empty(None), - empty_tables: &ty::TypeckTables::empty(None), + maybe_typeck_tables: None, access_levels: &access_levels, span_utils: SpanUtils::new(&tcx.sess), config: find_config(config), From 4af3e84be19786ffd4f4601eabc29dbf8fb3caf9 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 26 Jun 2020 05:55:23 +0300 Subject: [PATCH 30/33] rustc_lint: avoid using TypeckTables::empty for LateContext. --- src/librustc_lint/context.rs | 29 ++++++++++--------- src/librustc_lint/late.rs | 2 -- src/librustc_lint/lib.rs | 1 + .../clippy/clippy_lints/src/map_unit_fn.rs | 8 ++--- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs index 0b4ae131af0fc..070cc8fa2c0c4 100644 --- a/src/librustc_lint/context.rs +++ b/src/librustc_lint/context.rs @@ -427,15 +427,12 @@ pub struct LateContext<'a, 'tcx> { /// Current body, or `None` if outside a body. pub enclosing_body: Option, - /// Type-checking side-tables for the current body. Access using the - /// `tables` method, which handles querying the tables on demand. + /// Type-checking side-tables for the current body. Access using the `tables` + /// and `maybe_tables` methods, which handle querying the tables on demand. // FIXME(eddyb) move all the code accessing internal fields like this, // to this module, to avoid exposing it to lint logic. pub(super) cached_typeck_tables: Cell>>, - // HACK(eddyb) replace this with having `Option` around `&TypeckTables`. - pub(super) empty_typeck_tables: &'a ty::TypeckTables<'tcx>, - /// Parameter environment for the item we are in. pub param_env: ty::ParamEnv<'tcx>, @@ -677,19 +674,23 @@ impl LintContext for EarlyContext<'_> { impl<'a, 'tcx> LateContext<'a, 'tcx> { /// Gets the type-checking side-tables for the current body, - /// or empty `TypeckTables` if outside a body. - // FIXME(eddyb) return `Option<&'tcx ty::TypeckTables<'tcx>>`, - // where `None` indicates we're outside a body. - pub fn tables(&self) -> &'a ty::TypeckTables<'tcx> { - if let Some(body) = self.enclosing_body { - self.cached_typeck_tables.get().unwrap_or_else(|| { + /// or `None` if outside a body. + pub fn maybe_tables(&self) -> Option<&'tcx ty::TypeckTables<'tcx>> { + self.cached_typeck_tables.get().or_else(|| { + self.enclosing_body.map(|body| { let tables = self.tcx.body_tables(body); self.cached_typeck_tables.set(Some(tables)); tables }) - } else { - self.empty_typeck_tables - } + }) + } + + /// Gets the type-checking side-tables for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + pub fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> { + self.maybe_tables().expect("`LateContext::tables` called outside of body") } pub fn current_lint_root(&self) -> hir::HirId { diff --git a/src/librustc_lint/late.rs b/src/librustc_lint/late.rs index ef62d44c4026b..9fe7edf6ccab2 100644 --- a/src/librustc_lint/late.rs +++ b/src/librustc_lint/late.rs @@ -378,7 +378,6 @@ fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>( tcx, enclosing_body: None, cached_typeck_tables: Cell::new(None), - empty_typeck_tables: &ty::TypeckTables::empty(None), param_env: ty::ParamEnv::empty(), access_levels, lint_store: unerased_lint_store(tcx), @@ -427,7 +426,6 @@ fn late_lint_pass_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tc tcx, enclosing_body: None, cached_typeck_tables: Cell::new(None), - empty_typeck_tables: &ty::TypeckTables::empty(None), param_env: ty::ParamEnv::empty(), access_levels, lint_store: unerased_lint_store(tcx), diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 4da98d201593b..a18631fe1c3c9 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -34,6 +34,7 @@ #![feature(never_type)] #![feature(nll)] #![feature(or_patterns)] +#![feature(track_caller)] #![recursion_limit = "256"] #[macro_use] diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index a4550f707ee22..90d4a34a19a29 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -160,10 +160,10 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a hir::Expr<'_>) } } -fn unit_closure<'a, 'tcx>( - cx: &LateContext<'a, 'tcx>, - expr: &'a hir::Expr<'a>, -) -> Option<(&'tcx hir::Param<'tcx>, &'a hir::Expr<'a>)> { +fn unit_closure<'tcx>( + cx: &LateContext<'_, 'tcx>, + expr: &hir::Expr<'_>, +) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind { let body = cx.tcx.hir().body(inner_expr_id); let body_expr = &body.value; From 4118090e698146d030b744387e13911805372b1f Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 26 Jun 2020 06:34:43 +0300 Subject: [PATCH 31/33] Remove `TypeckTables::empty(None)` and make hir_owner non-optional. --- .../infer/error_reporting/mod.rs | 2 +- src/librustc_infer/infer/mod.rs | 2 +- src/librustc_middle/ty/context.rs | 69 ++++------ .../traits/error_reporting/suggestions.rs | 2 +- src/librustc_typeck/check/method/suggest.rs | 127 +++++++++--------- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/check/writeback.rs | 15 +-- 7 files changed, 98 insertions(+), 121 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 7fdcbd31df3c5..b1ea222370742 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -1684,7 +1684,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Attempt to obtain the span of the parameter so we can // suggest adding an explicit lifetime bound to it. let generics = - self.in_progress_tables.and_then(|table| table.borrow().hir_owner).map(|table_owner| { + self.in_progress_tables.map(|table| table.borrow().hir_owner).map(|table_owner| { let hir_id = hir.as_local_hir_id(table_owner); let parent_id = hir.get_parent_item(hir_id); ( diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs index 76ac61c067280..27da514a17f4d 100644 --- a/src/librustc_infer/infer/mod.rs +++ b/src/librustc_infer/infer/mod.rs @@ -588,7 +588,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// Used only by `rustc_typeck` during body type-checking/inference, /// will initialize `in_progress_tables` with fresh `TypeckTables`. pub fn with_fresh_in_progress_tables(mut self, table_owner: LocalDefId) -> Self { - self.fresh_tables = Some(RefCell::new(ty::TypeckTables::empty(Some(table_owner)))); + self.fresh_tables = Some(RefCell::new(ty::TypeckTables::new(table_owner))); self } diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs index e2f601371b1ee..10bb788c9e9a6 100644 --- a/src/librustc_middle/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -188,7 +188,7 @@ pub struct CommonConsts<'tcx> { } pub struct LocalTableInContext<'a, V> { - hir_owner: Option, + hir_owner: LocalDefId, data: &'a ItemLocalMap, } @@ -199,42 +199,27 @@ pub struct LocalTableInContext<'a, V> { /// would be in a different frame of reference and using its `local_id` /// would result in lookup errors, or worse, in silently wrong data being /// stored/returned. -fn validate_hir_id_for_typeck_tables( - hir_owner: Option, - hir_id: hir::HirId, - mut_access: bool, -) { - if let Some(hir_owner) = hir_owner { - if hir_id.owner != hir_owner { - ty::tls::with(|tcx| { - bug!( - "node {} with HirId::owner {:?} cannot be placed in TypeckTables with hir_owner {:?}", - tcx.hir().node_to_string(hir_id), - hir_id.owner, - hir_owner - ) - }); - } - } else { - // We use "Null Object" TypeckTables in some of the analysis passes. - // These are just expected to be empty and their `hir_owner` is - // `None`. Therefore we cannot verify whether a given `HirId` would - // be a valid key for the given table. Instead we make sure that - // nobody tries to write to such a Null Object table. - if mut_access { - bug!("access to invalid TypeckTables") - } +fn validate_hir_id_for_typeck_tables(hir_owner: LocalDefId, hir_id: hir::HirId) { + if hir_id.owner != hir_owner { + ty::tls::with(|tcx| { + bug!( + "node {} with HirId::owner {:?} cannot be placed in TypeckTables with hir_owner {:?}", + tcx.hir().node_to_string(hir_id), + hir_id.owner, + hir_owner + ) + }); } } impl<'a, V> LocalTableInContext<'a, V> { pub fn contains_key(&self, id: hir::HirId) -> bool { - validate_hir_id_for_typeck_tables(self.hir_owner, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.data.contains_key(&id.local_id) } pub fn get(&self, id: hir::HirId) -> Option<&V> { - validate_hir_id_for_typeck_tables(self.hir_owner, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.data.get(&id.local_id) } @@ -252,28 +237,28 @@ impl<'a, V> ::std::ops::Index for LocalTableInContext<'a, V> { } pub struct LocalTableInContextMut<'a, V> { - hir_owner: Option, + hir_owner: LocalDefId, data: &'a mut ItemLocalMap, } impl<'a, V> LocalTableInContextMut<'a, V> { pub fn get_mut(&mut self, id: hir::HirId) -> Option<&mut V> { - validate_hir_id_for_typeck_tables(self.hir_owner, id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.data.get_mut(&id.local_id) } pub fn entry(&mut self, id: hir::HirId) -> Entry<'_, hir::ItemLocalId, V> { - validate_hir_id_for_typeck_tables(self.hir_owner, id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.data.entry(id.local_id) } pub fn insert(&mut self, id: hir::HirId, val: V) -> Option { - validate_hir_id_for_typeck_tables(self.hir_owner, id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.data.insert(id.local_id, val) } pub fn remove(&mut self, id: hir::HirId) -> Option { - validate_hir_id_for_typeck_tables(self.hir_owner, id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.data.remove(&id.local_id) } } @@ -324,7 +309,7 @@ pub struct GeneratorInteriorTypeCause<'tcx> { #[derive(RustcEncodable, RustcDecodable, Debug)] pub struct TypeckTables<'tcx> { /// The `HirId::owner` all `ItemLocalId`s in this table are relative to. - pub hir_owner: Option, + pub hir_owner: LocalDefId, /// Resolved definitions for `::X` associated paths and /// method calls, including those of overloaded operators. @@ -432,7 +417,7 @@ pub struct TypeckTables<'tcx> { } impl<'tcx> TypeckTables<'tcx> { - pub fn empty(hir_owner: Option) -> TypeckTables<'tcx> { + pub fn new(hir_owner: LocalDefId) -> TypeckTables<'tcx> { TypeckTables { hir_owner, type_dependent_defs: Default::default(), @@ -474,7 +459,7 @@ impl<'tcx> TypeckTables<'tcx> { } pub fn type_dependent_def(&self, id: HirId) -> Option<(DefKind, DefId)> { - validate_hir_id_for_typeck_tables(self.hir_owner, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.type_dependent_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) } @@ -521,7 +506,7 @@ impl<'tcx> TypeckTables<'tcx> { } pub fn node_type_opt(&self, id: hir::HirId) -> Option> { - validate_hir_id_for_typeck_tables(self.hir_owner, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.node_types.get(&id.local_id).cloned() } @@ -530,12 +515,12 @@ impl<'tcx> TypeckTables<'tcx> { } pub fn node_substs(&self, id: hir::HirId) -> SubstsRef<'tcx> { - validate_hir_id_for_typeck_tables(self.hir_owner, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.node_substs.get(&id.local_id).cloned().unwrap_or_else(|| InternalSubsts::empty()) } pub fn node_substs_opt(&self, id: hir::HirId) -> Option> { - validate_hir_id_for_typeck_tables(self.hir_owner, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id); self.node_substs.get(&id.local_id).cloned() } @@ -578,7 +563,7 @@ impl<'tcx> TypeckTables<'tcx> { } pub fn expr_adjustments(&self, expr: &hir::Expr<'_>) -> &[ty::adjustment::Adjustment<'tcx>] { - validate_hir_id_for_typeck_tables(self.hir_owner, expr.hir_id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, expr.hir_id); self.adjustments.get(&expr.hir_id.local_id).map_or(&[], |a| &a[..]) } @@ -657,7 +642,7 @@ impl<'tcx> TypeckTables<'tcx> { } pub fn is_coercion_cast(&self, hir_id: hir::HirId) -> bool { - validate_hir_id_for_typeck_tables(self.hir_owner, hir_id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, hir_id); self.coercion_casts.contains(&hir_id.local_id) } @@ -710,7 +695,7 @@ impl<'a, 'tcx> HashStable> for TypeckTables<'tcx> { hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| { let ty::UpvarId { var_path, closure_expr_id } = *up_var_id; - assert_eq!(Some(var_path.hir_id.owner), hir_owner); + assert_eq!(var_path.hir_id.owner, hir_owner); ( hcx.local_def_path_hash(var_path.hir_id.owner), diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 176bd90303ddd..69e848f24c044 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -1407,7 +1407,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); let query_tables; let tables: &TypeckTables<'tcx> = match &in_progress_tables { - Some(t) if t.hir_owner.map(|owner| owner.to_def_id()) == Some(generator_did_root) => t, + Some(t) if t.hir_owner.to_def_id() == generator_did_root => t, _ => { query_tables = self.tcx.typeck_tables_of(generator_did.expect_local()); &query_tables diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 67bdd04d3715c..f5503572e14e9 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -1039,22 +1039,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut suggested = false; if let (Some(ref param), Some(ref table)) = (param_type, self.in_progress_tables) { let table_owner = table.borrow().hir_owner; - if let Some(table_owner) = table_owner { - let generics = self.tcx.generics_of(table_owner.to_def_id()); - let type_param = generics.type_param(param, self.tcx); - let hir = &self.tcx.hir(); - if let Some(def_id) = type_param.def_id.as_local() { - let id = hir.as_local_hir_id(def_id); - // Get the `hir::Param` to verify whether it already has any bounds. - // We do this to avoid suggesting code that ends up as `T: FooBar`, - // instead we suggest `T: Foo + Bar` in that case. - match hir.get(id) { - Node::GenericParam(ref param) => { - let mut impl_trait = false; - let has_bounds = if let hir::GenericParamKind::Type { - synthetic: Some(_), - .. - } = ¶m.kind + let generics = self.tcx.generics_of(table_owner.to_def_id()); + let type_param = generics.type_param(param, self.tcx); + let hir = &self.tcx.hir(); + if let Some(def_id) = type_param.def_id.as_local() { + let id = hir.as_local_hir_id(def_id); + // Get the `hir::Param` to verify whether it already has any bounds. + // We do this to avoid suggesting code that ends up as `T: FooBar`, + // instead we suggest `T: Foo + Bar` in that case. + match hir.get(id) { + Node::GenericParam(ref param) => { + let mut impl_trait = false; + let has_bounds = + if let hir::GenericParamKind::Type { synthetic: Some(_), .. } = + ¶m.kind { // We've found `fn foo(x: impl Trait)` instead of // `fn foo(x: T)`. We want to suggest the correct @@ -1065,64 +1063,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { param.bounds.get(0) }; - let sp = hir.span(id); - let sp = if let Some(first_bound) = has_bounds { - // `sp` only covers `T`, change it so that it covers - // `T:` when appropriate - sp.until(first_bound.span()) - } else { - sp - }; - let trait_def_ids: FxHashSet = param - .bounds - .iter() - .filter_map(|bound| Some(bound.trait_ref()?.trait_def_id()?)) - .collect(); - if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { - err.span_suggestions( - sp, - &message(format!( - "restrict type parameter `{}` with", - param.name.ident(), - )), - candidates.iter().map(|t| { - format!( - "{}{} {}{}", - param.name.ident(), - if impl_trait { " +" } else { ":" }, - self.tcx.def_path_str(t.def_id), - if has_bounds.is_some() { " + " } else { "" }, - ) - }), - Applicability::MaybeIncorrect, - ); - } - suggested = true; - } - Node::Item(hir::Item { - kind: hir::ItemKind::Trait(.., bounds, _), - ident, - .. - }) => { - let (sp, sep, article) = if bounds.is_empty() { - (ident.span.shrink_to_hi(), ":", "a") - } else { - (bounds.last().unwrap().span().shrink_to_hi(), " +", "another") - }; + let sp = hir.span(id); + let sp = if let Some(first_bound) = has_bounds { + // `sp` only covers `T`, change it so that it covers + // `T:` when appropriate + sp.until(first_bound.span()) + } else { + sp + }; + let trait_def_ids: FxHashSet = param + .bounds + .iter() + .filter_map(|bound| Some(bound.trait_ref()?.trait_def_id()?)) + .collect(); + if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { err.span_suggestions( sp, - &message(format!("add {} supertrait for", article)), + &message(format!( + "restrict type parameter `{}` with", + param.name.ident(), + )), candidates.iter().map(|t| { - format!("{} {}", sep, self.tcx.def_path_str(t.def_id),) + format!( + "{}{} {}{}", + param.name.ident(), + if impl_trait { " +" } else { ":" }, + self.tcx.def_path_str(t.def_id), + if has_bounds.is_some() { " + " } else { "" }, + ) }), Applicability::MaybeIncorrect, ); - suggested = true; } - _ => {} + suggested = true; + } + Node::Item(hir::Item { + kind: hir::ItemKind::Trait(.., bounds, _), + ident, + .. + }) => { + let (sp, sep, article) = if bounds.is_empty() { + (ident.span.shrink_to_hi(), ":", "a") + } else { + (bounds.last().unwrap().span().shrink_to_hi(), " +", "another") + }; + err.span_suggestions( + sp, + &message(format!("add {} supertrait for", article)), + candidates.iter().map(|t| { + format!("{} {}", sep, self.tcx.def_path_str(t.def_id),) + }), + Applicability::MaybeIncorrect, + ); + suggested = true; } + _ => {} } - }; + } } if !suggested { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7c4048ab22302..7defe87e600ec 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1126,7 +1126,7 @@ fn typeck_tables_of_with_fallback<'tcx>( // Consistency check our TypeckTables instance can hold all ItemLocalIds // it will need to hold. - assert_eq!(tables.hir_owner, Some(id.owner)); + assert_eq!(tables.hir_owner, id.owner); tables } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 4704d8fc7666f..fa17696e02b30 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -109,12 +109,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { ) -> WritebackCx<'cx, 'tcx> { let owner = body.id().hir_id.owner; - WritebackCx { - fcx, - tables: ty::TypeckTables::empty(Some(owner)), - body, - rustc_dump_user_substs, - } + WritebackCx { fcx, tables: ty::TypeckTables::new(owner), body, rustc_dump_user_substs } } fn tcx(&self) -> TyCtxt<'tcx> { @@ -342,7 +337,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_closures(&mut self) { let fcx_tables = self.fcx.tables.borrow(); assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); - let common_hir_owner = fcx_tables.hir_owner.unwrap(); + let common_hir_owner = fcx_tables.hir_owner; for (&id, &origin) in fcx_tables.closure_kind_origins().iter() { let hir_id = hir::HirId { owner: common_hir_owner, local_id: id }; @@ -363,7 +358,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_user_provided_tys(&mut self) { let fcx_tables = self.fcx.tables.borrow(); assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); - let common_hir_owner = fcx_tables.hir_owner.unwrap(); + let common_hir_owner = fcx_tables.hir_owner; let mut errors_buffer = Vec::new(); for (&local_id, c_ty) in fcx_tables.user_provided_types().iter() { @@ -561,7 +556,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_liberated_fn_sigs(&mut self) { let fcx_tables = self.fcx.tables.borrow(); assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); - let common_hir_owner = fcx_tables.hir_owner.unwrap(); + let common_hir_owner = fcx_tables.hir_owner; for (&local_id, fn_sig) in fcx_tables.liberated_fn_sigs().iter() { let hir_id = hir::HirId { owner: common_hir_owner, local_id }; @@ -573,7 +568,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_fru_field_types(&mut self) { let fcx_tables = self.fcx.tables.borrow(); assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); - let common_hir_owner = fcx_tables.hir_owner.unwrap(); + let common_hir_owner = fcx_tables.hir_owner; for (&local_id, ftys) in fcx_tables.fru_field_types().iter() { let hir_id = hir::HirId { owner: common_hir_owner, local_id }; From 3ab7ae39ec77a39df27ac6c3fbde03cd3b763542 Mon Sep 17 00:00:00 2001 From: Nathan West Date: Tue, 19 May 2020 23:26:49 -0400 Subject: [PATCH 32/33] Bring net/parser.rs up to modern up to date with modern rust patterns Made the following changes throughout the IP address parser: - Replaced all uses of `is_some()` / `is_none()` with `?`. - "Upgraded" loops wherever possible; ie, replace `while` with `for`, etc. - Removed all cases of manual index tracking / incrementing. - Renamed several single-character variables with more expressive names. - Replaced several manual control flow segments with equivalent adapters (such as `Option::filter`). - Removed `read_seq_3`; replaced with simple sequences of `?`. - Parser now reslices its state when consuming, rather than carrying a separate state and index variable. - `read_digit` now uses `char::to_digit`. - Removed unnecessary casts back and forth between u8 and u32 - Added comments throughout, especially in the complex IPv6 parsing logic. - Added comprehensive local unit tests for the parser to validate these changes. --- src/libstd/net/parser.rs | 482 +++++++++++++++++++++++---------------- 1 file changed, 289 insertions(+), 193 deletions(-) diff --git a/src/libstd/net/parser.rs b/src/libstd/net/parser.rs index 3f38ee5471081..12d3baf633362 100644 --- a/src/libstd/net/parser.rs +++ b/src/libstd/net/parser.rs @@ -10,163 +10,132 @@ use crate::str::FromStr; struct Parser<'a> { // parsing as ASCII, so can use byte array - s: &'a [u8], - pos: usize, + state: &'a [u8], } impl<'a> Parser<'a> { - fn new(s: &'a str) -> Parser<'a> { - Parser { s: s.as_bytes(), pos: 0 } + fn new(input: &'a str) -> Parser<'a> { + Parser { state: input.as_bytes() } } fn is_eof(&self) -> bool { - self.pos == self.s.len() + self.state.is_empty() } - // Commit only if parser returns Some - fn read_atomically(&mut self, cb: F) -> Option + /// Run a parser, and restore the pre-parse state if it fails + fn read_atomically(&mut self, inner: F) -> Option where F: FnOnce(&mut Parser<'_>) -> Option, { - let pos = self.pos; - let r = cb(self); - if r.is_none() { - self.pos = pos; + let state = self.state; + let result = inner(self); + if result.is_none() { + self.state = state; } - r + result } - // Commit only if parser read till EOF - fn read_till_eof(&mut self, cb: F) -> Option + /// Run a parser, but fail if the entire input wasn't consumed. + /// Doesn't run atomically. + fn read_till_eof(&mut self, inner: F) -> Option where F: FnOnce(&mut Parser<'_>) -> Option, { - self.read_atomically(move |p| cb(p).filter(|_| p.is_eof())) + inner(self).filter(|_| self.is_eof()) } - // Apply 3 parsers sequentially - fn read_seq_3(&mut self, pa: PA, pb: PB, pc: PC) -> Option<(A, B, C)> + /// Same as read_till_eof, but returns a Result on failure + fn parse_with(&mut self, inner: F) -> Result where - PA: FnOnce(&mut Parser<'_>) -> Option, - PB: FnOnce(&mut Parser<'_>) -> Option, - PC: FnOnce(&mut Parser<'_>) -> Option, + F: FnOnce(&mut Parser<'_>) -> Option, { - self.read_atomically(move |p| { - let a = pa(p); - let b = if a.is_some() { pb(p) } else { None }; - let c = if b.is_some() { pc(p) } else { None }; - match (a, b, c) { - (Some(a), Some(b), Some(c)) => Some((a, b, c)), - _ => None, - } - }) + self.read_till_eof(inner).ok_or(AddrParseError(())) } - // Read next char + /// Read the next character from the input fn read_char(&mut self) -> Option { - if self.is_eof() { - None - } else { - let r = self.s[self.pos] as char; - self.pos += 1; - Some(r) - } - } - - // Return char and advance iff next char is equal to requested - fn read_given_char(&mut self, c: char) -> Option { - self.read_atomically(|p| match p.read_char() { - Some(next) if next == c => Some(next), - _ => None, + self.state.split_first().map(|(&b, tail)| { + self.state = tail; + b as char }) } - // Read digit - fn read_digit(&mut self, radix: u8) -> Option { - fn parse_digit(c: char, radix: u8) -> Option { - let c = c as u8; - // assuming radix is either 10 or 16 - if c >= b'0' && c <= b'9' { - Some(c - b'0') - } else if radix > 10 && c >= b'a' && c < b'a' + (radix - 10) { - Some(c - b'a' + 10) - } else if radix > 10 && c >= b'A' && c < b'A' + (radix - 10) { - Some(c - b'A' + 10) - } else { - None - } - } - - self.read_atomically(|p| p.read_char().and_then(|c| parse_digit(c, radix))) + /// Read the next character from the input if it matches the target + fn read_given_char(&mut self, target: char) -> Option { + self.read_atomically(|p| p.read_char().filter(|&c| c == target)) } - fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option { - let mut r = 0; - let mut digit_count = 0; - loop { - match self.read_digit(radix) { - Some(d) => { - r = r * (radix as u32) + (d as u32); - digit_count += 1; - if digit_count > max_digits || r >= upto { - return None; - } - } - None => { - if digit_count == 0 { - return None; - } else { - return Some(r); - } - } - }; - } + /// Helper for reading separators in an indexed loop. Reads the separator + /// character iff index > 0, then runs the parser. When used in a loop, + /// the separator character will only be read on index > 0 (see + /// read_ipv4_addr for an example) + fn read_separator(&mut self, sep: char, index: usize, inner: F) -> Option + where + F: FnOnce(&mut Parser<'_>) -> Option, + { + self.read_atomically(move |p| { + if index > 0 { + let _ = p.read_given_char(sep)?; + } + inner(p) + }) } - // Read number, failing if max_digits of number value exceeded - fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option { - self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto)) + // Read a single digit in the given radix. For instance, 0-9 in radix 10; + // 0-9A-F in radix 16. + fn read_digit(&mut self, radix: u32) -> Option { + self.read_atomically(move |p| p.read_char()?.to_digit(radix)) } - fn read_ipv4_addr_impl(&mut self) -> Option { - let mut bs = [0; 4]; - let mut i = 0; - while i < 4 { - if i != 0 && self.read_given_char('.').is_none() { - return None; + // Read a number off the front of the input in the given radix, stopping + // at the first non-digit character or eof. Fails if the number has more + // digits than max_digits, or the value is >= upto, or if there is no number. + fn read_number(&mut self, radix: u32, max_digits: u32, upto: u32) -> Option { + self.read_atomically(move |p| { + let mut result = 0; + let mut digit_count = 0; + + while let Some(digit) = p.read_digit(radix) { + result = (result * radix) + digit; + digit_count += 1; + if digit_count > max_digits || result >= upto { + return None; + } } - bs[i] = self.read_number(10, 3, 0x100).map(|n| n as u8)?; - i += 1; - } - Some(Ipv4Addr::new(bs[0], bs[1], bs[2], bs[3])) + if digit_count == 0 { None } else { Some(result) } + }) } - // Read IPv4 address + /// Read an IPv4 address fn read_ipv4_addr(&mut self) -> Option { - self.read_atomically(|p| p.read_ipv4_addr_impl()) - } + self.read_atomically(|p| { + let mut groups = [0; 4]; - fn read_ipv6_addr_impl(&mut self) -> Option { - fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> Ipv6Addr { - assert!(head.len() + tail.len() <= 8); - let mut gs = [0; 8]; - gs[..head.len()].copy_from_slice(head); - gs[(8 - tail.len())..8].copy_from_slice(tail); - Ipv6Addr::new(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7]) - } + for (i, slot) in groups.iter_mut().enumerate() { + *slot = p.read_separator('.', i, |p| p.read_number(10, 3, 0x100))? as u8; + } + + Some(groups.into()) + }) + } - fn read_groups(p: &mut Parser<'_>, groups: &mut [u16; 8], limit: usize) -> (usize, bool) { - let mut i = 0; - while i < limit { + /// Read an IPV6 Address + fn read_ipv6_addr(&mut self) -> Option { + /// Read a chunk of an ipv6 address into `groups`. Returns the number + /// of groups read, along with a bool indicating if an embedded + /// trailing ipv4 address was read. Specifically, read a series of + /// colon-separated ipv6 groups (0x0000 - 0xFFFF), with an optional + /// trailing embedded ipv4 address. + fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) { + let limit = groups.len(); + + for (i, slot) in groups.iter_mut().enumerate() { + // Try to read a trailing embedded ipv4 address. There must be + // at least two groups left. if i < limit - 1 { - let ipv4 = p.read_atomically(|p| { - if i == 0 || p.read_given_char(':').is_some() { - p.read_ipv4_addr() - } else { - None - } - }); + let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr()); + if let Some(v4_addr) = ipv4 { let octets = v4_addr.octets(); groups[i + 0] = ((octets[0] as u16) << 8) | (octets[1] as u16); @@ -175,83 +144,85 @@ impl<'a> Parser<'a> { } } - let group = p.read_atomically(|p| { - if i == 0 || p.read_given_char(':').is_some() { - p.read_number(16, 4, 0x10000).map(|n| n as u16) - } else { - None - } - }); + let group = p.read_separator(':', i, |p| p.read_number(16, 4, 0x10000)); + match group { - Some(g) => groups[i] = g, + Some(g) => *slot = g as u16, None => return (i, false), } - i += 1; } - (i, false) + (groups.len(), false) } - let mut head = [0; 8]; - let (head_size, head_ipv4) = read_groups(self, &mut head, 8); + self.read_atomically(|p| { + // Read the front part of the address; either the whole thing, or up + // to the first :: + let mut head = [0; 8]; + let (head_size, head_ipv4) = read_groups(p, &mut head); - if head_size == 8 { - return Some(Ipv6Addr::new( - head[0], head[1], head[2], head[3], head[4], head[5], head[6], head[7], - )); - } + if head_size == 8 { + return Some(head.into()); + } - // IPv4 part is not allowed before `::` - if head_ipv4 { - return None; - } + // IPv4 part is not allowed before `::` + if head_ipv4 { + return None; + } - // read `::` if previous code parsed less than 8 groups - if self.read_given_char(':').is_none() || self.read_given_char(':').is_none() { - return None; - } + // read `::` if previous code parsed less than 8 groups + // `::` indicates one or more groups of 16 bits of zeros + let _ = p.read_given_char(':')?; + let _ = p.read_given_char(':')?; - let mut tail = [0; 8]; - // `::` indicates one or more groups of 16 bits of zeros - let limit = 8 - (head_size + 1); - let (tail_size, _) = read_groups(self, &mut tail, limit); - Some(ipv6_addr_from_head_tail(&head[..head_size], &tail[..tail_size])) - } + // Read the back part of the address. The :: must contain at least one + // set of zeroes, so our max length is 7. + let mut tail = [0; 7]; + let limit = 8 - (head_size + 1); + let (tail_size, _) = read_groups(p, &mut tail[..limit]); - fn read_ipv6_addr(&mut self) -> Option { - self.read_atomically(|p| p.read_ipv6_addr_impl()) + // Concat the head and tail of the IP address + head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]); + + Some(head.into()) + }) } + /// Read an IP Address, either IPV4 or IPV6. fn read_ip_addr(&mut self) -> Option { - self.read_ipv4_addr().map(IpAddr::V4).or_else(|| self.read_ipv6_addr().map(IpAddr::V6)) + self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6)) } - fn read_socket_addr_v4(&mut self) -> Option { - let ip_addr = |p: &mut Parser<'_>| p.read_ipv4_addr(); - let colon = |p: &mut Parser<'_>| p.read_given_char(':'); - let port = |p: &mut Parser<'_>| p.read_number(10, 5, 0x10000).map(|n| n as u16); + /// Read a : followed by a port in base 10 + fn read_port(&mut self) -> Option { + self.read_atomically(|p| { + let _ = p.read_given_char(':')?; + let port = p.read_number(10, 5, 0x10000)?; + Some(port as u16) + }) + } - self.read_seq_3(ip_addr, colon, port).map(|t| { - let (ip, _, port): (Ipv4Addr, char, u16) = t; - SocketAddrV4::new(ip, port) + /// Read an IPV4 address with a port + fn read_socket_addr_v4(&mut self) -> Option { + self.read_atomically(|p| { + let ip = p.read_ipv4_addr()?; + let port = p.read_port()?; + Some(SocketAddrV4::new(ip, port)) }) } + /// Read an IPV6 address with a port fn read_socket_addr_v6(&mut self) -> Option { - let ip_addr = |p: &mut Parser<'_>| { - let open_br = |p: &mut Parser<'_>| p.read_given_char('['); - let ip_addr = |p: &mut Parser<'_>| p.read_ipv6_addr(); - let clos_br = |p: &mut Parser<'_>| p.read_given_char(']'); - p.read_seq_3(open_br, ip_addr, clos_br).map(|t| t.1) - }; - let colon = |p: &mut Parser<'_>| p.read_given_char(':'); - let port = |p: &mut Parser<'_>| p.read_number(10, 5, 0x10000).map(|n| n as u16); - - self.read_seq_3(ip_addr, colon, port).map(|t| { - let (ip, _, port): (Ipv6Addr, char, u16) = t; - SocketAddrV6::new(ip, port, 0, 0) + self.read_atomically(|p| { + let _ = p.read_given_char('[')?; + let ip = p.read_ipv6_addr()?; + let _ = p.read_given_char(']')?; + + let port = p.read_port()?; + Some(SocketAddrV6::new(ip, port, 0, 0)) }) } + /// Read an IP address with a port fn read_socket_addr(&mut self) -> Option { self.read_socket_addr_v4() .map(SocketAddr::V4) @@ -263,10 +234,7 @@ impl<'a> Parser<'a> { impl FromStr for IpAddr { type Err = AddrParseError; fn from_str(s: &str) -> Result { - match Parser::new(s).read_till_eof(|p| p.read_ip_addr()) { - Some(s) => Ok(s), - None => Err(AddrParseError(())), - } + Parser::new(s).parse_with(|p| p.read_ip_addr()) } } @@ -274,10 +242,7 @@ impl FromStr for IpAddr { impl FromStr for Ipv4Addr { type Err = AddrParseError; fn from_str(s: &str) -> Result { - match Parser::new(s).read_till_eof(|p| p.read_ipv4_addr()) { - Some(s) => Ok(s), - None => Err(AddrParseError(())), - } + Parser::new(s).parse_with(|p| p.read_ipv4_addr()) } } @@ -285,10 +250,7 @@ impl FromStr for Ipv4Addr { impl FromStr for Ipv6Addr { type Err = AddrParseError; fn from_str(s: &str) -> Result { - match Parser::new(s).read_till_eof(|p| p.read_ipv6_addr()) { - Some(s) => Ok(s), - None => Err(AddrParseError(())), - } + Parser::new(s).parse_with(|p| p.read_ipv6_addr()) } } @@ -296,10 +258,7 @@ impl FromStr for Ipv6Addr { impl FromStr for SocketAddrV4 { type Err = AddrParseError; fn from_str(s: &str) -> Result { - match Parser::new(s).read_till_eof(|p| p.read_socket_addr_v4()) { - Some(s) => Ok(s), - None => Err(AddrParseError(())), - } + Parser::new(s).parse_with(|p| p.read_socket_addr_v4()) } } @@ -307,10 +266,7 @@ impl FromStr for SocketAddrV4 { impl FromStr for SocketAddrV6 { type Err = AddrParseError; fn from_str(s: &str) -> Result { - match Parser::new(s).read_till_eof(|p| p.read_socket_addr_v6()) { - Some(s) => Ok(s), - None => Err(AddrParseError(())), - } + Parser::new(s).parse_with(|p| p.read_socket_addr_v6()) } } @@ -318,10 +274,7 @@ impl FromStr for SocketAddrV6 { impl FromStr for SocketAddr { type Err = AddrParseError; fn from_str(s: &str) -> Result { - match Parser::new(s).read_till_eof(|p| p.read_socket_addr()) { - Some(s) => Ok(s), - None => Err(AddrParseError(())), - } + Parser::new(s).parse_with(|p| p.read_socket_addr()) } } @@ -376,3 +329,146 @@ impl Error for AddrParseError { "invalid IP address syntax" } } + +#[cfg(test)] +mod tests { + // FIXME: These tests are all excellent candidates for AFL fuzz testing + use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; + use crate::str::FromStr; + + const PORT: u16 = 8080; + + const IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 0, 1); + const IPV4_STR: &str = "192.168.0.1"; + const IPV4_STR_PORT: &str = "192.168.0.1:8080"; + + const IPV6: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc0a8, 0x1); + const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1"; + const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1"; + const IPV6_STR_V4: &str = "2001:db8::192.168.0.1"; + const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080"; + + #[test] + fn parse_ipv4() { + let result: Ipv4Addr = IPV4_STR.parse().unwrap(); + assert_eq!(result, IPV4); + + assert!(Ipv4Addr::from_str(IPV4_STR_PORT).is_err()); + assert!(Ipv4Addr::from_str(IPV6_STR_FULL).is_err()); + assert!(Ipv4Addr::from_str(IPV6_STR_COMPRESS).is_err()); + assert!(Ipv4Addr::from_str(IPV6_STR_V4).is_err()); + assert!(Ipv4Addr::from_str(IPV6_STR_PORT).is_err()); + } + + #[test] + fn parse_ipv6() { + let result: Ipv6Addr = IPV6_STR_FULL.parse().unwrap(); + assert_eq!(result, IPV6); + + let result: Ipv6Addr = IPV6_STR_COMPRESS.parse().unwrap(); + assert_eq!(result, IPV6); + + let result: Ipv6Addr = IPV6_STR_V4.parse().unwrap(); + assert_eq!(result, IPV6); + + assert!(Ipv6Addr::from_str(IPV4_STR).is_err()); + assert!(Ipv6Addr::from_str(IPV4_STR_PORT).is_err()); + assert!(Ipv6Addr::from_str(IPV6_STR_PORT).is_err()); + } + + #[test] + fn parse_ip() { + let result: IpAddr = IPV4_STR.parse().unwrap(); + assert_eq!(result, IpAddr::from(IPV4)); + + let result: IpAddr = IPV6_STR_FULL.parse().unwrap(); + assert_eq!(result, IpAddr::from(IPV6)); + + let result: IpAddr = IPV6_STR_COMPRESS.parse().unwrap(); + assert_eq!(result, IpAddr::from(IPV6)); + + let result: IpAddr = IPV6_STR_V4.parse().unwrap(); + assert_eq!(result, IpAddr::from(IPV6)); + + assert!(IpAddr::from_str(IPV4_STR_PORT).is_err()); + assert!(IpAddr::from_str(IPV6_STR_PORT).is_err()); + } + + #[test] + fn parse_socket_v4() { + let result: SocketAddrV4 = IPV4_STR_PORT.parse().unwrap(); + assert_eq!(result, SocketAddrV4::new(IPV4, PORT)); + + assert!(SocketAddrV4::from_str(IPV4_STR).is_err()); + assert!(SocketAddrV4::from_str(IPV6_STR_FULL).is_err()); + assert!(SocketAddrV4::from_str(IPV6_STR_COMPRESS).is_err()); + assert!(SocketAddrV4::from_str(IPV6_STR_V4).is_err()); + assert!(SocketAddrV4::from_str(IPV6_STR_PORT).is_err()); + } + + #[test] + fn parse_socket_v6() { + let result: SocketAddrV6 = IPV6_STR_PORT.parse().unwrap(); + assert_eq!(result, SocketAddrV6::new(IPV6, PORT, 0, 0)); + + assert!(SocketAddrV6::from_str(IPV4_STR).is_err()); + assert!(SocketAddrV6::from_str(IPV4_STR_PORT).is_err()); + assert!(SocketAddrV6::from_str(IPV6_STR_FULL).is_err()); + assert!(SocketAddrV6::from_str(IPV6_STR_COMPRESS).is_err()); + assert!(SocketAddrV6::from_str(IPV6_STR_V4).is_err()); + } + + #[test] + fn parse_socket() { + let result: SocketAddr = IPV4_STR_PORT.parse().unwrap(); + assert_eq!(result, SocketAddr::from((IPV4, PORT))); + + let result: SocketAddr = IPV6_STR_PORT.parse().unwrap(); + assert_eq!(result, SocketAddr::from((IPV6, PORT))); + + assert!(SocketAddr::from_str(IPV4_STR).is_err()); + assert!(SocketAddr::from_str(IPV6_STR_FULL).is_err()); + assert!(SocketAddr::from_str(IPV6_STR_COMPRESS).is_err()); + assert!(SocketAddr::from_str(IPV6_STR_V4).is_err()); + } + + #[test] + fn ipv6_corner_cases() { + let result: Ipv6Addr = "1::".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0)); + + let result: Ipv6Addr = "1:1::".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(1, 1, 0, 0, 0, 0, 0, 0)); + + let result: Ipv6Addr = "::1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + + let result: Ipv6Addr = "::1:1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 1, 1)); + + let result: Ipv6Addr = "::".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + + let result: Ipv6Addr = "::192.168.0.1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1)); + + let result: Ipv6Addr = "::1:192.168.0.1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 1, 0xc0a8, 0x1)); + + let result: Ipv6Addr = "1:1:1:1:1:1:192.168.0.1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(1, 1, 1, 1, 1, 1, 0xc0a8, 0x1)); + } + + // Things that might not seem like failures but are + #[test] + fn ipv6_corner_failures() { + // No IP address before the :: + assert!(Ipv6Addr::from_str("1:192.168.0.1::").is_err()); + + // :: must have at least 1 set of zeroes + assert!(Ipv6Addr::from_str("1:1:1:1::1:1:1:1").is_err()); + + // Need brackets for a port + assert!(SocketAddrV6::from_str("1:1:1:1:1:1:1:1:8080").is_err()); + } +} From 0e8a69a3e8eacd248c6233227dadbc2db0aaa1c0 Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Tue, 30 Jun 2020 21:44:44 +0200 Subject: [PATCH 33/33] Address review comments and add UI test --- src/ci/docker/test-various/Dockerfile | 4 +- src/librustc_codegen_llvm/builder.rs | 4 +- src/librustc_span/symbol.rs | 2 +- .../saturating-float-casts-impl.rs | 507 ++++++++++++++++++ .../saturating-float-casts-wasm.rs | 14 + .../saturating-float-casts.rs | 503 +---------------- 6 files changed, 529 insertions(+), 505 deletions(-) create mode 100644 src/test/ui/numbers-arithmetic/saturating-float-casts-impl.rs create mode 100644 src/test/ui/numbers-arithmetic/saturating-float-casts-wasm.rs diff --git a/src/ci/docker/test-various/Dockerfile b/src/ci/docker/test-various/Dockerfile index 9276e4ed82d78..6775baa8c3273 100644 --- a/src/ci/docker/test-various/Dockerfile +++ b/src/ci/docker/test-various/Dockerfile @@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ wget \ patch -RUN curl -sL https://nodejs.org/dist/v9.2.0/node-v9.2.0-linux-x64.tar.xz | \ +RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | \ tar -xJ WORKDIR /build/ @@ -30,7 +30,7 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ - --set build.nodejs=/node-v9.2.0-linux-x64/bin/node \ + --set build.nodejs=/node-v14.4.0-linux-x64/bin/node \ --set rust.lld # Some run-make tests have assertions about code size, and enabling debug diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index eed327d0bc156..89b70dce52c66 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -658,7 +658,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // `nontrapping-fptoint` target feature is activated. We'll use those if // they are available. if self.sess().target.target.arch == "wasm32" - && self.sess().target_features.contains(&sym::wasm_nontrapping_fptoint) + && self.sess().target_features.contains(&sym::nontrapping_fptoint) { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); @@ -683,7 +683,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // `nontrapping-fptoint` target feature is activated. We'll use those if // they are available. if self.sess().target.target.arch == "wasm32" - && self.sess().target_features.contains(&sym::wasm_nontrapping_fptoint) + && self.sess().target_features.contains(&sym::nontrapping_fptoint) { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 246112704acbd..905064f7b3c13 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -511,6 +511,7 @@ symbols! { None, non_exhaustive, non_modrs_mods, + nontrapping_fptoint: "nontrapping-fptoint", noreturn, no_niche, no_sanitize, @@ -837,7 +838,6 @@ symbols! { warn, wasm_import_module, wasm_target_feature, - wasm_nontrapping_fptoint: "nontrapping-fptoint", while_let, windows, windows_subsystem, diff --git a/src/test/ui/numbers-arithmetic/saturating-float-casts-impl.rs b/src/test/ui/numbers-arithmetic/saturating-float-casts-impl.rs new file mode 100644 index 0000000000000..6c3e503693c3c --- /dev/null +++ b/src/test/ui/numbers-arithmetic/saturating-float-casts-impl.rs @@ -0,0 +1,507 @@ +// ignore-test + +// Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. +// +// Some of these tests come from a similar file in miri, +// tests/run-pass/float.rs. Individual test cases are potentially duplicated +// with the previously existing tests, but since this runs so quickly anyway, +// we're not spending the time to figure out exactly which ones should be +// merged. + +extern crate test; + +use self::test::black_box; +use std::{f32, f64}; +#[cfg(not(target_os = "emscripten"))] +use std::{i128, u128}; +use std::{i16, i32, i64, i8, u16, u32, u64, u8}; + +macro_rules! test { + ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ( + // black_box disables constant evaluation to test run-time conversions: + assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, + "run-time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + + { + const X: $src_ty = $val; + const Y: $dest_ty = X as $dest_ty; + assert_eq!(Y, $expected, + "const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + } + ); + + ($fval:expr, f* -> $ity:ident, $ival:expr) => ( + test!($fval, f32 -> $ity, $ival); + test!($fval, f64 -> $ity, $ival); + ) +} + +macro_rules! common_fptoi_tests { + ($fty:ident -> $($ity:ident)+) => ({ $( + test!($fty::NAN, $fty -> $ity, 0); + test!($fty::INFINITY, $fty -> $ity, $ity::MAX); + test!($fty::NEG_INFINITY, $fty -> $ity, $ity::MIN); + // These two tests are not solely float->int tests, in particular the latter relies on + // `u128::MAX as f32` not being UB. But that's okay, since this file tests int->float + // as well, the test is just slightly misplaced. + test!($ity::MIN as $fty, $fty -> $ity, $ity::MIN); + test!($ity::MAX as $fty, $fty -> $ity, $ity::MAX); + test!(0., $fty -> $ity, 0); + test!($fty::MIN_POSITIVE, $fty -> $ity, 0); + test!(-0.9, $fty -> $ity, 0); + test!(1., $fty -> $ity, 1); + test!(42., $fty -> $ity, 42); + )+ }); + + (f* -> $($ity:ident)+) => ({ + common_fptoi_tests!(f32 -> $($ity)+); + common_fptoi_tests!(f64 -> $($ity)+); + }) +} + +macro_rules! fptoui_tests { + ($fty: ident -> $($ity: ident)+) => ({ $( + test!(-0., $fty -> $ity, 0); + test!(-$fty::MIN_POSITIVE, $fty -> $ity, 0); + test!(-0.99999994, $fty -> $ity, 0); + test!(-1., $fty -> $ity, 0); + test!(-100., $fty -> $ity, 0); + test!(#[allow(overflowing_literals)] -1e50, $fty -> $ity, 0); + test!(#[allow(overflowing_literals)] -1e130, $fty -> $ity, 0); + )+ }); + + (f* -> $($ity:ident)+) => ({ + fptoui_tests!(f32 -> $($ity)+); + fptoui_tests!(f64 -> $($ity)+); + }) +} + +use std::fmt::Debug; + +// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE. +#[track_caller] +#[inline(never)] +fn assert_eq(x: T, y: T) { + assert_eq!(x, y); +} + +trait FloatToInt: Copy { + fn cast(self) -> Int; + unsafe fn cast_unchecked(self) -> Int; +} + +impl FloatToInt for f32 { + fn cast(self) -> i8 { + self as _ + } + unsafe fn cast_unchecked(self) -> i8 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> i32 { + self as _ + } + unsafe fn cast_unchecked(self) -> i32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> u32 { + self as _ + } + unsafe fn cast_unchecked(self) -> u32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> i64 { + self as _ + } + unsafe fn cast_unchecked(self) -> i64 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> u64 { + self as _ + } + unsafe fn cast_unchecked(self) -> u64 { + self.to_int_unchecked() + } +} + +impl FloatToInt for f64 { + fn cast(self) -> i8 { + self as _ + } + unsafe fn cast_unchecked(self) -> i8 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> i32 { + self as _ + } + unsafe fn cast_unchecked(self) -> i32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> u32 { + self as _ + } + unsafe fn cast_unchecked(self) -> u32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> i64 { + self as _ + } + unsafe fn cast_unchecked(self) -> i64 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> u64 { + self as _ + } + unsafe fn cast_unchecked(self) -> u64 { + self.to_int_unchecked() + } +} +// FIXME emscripten does not support i128 +#[cfg(not(target_os = "emscripten"))] +impl FloatToInt for f64 { + fn cast(self) -> i128 { + self as _ + } + unsafe fn cast_unchecked(self) -> i128 { + self.to_int_unchecked() + } +} +// FIXME emscripten does not support i128 +#[cfg(not(target_os = "emscripten"))] +impl FloatToInt for f64 { + fn cast(self) -> u128 { + self as _ + } + unsafe fn cast_unchecked(self) -> u128 { + self.to_int_unchecked() + } +} + +/// Test this cast both via `as` and via `to_int_unchecked` (i.e., it must not saturate). +#[track_caller] +#[inline(never)] +fn test_both_cast(x: F, y: I) +where + F: FloatToInt, + I: PartialEq + Debug, +{ + assert_eq!(x.cast(), y); + assert_eq!(unsafe { x.cast_unchecked() }, y); +} + +fn casts() { + // f32 -> i8 + test_both_cast::(127.99, 127); + test_both_cast::(-128.99, -128); + + // f32 -> i32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x1p-149*/ f32::from_bits(0x00000001), 0); + test_both_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); + test_both_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); + test_both_cast::(/*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd), -1); + test_both_cast::(1.9, 1); + test_both_cast::(-1.9, -1); + test_both_cast::(5.0, 5); + test_both_cast::(-5.0, -5); + test_both_cast::(2147483520.0, 2147483520); + test_both_cast::(-2147483648.0, -2147483648); + // unrepresentable casts + assert_eq::(2147483648.0f32 as i32, i32::MAX); + assert_eq::(-2147483904.0f32 as i32, i32::MIN); + assert_eq::(f32::MAX as i32, i32::MAX); + assert_eq::(f32::MIN as i32, i32::MIN); + assert_eq::(f32::INFINITY as i32, i32::MAX); + assert_eq::(f32::NEG_INFINITY as i32, i32::MIN); + assert_eq::(f32::NAN as i32, 0); + assert_eq::((-f32::NAN) as i32, 0); + + // f32 -> u32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(-0.9999999, 0); + test_both_cast::(/*0x1p-149*/ f32::from_bits(0x1), 0); + test_both_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); + test_both_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); + test_both_cast::(1.9, 1); + test_both_cast::(5.0, 5); + test_both_cast::(2147483648.0, 0x8000_0000); + test_both_cast::(4294967040.0, 0u32.wrapping_sub(256)); + test_both_cast::(/*-0x1.ccccccp-1*/ f32::from_bits(0xbf666666), 0); + test_both_cast::(/*-0x1.fffffep-1*/ f32::from_bits(0xbf7fffff), 0); + test_both_cast::((u32::MAX - 128) as f32, u32::MAX - 255); // rounding loss + + // unrepresentable casts: + + // rounds up and then becomes unrepresentable + assert_eq::((u32::MAX - 127) as f32 as u32, u32::MAX); + + assert_eq::(4294967296.0f32 as u32, u32::MAX); + assert_eq::(-5.0f32 as u32, 0); + assert_eq::(f32::MAX as u32, u32::MAX); + assert_eq::(f32::MIN as u32, 0); + assert_eq::(f32::INFINITY as u32, u32::MAX); + assert_eq::(f32::NEG_INFINITY as u32, 0); + assert_eq::(f32::NAN as u32, 0); + assert_eq::((-f32::NAN) as u32, 0); + + // f32 -> i64 + test_both_cast::(4294967296.0, 4294967296); + test_both_cast::(-4294967296.0, -4294967296); + test_both_cast::(9223371487098961920.0, 9223371487098961920); + test_both_cast::(-9223372036854775808.0, -9223372036854775808); + + // f64 -> i8 + test_both_cast::(127.99, 127); + test_both_cast::(-128.99, -128); + + // f64 -> i32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); + test_both_cast::( + /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), + -1, + ); + test_both_cast::(1.9, 1); + test_both_cast::(-1.9, -1); + test_both_cast::(1e8, 100_000_000); + test_both_cast::(2147483647.0, 2147483647); + test_both_cast::(-2147483648.0, -2147483648); + // unrepresentable casts + assert_eq::(2147483648.0f64 as i32, i32::MAX); + assert_eq::(-2147483649.0f64 as i32, i32::MIN); + + // f64 -> i64 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1), 0); + test_both_cast::( + /*-0x0.0000000000001p-1022*/ f64::from_bits(0x8000000000000001), + 0, + ); + test_both_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); + test_both_cast::( + /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), + -1, + ); + test_both_cast::(5.0, 5); + test_both_cast::(5.9, 5); + test_both_cast::(-5.0, -5); + test_both_cast::(-5.9, -5); + test_both_cast::(4294967296.0, 4294967296); + test_both_cast::(-4294967296.0, -4294967296); + test_both_cast::(9223372036854774784.0, 9223372036854774784); + test_both_cast::(-9223372036854775808.0, -9223372036854775808); + // unrepresentable casts + assert_eq::(9223372036854775808.0f64 as i64, i64::MAX); + assert_eq::(-9223372036854777856.0f64 as i64, i64::MIN); + assert_eq::(f64::MAX as i64, i64::MAX); + assert_eq::(f64::MIN as i64, i64::MIN); + assert_eq::(f64::INFINITY as i64, i64::MAX); + assert_eq::(f64::NEG_INFINITY as i64, i64::MIN); + assert_eq::(f64::NAN as i64, 0); + assert_eq::((-f64::NAN) as i64, 0); + + // f64 -> u64 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(-0.99999999999, 0); + test_both_cast::(5.0, 5); + test_both_cast::(1e16, 10000000000000000); + test_both_cast::((u64::MAX - 1024) as f64, u64::MAX - 2047); // rounding loss + test_both_cast::(9223372036854775808.0, 9223372036854775808); + // unrepresentable casts + assert_eq::(-5.0f64 as u64, 0); + // rounds up and then becomes unrepresentable + assert_eq::((u64::MAX - 1023) as f64 as u64, u64::MAX); + assert_eq::(18446744073709551616.0f64 as u64, u64::MAX); + assert_eq::(f64::MAX as u64, u64::MAX); + assert_eq::(f64::MIN as u64, 0); + assert_eq::(f64::INFINITY as u64, u64::MAX); + assert_eq::(f64::NEG_INFINITY as u64, 0); + assert_eq::(f64::NAN as u64, 0); + assert_eq::((-f64::NAN) as u64, 0); + + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + // f64 -> i128 + assert_eq::(f64::MAX as i128, i128::MAX); + assert_eq::(f64::MIN as i128, i128::MIN); + + // f64 -> u128 + assert_eq::(f64::MAX as u128, u128::MAX); + assert_eq::(f64::MIN as u128, 0); + } + + // int -> f32 + assert_eq::(127i8 as f32, 127.0); + assert_eq::(2147483647i32 as f32, 2147483648.0); + assert_eq::((-2147483648i32) as f32, -2147483648.0); + assert_eq::(1234567890i32 as f32, /*0x1.26580cp+30*/ f32::from_bits(0x4e932c06)); + assert_eq::(16777217i32 as f32, 16777216.0); + assert_eq::((-16777217i32) as f32, -16777216.0); + assert_eq::(16777219i32 as f32, 16777220.0); + assert_eq::((-16777219i32) as f32, -16777220.0); + assert_eq::( + 0x7fffff4000000001i64 as f32, + /*0x1.fffffep+62*/ f32::from_bits(0x5effffff), + ); + assert_eq::( + 0x8000004000000001u64 as i64 as f32, + /*-0x1.fffffep+62*/ f32::from_bits(0xdeffffff), + ); + assert_eq::( + 0x0020000020000001i64 as f32, + /*0x1.000002p+53*/ f32::from_bits(0x5a000001), + ); + assert_eq::( + 0xffdfffffdfffffffu64 as i64 as f32, + /*-0x1.000002p+53*/ f32::from_bits(0xda000001), + ); + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + assert_eq::(i128::MIN as f32, -170141183460469231731687303715884105728.0f32); + assert_eq::(u128::MAX as f32, f32::INFINITY); // saturation + } + + // int -> f64 + assert_eq::(127i8 as f64, 127.0); + assert_eq::(i16::MIN as f64, -32768.0f64); + assert_eq::(2147483647i32 as f64, 2147483647.0); + assert_eq::(-2147483648i32 as f64, -2147483648.0); + assert_eq::(987654321i32 as f64, 987654321.0); + assert_eq::(9223372036854775807i64 as f64, 9223372036854775807.0); + assert_eq::(-9223372036854775808i64 as f64, -9223372036854775808.0); + assert_eq::(4669201609102990i64 as f64, 4669201609102990.0); // Feigenbaum (?) + assert_eq::(9007199254740993i64 as f64, 9007199254740992.0); + assert_eq::(-9007199254740993i64 as f64, -9007199254740992.0); + assert_eq::(9007199254740995i64 as f64, 9007199254740996.0); + assert_eq::(-9007199254740995i64 as f64, -9007199254740996.0); + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + // even that fits... + assert_eq::(u128::MAX as f64, 340282366920938463463374607431768211455.0f64); + } + + // f32 -> f64 + assert_eq::((0.0f32 as f64).to_bits(), 0.0f64.to_bits()); + assert_eq::(((-0.0f32) as f64).to_bits(), (-0.0f64).to_bits()); + assert_eq::(5.0f32 as f64, 5.0f64); + assert_eq::( + /*0x1p-149*/ f32::from_bits(0x1) as f64, + /*0x1p-149*/ f64::from_bits(0x36a0000000000000), + ); + assert_eq::( + /*-0x1p-149*/ f32::from_bits(0x80000001) as f64, + /*-0x1p-149*/ f64::from_bits(0xb6a0000000000000), + ); + assert_eq::( + /*0x1.fffffep+127*/ f32::from_bits(0x7f7fffff) as f64, + /*0x1.fffffep+127*/ f64::from_bits(0x47efffffe0000000), + ); + assert_eq::( + /*-0x1.fffffep+127*/ (-f32::from_bits(0x7f7fffff)) as f64, + /*-0x1.fffffep+127*/ -f64::from_bits(0x47efffffe0000000), + ); + assert_eq::( + /*0x1p-119*/ f32::from_bits(0x4000000) as f64, + /*0x1p-119*/ f64::from_bits(0x3880000000000000), + ); + assert_eq::( + /*0x1.8f867ep+125*/ f32::from_bits(0x7e47c33f) as f64, + 6.6382536710104395e+37, + ); + assert_eq::(f32::INFINITY as f64, f64::INFINITY); + assert_eq::(f32::NEG_INFINITY as f64, f64::NEG_INFINITY); + + // f64 -> f32 + assert_eq::((0.0f64 as f32).to_bits(), 0.0f32.to_bits()); + assert_eq::(((-0.0f64) as f32).to_bits(), (-0.0f32).to_bits()); + assert_eq::(5.0f64 as f32, 5.0f32); + assert_eq::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1) as f32, 0.0); + assert_eq::(/*-0x0.0000000000001p-1022*/ (-f64::from_bits(0x1)) as f32, -0.0); + assert_eq::( + /*0x1.fffffe0000000p-127*/ f64::from_bits(0x380fffffe0000000) as f32, + /*0x1p-149*/ f32::from_bits(0x800000), + ); + assert_eq::( + /*0x1.4eae4f7024c7p+108*/ f64::from_bits(0x46b4eae4f7024c70) as f32, + /*0x1.4eae5p+108*/ f32::from_bits(0x75a75728), + ); + assert_eq::(f64::MAX as f32, f32::INFINITY); + assert_eq::(f64::MIN as f32, f32::NEG_INFINITY); + assert_eq::(f64::INFINITY as f32, f32::INFINITY); + assert_eq::(f64::NEG_INFINITY as f32, f32::NEG_INFINITY); +} + +pub fn run() { + casts(); // from miri's tests + + common_fptoi_tests!(f* -> i8 i16 i32 i64 u8 u16 u32 u64); + fptoui_tests!(f* -> u8 u16 u32 u64); + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + common_fptoi_tests!(f* -> i128 u128); + fptoui_tests!(f* -> u128); + } + + // The following tests cover edge cases for some integer types. + + // # u8 + test!(254., f* -> u8, 254); + test!(256., f* -> u8, 255); + + // # i8 + test!(-127., f* -> i8, -127); + test!(-129., f* -> i8, -128); + test!(126., f* -> i8, 126); + test!(128., f* -> i8, 127); + + // # i32 + // -2147483648. is i32::MIN (exactly) + test!(-2147483648., f* -> i32, i32::MIN); + // 2147483648. is i32::MAX rounded up + test!(2147483648., f32 -> i32, 2147483647); + // With 24 significand bits, floats with magnitude in [2^30 + 1, 2^31] are rounded to + // multiples of 2^7. Therefore, nextDown(round(i32::MAX)) is 2^31 - 128: + test!(2147483520., f32 -> i32, 2147483520); + // Similarly, nextUp(i32::MIN) is i32::MIN + 2^8 and nextDown(i32::MIN) is i32::MIN - 2^7 + test!(-2147483904., f* -> i32, i32::MIN); + test!(-2147483520., f* -> i32, -2147483520); + + // # u32 + // round(MAX) and nextUp(round(MAX)) + test!(4294967040., f* -> u32, 4294967040); + test!(4294967296., f* -> u32, 4294967295); + + // # u128 + #[cfg(not(target_os = "emscripten"))] + { + // float->int: + test!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); + // nextDown(f32::MAX) = 2^128 - 2 * 2^104 + const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; + test!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); + } +} diff --git a/src/test/ui/numbers-arithmetic/saturating-float-casts-wasm.rs b/src/test/ui/numbers-arithmetic/saturating-float-casts-wasm.rs new file mode 100644 index 0000000000000..f2fd30030b975 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/saturating-float-casts-wasm.rs @@ -0,0 +1,14 @@ +// run-pass +// only-wasm32 +// compile-flags: -Zmir-opt-level=0 -C target-feature=+nontrapping-fptoint + +#![feature(test, stmt_expr_attributes)] +#![feature(track_caller)] +#![deny(overflowing_literals)] + +#[path = "saturating-float-casts-impl.rs"] +mod implementation; + +pub fn main() { + implementation::run(); +} diff --git a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs index e6d0c94a02fac..1e9436f868584 100644 --- a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs +++ b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs @@ -1,510 +1,13 @@ // run-pass // compile-flags:-Zmir-opt-level=0 -// Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. -// -// Some of these tests come from a similar file in miri, -// tests/run-pass/float.rs. Individual test cases are potentially duplicated -// with the previously existing tests, but since this runs so quickly anyway, -// we're not spending the time to figure out exactly which ones should be -// merged. #![feature(test, stmt_expr_attributes)] #![feature(track_caller)] #![deny(overflowing_literals)] -extern crate test; -use std::{f32, f64}; -#[cfg(not(target_os = "emscripten"))] -use std::{i128, u128}; -use std::{i16, i32, i64, i8, u16, u32, u64, u8}; -use test::black_box; - -macro_rules! test { - ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ( - // black_box disables constant evaluation to test run-time conversions: - assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, - "run-time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); - - { - const X: $src_ty = $val; - const Y: $dest_ty = X as $dest_ty; - assert_eq!(Y, $expected, - "const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); - } - ); - - ($fval:expr, f* -> $ity:ident, $ival:expr) => ( - test!($fval, f32 -> $ity, $ival); - test!($fval, f64 -> $ity, $ival); - ) -} - -macro_rules! common_fptoi_tests { - ($fty:ident -> $($ity:ident)+) => ({ $( - test!($fty::NAN, $fty -> $ity, 0); - test!($fty::INFINITY, $fty -> $ity, $ity::MAX); - test!($fty::NEG_INFINITY, $fty -> $ity, $ity::MIN); - // These two tests are not solely float->int tests, in particular the latter relies on - // `u128::MAX as f32` not being UB. But that's okay, since this file tests int->float - // as well, the test is just slightly misplaced. - test!($ity::MIN as $fty, $fty -> $ity, $ity::MIN); - test!($ity::MAX as $fty, $fty -> $ity, $ity::MAX); - test!(0., $fty -> $ity, 0); - test!($fty::MIN_POSITIVE, $fty -> $ity, 0); - test!(-0.9, $fty -> $ity, 0); - test!(1., $fty -> $ity, 1); - test!(42., $fty -> $ity, 42); - )+ }); - - (f* -> $($ity:ident)+) => ({ - common_fptoi_tests!(f32 -> $($ity)+); - common_fptoi_tests!(f64 -> $($ity)+); - }) -} - -macro_rules! fptoui_tests { - ($fty: ident -> $($ity: ident)+) => ({ $( - test!(-0., $fty -> $ity, 0); - test!(-$fty::MIN_POSITIVE, $fty -> $ity, 0); - test!(-0.99999994, $fty -> $ity, 0); - test!(-1., $fty -> $ity, 0); - test!(-100., $fty -> $ity, 0); - test!(#[allow(overflowing_literals)] -1e50, $fty -> $ity, 0); - test!(#[allow(overflowing_literals)] -1e130, $fty -> $ity, 0); - )+ }); - - (f* -> $($ity:ident)+) => ({ - fptoui_tests!(f32 -> $($ity)+); - fptoui_tests!(f64 -> $($ity)+); - }) -} - -use std::fmt::Debug; - -// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE. -#[track_caller] -#[inline(never)] -fn assert_eq(x: T, y: T) { - assert_eq!(x, y); -} - -trait FloatToInt: Copy { - fn cast(self) -> Int; - unsafe fn cast_unchecked(self) -> Int; -} - -impl FloatToInt for f32 { - fn cast(self) -> i8 { - self as _ - } - unsafe fn cast_unchecked(self) -> i8 { - self.to_int_unchecked() - } -} -impl FloatToInt for f32 { - fn cast(self) -> i32 { - self as _ - } - unsafe fn cast_unchecked(self) -> i32 { - self.to_int_unchecked() - } -} -impl FloatToInt for f32 { - fn cast(self) -> u32 { - self as _ - } - unsafe fn cast_unchecked(self) -> u32 { - self.to_int_unchecked() - } -} -impl FloatToInt for f32 { - fn cast(self) -> i64 { - self as _ - } - unsafe fn cast_unchecked(self) -> i64 { - self.to_int_unchecked() - } -} -impl FloatToInt for f32 { - fn cast(self) -> u64 { - self as _ - } - unsafe fn cast_unchecked(self) -> u64 { - self.to_int_unchecked() - } -} - -impl FloatToInt for f64 { - fn cast(self) -> i8 { - self as _ - } - unsafe fn cast_unchecked(self) -> i8 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> i32 { - self as _ - } - unsafe fn cast_unchecked(self) -> i32 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> u32 { - self as _ - } - unsafe fn cast_unchecked(self) -> u32 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> i64 { - self as _ - } - unsafe fn cast_unchecked(self) -> i64 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> u64 { - self as _ - } - unsafe fn cast_unchecked(self) -> u64 { - self.to_int_unchecked() - } -} -// FIXME emscripten does not support i128 -#[cfg(not(target_os = "emscripten"))] -impl FloatToInt for f64 { - fn cast(self) -> i128 { - self as _ - } - unsafe fn cast_unchecked(self) -> i128 { - self.to_int_unchecked() - } -} -// FIXME emscripten does not support i128 -#[cfg(not(target_os = "emscripten"))] -impl FloatToInt for f64 { - fn cast(self) -> u128 { - self as _ - } - unsafe fn cast_unchecked(self) -> u128 { - self.to_int_unchecked() - } -} - -/// Test this cast both via `as` and via `to_int_unchecked` (i.e., it must not saturate). -#[track_caller] -#[inline(never)] -fn test_both_cast(x: F, y: I) -where - F: FloatToInt, - I: PartialEq + Debug, -{ - assert_eq!(x.cast(), y); - assert_eq!(unsafe { x.cast_unchecked() }, y); -} - -fn casts() { - // f32 -> i8 - test_both_cast::(127.99, 127); - test_both_cast::(-128.99, -128); - - // f32 -> i32 - test_both_cast::(0.0, 0); - test_both_cast::(-0.0, 0); - test_both_cast::(/*0x1p-149*/ f32::from_bits(0x00000001), 0); - test_both_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); - test_both_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); - test_both_cast::(/*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd), -1); - test_both_cast::(1.9, 1); - test_both_cast::(-1.9, -1); - test_both_cast::(5.0, 5); - test_both_cast::(-5.0, -5); - test_both_cast::(2147483520.0, 2147483520); - test_both_cast::(-2147483648.0, -2147483648); - // unrepresentable casts - assert_eq::(2147483648.0f32 as i32, i32::MAX); - assert_eq::(-2147483904.0f32 as i32, i32::MIN); - assert_eq::(f32::MAX as i32, i32::MAX); - assert_eq::(f32::MIN as i32, i32::MIN); - assert_eq::(f32::INFINITY as i32, i32::MAX); - assert_eq::(f32::NEG_INFINITY as i32, i32::MIN); - assert_eq::(f32::NAN as i32, 0); - assert_eq::((-f32::NAN) as i32, 0); - - // f32 -> u32 - test_both_cast::(0.0, 0); - test_both_cast::(-0.0, 0); - test_both_cast::(-0.9999999, 0); - test_both_cast::(/*0x1p-149*/ f32::from_bits(0x1), 0); - test_both_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); - test_both_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); - test_both_cast::(1.9, 1); - test_both_cast::(5.0, 5); - test_both_cast::(2147483648.0, 0x8000_0000); - test_both_cast::(4294967040.0, 0u32.wrapping_sub(256)); - test_both_cast::(/*-0x1.ccccccp-1*/ f32::from_bits(0xbf666666), 0); - test_both_cast::(/*-0x1.fffffep-1*/ f32::from_bits(0xbf7fffff), 0); - test_both_cast::((u32::MAX - 128) as f32, u32::MAX - 255); // rounding loss - - // unrepresentable casts: - - // rounds up and then becomes unrepresentable - assert_eq::((u32::MAX - 127) as f32 as u32, u32::MAX); - - assert_eq::(4294967296.0f32 as u32, u32::MAX); - assert_eq::(-5.0f32 as u32, 0); - assert_eq::(f32::MAX as u32, u32::MAX); - assert_eq::(f32::MIN as u32, 0); - assert_eq::(f32::INFINITY as u32, u32::MAX); - assert_eq::(f32::NEG_INFINITY as u32, 0); - assert_eq::(f32::NAN as u32, 0); - assert_eq::((-f32::NAN) as u32, 0); - - // f32 -> i64 - test_both_cast::(4294967296.0, 4294967296); - test_both_cast::(-4294967296.0, -4294967296); - test_both_cast::(9223371487098961920.0, 9223371487098961920); - test_both_cast::(-9223372036854775808.0, -9223372036854775808); - - // f64 -> i8 - test_both_cast::(127.99, 127); - test_both_cast::(-128.99, -128); - - // f64 -> i32 - test_both_cast::(0.0, 0); - test_both_cast::(-0.0, 0); - test_both_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); - test_both_cast::( - /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), - -1, - ); - test_both_cast::(1.9, 1); - test_both_cast::(-1.9, -1); - test_both_cast::(1e8, 100_000_000); - test_both_cast::(2147483647.0, 2147483647); - test_both_cast::(-2147483648.0, -2147483648); - // unrepresentable casts - assert_eq::(2147483648.0f64 as i32, i32::MAX); - assert_eq::(-2147483649.0f64 as i32, i32::MIN); - - // f64 -> i64 - test_both_cast::(0.0, 0); - test_both_cast::(-0.0, 0); - test_both_cast::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1), 0); - test_both_cast::( - /*-0x0.0000000000001p-1022*/ f64::from_bits(0x8000000000000001), - 0, - ); - test_both_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); - test_both_cast::( - /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), - -1, - ); - test_both_cast::(5.0, 5); - test_both_cast::(5.9, 5); - test_both_cast::(-5.0, -5); - test_both_cast::(-5.9, -5); - test_both_cast::(4294967296.0, 4294967296); - test_both_cast::(-4294967296.0, -4294967296); - test_both_cast::(9223372036854774784.0, 9223372036854774784); - test_both_cast::(-9223372036854775808.0, -9223372036854775808); - // unrepresentable casts - assert_eq::(9223372036854775808.0f64 as i64, i64::MAX); - assert_eq::(-9223372036854777856.0f64 as i64, i64::MIN); - assert_eq::(f64::MAX as i64, i64::MAX); - assert_eq::(f64::MIN as i64, i64::MIN); - assert_eq::(f64::INFINITY as i64, i64::MAX); - assert_eq::(f64::NEG_INFINITY as i64, i64::MIN); - assert_eq::(f64::NAN as i64, 0); - assert_eq::((-f64::NAN) as i64, 0); - - // f64 -> u64 - test_both_cast::(0.0, 0); - test_both_cast::(-0.0, 0); - test_both_cast::(-0.99999999999, 0); - test_both_cast::(5.0, 5); - test_both_cast::(1e16, 10000000000000000); - test_both_cast::((u64::MAX - 1024) as f64, u64::MAX - 2047); // rounding loss - test_both_cast::(9223372036854775808.0, 9223372036854775808); - // unrepresentable casts - assert_eq::(-5.0f64 as u64, 0); - // rounds up and then becomes unrepresentable - assert_eq::((u64::MAX - 1023) as f64 as u64, u64::MAX); - assert_eq::(18446744073709551616.0f64 as u64, u64::MAX); - assert_eq::(f64::MAX as u64, u64::MAX); - assert_eq::(f64::MIN as u64, 0); - assert_eq::(f64::INFINITY as u64, u64::MAX); - assert_eq::(f64::NEG_INFINITY as u64, 0); - assert_eq::(f64::NAN as u64, 0); - assert_eq::((-f64::NAN) as u64, 0); - - // FIXME emscripten does not support i128 - #[cfg(not(target_os = "emscripten"))] - { - // f64 -> i128 - assert_eq::(f64::MAX as i128, i128::MAX); - assert_eq::(f64::MIN as i128, i128::MIN); - - // f64 -> u128 - assert_eq::(f64::MAX as u128, u128::MAX); - assert_eq::(f64::MIN as u128, 0); - } - - // int -> f32 - assert_eq::(127i8 as f32, 127.0); - assert_eq::(2147483647i32 as f32, 2147483648.0); - assert_eq::((-2147483648i32) as f32, -2147483648.0); - assert_eq::(1234567890i32 as f32, /*0x1.26580cp+30*/ f32::from_bits(0x4e932c06)); - assert_eq::(16777217i32 as f32, 16777216.0); - assert_eq::((-16777217i32) as f32, -16777216.0); - assert_eq::(16777219i32 as f32, 16777220.0); - assert_eq::((-16777219i32) as f32, -16777220.0); - assert_eq::( - 0x7fffff4000000001i64 as f32, - /*0x1.fffffep+62*/ f32::from_bits(0x5effffff), - ); - assert_eq::( - 0x8000004000000001u64 as i64 as f32, - /*-0x1.fffffep+62*/ f32::from_bits(0xdeffffff), - ); - assert_eq::( - 0x0020000020000001i64 as f32, - /*0x1.000002p+53*/ f32::from_bits(0x5a000001), - ); - assert_eq::( - 0xffdfffffdfffffffu64 as i64 as f32, - /*-0x1.000002p+53*/ f32::from_bits(0xda000001), - ); - // FIXME emscripten does not support i128 - #[cfg(not(target_os = "emscripten"))] - { - assert_eq::(i128::MIN as f32, -170141183460469231731687303715884105728.0f32); - assert_eq::(u128::MAX as f32, f32::INFINITY); // saturation - } - - // int -> f64 - assert_eq::(127i8 as f64, 127.0); - assert_eq::(i16::MIN as f64, -32768.0f64); - assert_eq::(2147483647i32 as f64, 2147483647.0); - assert_eq::(-2147483648i32 as f64, -2147483648.0); - assert_eq::(987654321i32 as f64, 987654321.0); - assert_eq::(9223372036854775807i64 as f64, 9223372036854775807.0); - assert_eq::(-9223372036854775808i64 as f64, -9223372036854775808.0); - assert_eq::(4669201609102990i64 as f64, 4669201609102990.0); // Feigenbaum (?) - assert_eq::(9007199254740993i64 as f64, 9007199254740992.0); - assert_eq::(-9007199254740993i64 as f64, -9007199254740992.0); - assert_eq::(9007199254740995i64 as f64, 9007199254740996.0); - assert_eq::(-9007199254740995i64 as f64, -9007199254740996.0); - // FIXME emscripten does not support i128 - #[cfg(not(target_os = "emscripten"))] - { - // even that fits... - assert_eq::(u128::MAX as f64, 340282366920938463463374607431768211455.0f64); - } - - // f32 -> f64 - assert_eq::((0.0f32 as f64).to_bits(), 0.0f64.to_bits()); - assert_eq::(((-0.0f32) as f64).to_bits(), (-0.0f64).to_bits()); - assert_eq::(5.0f32 as f64, 5.0f64); - assert_eq::( - /*0x1p-149*/ f32::from_bits(0x1) as f64, - /*0x1p-149*/ f64::from_bits(0x36a0000000000000), - ); - assert_eq::( - /*-0x1p-149*/ f32::from_bits(0x80000001) as f64, - /*-0x1p-149*/ f64::from_bits(0xb6a0000000000000), - ); - assert_eq::( - /*0x1.fffffep+127*/ f32::from_bits(0x7f7fffff) as f64, - /*0x1.fffffep+127*/ f64::from_bits(0x47efffffe0000000), - ); - assert_eq::( - /*-0x1.fffffep+127*/ (-f32::from_bits(0x7f7fffff)) as f64, - /*-0x1.fffffep+127*/ -f64::from_bits(0x47efffffe0000000), - ); - assert_eq::( - /*0x1p-119*/ f32::from_bits(0x4000000) as f64, - /*0x1p-119*/ f64::from_bits(0x3880000000000000), - ); - assert_eq::( - /*0x1.8f867ep+125*/ f32::from_bits(0x7e47c33f) as f64, - 6.6382536710104395e+37, - ); - assert_eq::(f32::INFINITY as f64, f64::INFINITY); - assert_eq::(f32::NEG_INFINITY as f64, f64::NEG_INFINITY); - - // f64 -> f32 - assert_eq::((0.0f64 as f32).to_bits(), 0.0f32.to_bits()); - assert_eq::(((-0.0f64) as f32).to_bits(), (-0.0f32).to_bits()); - assert_eq::(5.0f64 as f32, 5.0f32); - assert_eq::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1) as f32, 0.0); - assert_eq::(/*-0x0.0000000000001p-1022*/ (-f64::from_bits(0x1)) as f32, -0.0); - assert_eq::( - /*0x1.fffffe0000000p-127*/ f64::from_bits(0x380fffffe0000000) as f32, - /*0x1p-149*/ f32::from_bits(0x800000), - ); - assert_eq::( - /*0x1.4eae4f7024c7p+108*/ f64::from_bits(0x46b4eae4f7024c70) as f32, - /*0x1.4eae5p+108*/ f32::from_bits(0x75a75728), - ); - assert_eq::(f64::MAX as f32, f32::INFINITY); - assert_eq::(f64::MIN as f32, f32::NEG_INFINITY); - assert_eq::(f64::INFINITY as f32, f32::INFINITY); - assert_eq::(f64::NEG_INFINITY as f32, f32::NEG_INFINITY); -} +#[path = "saturating-float-casts-impl.rs"] +mod implementation; pub fn main() { - casts(); // from miri's tests - - common_fptoi_tests!(f* -> i8 i16 i32 i64 u8 u16 u32 u64); - fptoui_tests!(f* -> u8 u16 u32 u64); - // FIXME emscripten does not support i128 - #[cfg(not(target_os = "emscripten"))] - { - common_fptoi_tests!(f* -> i128 u128); - fptoui_tests!(f* -> u128); - } - - // The following tests cover edge cases for some integer types. - - // # u8 - test!(254., f* -> u8, 254); - test!(256., f* -> u8, 255); - - // # i8 - test!(-127., f* -> i8, -127); - test!(-129., f* -> i8, -128); - test!(126., f* -> i8, 126); - test!(128., f* -> i8, 127); - - // # i32 - // -2147483648. is i32::MIN (exactly) - test!(-2147483648., f* -> i32, i32::MIN); - // 2147483648. is i32::MAX rounded up - test!(2147483648., f32 -> i32, 2147483647); - // With 24 significand bits, floats with magnitude in [2^30 + 1, 2^31] are rounded to - // multiples of 2^7. Therefore, nextDown(round(i32::MAX)) is 2^31 - 128: - test!(2147483520., f32 -> i32, 2147483520); - // Similarly, nextUp(i32::MIN) is i32::MIN + 2^8 and nextDown(i32::MIN) is i32::MIN - 2^7 - test!(-2147483904., f* -> i32, i32::MIN); - test!(-2147483520., f* -> i32, -2147483520); - - // # u32 - // round(MAX) and nextUp(round(MAX)) - test!(4294967040., f* -> u32, 4294967040); - test!(4294967296., f* -> u32, 4294967295); - - // # u128 - #[cfg(not(target_os = "emscripten"))] - { - // float->int: - test!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); - // nextDown(f32::MAX) = 2^128 - 2 * 2^104 - const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; - test!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); - } + implementation::run(); }