From edd647e141d76152e00c133766b4cf239533856e Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 20 Jan 2023 18:29:35 -0800 Subject: [PATCH 01/16] Release notes for 1.67.0 --- RELEASES.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 2901bfcc3e3e9..79c49e9d7909e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,101 @@ +Version 1.67 (2023-01-26) +========================== + + + +Language +-------- + +- [Make `Sized` predicates coinductive, allowing cycles.](https://github.com/rust-lang/rust/pull/100386/) +- [`#[must_use]` annotations on `async fn` also affect the `Future::Output`.](https://github.com/rust-lang/rust/pull/100633/) +- [Elaborate supertrait obligations when deducing closure signatures.](https://github.com/rust-lang/rust/pull/101834/) +- [Invalid literals are no longer an error under `cfg(FALSE)`.](https://github.com/rust-lang/rust/pull/102944/) +- [Unreserve braced enum variants in value namespace.](https://github.com/rust-lang/rust/pull/103578/) + + + +Compiler +-------- + +- [Enable varargs support for calling conventions other than `C` or `cdecl`.](https://github.com/rust-lang/rust/pull/97971/) +- [Add new MIR constant propagation based on dataflow analysis.](https://github.com/rust-lang/rust/pull/101168/) +- [Optimize field ordering by grouping m\*2^n-sized fields with equivalently aligned ones.](https://github.com/rust-lang/rust/pull/102750/) +- [Stabilize native library modifier `verbatim`.](https://github.com/rust-lang/rust/pull/104360/) + +Added and removed targets: + +- [Add a tier 3 target for PowerPC on AIX](https://github.com/rust-lang/rust/pull/102293/), `powerpc64-ibm-aix`. +- [Add a tier 3 target for the Sony PlayStation 1](https://github.com/rust-lang/rust/pull/102689/), `mipsel-sony-psx`. +- [Add tier 3 `no_std` targets for the QNX Neutrino RTOS](https://github.com/rust-lang/rust/pull/102701/), + `aarch64-unknown-nto-qnx710` and `x86_64-pc-nto-qnx710`. +- [Remove tier 3 `linuxkernel` targets](https://github.com/rust-lang/rust/pull/104015/) (not used by the actual kernel). + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + + + +Libraries +--------- + +- [Merge `crossbeam-channel` into `std::sync::mpsc`.](https://github.com/rust-lang/rust/pull/93563/) +- [Fix inconsistent rounding of 0.5 when formatted to 0 decimal places.](https://github.com/rust-lang/rust/pull/102935/) +- [Derive `Eq` and `Hash` for `ControlFlow`.](https://github.com/rust-lang/rust/pull/103084/) +- [Don't build `compiler_builtins` with `-C panic=abort`.](https://github.com/rust-lang/rust/pull/103786/) + + + +Stabilized APIs +--------------- + +- [`{integer}::checked_ilog`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog) +- [`{integer}::checked_ilog2`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog2) +- [`{integer}::checked_ilog10`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog10) +- [`{integer}::ilog`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog) +- [`{integer}::ilog2`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog2) +- [`{integer}::ilog10`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog10) +- [`NonZeroU*::ilog2`](https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#method.ilog2) +- [`NonZeroU*::ilog10`](https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#method.ilog10) +- [`NonZero*::BITS`](https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#associatedconstant.BITS) + +These APIs are now stable in const contexts: + +- [`char::from_u32`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.from_u32) +- [`char::from_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.from_digit) +- [`char::to_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.to_digit) +- [`core::char::from_u32`](https://doc.rust-lang.org/stable/core/char/fn.from_u32.html) +- [`core::char::from_digit`](https://doc.rust-lang.org/stable/core/char/fn.from_digit.html) + + + +Compatibility Notes +------------------- + +- [Chains of `&&` and `||` will now drop temporaries from their sub-expressions in + evaluation order, left-to-right.](https://github.com/rust-lang/rust/pull/103293/) + Previously, it was "twisted" such that the _first_ expression dropped its + temporaries _last_, after all of the other expressions dropped in order. +- [Proc-macro derives using inaccessible names from parent modules is now a hard error.](https://github.com/rust-lang/rust/pull/84022/) + This has been a warning since 1.29.0, and denied by default since 1.58.0. + (**TODO**: revert proposed in ) +- [Underscore suffixes on string literals are now a hard error.](https://github.com/rust-lang/rust/pull/103914/) + This has been a future-compatibility warning since 1.20.0. +- [Stop passing `-export-dynamic` to `wasm-ld`.](https://github.com/rust-lang/rust/pull/105405/) +- [`main` is now mangled as `__main_void` on `wasm32-wasi`.](https://github.com/rust-lang/rust/pull/105468/) +- [Cargo now emits an error if there are multiple registries in the configuration + with the same index URL.](https://github.com/rust-lang/cargo/pull/10592) + + + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Rewrite LLVM's archive writer in Rust.](https://github.com/rust-lang/rust/pull/97485/) + Version 1.66.1 (2023-01-10) =========================== From 568d6c1ac78df9fb4a8cc988198f6af9371a53d0 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 20 Jan 2023 18:38:09 -0800 Subject: [PATCH 02/16] Expand 1.67 to 1.67.0 --- RELEASES.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 79c49e9d7909e..6acc8f3dfb4e6 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,7 +1,7 @@ -Version 1.67 (2023-01-26) +Version 1.67.0 (2023-01-26) ========================== - + Language -------- @@ -12,7 +12,7 @@ Language - [Invalid literals are no longer an error under `cfg(FALSE)`.](https://github.com/rust-lang/rust/pull/102944/) - [Unreserve braced enum variants in value namespace.](https://github.com/rust-lang/rust/pull/103578/) - + Compiler -------- @@ -33,7 +33,7 @@ Added and removed targets: Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. - + Libraries --------- @@ -43,7 +43,7 @@ Libraries - [Derive `Eq` and `Hash` for `ControlFlow`.](https://github.com/rust-lang/rust/pull/103084/) - [Don't build `compiler_builtins` with `-C panic=abort`.](https://github.com/rust-lang/rust/pull/103786/) - + Stabilized APIs --------------- @@ -66,7 +66,7 @@ These APIs are now stable in const contexts: - [`core::char::from_u32`](https://doc.rust-lang.org/stable/core/char/fn.from_u32.html) - [`core::char::from_digit`](https://doc.rust-lang.org/stable/core/char/fn.from_digit.html) - + Compatibility Notes ------------------- @@ -85,7 +85,7 @@ Compatibility Notes - [Cargo now emits an error if there are multiple registries in the configuration with the same index URL.](https://github.com/rust-lang/cargo/pull/10592) - + Internal Changes ---------------- From 43c18e92ea974ad11b10f8da46d9261f56170803 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 21 Jan 2023 14:48:18 -0800 Subject: [PATCH 03/16] Remove relnote for #84022 due to revert #107133 --- RELEASES.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 6acc8f3dfb4e6..6be38ecca2bc7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -75,9 +75,6 @@ Compatibility Notes evaluation order, left-to-right.](https://github.com/rust-lang/rust/pull/103293/) Previously, it was "twisted" such that the _first_ expression dropped its temporaries _last_, after all of the other expressions dropped in order. -- [Proc-macro derives using inaccessible names from parent modules is now a hard error.](https://github.com/rust-lang/rust/pull/84022/) - This has been a warning since 1.29.0, and denied by default since 1.58.0. - (**TODO**: revert proposed in ) - [Underscore suffixes on string literals are now a hard error.](https://github.com/rust-lang/rust/pull/103914/) This has been a future-compatibility warning since 1.20.0. - [Stop passing `-export-dynamic` to `wasm-ld`.](https://github.com/rust-lang/rust/pull/105405/) From ddcb02d10a575f88a0599893525ded7337ed648e Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 21 Jan 2023 14:56:57 -0800 Subject: [PATCH 04/16] Move 0.5 rounding to a compat note --- RELEASES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 6be38ecca2bc7..ea5a1a8729fcc 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -71,6 +71,9 @@ These APIs are now stable in const contexts: Compatibility Notes ------------------- +- [0.5 now rounds to 0 when formatted to 0 decimal places.](https://github.com/rust-lang/rust/pull/102935/) + This makes it consistent with the rest of floating point formatting that + rounds ties toward even digits. - [Chains of `&&` and `||` will now drop temporaries from their sub-expressions in evaluation order, left-to-right.](https://github.com/rust-lang/rust/pull/103293/) Previously, it was "twisted" such that the _first_ expression dropped its From b9be9e5fd1d2b7aace3875a77d1715bd9daa7e54 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 21 Jan 2023 15:08:48 -0800 Subject: [PATCH 05/16] Move the layout change to 1.67 compat notes --- RELEASES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index ea5a1a8729fcc..a63d4e8a043c6 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -71,6 +71,11 @@ These APIs are now stable in const contexts: Compatibility Notes ------------------- +- [The layout of `repr(Rust)` types now groups m\*2^n-sized fields with + equivalently aligned ones.](https://github.com/rust-lang/rust/pull/102750/) + This is intended to be an optimization, but it is also known to increase type + sizes in a few cases for the placement of enum tags. As a reminder, the layout + of `repr(Rust)` types is an implementation detail, subject to change. - [0.5 now rounds to 0 when formatted to 0 decimal places.](https://github.com/rust-lang/rust/pull/102935/) This makes it consistent with the rest of floating point formatting that rounds ties toward even digits. From 7f5ce942809e15f65d91089e8baacc6affdf6c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Mon, 23 Jan 2023 00:00:00 +0000 Subject: [PATCH 06/16] Bring tests back into rustc source tarball They were missing after recent move from src/test to tests. --- src/bootstrap/dist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 2d86ff1d2baea..02e35d2436e2f 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -962,7 +962,7 @@ impl Step for PlainSourceTarball { "Cargo.toml", "Cargo.lock", ]; - let src_dirs = ["src", "compiler", "library"]; + let src_dirs = ["src", "compiler", "library", "tests"]; copy_src_dirs(builder, &builder.src, &src_dirs, &[], &plain_dst_src); From e65b36110f943118b7e50c4e39e26ad1f07d8552 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 23 Jan 2023 17:10:54 -0700 Subject: [PATCH 07/16] rustdoc: rearrange HTML in primitive reference links This patch avoids hard-to-click single character links by making the generic part of the link: Before: &T After: &T --- src/librustdoc/html/format.rs | 10 ++-------- tests/rustdoc/whitespace-after-where-clause.enum.html | 2 +- tests/rustdoc/whitespace-after-where-clause.enum2.html | 2 +- .../rustdoc/whitespace-after-where-clause.struct.html | 2 +- .../rustdoc/whitespace-after-where-clause.struct2.html | 2 +- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index d3dc4065dfc72..33404a7683597 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1064,14 +1064,8 @@ fn fmt_type<'cx>( fmt_type(ty, f, use_absolute, cx)?; write!(f, ")") } - clean::Generic(..) => { - primitive_link( - f, - PrimitiveType::Reference, - &format!("{}{}{}", amp, lt, m), - cx, - )?; - fmt_type(ty, f, use_absolute, cx) + clean::Generic(name) => { + primitive_link(f, PrimitiveType::Reference, &format!("{amp}{lt}{m}{name}"), cx) } _ => { write!(f, "{}{}{}", amp, lt, m)?; diff --git a/tests/rustdoc/whitespace-after-where-clause.enum.html b/tests/rustdoc/whitespace-after-where-clause.enum.html index 20bde549a0378..eeb22878f3c63 100644 --- a/tests/rustdoc/whitespace-after-where-clause.enum.html +++ b/tests/rustdoc/whitespace-after-where-clause.enum.html @@ -1,4 +1,4 @@
pub enum Cow<'a, B>where
    B: ToOwned<dyn Clone> + ?Sized + 'a,
{ - Borrowed(&'a B), + Borrowed(&'a B), Whatever(u32), }
\ No newline at end of file diff --git a/tests/rustdoc/whitespace-after-where-clause.enum2.html b/tests/rustdoc/whitespace-after-where-clause.enum2.html index d9fc0c22309db..c8037c2a8df5a 100644 --- a/tests/rustdoc/whitespace-after-where-clause.enum2.html +++ b/tests/rustdoc/whitespace-after-where-clause.enum2.html @@ -1,4 +1,4 @@
pub enum Cow2<'a, B: ?Sized + ToOwned<dyn Clone> + 'a> {
-    Borrowed(&'a B),
+    Borrowed(&'a B),
     Whatever(u32),
 }
\ No newline at end of file diff --git a/tests/rustdoc/whitespace-after-where-clause.struct.html b/tests/rustdoc/whitespace-after-where-clause.struct.html index f375265d7c183..5892270b2f930 100644 --- a/tests/rustdoc/whitespace-after-where-clause.struct.html +++ b/tests/rustdoc/whitespace-after-where-clause.struct.html @@ -1,4 +1,4 @@
pub struct Struct<'a, B>where
    B: ToOwned<dyn Clone> + ?Sized + 'a,
{ - pub a: &'a B, + pub a: &'a B, pub b: u32, }
\ No newline at end of file diff --git a/tests/rustdoc/whitespace-after-where-clause.struct2.html b/tests/rustdoc/whitespace-after-where-clause.struct2.html index 1c59962eb1c58..d3952b0c56699 100644 --- a/tests/rustdoc/whitespace-after-where-clause.struct2.html +++ b/tests/rustdoc/whitespace-after-where-clause.struct2.html @@ -1,4 +1,4 @@
pub struct Struct2<'a, B: ?Sized + ToOwned<dyn Clone> + 'a> {
-    pub a: &'a B,
+    pub a: &'a B,
     pub b: u32,
 }
\ No newline at end of file From e6e93e021e7be6c9ae400145a2aa235f16f9eb45 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 24 Jan 2023 12:41:18 +0100 Subject: [PATCH 08/16] add test where we ignore hr implied bounds --- tests/ui/regions/higher-ranked-implied.rs | 14 +++++++++++++ tests/ui/regions/higher-ranked-implied.stderr | 21 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/ui/regions/higher-ranked-implied.rs create mode 100644 tests/ui/regions/higher-ranked-implied.stderr diff --git a/tests/ui/regions/higher-ranked-implied.rs b/tests/ui/regions/higher-ranked-implied.rs new file mode 100644 index 0000000000000..103884c50313f --- /dev/null +++ b/tests/ui/regions/higher-ranked-implied.rs @@ -0,0 +1,14 @@ +// FIXME: This test should pass as the first two fields add implied bounds that +// `'a` is equal to `'b` while the last one should simply use that fact. With +// the current implementation this errors. We have to be careful as implied bounds +// are only sound if they're also correctly checked. + +struct Inv(*mut T); // `T` is invariant. +type A = for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>); +type B = for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>); + +fn main() { + let x: A = |_, _, _| (); + let y: B = x; //~ ERROR mismatched types + let _: A = y; //~ ERROR mismatched types +} diff --git a/tests/ui/regions/higher-ranked-implied.stderr b/tests/ui/regions/higher-ranked-implied.stderr new file mode 100644 index 0000000000000..9d80eacd7c320 --- /dev/null +++ b/tests/ui/regions/higher-ranked-implied.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-implied.rs:12:16 + | +LL | let y: B = x; + | ^ one type is more general than the other + | + = note: expected fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>)` + found fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>)` + +error[E0308]: mismatched types + --> $DIR/higher-ranked-implied.rs:13:16 + | +LL | let _: A = y; + | ^ one type is more general than the other + | + = note: expected fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>)` + found fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>)` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From ad7393668f7f54372b974baa37854601756bacc3 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Tue, 24 Jan 2023 04:13:52 -0800 Subject: [PATCH 09/16] Delete `SimplifyArmIdentity` and `SimplifyBranchSame` mir opts --- compiler/rustc_mir_transform/src/lib.rs | 3 - .../rustc_mir_transform/src/simplify_try.rs | 822 ------------------ ..._regression.encode.SimplifyBranchSame.diff | 29 - tests/mir-opt/76803_regression.rs | 19 - .../issue_73223.main.SimplifyArmIdentity.diff | 156 ---- tests/mir-opt/issue_73223.rs | 12 - 6 files changed, 1041 deletions(-) delete mode 100644 compiler/rustc_mir_transform/src/simplify_try.rs delete mode 100644 tests/mir-opt/76803_regression.encode.SimplifyBranchSame.diff delete mode 100644 tests/mir-opt/76803_regression.rs delete mode 100644 tests/mir-opt/issue_73223.main.SimplifyArmIdentity.diff delete mode 100644 tests/mir-opt/issue_73223.rs diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 20b7fdcfe6d4d..4a598862d10f8 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -90,7 +90,6 @@ mod shim; pub mod simplify; mod simplify_branches; mod simplify_comparison_integral; -mod simplify_try; mod sroa; mod uninhabited_enum_branching; mod unreachable_prop; @@ -567,8 +566,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &o1(simplify_branches::SimplifyConstCondition::new("after-const-prop")), &early_otherwise_branch::EarlyOtherwiseBranch, &simplify_comparison_integral::SimplifyComparisonIntegral, - &simplify_try::SimplifyArmIdentity, - &simplify_try::SimplifyBranchSame, &dead_store_elimination::DeadStoreElimination, &dest_prop::DestinationPropagation, &o1(simplify_branches::SimplifyConstCondition::new("final")), diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs deleted file mode 100644 index e4f3ace9a93da..0000000000000 --- a/compiler/rustc_mir_transform/src/simplify_try.rs +++ /dev/null @@ -1,822 +0,0 @@ -//! The general point of the optimizations provided here is to simplify something like: -//! -//! ```rust -//! # fn foo(x: Result) -> Result { -//! match x { -//! Ok(x) => Ok(x), -//! Err(x) => Err(x) -//! } -//! # } -//! ``` -//! -//! into just `x`. - -use crate::{simplify, MirPass}; -use itertools::Itertools as _; -use rustc_index::{bit_set::BitSet, vec::IndexVec}; -use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::*; -use rustc_middle::ty::{self, List, Ty, TyCtxt}; -use rustc_target::abi::VariantIdx; -use std::iter::{once, Enumerate, Peekable}; -use std::slice::Iter; - -/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move. -/// -/// This is done by transforming basic blocks where the statements match: -/// -/// ```ignore (MIR) -/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY ); -/// _TMP_2 = _LOCAL_TMP; -/// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2; -/// discriminant(_LOCAL_0) = VAR_IDX; -/// ``` -/// -/// into: -/// -/// ```ignore (MIR) -/// _LOCAL_0 = move _LOCAL_1 -/// ``` -pub struct SimplifyArmIdentity; - -#[derive(Debug)] -struct ArmIdentityInfo<'tcx> { - /// Storage location for the variant's field - local_temp_0: Local, - /// Storage location holding the variant being read from - local_1: Local, - /// The variant field being read from - vf_s0: VarField<'tcx>, - /// Index of the statement which loads the variant being read - get_variant_field_stmt: usize, - - /// Tracks each assignment to a temporary of the variant's field - field_tmp_assignments: Vec<(Local, Local)>, - - /// Storage location holding the variant's field that was read from - local_tmp_s1: Local, - /// Storage location holding the enum that we are writing to - local_0: Local, - /// The variant field being written to - vf_s1: VarField<'tcx>, - - /// Storage location that the discriminant is being written to - set_discr_local: Local, - /// The variant being written - set_discr_var_idx: VariantIdx, - - /// Index of the statement that should be overwritten as a move - stmt_to_overwrite: usize, - /// SourceInfo for the new move - source_info: SourceInfo, - - /// Indices of matching Storage{Live,Dead} statements encountered. - /// (StorageLive index,, StorageDead index, Local) - storage_stmts: Vec<(usize, usize, Local)>, - - /// The statements that should be removed (turned into nops) - stmts_to_remove: Vec, - - /// Indices of debug variables that need to be adjusted to point to - // `{local_0}.{dbg_projection}`. - dbg_info_to_adjust: Vec, - - /// The projection used to rewrite debug info. - dbg_projection: &'tcx List>, -} - -fn get_arm_identity_info<'a, 'tcx>( - stmts: &'a [Statement<'tcx>], - locals_count: usize, - debug_info: &'a [VarDebugInfo<'tcx>], -) -> Option> { - // This can't possibly match unless there are at least 3 statements in the block - // so fail fast on tiny blocks. - if stmts.len() < 3 { - return None; - } - - let mut tmp_assigns = Vec::new(); - let mut nop_stmts = Vec::new(); - let mut storage_stmts = Vec::new(); - let mut storage_live_stmts = Vec::new(); - let mut storage_dead_stmts = Vec::new(); - - type StmtIter<'a, 'tcx> = Peekable>>>; - - fn is_storage_stmt(stmt: &Statement<'_>) -> bool { - matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_)) - } - - /// Eats consecutive Statements which match `test`, performing the specified `action` for each. - /// The iterator `stmt_iter` is not advanced if none were matched. - fn try_eat<'a, 'tcx>( - stmt_iter: &mut StmtIter<'a, 'tcx>, - test: impl Fn(&'a Statement<'tcx>) -> bool, - mut action: impl FnMut(usize, &'a Statement<'tcx>), - ) { - while stmt_iter.peek().map_or(false, |(_, stmt)| test(stmt)) { - let (idx, stmt) = stmt_iter.next().unwrap(); - - action(idx, stmt); - } - } - - /// Eats consecutive `StorageLive` and `StorageDead` Statements. - /// The iterator `stmt_iter` is not advanced if none were found. - fn try_eat_storage_stmts( - stmt_iter: &mut StmtIter<'_, '_>, - storage_live_stmts: &mut Vec<(usize, Local)>, - storage_dead_stmts: &mut Vec<(usize, Local)>, - ) { - try_eat(stmt_iter, is_storage_stmt, |idx, stmt| { - if let StatementKind::StorageLive(l) = stmt.kind { - storage_live_stmts.push((idx, l)); - } else if let StatementKind::StorageDead(l) = stmt.kind { - storage_dead_stmts.push((idx, l)); - } - }) - } - - fn is_tmp_storage_stmt(stmt: &Statement<'_>) -> bool { - use rustc_middle::mir::StatementKind::Assign; - if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind { - place.as_local().is_some() && p.as_local().is_some() - } else { - false - } - } - - /// Eats consecutive `Assign` Statements. - // The iterator `stmt_iter` is not advanced if none were found. - fn try_eat_assign_tmp_stmts( - stmt_iter: &mut StmtIter<'_, '_>, - tmp_assigns: &mut Vec<(Local, Local)>, - nop_stmts: &mut Vec, - ) { - try_eat(stmt_iter, is_tmp_storage_stmt, |idx, stmt| { - use rustc_middle::mir::StatementKind::Assign; - if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = - &stmt.kind - { - tmp_assigns.push((place.as_local().unwrap(), p.as_local().unwrap())); - nop_stmts.push(idx); - } - }) - } - - fn find_storage_live_dead_stmts_for_local( - local: Local, - stmts: &[Statement<'_>], - ) -> Option<(usize, usize)> { - trace!("looking for {:?}", local); - let mut storage_live_stmt = None; - let mut storage_dead_stmt = None; - for (idx, stmt) in stmts.iter().enumerate() { - if stmt.kind == StatementKind::StorageLive(local) { - storage_live_stmt = Some(idx); - } else if stmt.kind == StatementKind::StorageDead(local) { - storage_dead_stmt = Some(idx); - } - } - - Some((storage_live_stmt?, storage_dead_stmt.unwrap_or(usize::MAX))) - } - - // Try to match the expected MIR structure with the basic block we're processing. - // We want to see something that looks like: - // ``` - // (StorageLive(_) | StorageDead(_));* - // _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); - // (StorageLive(_) | StorageDead(_));* - // (tmp_n+1 = tmp_n);* - // (StorageLive(_) | StorageDead(_));* - // (tmp_n+1 = tmp_n);* - // ((LOCAL_FROM as Variant).FIELD: TY) = move tmp; - // discriminant(LOCAL_FROM) = VariantIdx; - // (StorageLive(_) | StorageDead(_));* - // ``` - let mut stmt_iter = stmts.iter().enumerate().peekable(); - - try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); - - let (get_variant_field_stmt, stmt) = stmt_iter.next()?; - let (local_tmp_s0, local_1, vf_s0, dbg_projection) = match_get_variant_field(stmt)?; - - try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); - - try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); - - try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); - - try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); - - let (idx, stmt) = stmt_iter.next()?; - let (local_tmp_s1, local_0, vf_s1) = match_set_variant_field(stmt)?; - nop_stmts.push(idx); - - let (idx, stmt) = stmt_iter.next()?; - let (set_discr_local, set_discr_var_idx) = match_set_discr(stmt)?; - let discr_stmt_source_info = stmt.source_info; - nop_stmts.push(idx); - - try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); - - for (live_idx, live_local) in storage_live_stmts { - if let Some(i) = storage_dead_stmts.iter().rposition(|(_, l)| *l == live_local) { - let (dead_idx, _) = storage_dead_stmts.swap_remove(i); - storage_stmts.push((live_idx, dead_idx, live_local)); - - if live_local == local_tmp_s0 { - nop_stmts.push(get_variant_field_stmt); - } - } - } - // We sort primitive usize here so we can use unstable sort - nop_stmts.sort_unstable(); - - // Use one of the statements we're going to discard between the point - // where the storage location for the variant field becomes live and - // is killed. - let (live_idx, dead_idx) = find_storage_live_dead_stmts_for_local(local_tmp_s0, stmts)?; - let stmt_to_overwrite = - nop_stmts.iter().find(|stmt_idx| live_idx < **stmt_idx && **stmt_idx < dead_idx); - - let mut tmp_assigned_vars = BitSet::new_empty(locals_count); - for (l, r) in &tmp_assigns { - tmp_assigned_vars.insert(*l); - tmp_assigned_vars.insert(*r); - } - - let dbg_info_to_adjust: Vec<_> = debug_info - .iter() - .enumerate() - .filter_map(|(i, var_info)| { - if let VarDebugInfoContents::Place(p) = var_info.value { - if tmp_assigned_vars.contains(p.local) { - return Some(i); - } - } - - None - }) - .collect(); - - Some(ArmIdentityInfo { - local_temp_0: local_tmp_s0, - local_1, - vf_s0, - get_variant_field_stmt, - field_tmp_assignments: tmp_assigns, - local_tmp_s1, - local_0, - vf_s1, - set_discr_local, - set_discr_var_idx, - stmt_to_overwrite: *stmt_to_overwrite?, - source_info: discr_stmt_source_info, - storage_stmts, - stmts_to_remove: nop_stmts, - dbg_info_to_adjust, - dbg_projection, - }) -} - -fn optimization_applies<'tcx>( - opt_info: &ArmIdentityInfo<'tcx>, - local_decls: &IndexVec>, - local_uses: &IndexVec, - var_debug_info: &[VarDebugInfo<'tcx>], -) -> bool { - trace!("testing if optimization applies..."); - - // FIXME(wesleywiser): possibly relax this restriction? - if opt_info.local_0 == opt_info.local_1 { - trace!("NO: moving into ourselves"); - return false; - } else if opt_info.vf_s0 != opt_info.vf_s1 { - trace!("NO: the field-and-variant information do not match"); - return false; - } else if local_decls[opt_info.local_0].ty != local_decls[opt_info.local_1].ty { - // FIXME(Centril,oli-obk): possibly relax to same layout? - trace!("NO: source and target locals have different types"); - return false; - } else if (opt_info.local_0, opt_info.vf_s0.var_idx) - != (opt_info.set_discr_local, opt_info.set_discr_var_idx) - { - trace!("NO: the discriminants do not match"); - return false; - } - - // Verify the assignment chain consists of the form b = a; c = b; d = c; etc... - if opt_info.field_tmp_assignments.is_empty() { - trace!("NO: no assignments found"); - return false; - } - let mut last_assigned_to = opt_info.field_tmp_assignments[0].1; - let source_local = last_assigned_to; - for (l, r) in &opt_info.field_tmp_assignments { - if *r != last_assigned_to { - trace!("NO: found unexpected assignment {:?} = {:?}", l, r); - return false; - } - - last_assigned_to = *l; - } - - // Check that the first and last used locals are only used twice - // since they are of the form: - // - // ``` - // _first = ((_x as Variant).n: ty); - // _n = _first; - // ... - // ((_y as Variant).n: ty) = _n; - // discriminant(_y) = z; - // ``` - for (l, r) in &opt_info.field_tmp_assignments { - if local_uses[*l] != 2 { - warn!("NO: FAILED assignment chain local {:?} was used more than twice", l); - return false; - } else if local_uses[*r] != 2 { - warn!("NO: FAILED assignment chain local {:?} was used more than twice", r); - return false; - } - } - - // Check that debug info only points to full Locals and not projections. - for dbg_idx in &opt_info.dbg_info_to_adjust { - let dbg_info = &var_debug_info[*dbg_idx]; - if let VarDebugInfoContents::Place(p) = dbg_info.value { - if !p.projection.is_empty() { - trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, p); - return false; - } - } - } - - if source_local != opt_info.local_temp_0 { - trace!( - "NO: start of assignment chain does not match enum variant temp: {:?} != {:?}", - source_local, - opt_info.local_temp_0 - ); - return false; - } else if last_assigned_to != opt_info.local_tmp_s1 { - trace!( - "NO: end of assignment chain does not match written enum temp: {:?} != {:?}", - last_assigned_to, - opt_info.local_tmp_s1 - ); - return false; - } - - trace!("SUCCESS: optimization applies!"); - true -} - -impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - // FIXME(77359): This optimization can result in unsoundness. - if !tcx.sess.opts.unstable_opts.unsound_mir_opts { - return; - } - - let source = body.source; - trace!("running SimplifyArmIdentity on {:?}", source); - - let local_uses = LocalUseCounter::get_local_uses(body); - for bb in body.basic_blocks.as_mut() { - if let Some(opt_info) = - get_arm_identity_info(&bb.statements, body.local_decls.len(), &body.var_debug_info) - { - trace!("got opt_info = {:#?}", opt_info); - if !optimization_applies( - &opt_info, - &body.local_decls, - &local_uses, - &body.var_debug_info, - ) { - debug!("optimization skipped for {:?}", source); - continue; - } - - // Also remove unused Storage{Live,Dead} statements which correspond - // to temps used previously. - for (live_idx, dead_idx, local) in &opt_info.storage_stmts { - // The temporary that we've read the variant field into is scoped to this block, - // so we can remove the assignment. - if *local == opt_info.local_temp_0 { - bb.statements[opt_info.get_variant_field_stmt].make_nop(); - } - - for (left, right) in &opt_info.field_tmp_assignments { - if local == left || local == right { - bb.statements[*live_idx].make_nop(); - bb.statements[*dead_idx].make_nop(); - } - } - } - - // Right shape; transform - for stmt_idx in opt_info.stmts_to_remove { - bb.statements[stmt_idx].make_nop(); - } - - let stmt = &mut bb.statements[opt_info.stmt_to_overwrite]; - stmt.source_info = opt_info.source_info; - stmt.kind = StatementKind::Assign(Box::new(( - opt_info.local_0.into(), - Rvalue::Use(Operand::Move(opt_info.local_1.into())), - ))); - - bb.statements.retain(|stmt| stmt.kind != StatementKind::Nop); - - // Fix the debug info to point to the right local - for dbg_index in opt_info.dbg_info_to_adjust { - let dbg_info = &mut body.var_debug_info[dbg_index]; - assert!( - matches!(dbg_info.value, VarDebugInfoContents::Place(_)), - "value was not a Place" - ); - if let VarDebugInfoContents::Place(p) = &mut dbg_info.value { - assert!(p.projection.is_empty()); - p.local = opt_info.local_0; - p.projection = opt_info.dbg_projection; - } - } - - trace!("block is now {:?}", bb.statements); - } - } - } -} - -struct LocalUseCounter { - local_uses: IndexVec, -} - -impl LocalUseCounter { - fn get_local_uses(body: &Body<'_>) -> IndexVec { - let mut counter = LocalUseCounter { local_uses: IndexVec::from_elem(0, &body.local_decls) }; - counter.visit_body(body); - counter.local_uses - } -} - -impl Visitor<'_> for LocalUseCounter { - fn visit_local(&mut self, local: Local, context: PlaceContext, _location: Location) { - if context.is_storage_marker() - || context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) - { - return; - } - - self.local_uses[local] += 1; - } -} - -/// Match on: -/// ```ignore (MIR) -/// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); -/// ``` -fn match_get_variant_field<'tcx>( - stmt: &Statement<'tcx>, -) -> Option<(Local, Local, VarField<'tcx>, &'tcx List>)> { - match &stmt.kind { - StatementKind::Assign(box ( - place_into, - Rvalue::Use(Operand::Copy(pf) | Operand::Move(pf)), - )) => { - let local_into = place_into.as_local()?; - let (local_from, vf) = match_variant_field_place(*pf)?; - Some((local_into, local_from, vf, pf.projection)) - } - _ => None, - } -} - -/// Match on: -/// ```ignore (MIR) -/// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO; -/// ``` -fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { - match &stmt.kind { - StatementKind::Assign(box (place_from, Rvalue::Use(Operand::Move(place_into)))) => { - let local_into = place_into.as_local()?; - let (local_from, vf) = match_variant_field_place(*place_from)?; - Some((local_into, local_from, vf)) - } - _ => None, - } -} - -/// Match on: -/// ```ignore (MIR) -/// discriminant(_LOCAL_TO_SET) = VAR_IDX; -/// ``` -fn match_set_discr(stmt: &Statement<'_>) -> Option<(Local, VariantIdx)> { - match &stmt.kind { - StatementKind::SetDiscriminant { place, variant_index } => { - Some((place.as_local()?, *variant_index)) - } - _ => None, - } -} - -#[derive(PartialEq, Debug)] -struct VarField<'tcx> { - field: Field, - field_ty: Ty<'tcx>, - var_idx: VariantIdx, -} - -/// Match on `((_LOCAL as Variant).FIELD: TY)`. -fn match_variant_field_place(place: Place<'_>) -> Option<(Local, VarField<'_>)> { - match place.as_ref() { - PlaceRef { - local, - projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)], - } => Some((local, VarField { field, field_ty: ty, var_idx })), - _ => None, - } -} - -/// Simplifies `SwitchInt(_) -> [targets]`, -/// where all the `targets` have the same form, -/// into `goto -> target_first`. -pub struct SimplifyBranchSame; - -impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - // This optimization is disabled by default for now due to - // soundness concerns; see issue #89485 and PR #89489. - if !tcx.sess.opts.unstable_opts.unsound_mir_opts { - return; - } - - trace!("Running SimplifyBranchSame on {:?}", body.source); - let finder = SimplifyBranchSameOptimizationFinder { body, tcx }; - let opts = finder.find(); - - let did_remove_blocks = opts.len() > 0; - for opt in opts.iter() { - trace!("SUCCESS: Applying optimization {:?}", opt); - // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`. - body.basic_blocks_mut()[opt.bb_to_opt_terminator].terminator_mut().kind = - TerminatorKind::Goto { target: opt.bb_to_goto }; - } - - if did_remove_blocks { - // We have dead blocks now, so remove those. - simplify::remove_dead_blocks(tcx, body); - } - } -} - -#[derive(Debug)] -struct SimplifyBranchSameOptimization { - /// All basic blocks are equal so go to this one - bb_to_goto: BasicBlock, - /// Basic block where the terminator can be simplified to a goto - bb_to_opt_terminator: BasicBlock, -} - -struct SwitchTargetAndValue { - target: BasicBlock, - // None in case of the `otherwise` case - value: Option, -} - -struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> { - body: &'a Body<'tcx>, - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> { - fn find(&self) -> Vec { - self.body - .basic_blocks - .iter_enumerated() - .filter_map(|(bb_idx, bb)| { - let (discr_switched_on, targets_and_values) = match &bb.terminator().kind { - TerminatorKind::SwitchInt { targets, discr, .. } => { - let targets_and_values: Vec<_> = targets.iter() - .map(|(val, target)| SwitchTargetAndValue { target, value: Some(val) }) - .chain(once(SwitchTargetAndValue { target: targets.otherwise(), value: None })) - .collect(); - (discr, targets_and_values) - }, - _ => return None, - }; - - // find the adt that has its discriminant read - // assuming this must be the last statement of the block - let adt_matched_on = match &bb.statements.last()?.kind { - StatementKind::Assign(box (place, rhs)) - if Some(*place) == discr_switched_on.place() => - { - match rhs { - Rvalue::Discriminant(adt_place) if adt_place.ty(self.body, self.tcx).ty.is_enum() => adt_place, - _ => { - trace!("NO: expected a discriminant read of an enum instead of: {:?}", rhs); - return None; - } - } - } - other => { - trace!("NO: expected an assignment of a discriminant read to a place. Found: {:?}", other); - return None - }, - }; - - let mut iter_bbs_reachable = targets_and_values - .iter() - .map(|target_and_value| (target_and_value, &self.body.basic_blocks[target_and_value.target])) - .filter(|(_, bb)| { - // Reaching `unreachable` is UB so assume it doesn't happen. - bb.terminator().kind != TerminatorKind::Unreachable - }) - .peekable(); - - let bb_first = iter_bbs_reachable.peek().map_or(&targets_and_values[0], |(idx, _)| *idx); - let mut all_successors_equivalent = StatementEquality::TrivialEqual; - - // All successor basic blocks must be equal or contain statements that are pairwise considered equal. - for ((target_and_value_l,bb_l), (target_and_value_r,bb_r)) in iter_bbs_reachable.tuple_windows() { - let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup - && bb_l.terminator().kind == bb_r.terminator().kind - && bb_l.statements.len() == bb_r.statements.len(); - let statement_check = || { - bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| { - let stmt_equality = self.statement_equality(*adt_matched_on, &l, target_and_value_l, &r, target_and_value_r); - if matches!(stmt_equality, StatementEquality::NotEqual) { - // short circuit - None - } else { - Some(acc.combine(&stmt_equality)) - } - }) - .unwrap_or(StatementEquality::NotEqual) - }; - if !trivial_checks { - all_successors_equivalent = StatementEquality::NotEqual; - break; - } - all_successors_equivalent = all_successors_equivalent.combine(&statement_check()); - }; - - match all_successors_equivalent{ - StatementEquality::TrivialEqual => { - // statements are trivially equal, so just take first - trace!("Statements are trivially equal"); - Some(SimplifyBranchSameOptimization { - bb_to_goto: bb_first.target, - bb_to_opt_terminator: bb_idx, - }) - } - StatementEquality::ConsideredEqual(bb_to_choose) => { - trace!("Statements are considered equal"); - Some(SimplifyBranchSameOptimization { - bb_to_goto: bb_to_choose, - bb_to_opt_terminator: bb_idx, - }) - } - StatementEquality::NotEqual => { - trace!("NO: not all successors of basic block {:?} were equivalent", bb_idx); - None - } - } - }) - .collect() - } - - /// Tests if two statements can be considered equal - /// - /// Statements can be trivially equal if the kinds match. - /// But they can also be considered equal in the following case A: - /// ```ignore (MIR) - /// discriminant(_0) = 0; // bb1 - /// _0 = move _1; // bb2 - /// ``` - /// In this case the two statements are equal iff - /// - `_0` is an enum where the variant index 0 is fieldless, and - /// - bb1 was targeted by a switch where the discriminant of `_1` was switched on - fn statement_equality( - &self, - adt_matched_on: Place<'tcx>, - x: &Statement<'tcx>, - x_target_and_value: &SwitchTargetAndValue, - y: &Statement<'tcx>, - y_target_and_value: &SwitchTargetAndValue, - ) -> StatementEquality { - let helper = |rhs: &Rvalue<'tcx>, - place: &Place<'tcx>, - variant_index: VariantIdx, - switch_value: u128, - side_to_choose| { - let place_type = place.ty(self.body, self.tcx).ty; - let adt = match *place_type.kind() { - ty::Adt(adt, _) if adt.is_enum() => adt, - _ => return StatementEquality::NotEqual, - }; - // We need to make sure that the switch value that targets the bb with - // SetDiscriminant is the same as the variant discriminant. - let variant_discr = adt.discriminant_for_variant(self.tcx, variant_index).val; - if variant_discr != switch_value { - trace!( - "NO: variant discriminant {} does not equal switch value {}", - variant_discr, - switch_value - ); - return StatementEquality::NotEqual; - } - let variant_is_fieldless = adt.variant(variant_index).fields.is_empty(); - if !variant_is_fieldless { - trace!("NO: variant {:?} was not fieldless", variant_index); - return StatementEquality::NotEqual; - } - - match rhs { - Rvalue::Use(operand) if operand.place() == Some(adt_matched_on) => { - StatementEquality::ConsideredEqual(side_to_choose) - } - _ => { - trace!( - "NO: RHS of assignment was {:?}, but expected it to match the adt being matched on in the switch, which is {:?}", - rhs, - adt_matched_on - ); - StatementEquality::NotEqual - } - } - }; - match (&x.kind, &y.kind) { - // trivial case - (x, y) if x == y => StatementEquality::TrivialEqual, - - // check for case A - ( - StatementKind::Assign(box (_, rhs)), - &StatementKind::SetDiscriminant { ref place, variant_index }, - ) if y_target_and_value.value.is_some() => { - // choose basic block of x, as that has the assign - helper( - rhs, - place, - variant_index, - y_target_and_value.value.unwrap(), - x_target_and_value.target, - ) - } - ( - &StatementKind::SetDiscriminant { ref place, variant_index }, - &StatementKind::Assign(box (_, ref rhs)), - ) if x_target_and_value.value.is_some() => { - // choose basic block of y, as that has the assign - helper( - rhs, - place, - variant_index, - x_target_and_value.value.unwrap(), - y_target_and_value.target, - ) - } - _ => { - trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y); - StatementEquality::NotEqual - } - } - } -} - -#[derive(Copy, Clone, Eq, PartialEq)] -enum StatementEquality { - /// The two statements are trivially equal; same kind - TrivialEqual, - /// The two statements are considered equal, but may be of different kinds. The BasicBlock field is the basic block to jump to when performing the branch-same optimization. - /// For example, `_0 = _1` and `discriminant(_0) = discriminant(0)` are considered equal if 0 is a fieldless variant of an enum. But we don't want to jump to the basic block with the SetDiscriminant, as that is not legal if _1 is not the 0 variant index - ConsideredEqual(BasicBlock), - /// The two statements are not equal - NotEqual, -} - -impl StatementEquality { - fn combine(&self, other: &StatementEquality) -> StatementEquality { - use StatementEquality::*; - match (self, other) { - (TrivialEqual, TrivialEqual) => TrivialEqual, - (TrivialEqual, ConsideredEqual(b)) | (ConsideredEqual(b), TrivialEqual) => { - ConsideredEqual(*b) - } - (ConsideredEqual(b1), ConsideredEqual(b2)) => { - if b1 == b2 { - ConsideredEqual(*b1) - } else { - NotEqual - } - } - (_, NotEqual) | (NotEqual, _) => NotEqual, - } - } -} diff --git a/tests/mir-opt/76803_regression.encode.SimplifyBranchSame.diff b/tests/mir-opt/76803_regression.encode.SimplifyBranchSame.diff deleted file mode 100644 index 9780332d8bf18..0000000000000 --- a/tests/mir-opt/76803_regression.encode.SimplifyBranchSame.diff +++ /dev/null @@ -1,29 +0,0 @@ -- // MIR for `encode` before SimplifyBranchSame -+ // MIR for `encode` after SimplifyBranchSame - - fn encode(_1: Type) -> Type { - debug v => _1; // in scope 0 at $DIR/76803_regression.rs:+0:15: +0:16 - let mut _0: Type; // return place in scope 0 at $DIR/76803_regression.rs:+0:27: +0:31 - let mut _2: isize; // in scope 0 at $DIR/76803_regression.rs:+2:9: +2:16 - - bb0: { - _2 = discriminant(_1); // scope 0 at $DIR/76803_regression.rs:+1:11: +1:12 - switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/76803_regression.rs:+1:5: +1:12 - } - - bb1: { - _0 = move _1; // scope 0 at $DIR/76803_regression.rs:+3:14: +3:15 - goto -> bb3; // scope 0 at $DIR/76803_regression.rs:+3:14: +3:15 - } - - bb2: { - Deinit(_0); // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27 - discriminant(_0) = 1; // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27 - goto -> bb3; // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27 - } - - bb3: { - return; // scope 0 at $DIR/76803_regression.rs:+5:2: +5:2 - } - } - diff --git a/tests/mir-opt/76803_regression.rs b/tests/mir-opt/76803_regression.rs deleted file mode 100644 index 05dc3c9784109..0000000000000 --- a/tests/mir-opt/76803_regression.rs +++ /dev/null @@ -1,19 +0,0 @@ -// compile-flags: -Z mir-opt-level=1 -// EMIT_MIR 76803_regression.encode.SimplifyBranchSame.diff - -#[derive(Debug, Eq, PartialEq)] -pub enum Type { - A, - B, -} - -pub fn encode(v: Type) -> Type { - match v { - Type::A => Type::B, - _ => v, - } -} - -fn main() { - assert_eq!(Type::B, encode(Type::A)); -} diff --git a/tests/mir-opt/issue_73223.main.SimplifyArmIdentity.diff b/tests/mir-opt/issue_73223.main.SimplifyArmIdentity.diff deleted file mode 100644 index bf3bcfdb59442..0000000000000 --- a/tests/mir-opt/issue_73223.main.SimplifyArmIdentity.diff +++ /dev/null @@ -1,156 +0,0 @@ -- // MIR for `main` before SimplifyArmIdentity -+ // MIR for `main` after SimplifyArmIdentity - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue_73223.rs:+0:11: +0:11 - let _1: i32; // in scope 0 at $DIR/issue_73223.rs:+1:9: +1:14 - let mut _2: std::option::Option; // in scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 - let mut _3: isize; // in scope 0 at $DIR/issue_73223.rs:+2:9: +2:16 - let _4: i32; // in scope 0 at $DIR/issue_73223.rs:+2:14: +2:15 - let mut _6: i32; // in scope 0 at $DIR/issue_73223.rs:+6:22: +6:27 - let mut _7: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _8: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _11: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _12: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _13: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _14: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let _16: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _17: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _18: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let _19: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _20: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let _21: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _22: std::option::Option>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _24: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _25: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - scope 1 { - debug split => _1; // in scope 1 at $DIR/issue_73223.rs:+1:9: +1:14 - let _5: std::option::Option; // in scope 1 at $DIR/issue_73223.rs:+6:9: +6:14 - scope 3 { - debug _prev => _5; // in scope 3 at $DIR/issue_73223.rs:+6:9: +6:14 - let _9: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let _10: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let mut _23: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - scope 4 { - debug left_val => _9; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - debug right_val => _10; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - let _15: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - scope 5 { - debug kind => _15; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - } - } - } - } - scope 2 { - debug v => _4; // in scope 2 at $DIR/issue_73223.rs:+2:14: +2:15 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue_73223.rs:+1:9: +1:14 - StorageLive(_2); // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 - Deinit(_2); // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 - ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 - discriminant(_2) = 1; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 - _3 = const 1_isize; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 - goto -> bb3; // scope 0 at $DIR/issue_73223.rs:+1:17: +1:30 - } - - bb1: { - StorageDead(_2); // scope 0 at $DIR/issue_73223.rs:+4:6: +4:7 - StorageDead(_1); // scope 0 at $DIR/issue_73223.rs:+8:1: +8:2 - return; // scope 0 at $DIR/issue_73223.rs:+8:2: +8:2 - } - - bb2: { - unreachable; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30 - } - - bb3: { - StorageLive(_4); // scope 0 at $DIR/issue_73223.rs:+2:14: +2:15 - _4 = ((_2 as Some).0: i32); // scope 0 at $DIR/issue_73223.rs:+2:14: +2:15 - _1 = _4; // scope 2 at $DIR/issue_73223.rs:+2:20: +2:21 - StorageDead(_4); // scope 0 at $DIR/issue_73223.rs:+2:20: +2:21 - StorageDead(_2); // scope 0 at $DIR/issue_73223.rs:+4:6: +4:7 - StorageLive(_5); // scope 1 at $DIR/issue_73223.rs:+6:9: +6:14 - StorageLive(_6); // scope 1 at $DIR/issue_73223.rs:+6:22: +6:27 - _6 = _1; // scope 1 at $DIR/issue_73223.rs:+6:22: +6:27 - Deinit(_5); // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28 - ((_5 as Some).0: i32) = move _6; // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28 - discriminant(_5) = 1; // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28 - StorageDead(_6); // scope 1 at $DIR/issue_73223.rs:+6:27: +6:28 - StorageLive(_24); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_25); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _7 = &_1; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _23 = const _; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL - // + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) } - _8 = _23; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - Deinit(_24); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - Deinit(_25); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _24 = move _7; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _25 = move _8; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _9 = _24; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _10 = _25; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_13); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _13 = (*_9); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_14); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _14 = const 1_i32; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _12 = Eq(move _13, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_14); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_13); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _11 = Not(move _12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - switchInt(move _11) -> [0: bb5, otherwise: bb4]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - } - - bb4: { - StorageLive(_15); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - Deinit(_15); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - discriminant(_15) = 0; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_16); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_17); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _17 = const core::panicking::AssertKind::Eq; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL - // + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) } - StorageLive(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _19 = _9; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _18 = _19; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_20); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_21); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _21 = _10; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _20 = _21; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageLive(_22); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - Deinit(_22); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - discriminant(_22) = 0; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _16 = core::panicking::assert_failed::(const core::panicking::AssertKind::Eq, move _18, move _20, move _22); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'a, 'b, 'c> fn(core::panicking::AssertKind, &'a i32, &'b i32, Option>) -> ! {core::panicking::assert_failed::}, val: Value() } - // mir::Constant - // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL - // + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) } - } - - bb5: { - StorageDead(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_24); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_25); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - StorageDead(_5); // scope 1 at $DIR/issue_73223.rs:+8:1: +8:2 - StorageDead(_1); // scope 0 at $DIR/issue_73223.rs:+8:1: +8:2 - return; // scope 0 at $DIR/issue_73223.rs:+8:2: +8:2 - } - } - diff --git a/tests/mir-opt/issue_73223.rs b/tests/mir-opt/issue_73223.rs deleted file mode 100644 index be114cab719c0..0000000000000 --- a/tests/mir-opt/issue_73223.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - let split = match Some(1) { - Some(v) => v, - None => return, - }; - - let _prev = Some(split); - assert_eq!(split, 1); -} - - -// EMIT_MIR issue_73223.main.SimplifyArmIdentity.diff From 11a94f242d48739a557b6e09cbd8d3cde2fcdedf Mon Sep 17 00:00:00 2001 From: KaDiWa Date: Tue, 24 Jan 2023 16:46:45 +0100 Subject: [PATCH 10/16] rustdoc: prevent scroll bar on source viewer --- src/librustdoc/html/static/css/rustdoc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 424bbb0ec42be..bf83ff2044e69 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -533,7 +533,7 @@ ul.block, .block li { .rustdoc .example-wrap > pre { margin: 0; flex-grow: 1; - overflow-x: auto; + overflow: auto hidden; } .rustdoc .example-wrap > pre.example-line-numbers, From 430dab0b424abdf68d9071232a654874771570bc Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 24 Jan 2023 23:24:25 +0000 Subject: [PATCH 11/16] implement builtin candidate --- .../src/solve/assembly.rs | 7 ++ .../src/solve/project_goals.rs | 92 +++++++++++++++++++ .../src/solve/trait_goals.rs | 7 ++ tests/ui/traits/new-solver/pointee.rs | 23 +++++ 4 files changed, 129 insertions(+) create mode 100644 tests/ui/traits/new-solver/pointee.rs diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index cdb72d49834f0..0b642fcba2812 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -133,6 +133,11 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + + fn consider_builtin_pointee_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -259,6 +264,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_fn_trait_candidates(self, goal, kind) } else if lang_items.tuple_trait() == Some(trait_def_id) { G::consider_builtin_tuple_candidate(self, goal) + } else if lang_items.pointee_trait() == Some(trait_def_id) { + G::consider_builtin_pointee_candidate(self, goal) } else { Err(NoSolution) }; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 32e15f03998b3..914a1d6a66a67 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -7,6 +7,7 @@ use super::{Certainty, EvalCtxt, Goal, QueryResult}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; @@ -391,6 +392,97 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) -> QueryResult<'tcx> { bug!("`Tuple` does not have an associated type: {:?}", goal); } + + fn consider_builtin_pointee_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); + ecx.infcx.probe(|_| { + let metadata_ty = match goal.predicate.self_ty().kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Never + | ty::Foreign(..) => tcx.types.unit, + + ty::Error(e) => tcx.ty_error_with_guaranteed(*e), + + ty::Str | ty::Slice(_) => tcx.types.usize, + + ty::Dynamic(_, _, _) => { + let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None); + tcx.bound_type_of(dyn_metadata) + .subst(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())]) + } + + ty::Infer(ty::TyVar(..)) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + // FIXME(erica_solver, ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints. + let sized_predicate = ty::Binder::dummy(tcx.at(DUMMY_SP).mk_trait_ref( + LangItem::Sized, + [ty::GenericArg::from(goal.predicate.self_ty())], + )) + .without_const(); + + let mut nested_goals = ecx.infcx.eq( + goal.param_env, + goal.predicate.term.ty().unwrap(), + tcx.types.unit, + )?; + nested_goals.push(goal.with(tcx, sized_predicate)); + + return ecx.evaluate_all_and_make_canonical_response(nested_goals); + } + + ty::Adt(def, substs) if def.is_struct() => { + match def.non_enum_variant().fields.last() { + None => tcx.types.unit, + Some(field_def) => { + let self_ty = field_def.ty(tcx, substs); + let new_goal = goal.with( + tcx, + ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), + ); + return ecx.evaluate_all_and_make_canonical_response(vec![new_goal]); + } + } + } + ty::Adt(_, _) => tcx.types.unit, + + ty::Tuple(elements) => match elements.last() { + None => tcx.types.unit, + Some(&self_ty) => { + let new_goal = goal.with( + tcx, + ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), + ); + return ecx.evaluate_all_and_make_canonical_response(vec![new_goal]); + } + }, + + ty::Infer(ty::FreshTy(..) | ty::FreshIntTy(..) | ty::FreshFloatTy(..)) + | ty::Bound(..) => bug!( + "unexpected self ty `{:?}` when normalizing `::Metadata`", + goal.predicate.self_ty() + ), + }; + + let nested_goals = + ecx.infcx.eq(goal.param_env, goal.predicate.term.ty().unwrap(), metadata_ty)?; + ecx.evaluate_all_and_make_canonical_response(nested_goals) + }) + } } /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 4b6d673c999c9..67bd249566546 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -185,6 +185,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) } } + + fn consider_builtin_pointee_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + _goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + ecx.make_canonical_response(Certainty::Yes) + } } impl<'tcx> EvalCtxt<'_, 'tcx> { diff --git a/tests/ui/traits/new-solver/pointee.rs b/tests/ui/traits/new-solver/pointee.rs new file mode 100644 index 0000000000000..fa6ee2e2daf64 --- /dev/null +++ b/tests/ui/traits/new-solver/pointee.rs @@ -0,0 +1,23 @@ +// compile-flags: -Ztrait-solver=next +// check-pass +#![feature(ptr_metadata)] + +use std::ptr::{DynMetadata, Pointee}; + +trait Trait {} +struct MyDst(T); + +fn works() { + let _: ::Metadata = (); + let _: <[T] as Pointee>::Metadata = 1_usize; + let _: ::Metadata = 1_usize; + let _: as Pointee>::Metadata = give::>>(); + let _: as Pointee>::Metadata = (); + let _: <((((([u8],),),),),) as Pointee>::Metadata = 1_usize; +} + +fn give() -> U { + loop {} +} + +fn main() {} From 2f924b0e3cdebc341c88056af8fa0bacaed5acde Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 24 Jan 2023 23:29:02 +0000 Subject: [PATCH 12/16] sorry erica --- compiler/rustc_trait_selection/src/solve/project_goals.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 914a1d6a66a67..d33dfba6247d8 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -428,7 +428,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { } ty::Infer(ty::TyVar(..)) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - // FIXME(erica_solver, ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints. + // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints. let sized_predicate = ty::Binder::dummy(tcx.at(DUMMY_SP).mk_trait_ref( LangItem::Sized, [ty::GenericArg::from(goal.predicate.self_ty())], From a418e39b75a8b3628cfea0b233de2f8985331f6d Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 24 Jan 2023 23:32:47 +0000 Subject: [PATCH 13/16] no without_constness --- compiler/rustc_trait_selection/src/solve/project_goals.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index d33dfba6247d8..e5072d8e2d152 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -432,8 +432,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { let sized_predicate = ty::Binder::dummy(tcx.at(DUMMY_SP).mk_trait_ref( LangItem::Sized, [ty::GenericArg::from(goal.predicate.self_ty())], - )) - .without_const(); + )); let mut nested_goals = ecx.infcx.eq( goal.param_env, From 273254e3f85aff5d5097266c272ef2272ea1d9ed Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Jan 2023 15:39:59 -0700 Subject: [PATCH 14/16] rustdoc: use smarter encoding for playground URL The old way would compress okay with DEFLATE, but this version makes uncompressed docs smaller, which matters for memory usage and stuff like `cargo doc`. Try it out: In local testing, this change shrinks sample pages by anywhere between 5.0% and 0.044% $ du -b after.dir/std/vec/struct.Vec.html before.dir/std/vec/struct.Vec.html 753023 after.dir/std/vec/struct.Vec.html 781842 before.dir/std/vec/struct.Vec.html 100*((753023-781842)/781842)=-3.7 $ du -b after.dir/std/num/struct.Wrapping.html before.dir/std/num/struct.Wrapping.html 3189989 after.dir/std/num/struct.Wrapping.html 3204351 before.dir/std/num/struct.Wrapping.html 100*((3189989-3204351)/3204351)=-0.044 $ du -b after.dir/std/keyword.match.html before.dir/std/keyword.match.html 8067 after.dir/std/keyword.match.html 8495 before.dir/std/keyword.match.html 100*((8067-8495)/8495)=-5.0 Gzipped tarball sizes seem shrunk, but not by much. du -s before.tar.gz after.tar.gz 69600 before.tar.gz 69492 after.tar.gz 100*((69492-69600)/69600)=-0.16 --- src/librustdoc/html/markdown.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 4ff67fe1551dd..16e048ffa2568 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -296,7 +296,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; // These characters don't need to be escaped in a URI. - // FIXME: use a library function for percent encoding. + // See https://url.spec.whatwg.org/#query-percent-encode-set + // and https://url.spec.whatwg.org/#urlencoded-parsing fn dont_escape(c: u8) -> bool { (b'a' <= c && c <= b'z') || (b'A' <= c && c <= b'Z') @@ -304,17 +305,41 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { || c == b'-' || c == b'_' || c == b'.' + || c == b',' || c == b'~' || c == b'!' || c == b'\'' || c == b'(' || c == b')' + || c == b'[' + || c == b']' + || c == b'{' + || c == b'}' || c == b'*' + || c == b'/' + || c == b'|' + || c == b'^' + || c == b'\\' + || c == b';' + || c == b':' + || c == b'?' + || c == b'<' + || c == b'>' + // As described in urlencoded-parsing, the + // first `=` is the one that separates key from + // value. Following `=`s are part of the value. + || c == b'=' } let mut test_escaped = String::new(); for b in test.bytes() { if dont_escape(b) { test_escaped.push(char::from(b)); + } else if b == b' ' { + // URL queries are decoded with + replaced with SP + test_escaped.push('+'); + } else if b == b'%' { + test_escaped.push('%'); + test_escaped.push('%'); } else { write!(test_escaped, "%{:02X}", b).unwrap(); } From e500c442c110b8e6b10962ef755b903d048f28fa Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Jan 2023 17:39:02 -0700 Subject: [PATCH 15/16] rustdoc: update tests for smarter playground url encoding --- tests/rustdoc/playground-arg.rs | 2 +- tests/rustdoc/playground.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/rustdoc/playground-arg.rs b/tests/rustdoc/playground-arg.rs index 69c8962653931..5a19a926fd212 100644 --- a/tests/rustdoc/playground-arg.rs +++ b/tests/rustdoc/playground-arg.rs @@ -10,4 +10,4 @@ pub fn dummy() {} // ensure that `extern crate foo;` was inserted into code snips automatically: -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20r%23foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23![allow(unused)]%0Aextern+crate+r%23foo;%0Afn+main()+{%0Ause+foo::dummy;%0Adummy();%0A}&edition=2015"]' "Run" diff --git a/tests/rustdoc/playground.rs b/tests/rustdoc/playground.rs index 877ea1cfba15a..21a9acea4479c 100644 --- a/tests/rustdoc/playground.rs +++ b/tests/rustdoc/playground.rs @@ -22,6 +22,6 @@ //! } //! ``` -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D&edition=2015"]' "Run" -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0Aprintln!(%22Hello%2C%20world!%22)%3B%0A%7D&edition=2015"]' "Run" -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D&version=nightly&edition=2015"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23![allow(unused)]%0Afn+main()+{%0Aprintln!(%22Hello,+world!%22);%0A}&edition=2015"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23![allow(unused)]%0Afn+main()+{%0A++++println!(%22Hello,+world!%22);%0A}&edition=2015"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23![allow(unused)]%0A%23![feature(something)]%0A%0Afn+main()+{%0A++++println!(%22Hello,+world!%22);%0A}&version=nightly&edition=2015"]' "Run" From 0db0419edf8492457910f610b8cd98cef92f70ca Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 24 Jan 2023 18:39:56 -0700 Subject: [PATCH 16/16] rustdoc: add comment for why playground URLs have unencoded angle brackets --- src/librustdoc/html/markdown.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 16e048ffa2568..bc0fe59fb0af2 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -323,6 +323,15 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { || c == b';' || c == b':' || c == b'?' + // While these would be encoded if we used the URL + // serializer algorithm, they don't actually need to be + // encoded for the [query mode parser] to give the correct + // result. The HTML [attribute parser] does the right + // thing with them as well, as long as the attribute is + // quoted (which it is). + // + // [query mode parser]: https://url.spec.whatwg.org/#query-state + // [attribute parser]: https://html.spec.whatwg.org/#attribute-value-(double-quoted)-state || c == b'<' || c == b'>' // As described in urlencoded-parsing, the