From 3e0c1c8e0cdeab8871f96b1703f6b8a852d51568 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 25 May 2022 07:30:04 +0000 Subject: [PATCH 01/17] Add WIP stable MIR crate --- Cargo.toml | 10 ++++++++++ src/lib.rs | 10 ++++++++++ src/mir.rs | 10 ++++++++++ src/very_unstable.rs | 9 +++++++++ 4 files changed, 39 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/mir.rs create mode 100644 src/very_unstable.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000..0c5a19d4034aa --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rustc_smir" +version = "0.0.0" +edition = "2021" + +[dependencies] +rustc_middle = { path = "../rustc_middle" } +rustc_driver = { path = "../rustc_driver" } +rustc_borrowck = { path = "../rustc_borrowck" } +rustc_interface = { path = "../rustc_interface" } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000..405ee1388ddca --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +//! The WIP stable interface to rustc internals. + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", + test(attr(allow(unused_variables), deny(warnings))) +)] + +pub mod mir; + +pub mod very_unstable; diff --git a/src/mir.rs b/src/mir.rs new file mode 100644 index 0000000000000..97969be669dc6 --- /dev/null +++ b/src/mir.rs @@ -0,0 +1,10 @@ +pub use rustc_middle::mir::{ + visit::MutVisitor, AggregateKind, AssertKind, BasicBlock, BasicBlockData, BinOp, BindingForm, + BlockTailInfo, Body, BorrowKind, CastKind, ClearCrossCrate, Constant, ConstantKind, + CopyNonOverlapping, Coverage, FakeReadCause, Field, GeneratorInfo, ImplicitSelfKind, + InlineAsmOperand, Local, LocalDecl, LocalInfo, LocalKind, Location, MirPhase, MirSource, + NullOp, Operand, Place, PlaceRef, ProjectionElem, ProjectionKind, Promoted, RetagKind, Rvalue, + Safety, SourceInfo, SourceScope, SourceScopeData, SourceScopeLocalData, Statement, + StatementKind, UnOp, UserTypeProjection, UserTypeProjections, VarBindingForm, VarDebugInfo, + VarDebugInfoContents, +}; diff --git a/src/very_unstable.rs b/src/very_unstable.rs new file mode 100644 index 0000000000000..8ba0251629d7f --- /dev/null +++ b/src/very_unstable.rs @@ -0,0 +1,9 @@ +//! This module reexports various crates and modules from unstable rustc APIs. +//! Add anything you need here and it will get slowly transferred to a stable API. +//! Only use rustc_smir in your dependencies and use the reexports here instead of +//! directly referring to the unstable crates. + +pub use rustc_borrowck as borrowck; +pub use rustc_driver as driver; +pub use rustc_interface as interface; +pub use rustc_middle as middle; From d9a3f5cfebd189044fe294ec8398a6cac6ee613d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 25 May 2022 08:41:50 +0000 Subject: [PATCH 02/17] Add instructions --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..de358941b786c --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +This crate is regularly synced with its mirror in the rustc repo at `compiler/rustc_smir`. + +We use `git subtree` for this to preserve commits and allow the rustc repo to +edit these crates without having to touch this repo. This keeps the crates compiling +while allowing us to independently work on them here. The effort of keeping them in +sync is pushed entirely onto us, without affecting rustc workflows negatively. +This may change in the future, but changes to policy should only be done via a +compiler team MCP. + +## Instructions for syncing + +### Updating this repository + +In the rustc repo, execute + +``` +git subtree push --prefix=compiler/rustc_smir url_to_your_fork_of_project_stable_mir some_feature_branch +``` + +and then open a PR of your `some_feature_branch` against https://github.com/rust-lang/project-stable-mir + +### Updating the rustc librar + + +In the rustc repo, execute + +``` +git subtree pull --prefix=compiler/rustc_smir https://github.com/rust-lang/project-stable-mir smir +``` + +Note: only ever sync to rustc from the project-stable-mir's `smir` branch. Do not sync with your own forks. + +Then open a PR against rustc just like a regular PR. From fe76f0324c67c65b4a54097e874998f73f366b5f Mon Sep 17 00:00:00 2001 From: Xavier Denis Date: Wed, 25 May 2022 13:20:15 +0200 Subject: [PATCH 03/17] Add additional crates to `very_unstable Adds some additional crates used by Creusot. --- src/very_unstable.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/very_unstable.rs b/src/very_unstable.rs index 8ba0251629d7f..dacdabf1ccb23 100644 --- a/src/very_unstable.rs +++ b/src/very_unstable.rs @@ -5,5 +5,10 @@ pub use rustc_borrowck as borrowck; pub use rustc_driver as driver; +pub use rustc_hir as hir; pub use rustc_interface as interface; pub use rustc_middle as middle; +pub use rustc_mir_dataflow as dataflow; +pub use rustc_mir_transform as transform; +pub use rustc_serialize as serialize; +pub use rustc_trait_selection as trait_selection; From e5b824c535242866ee004e2c6ef8da5ad162b1ad Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 25 May 2022 08:48:14 +0000 Subject: [PATCH 04/17] Documentation never hurts --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 405ee1388ddca..a867520fc6810 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,10 @@ //! The WIP stable interface to rustc internals. +//! +//! For more information see https://github.com/rust-lang/project-stable-mir +//! +//! # Note +//! +//! This API is still completely unstable and subject to change. #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", From 36f3c03421513d6b14a26538f2971f75d1e9f5bb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 25 May 2022 09:06:12 +0000 Subject: [PATCH 05/17] Update instructions --- README.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de358941b786c..92eb926f5058e 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,38 @@ git subtree push --prefix=compiler/rustc_smir url_to_your_fork_of_project_stable and then open a PR of your `some_feature_branch` against https://github.com/rust-lang/project-stable-mir -### Updating the rustc librar +### Updating the rustc library +First we need to bump our stack limit, as the rustc repo otherwise quickly hits that: + +``` +ulimit -s 60000 +``` + +#### Maximum function recursion depth (1000) reached + +Then we need to disable `dash` as the default shell for sh scripts, as otherwise we run into a +hard limit of a recursion depth of 1000: + +``` +sudo dpkg-reconfigure dash +``` + +and then select `No` to disable dash. + + +#### Patching your `git worktree` + +The regular git worktree does not scale to repos of the size of the rustc repo. +So download the `git-subtree.sh` from https://github.com/gitgitgadget/git/pull/493/files and run + +``` +sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree +sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +``` + +#### Actually doing a sync In the rustc repo, execute From 9da5e13294177621854dfd6a249863286c482fb1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 25 May 2022 09:11:09 +0000 Subject: [PATCH 06/17] Rustfmt --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a867520fc6810..2b1c2bb3d2b84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! The WIP stable interface to rustc internals. -//! +//! //! For more information see https://github.com/rust-lang/project-stable-mir -//! +//! //! # Note //! //! This API is still completely unstable and subject to change. From 615f8c5dbbd59b9ec734efaeb36b0e619c9e3e38 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 2 Jun 2022 09:54:57 +0000 Subject: [PATCH 07/17] record which nightly we support --- rust-toolchain.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000000..7b696fc1f5cec --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2022-06-01" +components = [ "rustfmt", "rustc-dev" ] From 9abcb5c7b574cf316eb23d3f469187bb86ba3019 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 2 Jun 2022 09:55:54 +0000 Subject: [PATCH 08/17] Make the crate work both in rustc and locally --- .gitignore | 1 + Cargo.toml | 11 +++++++---- README.md | 12 ++++++++++++ src/lib.rs | 2 ++ src/mir.rs | 2 +- src/very_unstable.rs | 31 ++++++++++++++++++++++--------- 6 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000..eb5a316cbd195 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target diff --git a/Cargo.toml b/Cargo.toml index 0c5a19d4034aa..bc0cac5a0bcef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,10 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_middle = { path = "../rustc_middle" } -rustc_driver = { path = "../rustc_driver" } -rustc_borrowck = { path = "../rustc_borrowck" } -rustc_interface = { path = "../rustc_interface" } +rustc_middle = { path = "../rustc_middle", optional = true } +rustc_driver = { path = "../rustc_driver", optional = true } +rustc_borrowck = { path = "../rustc_borrowck", optional = true } +rustc_interface = { path = "../rustc_interface", optional = true } + +[features] +default = ["rustc_middle", "rustc_driver", "rustc_borrowck", "rustc_interface"] diff --git a/README.md b/README.md index 92eb926f5058e..ae49098dd0ce6 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,18 @@ sync is pushed entirely onto us, without affecting rustc workflows negatively. This may change in the future, but changes to policy should only be done via a compiler team MCP. +## Instructions for working on this crate locally + +Since the crate is the same in the rustc repo and here, the dependencies on rustc_* crates +will only either work here or there, but never in both places at the same time. Thus we use +optional dependencies on the rustc_* crates, requiring local development to use + +``` +cargo build --no-default-features -Zavoid-dev-deps +``` + +in order to compile successfully. + ## Instructions for syncing ### Updating this repository diff --git a/src/lib.rs b/src/lib.rs index 2b1c2bb3d2b84..decdae953d95b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,8 @@ test(attr(allow(unused_variables), deny(warnings))) )] +#![cfg_attr(not(feature = "default"), feature(rustc_private))] + pub mod mir; pub mod very_unstable; diff --git a/src/mir.rs b/src/mir.rs index 97969be669dc6..855605b1a4f9d 100644 --- a/src/mir.rs +++ b/src/mir.rs @@ -1,4 +1,4 @@ -pub use rustc_middle::mir::{ +pub use crate::very_unstable::middle::mir::{ visit::MutVisitor, AggregateKind, AssertKind, BasicBlock, BasicBlockData, BinOp, BindingForm, BlockTailInfo, Body, BorrowKind, CastKind, ClearCrossCrate, Constant, ConstantKind, CopyNonOverlapping, Coverage, FakeReadCause, Field, GeneratorInfo, ImplicitSelfKind, diff --git a/src/very_unstable.rs b/src/very_unstable.rs index dacdabf1ccb23..12ba133dbb169 100644 --- a/src/very_unstable.rs +++ b/src/very_unstable.rs @@ -3,12 +3,25 @@ //! Only use rustc_smir in your dependencies and use the reexports here instead of //! directly referring to the unstable crates. -pub use rustc_borrowck as borrowck; -pub use rustc_driver as driver; -pub use rustc_hir as hir; -pub use rustc_interface as interface; -pub use rustc_middle as middle; -pub use rustc_mir_dataflow as dataflow; -pub use rustc_mir_transform as transform; -pub use rustc_serialize as serialize; -pub use rustc_trait_selection as trait_selection; +macro_rules! crates { + ($($rustc_name:ident -> $name:ident,)*) => { + $( + #[cfg(not(feature = "default"))] + pub extern crate $rustc_name as $name; + #[cfg(feature = "default")] + pub use $rustc_name as $name; + )* + } +} + +crates! { + rustc_borrowck -> borrowck, + rustc_driver -> driver, + rustc_hir -> hir, + rustc_interface -> interface, + rustc_middle -> middle, + rustc_mir_dataflow -> dataflow, + rustc_mir_transform -> transform, + rustc_serialize -> serialize, + rustc_trait_selection -> trait_selection, +} From 9960cc1f084694f096d6876e1afd698cce1222c8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 2 Jun 2022 10:08:38 +0000 Subject: [PATCH 09/17] Ship rustc_smir with rustc --- Cargo.lock | 11 +++++++++++ compiler/rustc/Cargo.toml | 3 +++ 2 files changed, 14 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 7ed327e9f4ccb..e5908889dce6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3410,6 +3410,7 @@ dependencies = [ "jemalloc-sys", "rustc_codegen_ssa", "rustc_driver", + "rustc_smir", ] [[package]] @@ -4401,6 +4402,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "rustc_smir" +version = "0.0.0" +dependencies = [ + "rustc_borrowck", + "rustc_driver", + "rustc_interface", + "rustc_middle", +] + [[package]] name = "rustc_span" version = "0.0.0" diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 5e0bb1a7f95d1..27ee3dd2aeafc 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -9,6 +9,9 @@ rustc_driver = { path = "../rustc_driver" } # Make sure rustc_codegen_ssa ends up in the sysroot, because this # crate is intended to be used by codegen backends, which may not be in-tree. rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } +# Make sure rustc_smir ends up in the sysroot, because this +# crate is intended to be used by stable MIR consumers, which are not in-tree +rustc_smir = { path = "../rustc_smir" } [dependencies.jemalloc-sys] version = "0.5.0" From 0324ac8ae014afc3eaa1bb85f169a025274e54bc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 2 Jun 2022 10:15:07 +0000 Subject: [PATCH 10/17] List all crates used in crate source in Cargo.toml --- Cargo.lock | 5 +++++ compiler/rustc_smir/Cargo.toml | 21 ++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5908889dce6c..75eedc90dbc7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4408,8 +4408,13 @@ version = "0.0.0" dependencies = [ "rustc_borrowck", "rustc_driver", + "rustc_hir", "rustc_interface", "rustc_middle", + "rustc_mir_dataflow", + "rustc_mir_transform", + "rustc_serialize", + "rustc_trait_selection", ] [[package]] diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index bc0cac5a0bcef..5e0d1f369a6a2 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -4,10 +4,25 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_middle = { path = "../rustc_middle", optional = true } -rustc_driver = { path = "../rustc_driver", optional = true } rustc_borrowck = { path = "../rustc_borrowck", optional = true } +rustc_driver = { path = "../rustc_driver", optional = true } +rustc_hir = { path = "../rustc_hir", optional = true } rustc_interface = { path = "../rustc_interface", optional = true } +rustc_middle = { path = "../rustc_middle", optional = true } +rustc_mir_dataflow = { path = "../rustc_mir_dataflow", optional = true } +rustc_mir_transform = { path = "../rustc_mir_transform", optional = true } +rustc_serialize = { path = "../rustc_serialize", optional = true } +rustc_trait_selection = { path = "../rustc_trait_selection", optional = true } [features] -default = ["rustc_middle", "rustc_driver", "rustc_borrowck", "rustc_interface"] +default = [ + "rustc_borrowck", + "rustc_driver", + "rustc_hir", + "rustc_interface", + "rustc_middle", + "rustc_mir_dataflow", + "rustc_mir_transform", + "rustc_serialize", + "rustc_trait_selection", +] From 7cba9ed4f7089774f10243b8b4825dd5c34759da Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 2 Jun 2022 10:29:00 +0000 Subject: [PATCH 11/17] Rustfmt --- compiler/rustc_smir/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index decdae953d95b..5c7aaf35b9032 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -10,7 +10,6 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) )] - #![cfg_attr(not(feature = "default"), feature(rustc_private))] pub mod mir; From e5245ef1eb2bacb07f7e2473b845d86c3bdd1b01 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 Jun 2022 20:47:06 -0400 Subject: [PATCH 12/17] interpret: unify offset_from check with offset check --- .../src/interpret/intrinsics.rs | 137 +++++++++--------- .../const-ptr/forbidden_slices.32bit.stderr | 4 +- .../const-ptr/forbidden_slices.64bit.stderr | 4 +- src/test/ui/consts/offset_from_ub.rs | 12 +- src/test/ui/consts/offset_from_ub.stderr | 14 +- 5 files changed, 87 insertions(+), 84 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c5770f505262e..e51c51cf45e5e 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -313,78 +313,82 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let a = self.read_pointer(&args[0])?; let b = self.read_pointer(&args[1])?; - // Special case: if both scalars are *equal integers* - // and not null, we pretend there is an allocation of size 0 right there, - // and their offset is 0. (There's never a valid object at null, making it an - // exception from the exception.) - // This is the dual to the special exception for offset-by-0 - // in the inbounds pointer offset operation (see `ptr_offset_inbounds` below). - match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) { - (Err(a), Err(b)) if a == b && a != 0 => { - // Both are the same non-null integer. - self.write_scalar(Scalar::from_machine_isize(0, self), dest)?; - } - (Err(offset), _) | (_, Err(offset)) => { - throw_ub!(DanglingIntPointer(offset, CheckInAllocMsg::OffsetFromTest)); - } - (Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => { - // Both are pointers. They must be into the same allocation. - if a_alloc_id != b_alloc_id { - throw_ub_format!( - "{} cannot compute offset of pointers into different allocations.", - intrinsic_name, - ); + let usize_layout = self.layout_of(self.tcx.types.usize)?; + let isize_layout = self.layout_of(self.tcx.types.isize)?; + + // Get offsets for both that are at least relative to the same base. + let (a_offset, b_offset) = + match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) { + (Err(a), Err(b)) => { + // Neither poiner points to an allocation. + // If these are inequal or null, this *will* fail the deref check below. + (a, b) } - // And they must both be valid for zero-sized accesses ("in-bounds or one past the end"). - self.check_ptr_access_align( - a, - Size::ZERO, - Align::ONE, - CheckInAllocMsg::OffsetFromTest, - )?; - self.check_ptr_access_align( - b, - Size::ZERO, - Align::ONE, - CheckInAllocMsg::OffsetFromTest, - )?; - - if intrinsic_name == sym::ptr_offset_from_unsigned && a_offset < b_offset { + (Err(_), _) | (_, Err(_)) => { + // We managed to find a valid allocation for one pointer, but not the other. + // That means they are definitely not pointing to the same allocation. throw_ub_format!( - "{} cannot compute a negative offset, but {} < {}", - intrinsic_name, - a_offset.bytes(), - b_offset.bytes(), + "{} called on pointers into different allocations", + intrinsic_name ); } - - // Compute offset. - let usize_layout = self.layout_of(self.tcx.types.usize)?; - let isize_layout = self.layout_of(self.tcx.types.isize)?; - let ret_layout = if intrinsic_name == sym::ptr_offset_from { - isize_layout - } else { - usize_layout - }; - - // The subtraction is always done in `isize` to enforce - // the "no more than `isize::MAX` apart" requirement. - let a_offset = ImmTy::from_uint(a_offset.bytes(), isize_layout); - let b_offset = ImmTy::from_uint(b_offset.bytes(), isize_layout); - let (val, overflowed, _ty) = - self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?; - if overflowed { - throw_ub_format!("Pointers were too far apart for {}", intrinsic_name); + (Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => { + // Found allocation for both. They must be into the same allocation. + if a_alloc_id != b_alloc_id { + throw_ub_format!( + "{} called on pointers into different allocations", + intrinsic_name + ); + } + // Use these offsets for distance calculation. + (a_offset.bytes(), b_offset.bytes()) } - - let pointee_layout = self.layout_of(substs.type_at(0))?; - // This re-interprets an isize at ret_layout, but we already checked - // that if ret_layout is usize, then the result must be non-negative. - let val = ImmTy::from_scalar(val, ret_layout); - let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout); - self.exact_div(&val, &size, dest)?; + }; + + // Compute distance. + let distance = { + // The subtraction is always done in `isize` to enforce + // the "no more than `isize::MAX` apart" requirement. + let a_offset = ImmTy::from_uint(a_offset, isize_layout); + let b_offset = ImmTy::from_uint(b_offset, isize_layout); + let (val, overflowed, _ty) = + self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?; + if overflowed { + throw_ub_format!("pointers were too far apart for {}", intrinsic_name); } + val.to_machine_isize(self)? + }; + + // Check that the range between them is dereferenceable ("in-bounds or one past the + // end of the same allocation"). This is like the check in ptr_offset_inbounds. + let min_ptr = if distance >= 0 { b } else { a }; + self.check_ptr_access_align( + min_ptr, + Size::from_bytes(distance.unsigned_abs()), + Align::ONE, + CheckInAllocMsg::OffsetFromTest, + )?; + + if intrinsic_name == sym::ptr_offset_from_unsigned && distance < 0 { + throw_ub_format!( + "{} called when first pointer has smaller offset than second: {} < {}", + intrinsic_name, + a_offset, + b_offset, + ); } + + // Perform division by size to compute return value. + let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned { + usize_layout + } else { + isize_layout + }; + let pointee_layout = self.layout_of(substs.type_at(0))?; + // If ret_layout is unsigned, we checked that so is the distance, so we are good. + let val = ImmTy::from_int(distance, ret_layout); + let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout); + self.exact_div(&val, &size, dest)?; } sym::transmute => { @@ -575,11 +579,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // memory between these pointers must be accessible. Note that we do not require the // pointers to be properly aligned (unlike a read/write operation). let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; - let size = offset_bytes.unsigned_abs(); // This call handles checking for integer/null pointers. self.check_ptr_access_align( min_ptr, - Size::from_bytes(size), + Size::from_bytes(offset_bytes.unsigned_abs()), Align::ONE, CheckInAllocMsg::PointerArithmeticTest, )?; diff --git a/src/test/ui/const-ptr/forbidden_slices.32bit.stderr b/src/test/ui/const-ptr/forbidden_slices.32bit.stderr index 5b0b9aa9666e5..023d5ea34d871 100644 --- a/src/test/ui/const-ptr/forbidden_slices.32bit.stderr +++ b/src/test/ui/const-ptr/forbidden_slices.32bit.stderr @@ -243,7 +243,7 @@ error[E0080]: could not evaluate static initializer LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | ptr_offset_from_unsigned cannot compute offset of pointers into different allocations. + | ptr_offset_from_unsigned called on pointers into different allocations | inside `ptr::const_ptr::::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL @@ -262,7 +262,7 @@ error[E0080]: could not evaluate static initializer LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | ptr_offset_from_unsigned cannot compute offset of pointers into different allocations. + | ptr_offset_from_unsigned called on pointers into different allocations | inside `ptr::const_ptr::::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL diff --git a/src/test/ui/const-ptr/forbidden_slices.64bit.stderr b/src/test/ui/const-ptr/forbidden_slices.64bit.stderr index b75e6c5024667..ed89c253d532c 100644 --- a/src/test/ui/const-ptr/forbidden_slices.64bit.stderr +++ b/src/test/ui/const-ptr/forbidden_slices.64bit.stderr @@ -243,7 +243,7 @@ error[E0080]: could not evaluate static initializer LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | ptr_offset_from_unsigned cannot compute offset of pointers into different allocations. + | ptr_offset_from_unsigned called on pointers into different allocations | inside `ptr::const_ptr::::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL @@ -262,7 +262,7 @@ error[E0080]: could not evaluate static initializer LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | ptr_offset_from_unsigned cannot compute offset of pointers into different allocations. + | ptr_offset_from_unsigned called on pointers into different allocations | inside `ptr::const_ptr::::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL diff --git a/src/test/ui/consts/offset_from_ub.rs b/src/test/ui/consts/offset_from_ub.rs index f604f57e39d98..10e368ba13396 100644 --- a/src/test/ui/consts/offset_from_ub.rs +++ b/src/test/ui/consts/offset_from_ub.rs @@ -15,7 +15,7 @@ pub const DIFFERENT_ALLOC: usize = { let uninit2 = std::mem::MaybeUninit::::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed - //~| ptr_offset_from cannot compute offset of pointers into different allocations. + //~| pointers into different allocations offset as usize }; @@ -41,7 +41,7 @@ pub const DIFFERENT_INT: isize = { // offset_from with two different integers: l let ptr1 = 8 as *const u8; let ptr2 = 16 as *const u8; unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed - //~| 0x10 is not a valid pointer + //~| 0x8 is not a valid pointer }; const OUT_OF_BOUNDS_1: isize = { @@ -50,7 +50,7 @@ const OUT_OF_BOUNDS_1: isize = { let end_ptr = (start_ptr).wrapping_add(length); // First ptr is out of bounds unsafe { ptr_offset_from(end_ptr, start_ptr) } //~ERROR evaluation of constant value failed - //~| pointer at offset 10 is out-of-bounds + //~| pointer to 10 bytes starting at offset 0 is out-of-bounds }; const OUT_OF_BOUNDS_2: isize = { @@ -59,7 +59,7 @@ const OUT_OF_BOUNDS_2: isize = { let end_ptr = (start_ptr).wrapping_add(length); // Second ptr is out of bounds unsafe { ptr_offset_from(start_ptr, end_ptr) } //~ERROR evaluation of constant value failed - //~| pointer at offset 10 is out-of-bounds + //~| pointer to 10 bytes starting at offset 0 is out-of-bounds }; const OUT_OF_BOUNDS_SAME: isize = { @@ -76,7 +76,7 @@ pub const DIFFERENT_ALLOC_UNSIGNED: usize = { let uninit2 = std::mem::MaybeUninit::::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed - //~| ptr_offset_from_unsigned cannot compute offset of pointers into different allocations. + //~| pointers into different allocations offset as usize }; @@ -84,7 +84,7 @@ const WRONG_ORDER_UNSIGNED: usize = { let a = ['a', 'b', 'c']; let p = a.as_ptr(); unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } //~ERROR evaluation of constant value failed - //~| cannot compute a negative offset, but 0 < 8 + //~| first pointer has smaller offset than second: 0 < 8 }; fn main() {} diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr index 4c98fd72cacc3..eb7f1d7a6b252 100644 --- a/src/test/ui/consts/offset_from_ub.stderr +++ b/src/test/ui/consts/offset_from_ub.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:17:27 | LL | let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from cannot compute offset of pointers into different allocations. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from called on pointers into different allocations error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -10,7 +10,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::ptr_offset_from(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | out-of-bounds offset_from: 0x2a is not a valid pointer + | ptr_offset_from called on pointers into different allocations | inside `ptr::const_ptr::::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $DIR/offset_from_ub.rs:23:14 @@ -34,19 +34,19 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:43:14 | LL | unsafe { ptr_offset_from(ptr2, ptr1) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: 0x10 is not a valid pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: 0x8 is not a valid pointer error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:52:14 | LL | unsafe { ptr_offset_from(end_ptr, start_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc20 has size 4, so pointer at offset 10 is out-of-bounds + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc20 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:61:14 | LL | unsafe { ptr_offset_from(start_ptr, end_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc23 has size 4, so pointer at offset 10 is out-of-bounds + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc23 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:69:14 @@ -58,13 +58,13 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:78:27 | LL | let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute offset of pointers into different allocations. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned called on pointers into different allocations error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:86:14 | LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute a negative offset, but 0 < 8 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned called when first pointer has smaller offset than second: 0 < 8 error: aborting due to 10 previous errors From 2f923c4d37c7ad5a77544d6542ca167e026f238f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 11 Jun 2022 11:15:44 -0700 Subject: [PATCH 13/17] Make type_changing_struct_update no longer incomplete --- compiler/rustc_feature/src/active.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 35473b6e97abe..3dbaf0ddf6417 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -523,7 +523,7 @@ declare_features! ( (active, type_ascription, "1.6.0", Some(23416), None), /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) - (incomplete, type_changing_struct_update, "1.58.0", Some(86555), None), + (active, type_changing_struct_update, "1.58.0", Some(86555), None), /// Allows unsized fn parameters. (active, unsized_fn_params, "1.49.0", Some(48055), None), /// Allows unsized rvalues at arguments and parameters. From ddd18a51db571b1202d0be7d2ed358298975f63c Mon Sep 17 00:00:00 2001 From: Yoke <834902408@qq.com> Date: Mon, 13 Jun 2022 17:20:24 +0800 Subject: [PATCH 14/17] del unrelated comment issues97981 --- src/test/ui/variance/variance-object-types.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/ui/variance/variance-object-types.rs b/src/test/ui/variance/variance-object-types.rs index 14e11f681b100..cf385825edbc6 100644 --- a/src/test/ui/variance/variance-object-types.rs +++ b/src/test/ui/variance/variance-object-types.rs @@ -1,6 +1,3 @@ -// Test that Cell is considered invariant with respect to its -// type. - #![feature(rustc_attrs)] use std::cell::Cell; From 55b3c443d695a0e53596f86406fb5b3f2b05a560 Mon Sep 17 00:00:00 2001 From: Yoke <834902408@qq.com> Date: Mon, 13 Jun 2022 18:25:49 +0800 Subject: [PATCH 15/17] remove use Cell in variance-object-types.rs --- src/test/ui/variance/variance-object-types.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/ui/variance/variance-object-types.rs b/src/test/ui/variance/variance-object-types.rs index cf385825edbc6..6ded24cd1e9ed 100644 --- a/src/test/ui/variance/variance-object-types.rs +++ b/src/test/ui/variance/variance-object-types.rs @@ -1,6 +1,5 @@ #![feature(rustc_attrs)] -use std::cell::Cell; // For better or worse, associated types are invariant, and hence we // get an invariant result for `'a`. From c15fed5b7bec9ad0e1b851fec69c78b5752348b4 Mon Sep 17 00:00:00 2001 From: Yoke <834902408@qq.com> Date: Mon, 13 Jun 2022 18:51:28 +0800 Subject: [PATCH 16/17] Update variance-object-types.stderr --- src/test/ui/variance/variance-object-types.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/variance/variance-object-types.stderr b/src/test/ui/variance/variance-object-types.stderr index d97d222e711ad..2a5d8f91e1ec9 100644 --- a/src/test/ui/variance/variance-object-types.stderr +++ b/src/test/ui/variance/variance-object-types.stderr @@ -1,5 +1,5 @@ error[E0208]: [o] - --> $DIR/variance-object-types.rs:11:1 + --> $DIR/variance-object-types.rs:7:1 | LL | / struct Foo<'a> { LL | | x: Box &'a i32 + 'static> From 1c2c236a796c50277bc86174aa929e5ca422fc47 Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 13 Jun 2022 11:08:14 -0300 Subject: [PATCH 17/17] [RFC 2011] Minimal initial implementation --- .../src/assert/context.rs | 307 ++++++++++++++++-- compiler/rustc_builtin_macros/src/lib.rs | 1 + compiler/rustc_span/src/symbol.rs | 7 + src/test/ui/macros/assert-trailing-junk.rs | 3 + ...t-trailing-junk.with-generic-asset.stderr} | 14 +- ...trailing-junk.without-generic-asset.stderr | 54 +++ src/test/ui/macros/assert.rs | 3 + ...tderr => assert.with-generic-asset.stderr} | 8 +- .../assert.without-generic-asset.stderr | 28 ++ .../all-expr-kinds.rs | 142 ++++++++ .../all-not-available-cases.rs | 42 +++ ...errors-does-not-create-unnecessary-code.rs | 13 + ...ptures-does-not-create-unnecessary-code.rs | 13 + .../auxiliary/common.rs | 25 ++ .../rfc-2011-nicer-assert-messages/codegen.rs | 9 + .../codegen.stdout | 29 ++ .../feature-gate-generic_assert.rs | 8 +- 17 files changed, 665 insertions(+), 41 deletions(-) rename src/test/ui/macros/{assert-trailing-junk.stderr => assert-trailing-junk.with-generic-asset.stderr} (86%) create mode 100644 src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr rename src/test/ui/macros/{assert.stderr => assert.with-generic-asset.stderr} (87%) create mode 100644 src/test/ui/macros/assert.without-generic-asset.stderr create mode 100644 src/test/ui/rfc-2011-nicer-assert-messages/all-expr-kinds.rs create mode 100644 src/test/ui/rfc-2011-nicer-assert-messages/all-not-available-cases.rs create mode 100644 src/test/ui/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs create mode 100644 src/test/ui/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs create mode 100644 src/test/ui/rfc-2011-nicer-assert-messages/auxiliary/common.rs create mode 100644 src/test/ui/rfc-2011-nicer-assert-messages/codegen.rs create mode 100644 src/test/ui/rfc-2011-nicer-assert-messages/codegen.stdout diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 8d187a4be8aee..37a4bf5fdcad7 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -1,44 +1,299 @@ -use rustc_ast::{ptr::P, Expr, Path}; +use crate::assert::expr_if_not; +use rustc_ast::{ + attr, + ptr::P, + token, + tokenstream::{DelimSpan, TokenStream, TokenTree}, + BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, Path, + PathSegment, Stmt, UseTree, UseTreeKind, DUMMY_NODE_ID, +}; +use rustc_ast_pretty::pprust; +use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::ExtCtxt; -use rustc_span::Span; +use rustc_span::{ + symbol::{sym, Ident, Symbol}, + Span, +}; pub(super) struct Context<'cx, 'a> { + // Top-level `let captureN = Capture::new()` statements + capture_decls: Vec, cx: &'cx ExtCtxt<'a>, + // Formatting string used for debugging + fmt_string: String, + // Top-level `let __local_bindN = &expr` statements + local_bind_decls: Vec, + // Used to avoid capturing duplicated paths + // + // ```rust + // let a = 1i32; + // assert!(add(a, a) == 3); + // ``` + paths: FxHashSet, span: Span, } impl<'cx, 'a> Context<'cx, 'a> { pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self { - Self { cx, span } + Self { + capture_decls: <_>::default(), + cx, + fmt_string: <_>::default(), + local_bind_decls: <_>::default(), + paths: <_>::default(), + span, + } } - /// Builds the whole `assert!` expression. + /// Builds the whole `assert!` expression. For example, `let elem = 1; assert!(elem == 1);` expands to: /// + /// ```rust + /// let elem = 1; /// { - /// use ::core::asserting::{ ... }; + /// #[allow(unused_imports)] + /// use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + /// let mut __capture0 = ::core::asserting::Capture::new(); + /// let __local_bind0 = &elem; + /// if !( + /// *{ + /// (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + /// __local_bind0 + /// } == 1 + /// ) { + /// panic!("Assertion failed: elem == 1\nWith captures:\n elem = {}", __capture0) + /// } + /// } + /// ``` + pub(super) fn build(mut self, mut cond_expr: P, panic_path: Path) -> P { + let expr_str = pprust::expr_to_string(&cond_expr); + self.manage_cond_expr(&mut cond_expr); + let initial_imports = self.build_initial_imports(); + let panic = self.build_panic(&expr_str, panic_path); + + let Self { capture_decls, cx, local_bind_decls, span, .. } = self; + + let mut stmts = Vec::with_capacity(4); + stmts.push(initial_imports); + stmts.extend(capture_decls.into_iter().map(|c| c.decl)); + stmts.extend(local_bind_decls); + stmts.push(cx.stmt_expr(expr_if_not(cx, span, cond_expr, panic, None))); + cx.expr_block(cx.block(span, stmts)) + } + + /// Initial **trait** imports + /// + /// use ::core::asserting::{ ... }; + fn build_initial_imports(&self) -> Stmt { + let nested_tree = |this: &Self, sym| { + ( + UseTree { + prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]), + kind: UseTreeKind::Simple(None, DUMMY_NODE_ID, DUMMY_NODE_ID), + span: this.span, + }, + DUMMY_NODE_ID, + ) + }; + self.cx.stmt_item( + self.span, + self.cx.item( + self.span, + Ident::empty(), + vec![self.cx.attribute(attr::mk_list_item( + Ident::new(sym::allow, self.span), + vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))], + ))], + ItemKind::Use(UseTree { + prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])), + kind: UseTreeKind::Nested(vec![ + nested_tree(self, sym::TryCaptureGeneric), + nested_tree(self, sym::TryCapturePrintable), + ]), + span: self.span, + }), + ), + ) + } + + /// The necessary custom `panic!(...)` expression. + /// + /// panic!( + /// "Assertion failed: ... \n With expansion: ...", + /// __capture0, + /// ... + /// ); + fn build_panic(&self, expr_str: &str, panic_path: Path) -> P { + let escaped_expr_str = escape_to_fmt(expr_str); + let initial = [ + TokenTree::token( + token::Literal(token::Lit { + kind: token::LitKind::Str, + symbol: Symbol::intern(&if self.fmt_string.is_empty() { + format!("Assertion failed: {escaped_expr_str}") + } else { + format!( + "Assertion failed: {escaped_expr_str}\nWith captures:\n{}", + &self.fmt_string + ) + }), + suffix: None, + }), + self.span, + ), + TokenTree::token(token::Comma, self.span), + ]; + let captures = self.capture_decls.iter().flat_map(|cap| { + [ + TokenTree::token(token::Ident(cap.ident.name, false), cap.ident.span), + TokenTree::token(token::Comma, self.span), + ] + }); + self.cx.expr( + self.span, + ExprKind::MacCall(MacCall { + path: panic_path, + args: P(MacArgs::Delimited( + DelimSpan::from_single(self.span), + MacDelimiter::Parenthesis, + initial.into_iter().chain(captures).collect::(), + )), + prior_type_ascription: None, + }), + ) + } + + /// Recursive function called until `cond_expr` and `fmt_str` are fully modified. + /// + /// See [Self::manage_initial_capture] and [Self::manage_try_capture] + fn manage_cond_expr(&mut self, expr: &mut P) { + match (*expr).kind { + ExprKind::Binary(_, ref mut lhs, ref mut rhs) => { + self.manage_cond_expr(lhs); + self.manage_cond_expr(rhs); + } + ExprKind::Path(_, Path { ref segments, .. }) if let &[ref path_segment] = &segments[..] => { + let path_ident = path_segment.ident; + self.manage_initial_capture(expr, path_ident); + } + _ => {} + } + } + + /// Pushes the top-level declarations and modifies `expr` to try capturing variables. /// - /// let mut __capture0 = Capture::new(); - /// ... - /// ... - /// ... + /// `fmt_str`, the formatting string used for debugging, is constructed to show possible + /// captured variables. + fn manage_initial_capture(&mut self, expr: &mut P, path_ident: Ident) { + if self.paths.contains(&path_ident) { + return; + } else { + self.fmt_string.push_str(" "); + self.fmt_string.push_str(path_ident.as_str()); + self.fmt_string.push_str(" = {:?}\n"); + let _ = self.paths.insert(path_ident); + } + let curr_capture_idx = self.capture_decls.len(); + let capture_string = format!("__capture{curr_capture_idx}"); + let ident = Ident::new(Symbol::intern(&capture_string), self.span); + let init_std_path = self.cx.std_path(&[sym::asserting, sym::Capture, sym::new]); + let init = self.cx.expr_call( + self.span, + self.cx.expr_path(self.cx.path(self.span, init_std_path)), + vec![], + ); + let capture = Capture { decl: self.cx.stmt_let(self.span, true, ident, init), ident }; + self.capture_decls.push(capture); + self.manage_try_capture(ident, curr_capture_idx, expr); + } + + /// Tries to copy `__local_bindN` into `__captureN`. /// - /// if !{ - /// ... - /// ... - /// ... - /// } { - /// panic!( - /// "Assertion failed: ... \n With expansion: ...", - /// __capture0, - /// ... - /// ... - /// ... - /// ); - /// } + /// *{ + /// (&Wrapper(__local_bindN)).try_capture(&mut __captureN); + /// __local_bindN /// } - pub(super) fn build(self, _cond_expr: P, _panic_path: Path) -> P { - let Self { cx, span, .. } = self; - let stmts = Vec::new(); - cx.expr_block(cx.block(span, stmts)) + fn manage_try_capture(&mut self, capture: Ident, curr_capture_idx: usize, expr: &mut P) { + let local_bind_string = format!("__local_bind{curr_capture_idx}"); + let local_bind = Ident::new(Symbol::intern(&local_bind_string), self.span); + self.local_bind_decls.push(self.cx.stmt_let( + self.span, + false, + local_bind, + self.cx.expr_addr_of(self.span, expr.clone()), + )); + let wrapper = self.cx.expr_call( + self.span, + self.cx.expr_path( + self.cx.path(self.span, self.cx.std_path(&[sym::asserting, sym::Wrapper])), + ), + vec![self.cx.expr_path(Path::from_ident(local_bind))], + ); + let try_capture_call = self + .cx + .stmt_expr(expr_method_call( + self.cx, + PathSegment { + args: None, + id: DUMMY_NODE_ID, + ident: Ident::new(sym::try_capture, self.span), + }, + vec![ + expr_paren(self.cx, self.span, self.cx.expr_addr_of(self.span, wrapper)), + expr_addr_of_mut( + self.cx, + self.span, + self.cx.expr_path(Path::from_ident(capture)), + ), + ], + self.span, + )) + .add_trailing_semicolon(); + let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind)); + let ret = self.cx.stmt_expr(local_bind_path); + let block = self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret])); + *expr = self.cx.expr_deref(self.span, block); + } +} + +/// Information about a captured element. +#[derive(Debug)] +struct Capture { + // Generated indexed `Capture` statement. + // + // `let __capture{} = Capture::new();` + decl: Stmt, + // The name of the generated indexed `Capture` variable. + // + // `__capture{}` + ident: Ident, +} + +/// Escapes to use as a formatting string. +fn escape_to_fmt(s: &str) -> String { + let mut rslt = String::with_capacity(s.len()); + for c in s.chars() { + rslt.extend(c.escape_debug()); + match c { + '{' | '}' => rslt.push(c), + _ => {} + } } + rslt +} + +fn expr_addr_of_mut(cx: &ExtCtxt<'_>, sp: Span, e: P) -> P { + cx.expr(sp, ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e)) +} + +fn expr_method_call( + cx: &ExtCtxt<'_>, + path: PathSegment, + args: Vec>, + span: Span, +) -> P { + cx.expr(span, ExprKind::MethodCall(path, args, span)) +} + +fn expr_paren(cx: &ExtCtxt<'_>, sp: Span, e: P) -> P { + cx.expr(sp, ExprKind::Paren(e)) } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 124d0d18cdbe2..11565ba72d755 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -6,6 +6,7 @@ #![feature(array_windows)] #![feature(box_patterns)] #![feature(decl_macro)] +#![feature(if_let_guard)] #![feature(is_sorted)] #![feature(let_chains)] #![feature(let_else)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7b0fa65e8086b..2af43087e8de6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -156,6 +156,7 @@ symbols! { C, CStr, CString, + Capture, Center, Clone, Continue, @@ -263,6 +264,8 @@ symbols! { ToOwned, ToString, Try, + TryCaptureGeneric, + TryCapturePrintable, TryFrom, TryInto, Ty, @@ -272,6 +275,7 @@ symbols! { UnsafeArg, Vec, VecDeque, + Wrapper, Yield, _DECLS, _Self, @@ -354,6 +358,7 @@ symbols! { assert_receiver_is_total_eq, assert_uninit_valid, assert_zero_valid, + asserting, associated_const_equality, associated_consts, associated_type_bounds, @@ -1432,6 +1437,7 @@ symbols! { truncf32, truncf64, try_blocks, + try_capture, try_from, try_into, try_trait_v2, @@ -1494,6 +1500,7 @@ symbols! { unsized_tuple_coercion, unstable, untagged_unions, + unused_imports, unused_qualifications, unwind, unwind_attributes, diff --git a/src/test/ui/macros/assert-trailing-junk.rs b/src/test/ui/macros/assert-trailing-junk.rs index cd7faf9bf8bfb..da725e19e2ada 100644 --- a/src/test/ui/macros/assert-trailing-junk.rs +++ b/src/test/ui/macros/assert-trailing-junk.rs @@ -1,3 +1,6 @@ +// revisions: with-generic-asset without-generic-asset +// [with-generic-asset] compile-flags: --cfg feature="generic_assert" + // Ensure assert macro does not ignore trailing garbage. // // See https://github.com/rust-lang/rust/issues/60024 for details. diff --git a/src/test/ui/macros/assert-trailing-junk.stderr b/src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr similarity index 86% rename from src/test/ui/macros/assert-trailing-junk.stderr rename to src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr index eb001429c5522..09dd16a0b0d89 100644 --- a/src/test/ui/macros/assert-trailing-junk.stderr +++ b/src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr @@ -1,17 +1,17 @@ error: expected one of `,`, `.`, `?`, or an operator, found `some` - --> $DIR/assert-trailing-junk.rs:6:18 + --> $DIR/assert-trailing-junk.rs:9:18 | LL | assert!(true some extra junk, "whatever"); | ^^^^ expected one of `,`, `.`, `?`, or an operator error: expected one of `,`, `.`, `?`, or an operator, found `some` - --> $DIR/assert-trailing-junk.rs:9:18 + --> $DIR/assert-trailing-junk.rs:12:18 | LL | assert!(true some extra junk); | ^^^^ expected one of `,`, `.`, `?`, or an operator error: no rules expected the token `blah` - --> $DIR/assert-trailing-junk.rs:12:30 + --> $DIR/assert-trailing-junk.rs:15:30 | LL | assert!(true, "whatever" blah); | -^^^^ no rules expected this token in macro call @@ -19,7 +19,7 @@ LL | assert!(true, "whatever" blah); | help: missing comma here error: unexpected string literal - --> $DIR/assert-trailing-junk.rs:15:18 + --> $DIR/assert-trailing-junk.rs:18:18 | LL | assert!(true "whatever" blah); | -^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | assert!(true "whatever" blah); | help: try adding a comma error: no rules expected the token `blah` - --> $DIR/assert-trailing-junk.rs:15:29 + --> $DIR/assert-trailing-junk.rs:18:29 | LL | assert!(true "whatever" blah); | -^^^^ no rules expected this token in macro call @@ -35,7 +35,7 @@ LL | assert!(true "whatever" blah); | help: missing comma here error: macro requires an expression as an argument - --> $DIR/assert-trailing-junk.rs:19:5 + --> $DIR/assert-trailing-junk.rs:22:5 | LL | assert!(true;); | ^^^^^^^^^^^^-^ @@ -43,7 +43,7 @@ LL | assert!(true;); | help: try removing semicolon error: unexpected string literal - --> $DIR/assert-trailing-junk.rs:22:27 + --> $DIR/assert-trailing-junk.rs:25:27 | LL | assert!(false || true "error message"); | -^^^^^^^^^^^^^^^ diff --git a/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr b/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr new file mode 100644 index 0000000000000..09dd16a0b0d89 --- /dev/null +++ b/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr @@ -0,0 +1,54 @@ +error: expected one of `,`, `.`, `?`, or an operator, found `some` + --> $DIR/assert-trailing-junk.rs:9:18 + | +LL | assert!(true some extra junk, "whatever"); + | ^^^^ expected one of `,`, `.`, `?`, or an operator + +error: expected one of `,`, `.`, `?`, or an operator, found `some` + --> $DIR/assert-trailing-junk.rs:12:18 + | +LL | assert!(true some extra junk); + | ^^^^ expected one of `,`, `.`, `?`, or an operator + +error: no rules expected the token `blah` + --> $DIR/assert-trailing-junk.rs:15:30 + | +LL | assert!(true, "whatever" blah); + | -^^^^ no rules expected this token in macro call + | | + | help: missing comma here + +error: unexpected string literal + --> $DIR/assert-trailing-junk.rs:18:18 + | +LL | assert!(true "whatever" blah); + | -^^^^^^^^^^ + | | + | help: try adding a comma + +error: no rules expected the token `blah` + --> $DIR/assert-trailing-junk.rs:18:29 + | +LL | assert!(true "whatever" blah); + | -^^^^ no rules expected this token in macro call + | | + | help: missing comma here + +error: macro requires an expression as an argument + --> $DIR/assert-trailing-junk.rs:22:5 + | +LL | assert!(true;); + | ^^^^^^^^^^^^-^ + | | + | help: try removing semicolon + +error: unexpected string literal + --> $DIR/assert-trailing-junk.rs:25:27 + | +LL | assert!(false || true "error message"); + | -^^^^^^^^^^^^^^^ + | | + | help: try adding a comma + +error: aborting due to 7 previous errors + diff --git a/src/test/ui/macros/assert.rs b/src/test/ui/macros/assert.rs index 71b0dbb19e262..a314db907b8a2 100644 --- a/src/test/ui/macros/assert.rs +++ b/src/test/ui/macros/assert.rs @@ -1,3 +1,6 @@ +// revisions: with-generic-asset without-generic-asset +// [with-generic-asset] compile-flags: --cfg feature="generic_assert" + fn main() { assert!(); //~ ERROR requires a boolean expression assert!(struct); //~ ERROR expected expression diff --git a/src/test/ui/macros/assert.stderr b/src/test/ui/macros/assert.with-generic-asset.stderr similarity index 87% rename from src/test/ui/macros/assert.stderr rename to src/test/ui/macros/assert.with-generic-asset.stderr index 57e5c77a56692..51d8f28a35c39 100644 --- a/src/test/ui/macros/assert.stderr +++ b/src/test/ui/macros/assert.with-generic-asset.stderr @@ -1,17 +1,17 @@ error: macro requires a boolean expression as an argument - --> $DIR/assert.rs:2:5 + --> $DIR/assert.rs:5:5 | LL | assert!(); | ^^^^^^^^^ boolean expression required error: expected expression, found keyword `struct` - --> $DIR/assert.rs:3:13 + --> $DIR/assert.rs:6:13 | LL | assert!(struct); | ^^^^^^ expected expression error: macro requires a boolean expression as an argument - --> $DIR/assert.rs:4:5 + --> $DIR/assert.rs:7:5 | LL | debug_assert!(); | ^^^^^^^^^^^^^^^ boolean expression required @@ -19,7 +19,7 @@ LL | debug_assert!(); = note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected expression, found keyword `struct` - --> $DIR/assert.rs:5:19 + --> $DIR/assert.rs:8:19 | LL | debug_assert!(struct); | ^^^^^^ expected expression diff --git a/src/test/ui/macros/assert.without-generic-asset.stderr b/src/test/ui/macros/assert.without-generic-asset.stderr new file mode 100644 index 0000000000000..51d8f28a35c39 --- /dev/null +++ b/src/test/ui/macros/assert.without-generic-asset.stderr @@ -0,0 +1,28 @@ +error: macro requires a boolean expression as an argument + --> $DIR/assert.rs:5:5 + | +LL | assert!(); + | ^^^^^^^^^ boolean expression required + +error: expected expression, found keyword `struct` + --> $DIR/assert.rs:6:13 + | +LL | assert!(struct); + | ^^^^^^ expected expression + +error: macro requires a boolean expression as an argument + --> $DIR/assert.rs:7:5 + | +LL | debug_assert!(); + | ^^^^^^^^^^^^^^^ boolean expression required + | + = note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found keyword `struct` + --> $DIR/assert.rs:8:19 + | +LL | debug_assert!(struct); + | ^^^^^^ expected expression + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/all-expr-kinds.rs b/src/test/ui/rfc-2011-nicer-assert-messages/all-expr-kinds.rs new file mode 100644 index 0000000000000..14533ec2481d5 --- /dev/null +++ b/src/test/ui/rfc-2011-nicer-assert-messages/all-expr-kinds.rs @@ -0,0 +1,142 @@ +// edition:2021 +// ignore-tidy-linelength +// run-pass + +#![allow(path_statements, unused_allocation)] +#![feature(box_syntax, core_intrinsics, generic_assert, generic_assert_internals)] + +macro_rules! test { + ( + let mut $elem_ident:ident = $elem_expr:expr; + [ $($assert:tt)* ] => $msg:literal + ) => { + { + #[allow(unused_assignments, unused_mut, unused_variables)] + let rslt = std::panic::catch_unwind(|| { + let mut $elem_ident = $elem_expr; + assert!($($assert)*); + }); + let err = rslt.unwrap_err(); + if let Some(elem) = err.downcast_ref::() { + assert_eq!(elem, &$msg); + } + else if let Some(elem) = err.downcast_ref::<&str>() { + assert_eq!(elem, &$msg); + } + else { + panic!("assert!( ... ) should return a string"); + } + } + } +} + +macro_rules! tests { + ( + let mut $elem_ident:ident = $elem_expr:expr; + + $( + [ $($elem_assert:tt)* ] => $elem_msg:literal + )+ + ) => { + $( + test!( + let mut $elem_ident = $elem_expr; + [ $($elem_assert)* ] => $elem_msg + ); + )+ + } +} + +const FOO: Foo = Foo { bar: 1 }; + +#[derive(Clone, Copy, Debug, PartialEq)] +struct Foo { + bar: i32 +} + +fn main() { + // ***** Allowed ***** + + tests!( + let mut elem = 1i32; + + // binary + [ elem + 1 == 3 ] => "Assertion failed: elem + 1 == 3\nWith captures:\n elem = 1\n" + ); + + // ***** Disallowed ***** + + tests!( + let mut elem = 1i32; + + // assign + [ { let local = elem; local } == 3 ] => "Assertion failed: { let local = elem; local } == 3" + + // assign op + [ { elem += 1; elem } == 3 ] => "Assertion failed: { elem += 1; elem } == 3" + + // async + [ { let _ = async { elem }; elem } == 3 ] => "Assertion failed: { let _ = async { elem }; elem } == 3" + + // await + + // block + [ { elem } == 3 ] => "Assertion failed: { elem } == 3" + + // box + [ box elem == box 3 ] => "Assertion failed: box elem == box 3" + + // break + [ loop { break elem; } == 3 ] => "Assertion failed: loop { break elem; } == 3" + + // closure + [(|| elem)() == 3 ] => "Assertion failed: (|| elem)() == 3" + + // const block + + // continue + + // err + + // field + [ FOO.bar == 3 ] => "Assertion failed: FOO.bar == 3" + + // for loop + [ { for _ in 0..elem { elem; } elem } == 3 ] => "Assertion failed: { for _ in 0..elem { elem; } elem } == 3" + + // if + [ if true { elem } else { elem } == 3 ] => "Assertion failed: if true { elem } else { elem } == 3" + + // inline asm + + // let + [ if let true = true { elem } else { elem } == 3 ] => "Assertion failed: if let true = true { elem } else { elem } == 3" + + // lit + + // loop + [ loop { elem; break elem; } == 3 ] => "Assertion failed: loop { elem; break elem; } == 3" + + // mac call + + // match + [ match elem { _ => elem } == 3 ] => "Assertion failed: match elem { _ => elem, } == 3" + + // ret + [ (|| { return elem; })() == 3 ] => "Assertion failed: (|| { return elem; })() == 3" + + // try + [ (|| { Some(Some(elem)?) })() == Some(3) ] => "Assertion failed: (|| { Some(Some(elem)?) })() == Some(3)" + + // try block + + // underscore + + // while + [ { while false { elem; break; } elem } == 3 ] => "Assertion failed: { while false { elem; break; } elem } == 3" + + // yeet + + // yield + ); +} diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/all-not-available-cases.rs b/src/test/ui/rfc-2011-nicer-assert-messages/all-not-available-cases.rs new file mode 100644 index 0000000000000..ecbba8c443ac7 --- /dev/null +++ b/src/test/ui/rfc-2011-nicer-assert-messages/all-not-available-cases.rs @@ -0,0 +1,42 @@ +// aux-build:common.rs +// ignore-tidy-linelength +// run-pass + +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] + +extern crate common; + +#[derive(Clone, Copy, PartialEq)] +struct CopyNoDebug(i32); + +#[derive(Debug, PartialEq)] +struct NoCopyDebug(i32); + +#[derive(PartialEq)] +struct NoCopyNoDebug(i32); + +fn main() { + // Has Copy but does not have Debug + common::test!( + let mut copy_no_debug = CopyNoDebug(1); + [ copy_no_debug == CopyNoDebug(3) ] => "Assertion failed: copy_no_debug == CopyNoDebug(3)\nWith captures:\n copy_no_debug = N/A\n" + ); + + // Does not have Copy but has Debug + common::test!( + let mut no_copy_debug = NoCopyDebug(1); + [ no_copy_debug == NoCopyDebug(3) ] => "Assertion failed: no_copy_debug == NoCopyDebug(3)\nWith captures:\n no_copy_debug = N/A\n" + ); + + // Does not have Copy and does not have Debug + common::test!( + let mut no_copy_no_debug = NoCopyNoDebug(1); + [ no_copy_no_debug == NoCopyNoDebug(3) ] => "Assertion failed: no_copy_no_debug == NoCopyNoDebug(3)\nWith captures:\n no_copy_no_debug = N/A\n" + ); + + // Unevaluated (Expression short-circuited) + common::test!( + let mut elem = true; + [ false && elem ] => "Assertion failed: false && elem\nWith captures:\n elem = N/A\n" + ); +} diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs b/src/test/ui/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs new file mode 100644 index 0000000000000..6a1435f792bf4 --- /dev/null +++ b/src/test/ui/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs @@ -0,0 +1,13 @@ +// compile-flags: --test +// run-pass + +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] + +#[should_panic(expected = "Custom user message")] +#[test] +fn test() { + assert!(1 == 3, "Custom user message"); +} + +fn main() { +} diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs b/src/test/ui/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs new file mode 100644 index 0000000000000..d4c1df6b94a81 --- /dev/null +++ b/src/test/ui/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs @@ -0,0 +1,13 @@ +// aux-build:common.rs +// run-pass + +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] + +extern crate common; + +fn main() { + common::test!( + let mut _nothing = (); + [ 1 == 3 ] => "Assertion failed: 1 == 3" + ); +} diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/auxiliary/common.rs b/src/test/ui/rfc-2011-nicer-assert-messages/auxiliary/common.rs new file mode 100644 index 0000000000000..903ed507c2e51 --- /dev/null +++ b/src/test/ui/rfc-2011-nicer-assert-messages/auxiliary/common.rs @@ -0,0 +1,25 @@ +#[macro_export] +macro_rules! test { + ( + let mut $elem_ident:ident = $elem_expr:expr; + [ $($assert:tt)* ] => $msg:literal + ) => { + { + #[allow(unused_assignments, unused_mut, unused_variables)] + let rslt = std::panic::catch_unwind(|| { + let mut $elem_ident = $elem_expr; + assert!($($assert)*); + }); + let err = rslt.unwrap_err(); + if let Some(elem) = err.downcast_ref::() { + assert_eq!(elem, &$msg); + } + else if let Some(elem) = err.downcast_ref::<&str>() { + assert_eq!(elem, &$msg); + } + else { + panic!("assert!( ... ) should return a string"); + } + } + } +} diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/codegen.rs b/src/test/ui/rfc-2011-nicer-assert-messages/codegen.rs new file mode 100644 index 0000000000000..1db9d33c72aee --- /dev/null +++ b/src/test/ui/rfc-2011-nicer-assert-messages/codegen.rs @@ -0,0 +1,9 @@ +// check-pass +// compile-flags: -Z unpretty=expanded + +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] + +fn main() { + let elem = 1i32; + assert!(elem == 1); +} diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/codegen.stdout b/src/test/ui/rfc-2011-nicer-assert-messages/codegen.stdout new file mode 100644 index 0000000000000..a590eb3223254 --- /dev/null +++ b/src/test/ui/rfc-2011-nicer-assert-messages/codegen.stdout @@ -0,0 +1,29 @@ +#![feature(prelude_import)] +#![no_std] +// check-pass +// compile-flags: -Z unpretty=expanded + +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +fn main() { + let elem = 1i32; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if !(*{ + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + __local_bind0 + } == 1) { + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem == 1\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; +} diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs b/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs index f70ca87e304a9..01860adaac250 100644 --- a/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs +++ b/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs @@ -1,8 +1,7 @@ // compile-flags: --test +// ignore-tidy-linelength // run-pass -// `generic_assert` is completely unimplemented and doesn't generate any logic, thus the -// reason why this test currently passes #![feature(core_intrinsics, generic_assert, generic_assert_internals)] use std::fmt::{Debug, Formatter}; @@ -16,10 +15,11 @@ impl Debug for CopyDebug { } } +#[should_panic(expected = "Assertion failed: copy_debug == CopyDebug(3)\nWith captures:\n copy_debug = With great power comes great electricity bills\n")] #[test] fn test() { - let _copy_debug = CopyDebug(1); - assert!(_copy_debug == CopyDebug(3)); + let copy_debug = CopyDebug(1); + assert!(copy_debug == CopyDebug(3)); } fn main() {