diff --git a/.github/ISSUE_TEMPLATE/library_tracking_issue.md b/.github/ISSUE_TEMPLATE/library_tracking_issue.md new file mode 100644 index 000000000000..3e42594c8280 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/library_tracking_issue.md @@ -0,0 +1,63 @@ +--- +name: Library Tracking Issue +about: A tracking issue for an unstable library feature. +title: Tracking Issue for XXX +labels: C-tracking-issue, T-libs +--- + + +Feature gate: `#![feature(...)]` + +This is a tracking issue for ... + + + +### Public API + + + +```rust +... +``` + +### Steps / History + + + +- [ ] Implementation: ... +- [ ] Stabilization PR + +### Unresolved Questions + + + +- None yet. diff --git a/Cargo.lock b/Cargo.lock index 5c096e9f80c3..4676e4127e83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -355,6 +355,35 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "cargo-credential" +version = "0.1.0" + +[[package]] +name = "cargo-credential-1password" +version = "0.1.0" +dependencies = [ + "cargo-credential", + "serde", + "serde_json", +] + +[[package]] +name = "cargo-credential-macos-keychain" +version = "0.1.0" +dependencies = [ + "cargo-credential", + "security-framework", +] + +[[package]] +name = "cargo-credential-wincred" +version = "0.1.0" +dependencies = [ + "cargo-credential", + "winapi 0.3.9", +] + [[package]] name = "cargo-miri" version = "0.1.0" @@ -725,9 +754,6 @@ checksum = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6" [[package]] name = "coverage_test_macros" version = "0.0.0" -dependencies = [ - "proc-macro2", -] [[package]] name = "cpuid-bool" @@ -869,9 +895,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.31" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9447ad28eee2a5cfb031c329d46bef77487244fff6a724b378885b8691a35f78" +checksum = "e268162af1a5fe89917ae25ba3b0a77c8da752bdc58e7dbb4f15b91fbd33756e" dependencies = [ "curl-sys", "libc", @@ -884,9 +910,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.34+curl-7.71.1" +version = "0.4.39+curl-7.74.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad4eff0be6985b7e709f64b5a541f700e9ad1407190a29f4884319eb663ed1d6" +checksum = "07a8ce861e7b68a0b394e814d7ee9f1b2750ff8bd10372c6ad3bacc10e86f874" dependencies = [ "cc", "libc", @@ -1304,9 +1330,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.13.12" +version = "0.13.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6f1a0238d7f8f8fd5ee642f4ebac4dbc03e03d1f78fbe7a3ede35dcf7e2224" +checksum = "186dd99cc77576e58344ad614fa9bb27bad9d048f85de3ca850c1f4e8b048260" dependencies = [ "bitflags", "libc", @@ -1319,9 +1345,9 @@ dependencies = [ [[package]] name = "git2-curl" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502d532a2d06184beb3bc869d4d90236e60934e3382c921b203fa3c33e212bd7" +checksum = "883539cb0ea94bab3f8371a98cd8e937bbe9ee7c044499184aa4c17deb643a50" dependencies = [ "curl", "git2", @@ -1348,6 +1374,15 @@ dependencies = [ "regex", ] +[[package]] +name = "gsgdt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d876ce7262df96262a2a19531da6ff9a86048224d49580a585fc5c04617825" +dependencies = [ + "serde", +] + [[package]] name = "handlebars" version = "3.4.0" @@ -1724,9 +1759,9 @@ dependencies = [ [[package]] name = "libgit2-sys" -version = "0.12.14+1.1.0" +version = "0.12.16+1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f25af58e6495f7caf2919d08f212de550cfa3ed2f5e744988938ea292b9f549" +checksum = "9f91b2f931ee975a98155195be8cd82d02e8e029d7d793d2bac1b8181ac97020" dependencies = [ "cc", "libc", @@ -3400,6 +3435,7 @@ dependencies = [ "byteorder", "crossbeam-utils 0.7.2", "libc", + "libz-sys", "proc-macro2", "quote", "serde", @@ -3932,6 +3968,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", + "rustc_type_ir", "smallvec 1.4.2", "tracing", ] @@ -3942,6 +3979,7 @@ version = "0.0.0" dependencies = [ "coverage_test_macros", "either", + "gsgdt", "itertools 0.9.0", "polonius-engine", "regex", @@ -4266,6 +4304,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "rustc_type_ir" +version = "0.0.0" +dependencies = [ + "bitflags", + "rustc_data_structures", + "rustc_index", + "rustc_serialize", +] + [[package]] name = "rustc_typeck" version = "0.0.0" @@ -4349,7 +4397,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.29" +version = "1.4.30" dependencies = [ "annotate-snippets 0.6.1", "anyhow", @@ -4424,6 +4472,29 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "security-framework" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.9.0" @@ -4471,18 +4542,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.115" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.115" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" dependencies = [ "proc-macro2", "quote", @@ -5272,7 +5343,7 @@ dependencies = [ "chrono", "lazy_static", "matchers", - "parking_lot 0.11.0", + "parking_lot 0.9.0", "regex", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index e1a36d880867..204c92045b11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ members = [ "src/tools/rust-installer", "src/tools/rust-demangler", "src/tools/cargo", + "src/tools/cargo/crates/credential/cargo-credential-1password", + "src/tools/cargo/crates/credential/cargo-credential-macos-keychain", + "src/tools/cargo/crates/credential/cargo-credential-wincred", "src/tools/rustdoc", "src/tools/rls", "src/tools/rustfmt", diff --git a/RELEASES.md b/RELEASES.md index 9fd796fd775b..8f04980e3903 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,131 @@ +Version 1.49.0 (2020-12-31) +============================ + +Language +----------------------- + +- [Unions can now implement `Drop`, and you can now have a field in a union + with `ManuallyDrop`.][77547] +- [You can now cast uninhabited enums to integers.][76199] +- [You can now bind by reference and by move in patterns.][76119] This + allows you to selectively borrow individual components of a type. E.g. + ```rust + #[derive(Debug)] + struct Person { + name: String, + age: u8, + } + + let person = Person { + name: String::from("Alice"), + age: 20, + }; + + // `name` is moved out of person, but `age` is referenced. + let Person { name, ref age } = person; + println!("{} {}", name, age); + ``` + +Compiler +----------------------- + +- [Added tier 1\* support for `aarch64-unknown-linux-gnu`.][78228] +- [Added tier 2 support for `aarch64-apple-darwin`.][75991] +- [Added tier 2 support for `aarch64-pc-windows-msvc`.][75914] +- [Added tier 3 support for `mipsel-unknown-none`.][78676] +- [Raised the minimum supported LLVM version to LLVM 9.][78848] +- [Output from threads spawned in tests is now captured.][78227] +- [Change os and vendor values to "none" and "unknown" for some targets][78951] + +\* Refer to Rust's [platform support page][forge-platform-support] for more +information on Rust's tiered platform support. + +Libraries +----------------------- + +- [`RangeInclusive` now checks for exhaustion when calling `contains` and indexing.][78109] +- [`ToString::to_string` now no longer shrinks the internal buffer in the default implementation.][77997] +- [`ops::{Index, IndexMut}` are now implemented for fixed sized arrays of any length.][74989] + +Stabilized APIs +--------------- + +- [`slice::select_nth_unstable`] +- [`slice::select_nth_unstable_by`] +- [`slice::select_nth_unstable_by_key`] + +The following previously stable methods are now `const`. + +- [`Poll::is_ready`] +- [`Poll::is_pending`] + +Cargo +----------------------- +- [Building a crate with `cargo-package` should now be independently reproducible.][cargo/8864] +- [`cargo-tree` now marks proc-macro crates.][cargo/8765] +- [Added `CARGO_PRIMARY_PACKAGE` build-time environment variable.][cargo/8758] This + variable will be set if the crate being built is one the user selected to build, either + with `-p` or through defaults. +- [You can now use glob patterns when specifying packages & targets.][cargo/8752] + + +Compatibility Notes +------------------- + +- [Demoted `i686-unknown-freebsd` from host tier 2 to target tier 2 support.][78746] +- [Macros that end with a semi-colon are now treated as statements even if they expand to nothing.][78376] +- [Rustc will now check for the validity of some built-in attributes on enum variants.][77015] + Previously such invalid or unused attributes could be ignored. +- Leading whitespace is stripped more uniformly in documentation comments, which may change behavior. You + read [this post about the changes][rustdoc-ws-post] for more details. +- [Trait bounds are no longer inferred for associated types.][79904] + +Internal Only +------------- +These changes provide no direct user facing benefits, but represent significant +improvements to the internals and overall performance of rustc and +related tools. + +- [rustc's internal crates are now compiled using the `initial-exec` Thread + Local Storage model.][78201] +- [Calculate visibilities once in resolve.][78077] +- [Added `system` to the `llvm-libunwind` bootstrap config option.][77703] +- [Added `--color` for configuring terminal color support to bootstrap.][79004] + + +[75991]: https://github.com/rust-lang/rust/pull/75991 +[78951]: https://github.com/rust-lang/rust/pull/78951 +[78848]: https://github.com/rust-lang/rust/pull/78848 +[78746]: https://github.com/rust-lang/rust/pull/78746 +[78376]: https://github.com/rust-lang/rust/pull/78376 +[78228]: https://github.com/rust-lang/rust/pull/78228 +[78227]: https://github.com/rust-lang/rust/pull/78227 +[78201]: https://github.com/rust-lang/rust/pull/78201 +[78109]: https://github.com/rust-lang/rust/pull/78109 +[78077]: https://github.com/rust-lang/rust/pull/78077 +[77997]: https://github.com/rust-lang/rust/pull/77997 +[77703]: https://github.com/rust-lang/rust/pull/77703 +[77547]: https://github.com/rust-lang/rust/pull/77547 +[77015]: https://github.com/rust-lang/rust/pull/77015 +[76199]: https://github.com/rust-lang/rust/pull/76199 +[76119]: https://github.com/rust-lang/rust/pull/76119 +[75914]: https://github.com/rust-lang/rust/pull/75914 +[74989]: https://github.com/rust-lang/rust/pull/74989 +[79004]: https://github.com/rust-lang/rust/pull/79004 +[78676]: https://github.com/rust-lang/rust/pull/78676 +[79904]: https://github.com/rust-lang/rust/issues/79904 +[cargo/8864]: https://github.com/rust-lang/cargo/pull/8864 +[cargo/8765]: https://github.com/rust-lang/cargo/pull/8765 +[cargo/8758]: https://github.com/rust-lang/cargo/pull/8758 +[cargo/8752]: https://github.com/rust-lang/cargo/pull/8752 +[`slice::select_nth_unstable`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable +[`slice::select_nth_unstable_by`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable_by +[`slice::select_nth_unstable_by_key`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable_by_key +[`hint::spin_loop`]: https://doc.rust-lang.org/stable/std/hint/fn.spin_loop.html +[`Poll::is_ready`]: https://doc.rust-lang.org/stable/std/task/enum.Poll.html#method.is_ready +[`Poll::is_pending`]: https://doc.rust-lang.org/stable/std/task/enum.Poll.html#method.is_pending +[rustdoc-ws-post]: https://blog.guillaume-gomez.fr/articles/2020-11-11+New+doc+comment+handling+in+rustdoc + Version 1.48.0 (2020-11-19) ========================== @@ -10,7 +138,7 @@ Language Compiler -------- - [Stabilised the `-C link-self-contained=` compiler flag.][76158] This tells - `rustc` whether to link its own C runtime and libraries or to rely on a external + `rustc` whether to link its own C runtime and libraries or to rely on a external linker to find them. (Supported only on `windows-gnu`, `linux-musl`, and `wasi` platforms.) - [You can now use `-C target-feature=+crt-static` on `linux-gnu` targets.][77386] Note: If you're using cargo you must explicitly pass the `--target` flag. @@ -82,7 +210,7 @@ Compatibility Notes - [Foreign exceptions are now caught by `catch_unwind` and will cause an abort.][70212] Note: This behaviour is not guaranteed and is still considered undefined behaviour, see the [`catch_unwind`] documentation for further information. - + Internal Only @@ -102,7 +230,7 @@ related tools. [76030]: https://github.com/rust-lang/rust/pull/76030/ [70212]: https://github.com/rust-lang/rust/pull/70212/ [27675]: https://github.com/rust-lang/rust/issues/27675/ -[54121]: https://github.com/rust-lang/rust/issues/54121/ +[54121]: https://github.com/rust-lang/rust/issues/54121/ [71274]: https://github.com/rust-lang/rust/pull/71274/ [77386]: https://github.com/rust-lang/rust/pull/77386/ [77153]: https://github.com/rust-lang/rust/pull/77153/ diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index f468bad635a9..a0493056b816 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -16,7 +16,7 @@ #![feature(new_uninit)] #![feature(maybe_uninit_slice)] #![feature(array_value_iter)] -#![feature(min_const_generics)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![feature(min_specialization)] #![cfg_attr(test, feature(test))] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 220bbed7e78b..cf31e566c384 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -167,10 +167,7 @@ pub enum GenericArgs { impl GenericArgs { pub fn is_angle_bracketed(&self) -> bool { - match *self { - AngleBracketed(..) => true, - _ => false, - } + matches!(self, AngleBracketed(..)) } pub fn span(&self) -> Span { @@ -629,23 +626,20 @@ impl Pat { /// Is this a `..` pattern? pub fn is_rest(&self) -> bool { - match self.kind { - PatKind::Rest => true, - _ => false, - } + matches!(self.kind, PatKind::Rest) } } -/// A single field in a struct pattern +/// A single field in a struct pattern. /// -/// Patterns like the fields of Foo `{ x, ref y, ref mut z }` -/// are treated the same as` x: x, y: ref y, z: ref mut z`, -/// except is_shorthand is true +/// Patterns like the fields of `Foo { x, ref y, ref mut z }` +/// are treated the same as `x: x, y: ref y, z: ref mut z`, +/// except when `is_shorthand` is true. #[derive(Clone, Encodable, Decodable, Debug)] pub struct FieldPat { - /// The identifier for the field + /// The identifier for the field. pub ident: Ident, - /// The pattern the field is destructured to + /// The pattern the field is destructured to. pub pat: P, pub is_shorthand: bool, pub attrs: AttrVec, @@ -852,10 +846,7 @@ impl BinOpKind { } } pub fn lazy(&self) -> bool { - match *self { - BinOpKind::And | BinOpKind::Or => true, - _ => false, - } + matches!(self, BinOpKind::And | BinOpKind::Or) } pub fn is_comparison(&self) -> bool { @@ -963,17 +954,11 @@ impl Stmt { } pub fn is_item(&self) -> bool { - match self.kind { - StmtKind::Item(_) => true, - _ => false, - } + matches!(self.kind, StmtKind::Item(_)) } pub fn is_expr(&self) -> bool { - match self.kind { - StmtKind::Expr(_) => true, - _ => false, - } + matches!(self.kind, StmtKind::Expr(_)) } } @@ -1107,15 +1092,9 @@ impl Expr { if let ExprKind::Block(ref block, _) = self.kind { match block.stmts.last().map(|last_stmt| &last_stmt.kind) { // Implicit return - Some(&StmtKind::Expr(_)) => true, - Some(&StmtKind::Semi(ref expr)) => { - if let ExprKind::Ret(_) = expr.kind { - // Last statement is explicit return. - true - } else { - false - } - } + Some(StmtKind::Expr(_)) => true, + // Last statement is an explicit return? + Some(StmtKind::Semi(expr)) => matches!(expr.kind, ExprKind::Ret(_)), // This is a block that doesn't end in either an implicit or explicit return. _ => false, } @@ -1128,7 +1107,7 @@ impl Expr { /// Is this expr either `N`, or `{ N }`. /// /// If this is not the case, name resolution does not resolve `N` when using - /// `feature(min_const_generics)` as more complex expressions are not supported. + /// `min_const_generics` as more complex expressions are not supported. pub fn is_potential_trivial_const_param(&self) -> bool { let this = if let ExprKind::Block(ref block, None) = self.kind { if block.stmts.len() == 1 { @@ -1652,26 +1631,17 @@ pub enum LitKind { impl LitKind { /// Returns `true` if this literal is a string. pub fn is_str(&self) -> bool { - match *self { - LitKind::Str(..) => true, - _ => false, - } + matches!(self, LitKind::Str(..)) } /// Returns `true` if this literal is byte literal string. pub fn is_bytestr(&self) -> bool { - match self { - LitKind::ByteStr(_) => true, - _ => false, - } + matches!(self, LitKind::ByteStr(_)) } /// Returns `true` if this is a numeric literal. pub fn is_numeric(&self) -> bool { - match *self { - LitKind::Int(..) | LitKind::Float(..) => true, - _ => false, - } + matches!(self, LitKind::Int(..) | LitKind::Float(..)) } /// Returns `true` if this literal has no suffix. @@ -1974,7 +1944,7 @@ impl TyKind { } pub fn is_unit(&self) -> bool { - if let TyKind::Tup(ref tys) = *self { tys.is_empty() } else { false } + matches!(self, TyKind::Tup(tys) if tys.is_empty()) } } @@ -2237,10 +2207,7 @@ impl FnDecl { self.inputs.get(0).map_or(false, Param::is_self) } pub fn c_variadic(&self) -> bool { - self.inputs.last().map_or(false, |arg| match arg.ty.kind { - TyKind::CVarArgs => true, - _ => false, - }) + self.inputs.last().map_or(false, |arg| matches!(arg.ty.kind, TyKind::CVarArgs)) } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 19c7c479f042..726ae5e51f7a 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -234,10 +234,7 @@ impl MetaItem { } pub fn is_word(&self) -> bool { - match self.kind { - MetaItemKind::Word => true, - _ => false, - } + matches!(self.kind, MetaItemKind::Word) } pub fn has_name(&self, name: Symbol) -> bool { diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f583825fbb3c..6dde304e8cfe 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -15,7 +15,7 @@ use rustc_span::hygiene::ExpnKind; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, FileName, RealFileName, Span, DUMMY_SP}; +use rustc_span::{self, edition::Edition, FileName, RealFileName, Span, DUMMY_SP}; use std::borrow::Cow; use std::{fmt, mem}; @@ -130,10 +130,7 @@ impl LitKind { } crate fn may_have_suffix(self) -> bool { - match self { - Integer | Float | Err => true, - _ => false, - } + matches!(self, Integer | Float | Err) } } @@ -305,10 +302,7 @@ impl TokenKind { } pub fn should_end_const_arg(&self) -> bool { - match self { - Gt | Ge | BinOp(Shr) | BinOpEq(Shr) => true, - _ => false, - } + matches!(self, Gt | Ge | BinOp(Shr) | BinOpEq(Shr)) } } @@ -346,18 +340,21 @@ impl Token { } pub fn is_op(&self) -> bool { - match self.kind { - OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..) - | Lifetime(..) | Interpolated(..) | Eof => false, - _ => true, - } + !matches!( + self.kind, + OpenDelim(..) + | CloseDelim(..) + | Literal(..) + | DocComment(..) + | Ident(..) + | Lifetime(..) + | Interpolated(..) + | Eof + ) } pub fn is_like_plus(&self) -> bool { - match self.kind { - BinOp(Plus) | BinOpEq(Plus) => true, - _ => false, - } + matches!(self.kind, BinOp(Plus) | BinOpEq(Plus)) } /// Returns `true` if the token can appear at the start of an expression. @@ -379,13 +376,10 @@ impl Token { ModSep | // global path Lifetime(..) | // labeled loop Pound => true, // expression attributes - Interpolated(ref nt) => match **nt { - NtLiteral(..) | + Interpolated(ref nt) => matches!(**nt, NtLiteral(..) | NtExpr(..) | NtBlock(..) | - NtPath(..) => true, - _ => false, - }, + NtPath(..)), _ => false, } } @@ -405,10 +399,7 @@ impl Token { Lifetime(..) | // lifetime bound in trait object Lt | BinOp(Shl) | // associated path ModSep => true, // global path - Interpolated(ref nt) => match **nt { - NtTy(..) | NtPath(..) => true, - _ => false, - }, + Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)), _ => false, } } @@ -417,10 +408,7 @@ impl Token { pub fn can_begin_const_arg(&self) -> bool { match self.kind { OpenDelim(Brace) => true, - Interpolated(ref nt) => match **nt { - NtExpr(..) | NtBlock(..) | NtLiteral(..) => true, - _ => false, - }, + Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)), _ => self.can_begin_literal_maybe_minus(), } } @@ -434,12 +422,9 @@ impl Token { || self == &OpenDelim(Paren) } - /// Returns `true` if the token is any literal + /// Returns `true` if the token is any literal. pub fn is_lit(&self) -> bool { - match self.kind { - Literal(..) => true, - _ => false, - } + matches!(self.kind, Literal(..)) } /// Returns `true` if the token is any literal, a minus (which can prefix a literal, @@ -705,7 +690,16 @@ pub enum NonterminalKind { Item, Block, Stmt, - Pat, + Pat2018 { + /// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the + /// edition of the span. This is used for diagnostics. + inferred: bool, + }, + Pat2021 { + /// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the + /// edition of the span. This is used for diagnostics. + inferred: bool, + }, Expr, Ty, Ident, @@ -718,12 +712,25 @@ pub enum NonterminalKind { } impl NonterminalKind { - pub fn from_symbol(symbol: Symbol) -> Option { + /// The `edition` closure is used to get the edition for the given symbol. Doing + /// `span.edition()` is expensive, so we do it lazily. + pub fn from_symbol( + symbol: Symbol, + edition: impl FnOnce() -> Edition, + ) -> Option { Some(match symbol { sym::item => NonterminalKind::Item, sym::block => NonterminalKind::Block, sym::stmt => NonterminalKind::Stmt, - sym::pat => NonterminalKind::Pat, + sym::pat => match edition() { + Edition::Edition2015 | Edition::Edition2018 => { + NonterminalKind::Pat2018 { inferred: true } + } + // FIXME(mark-i-m): uncomment when 2021 machinery is available. + //Edition::Edition2021 => NonterminalKind::Pat2021{inferred:true}, + }, + sym::pat2018 => NonterminalKind::Pat2018 { inferred: false }, + sym::pat2021 => NonterminalKind::Pat2021 { inferred: false }, sym::expr => NonterminalKind::Expr, sym::ty => NonterminalKind::Ty, sym::ident => NonterminalKind::Ident, @@ -741,7 +748,10 @@ impl NonterminalKind { NonterminalKind::Item => sym::item, NonterminalKind::Block => sym::block, NonterminalKind::Stmt => sym::stmt, - NonterminalKind::Pat => sym::pat, + NonterminalKind::Pat2018 { inferred: false } => sym::pat2018, + NonterminalKind::Pat2021 { inferred: false } => sym::pat2021, + NonterminalKind::Pat2018 { inferred: true } + | NonterminalKind::Pat2021 { inferred: true } => sym::pat, NonterminalKind::Expr => sym::expr, NonterminalKind::Ty => sym::ty, NonterminalKind::Ident => sym::ident, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index b2207f228162..0550f53a96fb 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -44,6 +44,12 @@ pub enum TokenTree { Delimited(DelimSpan, DelimToken, TokenStream), } +#[derive(Copy, Clone)] +pub enum CanSynthesizeMissingTokens { + Yes, + No, +} + // Ensure all fields of `TokenTree` is `Send` and `Sync`. #[cfg(parallel_compiler)] fn _dummy() diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 60422a2e5739..90786520fe80 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -12,14 +12,14 @@ use crate::ast; /// |x| 5 /// isn't parsed as (if true {...} else {...} | x) | 5 pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { - match e.kind { + !matches!( + e.kind, ast::ExprKind::If(..) - | ast::ExprKind::Match(..) - | ast::ExprKind::Block(..) - | ast::ExprKind::While(..) - | ast::ExprKind::Loop(..) - | ast::ExprKind::ForLoop(..) - | ast::ExprKind::TryBlock(..) => false, - _ => true, - } + | ast::ExprKind::Match(..) + | ast::ExprKind::Block(..) + | ast::ExprKind::While(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::ForLoop(..) + | ast::ExprKind::TryBlock(..) + ) } diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index e97c8cc4562f..542a330a0314 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -25,9 +25,8 @@ pub struct Comment { /// Makes a doc string more presentable to users. /// Used by rustdoc and perhaps other tools, but not by rustc. -pub fn beautify_doc_string(data: Symbol) -> String { - /// remove whitespace-only lines from the start/end of lines - fn vertical_trim(lines: Vec) -> Vec { +pub fn beautify_doc_string(data: Symbol) -> Symbol { + fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> { let mut i = 0; let mut j = lines.len(); // first line of all-stars should be omitted @@ -47,55 +46,58 @@ pub fn beautify_doc_string(data: Symbol) -> String { j -= 1; } - lines[i..j].to_vec() + if i != 0 || j != lines.len() { Some((i, j)) } else { None } } - /// remove a "[ \t]*\*" block from each line, if possible - fn horizontal_trim(lines: Vec) -> Vec { + fn get_horizontal_trim(lines: &[&str]) -> Option { let mut i = usize::MAX; - let mut can_trim = true; let mut first = true; - for line in &lines { + for line in lines { for (j, c) in line.chars().enumerate() { if j > i || !"* \t".contains(c) { - can_trim = false; - break; + return None; } if c == '*' { if first { i = j; first = false; } else if i != j { - can_trim = false; + return None; } break; } } if i >= line.len() { - can_trim = false; - } - if !can_trim { - break; + return None; } } + Some(i) + } - if can_trim { - lines.iter().map(|line| (&line[i + 1..line.len()]).to_string()).collect() + let data_s = data.as_str(); + if data_s.contains('\n') { + let mut lines = data_s.lines().collect::>(); + let mut changes = false; + let lines = if let Some((i, j)) = get_vertical_trim(&lines) { + changes = true; + // remove whitespace-only lines from the start/end of lines + &mut lines[i..j] } else { - lines + &mut lines + }; + if let Some(horizontal) = get_horizontal_trim(&lines) { + changes = true; + // remove a "[ \t]*\*" block from each line, if possible + for line in lines.iter_mut() { + *line = &line[horizontal + 1..]; + } + } + if changes { + return Symbol::intern(&lines.join("\n")); } } - - let data = data.as_str(); - if data.contains('\n') { - let lines = data.lines().map(|s| s.to_string()).collect::>(); - let lines = vertical_trim(lines); - let lines = horizontal_trim(lines); - lines.join("\n") - } else { - data.to_string() - } + data } /// Returns `None` if the first `col` chars of `s` contain a non-whitespace char. @@ -178,10 +180,8 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec { if doc_style.is_none() { - let code_to_the_right = match text[pos + token.len..].chars().next() { - Some('\r' | '\n') => false, - _ => true, - }; + let code_to_the_right = + !matches!(text[pos + token.len..].chars().next(), Some('\r' | '\n')); let style = match (code_to_the_left, code_to_the_right) { (_, true) => CommentStyle::Mixed, (false, false) => CommentStyle::Isolated, diff --git a/compiler/rustc_ast/src/util/comments/tests.rs b/compiler/rustc_ast/src/util/comments/tests.rs index e19198f863ba..98ab653e45f7 100644 --- a/compiler/rustc_ast/src/util/comments/tests.rs +++ b/compiler/rustc_ast/src/util/comments/tests.rs @@ -6,7 +6,7 @@ fn test_block_doc_comment_1() { with_default_session_globals(|| { let comment = "\n * Test \n ** Test\n * Test\n"; let stripped = beautify_doc_string(Symbol::intern(comment)); - assert_eq!(stripped, " Test \n* Test\n Test"); + assert_eq!(stripped.as_str(), " Test \n* Test\n Test"); }) } @@ -15,7 +15,7 @@ fn test_block_doc_comment_2() { with_default_session_globals(|| { let comment = "\n * Test\n * Test\n"; let stripped = beautify_doc_string(Symbol::intern(comment)); - assert_eq!(stripped, " Test\n Test"); + assert_eq!(stripped.as_str(), " Test\n Test"); }) } @@ -24,7 +24,7 @@ fn test_block_doc_comment_3() { with_default_session_globals(|| { let comment = "\n let a: *i32;\n *a = 5;\n"; let stripped = beautify_doc_string(Symbol::intern(comment)); - assert_eq!(stripped, " let a: *i32;\n *a = 5;"); + assert_eq!(stripped.as_str(), " let a: *i32;\n *a = 5;"); }) } @@ -32,12 +32,12 @@ fn test_block_doc_comment_3() { fn test_line_doc_comment() { with_default_session_globals(|| { let stripped = beautify_doc_string(Symbol::intern(" test")); - assert_eq!(stripped, " test"); + assert_eq!(stripped.as_str(), " test"); let stripped = beautify_doc_string(Symbol::intern("! test")); - assert_eq!(stripped, "! test"); + assert_eq!(stripped.as_str(), "! test"); let stripped = beautify_doc_string(Symbol::intern("test")); - assert_eq!(stripped, "test"); + assert_eq!(stripped.as_str(), "test"); let stripped = beautify_doc_string(Symbol::intern("!test")); - assert_eq!(stripped, "!test"); + assert_eq!(stripped.as_str(), "!test"); }) } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 6ad6e664316f..9b1642df1140 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -505,14 +505,19 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> { + let pat = self.lower_pat(&arm.pat); + let guard = arm.guard.as_ref().map(|cond| { + if let ExprKind::Let(ref pat, ref scrutinee) = cond.kind { + hir::Guard::IfLet(self.lower_pat(pat), self.lower_expr(scrutinee)) + } else { + hir::Guard::If(self.lower_expr(cond)) + } + }); hir::Arm { hir_id: self.next_id(), attrs: self.lower_attrs(&arm.attrs), - pat: self.lower_pat(&arm.pat), - guard: match arm.guard { - Some(ref x) => Some(hir::Guard::If(self.lower_expr(x))), - _ => None, - }, + pat, + guard, body: self.lower_expr(&arm.body), span: arm.span, } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 2e1b5a74a7b7..d17b29089d69 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -37,7 +37,7 @@ use rustc_ast::node_id::NodeMap; use rustc_ast::token::{self, DelimToken, Nonterminal, Token}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, DelimSpan, TokenStream, TokenTree}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::walk_list; use rustc_ast::{self as ast, *}; @@ -206,7 +206,8 @@ pub trait ResolverAstLowering { ) -> LocalDefId; } -type NtToTokenstream = fn(&Nonterminal, &ParseSess, Span) -> TokenStream; +type NtToTokenstream = + fn(&Nonterminal, &ParseSess, Span, CanSynthesizeMissingTokens) -> TokenStream; /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, /// and if so, what meaning it has. @@ -393,6 +394,47 @@ enum AnonymousLifetimeMode { PassThrough, } +struct TokenStreamLowering<'a> { + parse_sess: &'a ParseSess, + synthesize_tokens: CanSynthesizeMissingTokens, + nt_to_tokenstream: NtToTokenstream, +} + +impl<'a> TokenStreamLowering<'a> { + fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream { + tokens.into_trees().flat_map(|tree| self.lower_token_tree(tree).into_trees()).collect() + } + + fn lower_token_tree(&mut self, tree: TokenTree) -> TokenStream { + match tree { + TokenTree::Token(token) => self.lower_token(token), + TokenTree::Delimited(span, delim, tts) => { + TokenTree::Delimited(span, delim, self.lower_token_stream(tts)).into() + } + } + } + + fn lower_token(&mut self, token: Token) -> TokenStream { + match token.kind { + token::Interpolated(nt) => { + let tts = (self.nt_to_tokenstream)( + &nt, + self.parse_sess, + token.span, + self.synthesize_tokens, + ); + TokenTree::Delimited( + DelimSpan::from_single(token.span), + DelimToken::NoDelim, + self.lower_token_stream(tts), + ) + .into() + } + _ => TokenTree::Token(token).into(), + } + } +} + struct ImplTraitTypeIdVisitor<'a> { ids: &'a mut SmallVec<[NodeId; 1]>, } @@ -955,42 +997,51 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match *args { MacArgs::Empty => MacArgs::Empty, MacArgs::Delimited(dspan, delim, ref tokens) => { - MacArgs::Delimited(dspan, delim, self.lower_token_stream(tokens.clone())) - } - MacArgs::Eq(eq_span, ref tokens) => { - MacArgs::Eq(eq_span, self.lower_token_stream(tokens.clone())) - } - } - } - - fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream { - tokens.into_trees().flat_map(|tree| self.lower_token_tree(tree).into_trees()).collect() - } - - fn lower_token_tree(&mut self, tree: TokenTree) -> TokenStream { - match tree { - TokenTree::Token(token) => self.lower_token(token), - TokenTree::Delimited(span, delim, tts) => { - TokenTree::Delimited(span, delim, self.lower_token_stream(tts)).into() - } - } - } - - fn lower_token(&mut self, token: Token) -> TokenStream { - match token.kind { - token::Interpolated(nt) => { - let tts = (self.nt_to_tokenstream)(&nt, &self.sess.parse_sess, token.span); - TokenTree::Delimited( - DelimSpan::from_single(token.span), - DelimToken::NoDelim, - self.lower_token_stream(tts), + // This is either a non-key-value attribute, or a `macro_rules!` body. + // We either not have any nonterminals present (in the case of an attribute), + // or have tokens available for all nonterminals in the case of a nested + // `macro_rules`: e.g: + // + // ```rust + // macro_rules! outer { + // ($e:expr) => { + // macro_rules! inner { + // () => { $e } + // } + // } + // } + // ``` + // + // In both cases, we don't want to synthesize any tokens + MacArgs::Delimited( + dspan, + delim, + self.lower_token_stream(tokens.clone(), CanSynthesizeMissingTokens::No), ) - .into() } - _ => TokenTree::Token(token).into(), + // This is an inert key-value attribute - it will never be visible to macros + // after it gets lowered to HIR. Therefore, we can synthesize tokens with fake + // spans to handle nonterminals in `#[doc]` (e.g. `#[doc = $e]`). + MacArgs::Eq(eq_span, ref tokens) => MacArgs::Eq( + eq_span, + self.lower_token_stream(tokens.clone(), CanSynthesizeMissingTokens::Yes), + ), } } + fn lower_token_stream( + &self, + tokens: TokenStream, + synthesize_tokens: CanSynthesizeMissingTokens, + ) -> TokenStream { + TokenStreamLowering { + parse_sess: &self.sess.parse_sess, + synthesize_tokens, + nt_to_tokenstream: self.nt_to_tokenstream, + } + .lower_token_stream(tokens) + } + /// Given an associated type constraint like one of these: /// /// ``` @@ -1716,7 +1767,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } self.arena.alloc_from_iter(inputs.iter().map(|param| match param.pat.kind { PatKind::Ident(_, ident, _) => ident, - _ => Ident::new(kw::Invalid, param.pat.span), + _ => Ident::new(kw::Empty, param.pat.span), })) } @@ -1806,12 +1857,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { output, c_variadic, implicit_self: decl.inputs.get(0).map_or(hir::ImplicitSelfKind::None, |arg| { - let is_mutable_pat = match arg.pat.kind { - PatKind::Ident(BindingMode::ByValue(mt) | BindingMode::ByRef(mt), _, _) => { - mt == Mutability::Mut - } - _ => false, - }; + use BindingMode::{ByRef, ByValue}; + let is_mutable_pat = matches!( + arg.pat.kind, + PatKind::Ident(ByValue(Mutability::Mut) | ByRef(Mutability::Mut), ..) + ); match arg.ty.kind { TyKind::ImplicitSelf if is_mutable_pat => hir::ImplicitSelfKind::Mut, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 4ec3e39facc9..c40f00bc9e99 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -184,7 +184,7 @@ impl<'a> AstValidator<'a> { } fn check_lifetime(&self, ident: Ident) { - let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Invalid]; + let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Empty]; if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() { self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names"); } @@ -400,7 +400,7 @@ impl<'a> AstValidator<'a> { if let Defaultness::Default(def_span) = defaultness { let span = self.session.source_map().guess_head_span(span); self.err_handler() - .struct_span_err(span, "`default` is only allowed on items in `impl` definitions") + .struct_span_err(span, "`default` is only allowed on items in trait impls") .span_label(def_span, "`default` because of this") .emit(); } @@ -717,35 +717,46 @@ impl<'a> AstValidator<'a> { /// Checks that generic parameters are in the correct order, /// which is lifetimes, then types and then consts. (`<'a, T, const N: usize>`) -fn validate_generic_param_order<'a>( +fn validate_generic_param_order( sess: &Session, handler: &rustc_errors::Handler, - generics: impl Iterator, Span, Option)>, + generics: &[GenericParam], span: Span, ) { let mut max_param: Option = None; let mut out_of_order = FxHashMap::default(); let mut param_idents = vec![]; - for (kind, bounds, span, ident) in generics { + for param in generics { + let ident = Some(param.ident.to_string()); + let (kind, bounds, span) = (¶m.kind, Some(&*param.bounds), param.ident.span); + let (ord_kind, ident) = match ¶m.kind { + GenericParamKind::Lifetime => (ParamKindOrd::Lifetime, ident), + GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident), + GenericParamKind::Const { ref ty, kw_span: _ } => { + let ty = pprust::ty_to_string(ty); + let unordered = sess.features_untracked().const_generics; + (ParamKindOrd::Const { unordered }, Some(format!("const {}: {}", param.ident, ty))) + } + }; if let Some(ident) = ident { - param_idents.push((kind, bounds, param_idents.len(), ident)); + param_idents.push((kind, ord_kind, bounds, param_idents.len(), ident)); } let max_param = &mut max_param; match max_param { - Some(max_param) if *max_param > kind => { - let entry = out_of_order.entry(kind).or_insert((*max_param, vec![])); + Some(max_param) if *max_param > ord_kind => { + let entry = out_of_order.entry(ord_kind).or_insert((*max_param, vec![])); entry.1.push(span); } - Some(_) | None => *max_param = Some(kind), + Some(_) | None => *max_param = Some(ord_kind), }; } let mut ordered_params = "<".to_string(); if !out_of_order.is_empty() { - param_idents.sort_by_key(|&(po, _, i, _)| (po, i)); + param_idents.sort_by_key(|&(_, po, _, i, _)| (po, i)); let mut first = true; - for (_, bounds, _, ident) in param_idents { + for (kind, _, bounds, _, ident) in param_idents { if !first { ordered_params += ", "; } @@ -756,6 +767,16 @@ fn validate_generic_param_order<'a>( ordered_params += &pprust::bounds_to_string(&bounds); } } + match kind { + GenericParamKind::Type { default: Some(default) } => { + ordered_params += " = "; + ordered_params += &pprust::ty_to_string(default); + } + GenericParamKind::Type { default: None } => (), + GenericParamKind::Lifetime => (), + // FIXME(const_generics:defaults) + GenericParamKind::Const { ty: _, kw_span: _ } => (), + } first = false; } } @@ -773,14 +794,12 @@ fn validate_generic_param_order<'a>( err.span_suggestion( span, &format!( - "reorder the parameters: lifetimes{}", + "reorder the parameters: lifetimes, {}", if sess.features_untracked().const_generics { - ", then consts and types" - } else if sess.features_untracked().min_const_generics { - ", then types, then consts" + "then consts and types" } else { - ", then types" - }, + "then types, then consts" + } ), ordered_params.clone(), Applicability::MachineApplicable, @@ -1152,22 +1171,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { validate_generic_param_order( self.session, self.err_handler(), - generics.params.iter().map(|param| { - let ident = Some(param.ident.to_string()); - let (kind, ident) = match ¶m.kind { - GenericParamKind::Lifetime => (ParamKindOrd::Lifetime, ident), - GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident), - GenericParamKind::Const { ref ty, kw_span: _ } => { - let ty = pprust::ty_to_string(ty); - let unordered = self.session.features_untracked().const_generics; - ( - ParamKindOrd::Const { unordered }, - Some(format!("const {}: {}", param.ident, ty)), - ) - } - }; - (kind, Some(&*param.bounds), param.ident.span, ident) - }), + &generics.params, generics.span, ); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 9d54d89e080f..6a9d6d2ed121 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{AssocTyConstraint, AssocTyConstraintKind, NodeId}; -use rustc_ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData}; +use rustc_ast::{PatKind, RangeEnd, VariantData}; use rustc_errors::struct_span_err; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP}; use rustc_feature::{Features, GateIssue}; @@ -397,10 +397,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { match i.kind { ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => { let link_name = self.sess.first_attr_value_str_by_name(&i.attrs, sym::link_name); - let links_to_llvm = match link_name { - Some(val) => val.as_str().starts_with("llvm."), - _ => false, - }; + let links_to_llvm = + link_name.map_or(false, |val| val.as_str().starts_with("llvm.")); if links_to_llvm { gate_feature_post!( &self, @@ -529,19 +527,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_fn(self, fn_kind, span) } - fn visit_generic_param(&mut self, param: &'a GenericParam) { - if let GenericParamKind::Const { .. } = param.kind { - gate_feature_fn!( - &self, - |x: &Features| x.const_generics || x.min_const_generics, - param.ident.span, - sym::min_const_generics, - "const generics are unstable" - ); - } - visit::walk_generic_param(self, param) - } - fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) { if let AssocTyConstraintKind::Bound { .. } = constraint.kind { gate_feature_post!( @@ -620,7 +605,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { } }; } - gate_all!(if_let_guard, "`if let` guard is not implemented"); + gate_all!(if_let_guard, "`if let` guards are experimental"); gate_all!(let_chains, "`let` expressions in this position are experimental"); gate_all!(async_closure, "async closures are unstable"); gate_all!(generators, "yield syntax is experimental"); diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index 56e769ba6b71..ea298d28e72c 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -75,7 +75,7 @@ //! breaking inconsistently to become //! //! ``` -//! foo(hello, there +//! foo(hello, there, //! good, friends); //! ``` //! @@ -83,7 +83,7 @@ //! //! ``` //! foo(hello, -//! there +//! there, //! good, //! friends); //! ``` diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index fdb129d9e2aa..333a396a0b4f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2420,7 +2420,15 @@ impl<'a> State<'a> { if mutbl == ast::Mutability::Mut { self.s.word("mut "); } - self.print_pat(inner); + if let PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Mut), ..) = + inner.kind + { + self.popen(); + self.print_pat(inner); + self.pclose(); + } else { + self.print_pat(inner); + } } PatKind::Lit(ref e) => self.print_expr(&**e), PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => { @@ -2779,7 +2787,7 @@ impl<'a> State<'a> { self.print_explicit_self(&eself); } else { let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind { - ident.name == kw::Invalid + ident.name == kw::Empty } else { false }; diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index bb7562bc80c7..ead90f23ce7a 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -621,7 +621,7 @@ pub fn eval_condition( } } -#[derive(Encodable, Decodable, Clone, HashStable_Generic)] +#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic)] pub struct Deprecation { pub since: Option, /// The note to issue a reason. diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 957c8035399a..ca1226b445d9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -38,10 +38,9 @@ pub fn expand_deriving_clone( | ItemKind::Enum(_, Generics { ref params, .. }) => { let container_id = cx.current_expansion.id.expn_data().parent; if cx.resolver.has_derive_copy(container_id) - && !params.iter().any(|param| match param.kind { - ast::GenericParamKind::Type { .. } => true, - _ => false, - }) + && !params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) { bounds = vec![]; is_shallow = true; diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index a767de53dae1..e78d1368b357 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -257,7 +257,10 @@ pub struct Substructure<'a> { pub type_ident: Ident, /// ident of the method pub method_ident: Ident, - /// dereferenced access to any `Self_` or `Ptr(Self_, _)` arguments + /// dereferenced access to any [`Self_`] or [`Ptr(Self_, _)][ptr]` arguments + /// + /// [`Self_`]: ty::Ty::Self_ + /// [ptr]: ty::Ty::Ptr pub self_args: &'a [P], /// verbatim access to any other arguments pub nonself_args: &'a [P], @@ -401,12 +404,10 @@ impl<'a> TraitDef<'a> { let has_no_type_params = match item.kind { ast::ItemKind::Struct(_, ref generics) | ast::ItemKind::Enum(_, ref generics) - | ast::ItemKind::Union(_, ref generics) => { - !generics.params.iter().any(|param| match param.kind { - ast::GenericParamKind::Type { .. } => true, - _ => false, - }) - } + | ast::ItemKind::Union(_, ref generics) => !generics + .params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })), _ => unreachable!(), }; let container_id = cx.current_expansion.id.expn_data().parent; @@ -597,10 +598,7 @@ impl<'a> TraitDef<'a> { let mut ty_params = params .iter() - .filter_map(|param| match param.kind { - ast::GenericParamKind::Type { .. } => Some(param), - _ => None, - }) + .filter(|param| matches!(param.kind, ast::GenericParamKind::Type{..})) .peekable(); if ty_params.peek().is_some() { @@ -868,7 +866,7 @@ impl<'a> MethodDef<'a> { Self_ if nonstatic => { self_args.push(arg_expr); } - Ptr(ref ty, _) if (if let Self_ = **ty { true } else { false }) && nonstatic => { + Ptr(ref ty, _) if matches!(**ty, Self_) && nonstatic => { self_args.push(cx.expr_deref(trait_.span, arg_expr)) } _ => { diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 550524e652af..85ca1da6f1da 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1044,10 +1044,7 @@ pub fn expand_preparsed_format_args( let numbered_position_args = pieces.iter().any(|arg: &parse::Piece<'_>| match *arg { parse::String(_) => false, - parse::NextArgument(arg) => match arg.position { - parse::Position::ArgumentIs(_) => true, - _ => false, - }, + parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(_)), }); cx.build_index_map(); diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index f00dfd1241fb..0496c72cb005 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -580,10 +580,7 @@ pub mod printf { } fn is_flag(c: &char) -> bool { - match c { - '0' | '-' | '+' | ' ' | '#' | '\'' => true, - _ => false, - } + matches!(c, '0' | '-' | '+' | ' ' | '#' | '\'') } #[cfg(test)] diff --git a/compiler/rustc_builtin_macros/src/llvm_asm.rs b/compiler/rustc_builtin_macros/src/llvm_asm.rs index db73fdbe24ff..d72bfa660e58 100644 --- a/compiler/rustc_builtin_macros/src/llvm_asm.rs +++ b/compiler/rustc_builtin_macros/src/llvm_asm.rs @@ -87,13 +87,15 @@ fn parse_inline_asm<'a>( // parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription. let first_colon = tts .trees() - .position(|tt| match tt { - tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) => true, - _ => false, + .position(|tt| { + matches!( + tt, + tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) + ) }) .unwrap_or(tts.len()); let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect()); - let mut asm = kw::Invalid; + let mut asm = kw::Empty; let mut asm_str_style = None; let mut outputs = Vec::new(); let mut inputs = Vec::new(); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 4e91436199a5..7582d9805390 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -256,10 +256,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { // we're just not interested in this item. // // If we find one, try to locate a `#[proc_macro_derive]` attribute on it. - let is_fn = match item.kind { - ast::ItemKind::Fn(..) => true, - _ => false, - }; + let is_fn = matches!(item.kind, ast::ItemKind::Fn(..)); let mut found_attr: Option<&'a ast::Attribute> = None; diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json index 04ab5085c196..7618251acd5c 100644 --- a/compiler/rustc_codegen_cranelift/.vscode/settings.json +++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json @@ -1,6 +1,7 @@ { // source for rustc_* is not included in the rust-src component; disable the errors about this "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate"], + "rust-analyzer.assist.importMergeBehaviour": "last", "rust-analyzer.cargo.loadOutDirsFromCheck": true, "rust-analyzer.linkedProjects": [ "./Cargo.toml", diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 67ed41e76523..0382835269d1 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -50,7 +50,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" dependencies = [ "cranelift-entity", ] @@ -58,7 +58,7 @@ dependencies = [ [[package]] name = "cranelift-codegen" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" dependencies = [ "byteorder", "cranelift-bforest", @@ -76,7 +76,7 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" dependencies = [ "cranelift-codegen-shared", "cranelift-entity", @@ -85,17 +85,17 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" [[package]] name = "cranelift-entity" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" [[package]] name = "cranelift-frontend" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" dependencies = [ "cranelift-codegen", "log", @@ -103,10 +103,28 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cranelift-jit" +version = "0.68.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-module", + "cranelift-native", + "errno", + "libc", + "log", + "region", + "target-lexicon", + "winapi", +] + [[package]] name = "cranelift-module" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" dependencies = [ "anyhow", "cranelift-codegen", @@ -118,7 +136,7 @@ dependencies = [ [[package]] name = "cranelift-native" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" dependencies = [ "cranelift-codegen", "raw-cpuid", @@ -128,7 +146,7 @@ dependencies = [ [[package]] name = "cranelift-object" version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8f7f8ee0b4c5007ace6de29b45505c360450b1bb" dependencies = [ "anyhow", "cranelift-codegen", @@ -138,23 +156,6 @@ dependencies = [ "target-lexicon", ] -[[package]] -name = "cranelift-simplejit" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" -dependencies = [ - "cranelift-codegen", - "cranelift-entity", - "cranelift-module", - "cranelift-native", - "errno", - "libc", - "log", - "region", - "target-lexicon", - "winapi", -] - [[package]] name = "crc32fast" version = "1.2.1" @@ -325,9 +326,9 @@ dependencies = [ "ar", "cranelift-codegen", "cranelift-frontend", + "cranelift-jit", "cranelift-module", "cranelift-object", - "cranelift-simplejit", "gimli", "indexmap", "libloading", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index cbff06749d3e..8e1933bb14e7 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["dylib"] cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", features = ["unwind"] } cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } -cranelift-simplejit = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", optional = true } +cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", optional = true } cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } target-lexicon = "0.11.0" gimli = { version = "0.23.0", default-features = false, features = ["write"]} @@ -27,7 +27,7 @@ libloading = { version = "0.6.0", optional = true } #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } #cranelift-frontend = { path = "../wasmtime/cranelift/frontend" } #cranelift-module = { path = "../wasmtime/cranelift/module" } -#cranelift-simplejit = { path = "../wasmtime/cranelift/simplejit" } +#cranelift-jit = { path = "../wasmtime/cranelift/jit" } #cranelift-object = { path = "../wasmtime/cranelift/object" } #[patch.crates-io] @@ -35,7 +35,7 @@ libloading = { version = "0.6.0", optional = true } [features] default = ["jit", "inline_asm"] -jit = ["cranelift-simplejit", "libloading"] +jit = ["cranelift-jit", "libloading"] inline_asm = [] [profile.dev] diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index de54bf67f4a1..22d9e00923f0 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -2,7 +2,7 @@ > ⚠⚠⚠ Certain kinds of FFI don't work yet. ⚠⚠⚠ -The goal of this project is to create an alternative codegen backend for the rust compiler based on [Cranelift](https://github.com/bytecodealliance/wasmtime/blob/master/cranelift). +The goal of this project is to create an alternative codegen backend for the rust compiler based on [Cranelift](https://github.com/bytecodealliance/wasmtime/blob/main/cranelift). This has the potential to improve compilation times in debug mode. If your project doesn't use any of the things listed under "Not yet supported", it should work fine. If not please open an issue. @@ -68,7 +68,15 @@ $ $cg_clif_dir/build/cargo.sh jit or ```bash -$ $cg_clif_dir/build/bin/cg_clif --jit my_crate.rs +$ $cg_clif_dir/build/bin/cg_clif -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs +``` + +There is also an experimental lazy jit mode. In this mode functions are only compiled once they are +first called. It currently does not work with multi-threaded programs. When a not yet compiled +function is called from another thread than the main thread, you will get an ICE. + +```bash +$ $cg_clif_dir/build/cargo.sh lazy-jit ``` ### Shell @@ -77,7 +85,7 @@ These are a few functions that allow you to easily run rust code from the shell ```bash function jit_naked() { - echo "$@" | $cg_clif_dir/build/bin/cg_clif - --jit + echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Cllvm-args=mode=jit -Cprefer-dynamic } function jit() { diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock index a2b8f449f00f..990557694ead 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock @@ -47,9 +47,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "cc" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cfg-if" @@ -141,9 +141,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" +checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" dependencies = [ "rustc-std-workspace-core", ] diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml index e562dedb5324..3dbd28c286a2 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml @@ -5,13 +5,14 @@ version = "0.0.0" [dependencies] core = { path = "./sysroot_src/library/core" } -compiler_builtins = "0.1" alloc = { path = "./sysroot_src/library/alloc" } std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] } test = { path = "./sysroot_src/library/test" } alloc_system = { path = "./alloc_system" } +compiler_builtins = { version = "=0.1.36", default-features = false } + [patch.crates-io] rustc-std-workspace-core = { path = "./sysroot_src/library/rustc-std-workspace-core" } rustc-std-workspace-alloc = { path = "./sysroot_src/library/rustc-std-workspace-alloc" } diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index b38e25328a4e..015bbdfed464 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -15,6 +15,8 @@ fn main() { let stderr = ::std::io::stderr(); let mut stderr = stderr.lock(); + // FIXME support lazy jit when multi threading + #[cfg(not(lazy_jit))] std::thread::spawn(move || { println!("Hello from another thread!"); }); diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index ed1e64f45db0..d6ad24bcf26d 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1 +1 @@ -nightly-2020-11-27 +nightly-2020-12-23 diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo.sh b/compiler/rustc_codegen_cranelift/scripts/cargo.sh index dcd40acc02a5..a3d6d303057b 100755 --- a/compiler/rustc_codegen_cranelift/scripts/cargo.sh +++ b/compiler/rustc_codegen_cranelift/scripts/cargo.sh @@ -10,7 +10,9 @@ cmd=$1 shift || true if [[ "$cmd" = "jit" ]]; then -cargo "+${TOOLCHAIN}" rustc "$@" -- --jit +cargo "+${TOOLCHAIN}" rustc "$@" -- -Cllvm-args=mode=jit -Cprefer-dynamic +elif [[ "$cmd" = "lazy-jit" ]]; then +cargo "+${TOOLCHAIN}" rustc "$@" -- -Cllvm-args=mode=jit-lazy -Cprefer-dynamic else cargo "+${TOOLCHAIN}" "$cmd" "$@" fi diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs index 3327c10089d9..15388926ec9e 100755 --- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -4,7 +4,7 @@ pushd $(dirname "$0")/../ source build/config.sh popd -PROFILE=$1 OUTPUT=$2 exec $RUSTC $RUSTFLAGS --jit $0 +PROFILE=$1 OUTPUT=$2 exec $RUSTC $RUSTFLAGS -Cllvm-args=mode=jit -Cprefer-dynamic $0 #*/ //! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse diff --git a/compiler/rustc_codegen_cranelift/scripts/tests.sh b/compiler/rustc_codegen_cranelift/scripts/tests.sh index 114b6f30a4a9..a61774f479ec 100755 --- a/compiler/rustc_codegen_cranelift/scripts/tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/tests.sh @@ -15,7 +15,10 @@ function no_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] mini_core_hello_world" - CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC --jit example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" + CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" + + echo "[JIT-lazy] mini_core_hello_world" + CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" else echo "[JIT] mini_core_hello_world (skipped)" fi @@ -37,7 +40,10 @@ function base_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] std_example" - $MY_RUSTC --jit example/std_example.rs --target "$HOST_TRIPLE" + $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" + + echo "[JIT-lazy] std_example" + $MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/std_example.rs --cfg lazy_jit --target "$HOST_TRIPLE" else echo "[JIT] std_example (skipped)" fi diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index aee274ab4a82..76e1987459f8 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -64,7 +64,7 @@ pub(crate) fn fn_sig_for_fn_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx ty::Generator(_, substs, _) => { let sig = substs.as_generator().poly_sig(); - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let env_region = ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrEnv }); let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); let pin_did = tcx.require_lang_item(rustc_hir::LangItem::Pin, None); diff --git a/compiler/rustc_codegen_cranelift/src/backend.rs b/compiler/rustc_codegen_cranelift/src/backend.rs index 9e32259716f5..0ce34c904bdc 100644 --- a/compiler/rustc_codegen_cranelift/src/backend.rs +++ b/compiler/rustc_codegen_cranelift/src/backend.rs @@ -162,7 +162,7 @@ impl AddConstructor for ObjectProduct { } pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec { - let triple = crate::build_isa(sess, true).triple().clone(); + let triple = crate::build_isa(sess).triple().clone(); let binary_format = match triple.binary_format { target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf, @@ -193,7 +193,7 @@ pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object pub(crate) fn make_module(sess: &Session, name: String) -> ObjectModule { let mut builder = ObjectBuilder::new( - crate::build_isa(sess, true), + crate::build_isa(sess), name + ".o", cranelift_module::default_libcall_names(), ) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 72073896a723..34c9561d6762 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -118,6 +118,8 @@ pub(crate) fn codegen_fn<'tcx>( context.eliminate_unreachable_code(cx.module.isa()).unwrap(); context.dce(cx.module.isa()).unwrap(); + context.want_disasm = crate::pretty_clif::should_write_ir(tcx); + // Define function let module = &mut cx.module; tcx.sess.time("define function", || { @@ -140,6 +142,16 @@ pub(crate) fn codegen_fn<'tcx>( &clif_comments, ); + if let Some(mach_compile_result) = &context.mach_compile_result { + if let Some(disasm) = &mach_compile_result.disasm { + crate::pretty_clif::write_ir_file( + tcx, + &format!("{}.vcode", tcx.symbol_name(instance).name), + |file| file.write_all(disasm.as_bytes()), + ) + } + } + // Define debuginfo for function let isa = cx.module.isa(); let debug_context = &mut cx.debug_context; @@ -307,7 +319,9 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { } => { let discr = codegen_operand(fx, discr).load_scalar(fx); - if switch_ty.kind() == fx.tcx.types.bool.kind() { + let use_bool_opt = switch_ty.kind() == fx.tcx.types.bool.kind() + || (targets.iter().count() == 1 && targets.iter().next().unwrap().0 == 0); + if use_bool_opt { assert_eq!(targets.iter().count(), 1); let (then_value, then_block) = targets.iter().next().unwrap(); let then_block = fx.get_block(then_block); @@ -325,12 +339,22 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { let discr = crate::optimize::peephole::maybe_unwrap_bint(&mut fx.bcx, discr); let discr = crate::optimize::peephole::make_branchable_value(&mut fx.bcx, discr); - if test_zero { - fx.bcx.ins().brz(discr, then_block, &[]); - fx.bcx.ins().jump(else_block, &[]); + if let Some(taken) = crate::optimize::peephole::maybe_known_branch_taken( + &fx.bcx, discr, test_zero, + ) { + if taken { + fx.bcx.ins().jump(then_block, &[]); + } else { + fx.bcx.ins().jump(else_block, &[]); + } } else { - fx.bcx.ins().brnz(discr, then_block, &[]); - fx.bcx.ins().jump(else_block, &[]); + if test_zero { + fx.bcx.ins().brz(discr, then_block, &[]); + fx.bcx.ins().jump(else_block, &[]); + } else { + fx.bcx.ins().brnz(discr, then_block, &[]); + fx.bcx.ins().jump(else_block, &[]); + } } } else { let mut switch = ::cranelift_frontend::Switch::new(); diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs index f4d23ebcf4e4..58e45b4e9b97 100644 --- a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs @@ -44,9 +44,7 @@ fn main() { let mut callbacks = CraneliftPassesCallbacks::default(); rustc_driver::install_ice_hook(); let exit_code = rustc_driver::catch_with_exit_code(|| { - let mut use_jit = false; - - let mut args = std::env::args_os() + let args = std::env::args_os() .enumerate() .map(|(i, arg)| { arg.into_string().unwrap_or_else(|arg| { @@ -56,23 +54,10 @@ fn main() { ) }) }) - .filter(|arg| { - if arg == "--jit" { - use_jit = true; - false - } else { - true - } - }) .collect::>(); - if use_jit { - args.push("-Cprefer-dynamic".to_string()); - } let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { - Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { - config: rustc_codegen_cranelift::BackendConfig { use_jit }, - }) + Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None }) }))); run_compiler.run() }); diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs index 165d33dcfb50..8ee4cd46c94e 100644 --- a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs @@ -92,9 +92,7 @@ fn main() { let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); if use_clif { run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { - Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { - config: rustc_codegen_cranelift::BackendConfig { use_jit: false }, - }) + Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None }) }))); } run_compiler.run() diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 544b020b7119..beff84fb2e21 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -100,7 +100,10 @@ fn codegen_static_ref<'tcx>( let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id); assert!(!layout.is_unsized(), "unsized statics aren't supported"); assert!( - matches!(fx.bcx.func.global_values[local_data_id], GlobalValueData::Symbol { tls: false, ..}), + matches!( + fx.bcx.func.global_values[local_data_id], + GlobalValueData::Symbol { tls: false, .. } + ), "tls static referenced without Rvalue::ThreadLocalRef" ); CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout) @@ -447,7 +450,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut Constan data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64); } - module.define_data(data_id, &data_ctx).unwrap(); + // FIXME don't duplicate definitions in lazy jit mode + let _ = module.define_data(data_id, &data_ctx); cx.done.insert(data_id); } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs index c21835b1fc3a..6160f9b78d8b 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs @@ -74,10 +74,7 @@ impl WriterRelocate { /// Perform the collected relocations to be usable for JIT usage. #[cfg(feature = "jit")] - pub(super) fn relocate_for_jit( - mut self, - jit_module: &cranelift_simplejit::SimpleJITModule, - ) -> Vec { + pub(super) fn relocate_for_jit(mut self, jit_module: &cranelift_jit::JITModule) -> Vec { use std::convert::TryInto; for reloc in self.relocs.drain(..) { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index e0f62b64e6bb..49de927cdba0 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -15,11 +15,11 @@ pub(crate) struct UnwindContext<'tcx> { } impl<'tcx> UnwindContext<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { + pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa, pic_eh_frame: bool) -> Self { let mut frame_table = FrameTable::default(); let cie_id = if let Some(mut cie) = isa.create_systemv_cie() { - if isa.flags().is_pic() { + if pic_eh_frame { cie.fde_address_encoding = gimli::DwEhPe(gimli::DW_EH_PE_pcrel.0 | gimli::DW_EH_PE_sdata4.0); } @@ -80,7 +80,7 @@ impl<'tcx> UnwindContext<'tcx> { #[cfg(feature = "jit")] pub(crate) unsafe fn register_jit( self, - jit_module: &cranelift_simplejit::SimpleJITModule, + jit_module: &cranelift_jit::JITModule, ) -> Option { let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian( self.tcx, diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 491d6cbbf796..16f9bfc99189 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -8,7 +8,7 @@ use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::cstore::EncodedMetadata; -use rustc_middle::mir::mono::CodegenUnit; +use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; use rustc_session::cgu_reuse_tracker::CguReuse; use rustc_session::config::{DebugInfo, OutputType}; @@ -75,6 +75,7 @@ fn emit_module( name, kind, object: Some(tmp_file), + dwarf_object: None, bytecode: None, }, work_product, @@ -111,6 +112,7 @@ fn reuse_workproduct_for_cgu( name: cgu.name().to_string(), kind: ModuleKind::Regular, object, + dwarf_object: None, bytecode: None, } } @@ -144,11 +146,34 @@ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodege } } - let mut cx = crate::CodegenCx::new(tcx, module, tcx.sess.opts.debuginfo != DebugInfo::None); + let mut cx = crate::CodegenCx::new( + tcx, + module, + tcx.sess.opts.debuginfo != DebugInfo::None, + true, + ); super::predefine_mono_items(&mut cx, &mono_items); for (mono_item, (linkage, visibility)) in mono_items { let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); - super::codegen_mono_item(&mut cx, mono_item, linkage); + match mono_item { + MonoItem::Fn(inst) => { + cx.tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, inst, linkage) + }); + } + MonoItem::Static(def_id) => { + crate::constant::codegen_static(&mut cx.constants_cx, def_id) + } + MonoItem::GlobalAsm(hir_id) => { + let item = cx.tcx.hir().expect_item(hir_id); + if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind { + cx.global_asm.push_str(&*asm.as_str()); + cx.global_asm.push_str("\n\n"); + } else { + bug!("Expected GlobalAsm found {:?}", item); + } + } + } } let (mut module, global_asm, debug, mut unwind_context) = tcx.sess.time("finalize CodegenCx", || cx.finalize()); @@ -234,7 +259,7 @@ pub(super) fn run_aot( tcx.sess.abort_if_errors(); let mut allocator_module = new_module(tcx, "allocator_shim".to_string()); - let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa()); + let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa(), true); let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context); @@ -290,6 +315,7 @@ pub(super) fn run_aot( name: metadata_cgu_name, kind: ModuleKind::Metadata, object: Some(tmp_file), + dwarf_object: None, bytecode: None, }) } else { diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 5a844841c2ce..9a42c675cc14 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -1,16 +1,23 @@ //! The JIT driver uses [`cranelift_simplejit`] to JIT execute programs without writing any object //! files. +use std::cell::RefCell; use std::ffi::CString; use std::os::raw::{c_char, c_int}; use rustc_codegen_ssa::CrateInfo; +use rustc_middle::mir::mono::MonoItem; -use cranelift_simplejit::{SimpleJITBuilder, SimpleJITModule}; +use cranelift_jit::{JITBuilder, JITModule}; use crate::prelude::*; +use crate::{CodegenCx, CodegenMode}; -pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { +thread_local! { + pub static CURRENT_MODULE: RefCell> = RefCell::new(None); +} + +pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! { if !tcx.sess.opts.output_types.should_codegen() { tcx.sess.fatal("JIT mode doesn't work with `cargo check`."); } @@ -35,12 +42,13 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { let imported_symbols = load_imported_symbols_for_jit(tcx); - let mut jit_builder = SimpleJITBuilder::with_isa( - crate::build_isa(tcx.sess, false), + let mut jit_builder = JITBuilder::with_isa( + crate::build_isa(tcx.sess), cranelift_module::default_libcall_names(), ); + jit_builder.hotswap(matches!(codegen_mode, CodegenMode::JitLazy)); jit_builder.symbols(imported_symbols); - let mut jit_module = SimpleJITModule::new(jit_builder); + let mut jit_module = JITModule::new(jit_builder); assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type()); let sig = Signature { @@ -66,20 +74,42 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { .into_iter() .collect::>(); - let mut cx = crate::CodegenCx::new(tcx, jit_module, false); + let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); + + super::time(tcx, "codegen mono items", || { + super::predefine_mono_items(&mut cx, &mono_items); + for (mono_item, (linkage, visibility)) in mono_items { + let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); + match mono_item { + MonoItem::Fn(inst) => match codegen_mode { + CodegenMode::Aot => unreachable!(), + CodegenMode::Jit => { + cx.tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, inst, linkage) + }); + } + CodegenMode::JitLazy => codegen_shim(&mut cx, inst), + }, + MonoItem::Static(def_id) => { + crate::constant::codegen_static(&mut cx.constants_cx, def_id); + } + MonoItem::GlobalAsm(hir_id) => { + let item = cx.tcx.hir().expect_item(hir_id); + tcx.sess + .span_fatal(item.span, "Global asm is not supported in JIT mode"); + } + } + } + }); let (mut jit_module, global_asm, _debug, mut unwind_context) = - super::time(tcx, "codegen mono items", || { - super::predefine_mono_items(&mut cx, &mono_items); - for (mono_item, (linkage, visibility)) in mono_items { - let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); - super::codegen_mono_item(&mut cx, mono_item, linkage); - } - tcx.sess.time("finalize CodegenCx", || cx.finalize()) - }); + tcx.sess.time("finalize CodegenCx", || cx.finalize()); + jit_module.finalize_definitions(); + if !global_asm.is_empty() { - tcx.sess.fatal("Global asm is not supported in JIT mode"); + tcx.sess.fatal("Inline asm is not supported in JIT mode"); } + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut jit_module, &mut unwind_context, true); crate::allocator::codegen(tcx, &mut jit_module, &mut unwind_context); @@ -91,7 +121,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id); - println!("Rustc codegen cranelift will JIT run the executable, because --jit was passed"); + println!("Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed"); let f: extern "C" fn(c_int, *const *const c_char) -> c_int = unsafe { ::std::mem::transmute(finalized_main) }; @@ -107,11 +137,50 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { // useful as some dynamic linkers use it as a marker to jump over. argv.push(std::ptr::null()); + CURRENT_MODULE + .with(|current_module| assert!(current_module.borrow_mut().replace(jit_module).is_none())); + let ret = f(args.len() as c_int, argv.as_ptr()); std::process::exit(ret); } +#[no_mangle] +extern "C" fn __clif_jit_fn(instance_ptr: *const Instance<'static>) -> *const u8 { + rustc_middle::ty::tls::with(|tcx| { + // lift is used to ensure the correct lifetime for instance. + let instance = tcx.lift(unsafe { *instance_ptr }).unwrap(); + + CURRENT_MODULE.with(|jit_module| { + let mut jit_module = jit_module.borrow_mut(); + let jit_module = jit_module.as_mut().unwrap(); + let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); + + let (name, sig) = crate::abi::get_function_name_and_sig( + tcx, + cx.module.isa().triple(), + instance, + true, + ); + let func_id = cx + .module + .declare_function(&name, Linkage::Export, &sig) + .unwrap(); + cx.module.prepare_for_function_redefine(func_id).unwrap(); + + tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, instance, Linkage::Export) + }); + + let (jit_module, global_asm, _debug_context, unwind_context) = cx.finalize(); + assert!(global_asm.is_empty()); + jit_module.finalize_definitions(); + std::mem::forget(unsafe { unwind_context.register_jit(&jit_module) }); + jit_module.get_finalized_function(func_id) + }) + }) +} + fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { use rustc_middle::middle::dependency_format::Linkage; @@ -171,3 +240,68 @@ fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { imported_symbols } + +pub(super) fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx, impl Module>, inst: Instance<'tcx>) { + let tcx = cx.tcx; + + let pointer_type = cx.module.target_config().pointer_type(); + + let (name, sig) = + crate::abi::get_function_name_and_sig(tcx, cx.module.isa().triple(), inst, true); + let func_id = cx + .module + .declare_function(&name, Linkage::Export, &sig) + .unwrap(); + + let instance_ptr = Box::into_raw(Box::new(inst)); + + let jit_fn = cx + .module + .declare_function( + "__clif_jit_fn", + Linkage::Import, + &Signature { + call_conv: cx.module.target_config().default_call_conv, + params: vec![AbiParam::new(pointer_type)], + returns: vec![AbiParam::new(pointer_type)], + }, + ) + .unwrap(); + + let mut trampoline = Function::with_name_signature(ExternalName::default(), sig.clone()); + let mut builder_ctx = FunctionBuilderContext::new(); + let mut trampoline_builder = FunctionBuilder::new(&mut trampoline, &mut builder_ctx); + + let jit_fn = cx + .module + .declare_func_in_func(jit_fn, trampoline_builder.func); + let sig_ref = trampoline_builder.func.import_signature(sig); + + let entry_block = trampoline_builder.create_block(); + trampoline_builder.append_block_params_for_function_params(entry_block); + let fn_args = trampoline_builder + .func + .dfg + .block_params(entry_block) + .to_vec(); + + trampoline_builder.switch_to_block(entry_block); + let instance_ptr = trampoline_builder + .ins() + .iconst(pointer_type, instance_ptr as u64 as i64); + let jitted_fn = trampoline_builder.ins().call(jit_fn, &[instance_ptr]); + let jitted_fn = trampoline_builder.func.dfg.inst_results(jitted_fn)[0]; + let call_inst = trampoline_builder + .ins() + .call_indirect(sig_ref, jitted_fn, &fn_args); + let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec(); + trampoline_builder.ins().return_(&ret_vals); + + cx.module + .define_function( + func_id, + &mut Context::for_function(trampoline), + &mut cranelift_codegen::binemit::NullTrapSink {}, + ) + .unwrap(); +} diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index 7b8cc2ddd48d..9f4ea9a38655 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -7,6 +7,7 @@ use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::mir::mono::{Linkage as RLinkage, MonoItem, Visibility}; use crate::prelude::*; +use crate::CodegenMode; mod aot; #[cfg(feature = "jit")] @@ -20,24 +21,25 @@ pub(crate) fn codegen_crate( ) -> Box { tcx.sess.abort_if_errors(); - if config.use_jit { - let is_executable = tcx - .sess - .crate_types() - .contains(&rustc_session::config::CrateType::Executable); - if !is_executable { - tcx.sess.fatal("can't jit non-executable crate"); + match config.codegen_mode { + CodegenMode::Aot => aot::run_aot(tcx, metadata, need_metadata_module), + CodegenMode::Jit | CodegenMode::JitLazy => { + let is_executable = tcx + .sess + .crate_types() + .contains(&rustc_session::config::CrateType::Executable); + if !is_executable { + tcx.sess.fatal("can't jit non-executable crate"); + } + + #[cfg(feature = "jit")] + let _: ! = jit::run_jit(tcx, config.codegen_mode); + + #[cfg(not(feature = "jit"))] + tcx.sess + .fatal("jit support was disabled when compiling rustc_codegen_cranelift"); } - - #[cfg(feature = "jit")] - let _: ! = jit::run_jit(tcx); - - #[cfg(not(feature = "jit"))] - tcx.sess - .fatal("jit support was disabled when compiling rustc_codegen_cranelift"); } - - aot::run_aot(tcx, metadata, need_metadata_module) } fn predefine_mono_items<'tcx>( @@ -63,30 +65,6 @@ fn predefine_mono_items<'tcx>( }); } -fn codegen_mono_item<'tcx, M: Module>( - cx: &mut crate::CodegenCx<'tcx, M>, - mono_item: MonoItem<'tcx>, - linkage: Linkage, -) { - match mono_item { - MonoItem::Fn(inst) => { - cx.tcx - .sess - .time("codegen fn", || crate::base::codegen_fn(cx, inst, linkage)); - } - MonoItem::Static(def_id) => crate::constant::codegen_static(&mut cx.constants_cx, def_id), - MonoItem::GlobalAsm(hir_id) => { - let item = cx.tcx.hir().expect_item(hir_id); - if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind { - cx.global_asm.push_str(&*asm.as_str()); - cx.global_asm.push_str("\n\n"); - } else { - bug!("Expected GlobalAsm found {:?}", item); - } - } - } -} - fn time(tcx: TyCtxt<'_>, name: &'static str, f: impl FnOnce() -> R) -> R { if std::env::var("CG_CLIF_DISPLAY_CG_TIME") .as_ref() diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index 171445f2d71b..d58e4d499584 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -23,8 +23,8 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8` llvm.x86.sse2.pmovmskb.128 | llvm.x86.avx2.pmovmskb | llvm.x86.sse2.movmsk.pd, (c a) { - let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, a.layout()); - let lane_ty = fx.clif_type(lane_layout.ty).unwrap(); + let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx); + let lane_ty = fx.clif_type(lane_ty).unwrap(); assert!(lane_count <= 32); let mut res = fx.bcx.ins().iconst(types::I32, 0); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index df8aa1b3e698..be5b247bb9f0 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -171,27 +171,6 @@ macro validate_simd_type($fx:ident, $intrinsic:ident, $span:ident, $ty:expr) { } } -fn lane_type_and_count<'tcx>( - tcx: TyCtxt<'tcx>, - layout: TyAndLayout<'tcx>, -) -> (TyAndLayout<'tcx>, u16) { - assert!(layout.ty.is_simd()); - let lane_count = match layout.fields { - rustc_target::abi::FieldsShape::Array { stride: _, count } => u16::try_from(count).unwrap(), - _ => unreachable!("lane_type_and_count({:?})", layout), - }; - let lane_layout = layout - .field( - &ty::layout::LayoutCx { - tcx, - param_env: ParamEnv::reveal_all(), - }, - 0, - ) - .unwrap(); - (lane_layout, lane_count) -} - pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> Option { let (element, count) = match &layout.abi { Abi::Vector { element, count } => (element.clone(), *count), @@ -218,8 +197,10 @@ fn simd_for_each_lane<'tcx, M: Module>( ) { let layout = val.layout(); - let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); - let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + let ret_lane_layout = fx.layout_of(ret_lane_ty); assert_eq!(lane_count, ret_lane_count); for lane_idx in 0..lane_count { @@ -248,8 +229,10 @@ fn simd_pair_for_each_lane<'tcx, M: Module>( assert_eq!(x.layout(), y.layout()); let layout = x.layout(); - let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); - let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + let ret_lane_layout = fx.layout_of(ret_lane_ty); assert_eq!(lane_count, ret_lane_count); for lane in 0..lane_count { @@ -269,13 +252,14 @@ fn simd_reduce<'tcx, M: Module>( ret: CPlace<'tcx>, f: impl Fn(&mut FunctionCx<'_, 'tcx, M>, TyAndLayout<'tcx>, Value, Value) -> Value, ) { - let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, val.layout()); + let (lane_count, lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); assert_eq!(lane_layout, ret.layout()); let mut res_val = val.value_field(fx, mir::Field::new(0)).load_scalar(fx); for lane_idx in 1..lane_count { let lane = val - .value_field(fx, mir::Field::new(lane_idx.into())) + .value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())) .load_scalar(fx); res_val = f(fx, lane_layout, res_val, lane); } @@ -289,14 +273,14 @@ fn simd_reduce_bool<'tcx, M: Module>( ret: CPlace<'tcx>, f: impl Fn(&mut FunctionCx<'_, 'tcx, M>, Value, Value) -> Value, ) { - let (_lane_layout, lane_count) = lane_type_and_count(fx.tcx, val.layout()); + let (lane_count, _lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx); assert!(ret.layout().ty.is_bool()); let res_val = val.value_field(fx, mir::Field::new(0)).load_scalar(fx); let mut res_val = fx.bcx.ins().band_imm(res_val, 1); // mask to boolean for lane_idx in 1..lane_count { let lane = val - .value_field(fx, mir::Field::new(lane_idx.into())) + .value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())) .load_scalar(fx); let lane = fx.bcx.ins().band_imm(lane, 1); // mask to boolean res_val = f(fx, res_val, lane); @@ -460,9 +444,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( "abort" => { trap_abort(fx, "Called intrinsic::abort."); } - "unreachable" => { - trap_unreachable(fx, "[corruption] Called intrinsic::unreachable."); - } "transmute" => { crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", span); } @@ -575,12 +556,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( fx.bcx.call_memmove(fx.cx.module.target_config(), dst, src, byte_amount); } }; - discriminant_value, (c ptr) { - let pointee_layout = fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); - let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), pointee_layout); - let discr = crate::discriminant::codegen_get_discriminant(fx, val, ret.layout()); - ret.write_cvalue(fx, discr); - }; size_of_val, (c ptr) { let layout = fx.layout_of(T); let size = if layout.is_unsized() { @@ -641,22 +616,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( ); ret.write_cvalue(fx, res); }; - _ if intrinsic.starts_with("wrapping_"), (c x, c y) { - assert_eq!(x.layout().ty, y.layout().ty); - let bin_op = match intrinsic { - "wrapping_add" => BinOp::Add, - "wrapping_sub" => BinOp::Sub, - "wrapping_mul" => BinOp::Mul, - _ => unreachable!("intrinsic {}", intrinsic), - }; - let res = crate::num::codegen_int_binop( - fx, - bin_op, - x, - y, - ); - ret.write_cvalue(fx, res); - }; _ if intrinsic.starts_with("saturating_"), (c lhs, c rhs) { assert_eq!(lhs.layout().ty, rhs.layout().ty); let bin_op = match intrinsic { @@ -916,7 +875,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( dest.write_cvalue(fx, val); }; - size_of | pref_align_of | min_align_of | needs_drop | type_id | type_name | variant_count, () { + pref_align_of | min_align_of | needs_drop | type_id | type_name | variant_count, () { let const_val = fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap(); let val = crate::constant::codegen_const_value( diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 2b32e866e5ef..e0eb5c59590f 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -73,11 +73,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( assert_eq!(x.layout(), y.layout()); let layout = x.layout(); - let (lane_type, lane_count) = lane_type_and_count(fx.tcx, layout); - let (ret_lane_type, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); - assert_eq!(lane_type, ret_lane_type); - assert_eq!(n, ret_lane_count); + assert_eq!(lane_ty, ret_lane_ty); + assert_eq!(u64::from(n), ret_lane_count); let total_len = lane_count * 2; @@ -105,14 +105,14 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; for &idx in &indexes { - assert!(idx < total_len, "idx {} out of range 0..{}", idx, total_len); + assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len); } for (out_idx, in_idx) in indexes.into_iter().enumerate() { - let in_lane = if in_idx < lane_count { + let in_lane = if u64::from(in_idx) < lane_count { x.value_field(fx, mir::Field::new(in_idx.into())) } else { - y.value_field(fx, mir::Field::new((in_idx - lane_count).into())) + y.value_field(fx, mir::Field::new(usize::from(in_idx) - usize::try_from(lane_count).unwrap())) }; let out_lane = ret.place_field(fx, mir::Field::new(out_idx)); out_lane.write_cvalue(fx, in_lane); @@ -131,7 +131,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); - let (_lane_type, lane_count) = lane_type_and_count(fx.tcx, base.layout()); + let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count)); } @@ -160,7 +160,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); - let (_lane_type, lane_count) = lane_type_and_count(fx.tcx, v.layout()); + let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count)); } @@ -212,12 +212,13 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( assert_eq!(a.layout(), c.layout()); let layout = a.layout(); - let (_lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); - let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); assert_eq!(lane_count, ret_lane_count); + let ret_lane_layout = fx.layout_of(ret_lane_ty); for lane in 0..lane_count { - let lane = mir::Field::new(lane.into()); + let lane = mir::Field::new(lane.try_into().unwrap()); let a_lane = a.value_field(fx, lane).load_scalar(fx); let b_lane = b.value_field(fx, lane).load_scalar(fx); let c_lane = c.value_field(fx, lane).load_scalar(fx); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index ba9ee0d450ee..6e4f3bf2898d 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -5,7 +5,8 @@ associated_type_bounds, never_type, try_blocks, - hash_drain_filter + hash_drain_filter, + str_split_once )] #![warn(rust_2018_idioms)] #![warn(unused_lifetimes)] @@ -34,6 +35,7 @@ extern crate rustc_target; extern crate rustc_driver; use std::any::Any; +use std::str::FromStr; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; @@ -141,8 +143,8 @@ struct CodegenCx<'tcx, M: Module> { } impl<'tcx, M: Module> CodegenCx<'tcx, M> { - fn new(tcx: TyCtxt<'tcx>, module: M, debug_info: bool) -> Self { - let unwind_context = UnwindContext::new(tcx, module.isa()); + fn new(tcx: TyCtxt<'tcx>, module: M, debug_info: bool, pic_eh_frame: bool) -> Self { + let unwind_context = UnwindContext::new(tcx, module.isa(), pic_eh_frame); let debug_context = if debug_info { Some(DebugContext::new(tcx, module.isa())) } else { @@ -172,12 +174,55 @@ impl<'tcx, M: Module> CodegenCx<'tcx, M> { } #[derive(Copy, Clone, Debug)] +pub enum CodegenMode { + Aot, + Jit, + JitLazy, +} + +impl Default for CodegenMode { + fn default() -> Self { + CodegenMode::Aot + } +} + +impl FromStr for CodegenMode { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "aot" => Ok(CodegenMode::Aot), + "jit" => Ok(CodegenMode::Jit), + "jit-lazy" => Ok(CodegenMode::JitLazy), + _ => Err(format!("Unknown codegen mode `{}`", s)), + } + } +} + +#[derive(Copy, Clone, Debug, Default)] pub struct BackendConfig { - pub use_jit: bool, + pub codegen_mode: CodegenMode, +} + +impl BackendConfig { + fn from_opts(opts: &[String]) -> Result { + let mut config = BackendConfig::default(); + for opt in opts { + if let Some((name, value)) = opt.split_once('=') { + match name { + "mode" => config.codegen_mode = value.parse()?, + _ => return Err(format!("Unknown option `{}`", name)), + } + } else { + return Err(format!("Invalid option `{}`", opt)); + } + } + Ok(config) + } } pub struct CraneliftCodegenBackend { - pub config: BackendConfig, + pub config: Option, } impl CodegenBackend for CraneliftCodegenBackend { @@ -204,7 +249,13 @@ impl CodegenBackend for CraneliftCodegenBackend { metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box { - let res = driver::codegen_crate(tcx, metadata, need_metadata_module, self.config); + let config = if let Some(config) = self.config { + config + } else { + BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) + .unwrap_or_else(|err| tcx.sess.fatal(&err)) + }; + let res = driver::codegen_crate(tcx, metadata, need_metadata_module, config); rustc_symbol_mangling::test::report_symbol_names(tcx); @@ -250,17 +301,13 @@ fn target_triple(sess: &Session) -> target_lexicon::Triple { sess.target.llvm_target.parse().unwrap() } -fn build_isa(sess: &Session, enable_pic: bool) -> Box { +fn build_isa(sess: &Session) -> Box { use target_lexicon::BinaryFormat; let target_triple = crate::target_triple(sess); let mut flags_builder = settings::builder(); - if enable_pic { - flags_builder.enable("is_pic").unwrap(); - } else { - flags_builder.set("is_pic", "false").unwrap(); - } + flags_builder.enable("is_pic").unwrap(); flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided flags_builder .set( @@ -283,8 +330,6 @@ fn build_isa(sess: &Session, enable_pic: bool) -> Box { @@ -297,7 +342,7 @@ fn build_isa(sess: &Session, enable_pic: bool) -> Box { sess.warn("Optimizing for size is not supported. Just ignoring the request"); } - }*/ + } let flags = settings::Flags::new(flags_builder); @@ -311,7 +356,5 @@ fn build_isa(sess: &Session, enable_pic: bool) -> Box Box { - Box::new(CraneliftCodegenBackend { - config: BackendConfig { use_jit: false }, - }) + Box::new(CraneliftCodegenBackend { config: None }) } diff --git a/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs b/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs index f8e0f3af3d0a..a575ed8dc35f 100644 --- a/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs +++ b/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs @@ -73,7 +73,7 @@ pub(crate) fn make_branchable_value(bcx: &mut FunctionBuilder<'_>, arg: Value) - })() .unwrap_or_else(|| { match bcx.func.dfg.value_type(arg) { - types::I8 | types::I32 => { + types::I8 | types::I16 => { // WORKAROUND for brz.i8 and brnz.i8 not yet being implemented bcx.ins().uextend(types::I32, arg) } @@ -81,3 +81,40 @@ pub(crate) fn make_branchable_value(bcx: &mut FunctionBuilder<'_>, arg: Value) - } }) } + +/// Returns whether the branch is statically known to be taken or `None` if it isn't statically known. +pub(crate) fn maybe_known_branch_taken( + bcx: &FunctionBuilder<'_>, + arg: Value, + test_zero: bool, +) -> Option { + let arg_inst = if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) { + arg_inst + } else { + return None; + }; + + match bcx.func.dfg[arg_inst] { + InstructionData::UnaryBool { + opcode: Opcode::Bconst, + imm, + } => { + if test_zero { + Some(!imm) + } else { + Some(imm) + } + } + InstructionData::UnaryImm { + opcode: Opcode::Iconst, + imm, + } => { + if test_zero { + Some(imm.bits() == 0) + } else { + Some(imm.bits() != 0) + } + } + _ => None, + } +} diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs index a9f060e51d8f..22c94fec82fc 100644 --- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs @@ -53,6 +53,7 @@ //! ``` use std::fmt; +use std::io::Write; use cranelift_codegen::{ entity::SecondaryMap, @@ -200,32 +201,24 @@ impl FunctionCx<'_, '_, M> { } } -pub(crate) fn write_clif_file<'tcx>( - tcx: TyCtxt<'tcx>, - postfix: &str, - isa: Option<&dyn cranelift_codegen::isa::TargetIsa>, - instance: Instance<'tcx>, - context: &cranelift_codegen::Context, - mut clif_comments: &CommentWriter, -) { - use std::io::Write; - - if !cfg!(debug_assertions) - && !tcx +pub(crate) fn should_write_ir(tcx: TyCtxt<'_>) -> bool { + cfg!(debug_assertions) + || tcx .sess .opts .output_types .contains_key(&OutputType::LlvmAssembly) - { +} + +pub(crate) fn write_ir_file<'tcx>( + tcx: TyCtxt<'tcx>, + name: &str, + write: impl FnOnce(&mut dyn Write) -> std::io::Result<()>, +) { + if !should_write_ir(tcx) { return; } - let value_ranges = isa.map(|isa| { - context - .build_value_labels_ranges(isa) - .expect("value location ranges") - }); - let clif_output_dir = tcx.output_filenames(LOCAL_CRATE).with_extension("clif"); match std::fs::create_dir(&clif_output_dir) { @@ -234,41 +227,58 @@ pub(crate) fn write_clif_file<'tcx>( res @ Err(_) => res.unwrap(), } - let clif_file_name = clif_output_dir.join(format!( - "{}.{}.clif", - tcx.symbol_name(instance).name, - postfix - )); - - let mut clif = String::new(); - cranelift_codegen::write::decorate_function( - &mut clif_comments, - &mut clif, - &context.func, - &DisplayFunctionAnnotations { - isa: Some(&*crate::build_isa( - tcx.sess, true, /* PIC doesn't matter here */ - )), - value_ranges: value_ranges.as_ref(), - }, - ) - .unwrap(); + let clif_file_name = clif_output_dir.join(name); let res: std::io::Result<()> = try { let mut file = std::fs::File::create(clif_file_name)?; - let target_triple = crate::target_triple(tcx.sess); - writeln!(file, "test compile")?; - writeln!(file, "set is_pic")?; - writeln!(file, "set enable_simd")?; - writeln!(file, "target {} haswell", target_triple)?; - writeln!(file)?; - file.write_all(clif.as_bytes())?; + write(&mut file)?; }; if let Err(err) = res { - tcx.sess.warn(&format!("err writing clif file: {}", err)); + tcx.sess.warn(&format!("error writing ir file: {}", err)); } } +pub(crate) fn write_clif_file<'tcx>( + tcx: TyCtxt<'tcx>, + postfix: &str, + isa: Option<&dyn cranelift_codegen::isa::TargetIsa>, + instance: Instance<'tcx>, + context: &cranelift_codegen::Context, + mut clif_comments: &CommentWriter, +) { + write_ir_file( + tcx, + &format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), + |file| { + let value_ranges = isa.map(|isa| { + context + .build_value_labels_ranges(isa) + .expect("value location ranges") + }); + + let mut clif = String::new(); + cranelift_codegen::write::decorate_function( + &mut clif_comments, + &mut clif, + &context.func, + &DisplayFunctionAnnotations { + isa: Some(&*crate::build_isa(tcx.sess)), + value_ranges: value_ranges.as_ref(), + }, + ) + .unwrap(); + + writeln!(file, "test compile")?; + writeln!(file, "set is_pic")?; + writeln!(file, "set enable_simd")?; + writeln!(file, "target {} haswell", crate::target_triple(tcx.sess))?; + writeln!(file)?; + file.write_all(clif.as_bytes())?; + Ok(()) + }, + ); +} + impl fmt::Debug for FunctionCx<'_, '_, M> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{:?}", self.instance.substs)?; diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index cb40d4ed9a6d..5bcb11fd515a 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -480,17 +480,19 @@ impl<'tcx> CPlace<'tcx> { // fn(&T) -> for<'l> fn(&'l T) is allowed } (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { - let from_traits = fx - .tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from_traits); - let to_traits = fx - .tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to_traits); - assert_eq!( - from_traits, to_traits, - "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}", - from_traits, to_traits, fx, - ); + for (from, to) in from_traits.iter().zip(to_traits) { + let from = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from); + let to = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to); + assert_eq!( + from, to, + "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}", + from_traits, to_traits, fx, + ); + } // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed } _ => { diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 238abc0d8bdf..8f15586a9dc0 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -158,7 +158,8 @@ fn build_vtable<'tcx>( ) .unwrap(); - fx.cx.module.define_data(data_id, &data_ctx).unwrap(); + // FIXME don't duplicate definitions in lazy jit mode + let _ = fx.cx.module.define_data(data_id, &data_ctx); data_id } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 64fd1d09cc24..29415973ed07 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -6,7 +6,9 @@ use crate::llvm::{self, build_string, False, True}; use crate::{LlvmCodegenBackend, ModuleLlvm}; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::FxHashMap; @@ -728,7 +730,14 @@ pub unsafe fn optimize_thin_module( cgcx: &CodegenContext, ) -> Result, FatalError> { let diag_handler = cgcx.create_diag_handler(); - let tm = (cgcx.tm_factory.0)().map_err(|e| write::llvm_err(&diag_handler, &e))?; + + let module_name = &thin_module.shared.module_names[thin_module.idx]; + let split_dwarf_file = cgcx + .output_filenames + .split_dwarf_filename(cgcx.split_dwarf_kind, Some(module_name.to_str().unwrap())); + let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file }; + let tm = + (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, &e))?; // Right now the implementation we've got only works over serialized // modules, so we create a fresh new LLVM context and parse the module @@ -736,12 +745,8 @@ pub unsafe fn optimize_thin_module( // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); - let llmod_raw = parse_module( - llcx, - &thin_module.shared.module_names[thin_module.idx], - thin_module.data(), - &diag_handler, - )? as *const _; + let llmod_raw = + parse_module(llcx, &module_name, thin_module.data(), &diag_handler)? as *const _; let module = ModuleCodegen { module_llvm: ModuleLlvm { llmod_raw, llcx, tm }, name: thin_module.name().to_string(), diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 7407dfc455d8..230e11f274ea 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -11,7 +11,10 @@ use crate::llvm_util; use crate::type_::Type; use crate::LlvmCodegenBackend; use crate::ModuleLlvm; -use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; +use rustc_codegen_ssa::back::write::{ + BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig, + TargetMachineFactoryFn, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; use rustc_data_structures::small_c_str::SmallCStr; @@ -20,7 +23,9 @@ use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath}; +use rustc_session::config::{ + self, Lto, OutputType, Passes, SanitizerSet, SplitDwarfKind, SwitchWithOptPath, +}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; @@ -49,11 +54,31 @@ pub fn write_output_file( pm: &llvm::PassManager<'ll>, m: &'ll llvm::Module, output: &Path, + dwo_output: Option<&Path>, file_type: llvm::FileType, ) -> Result<(), FatalError> { unsafe { let output_c = path_to_c_string(output); - let result = llvm::LLVMRustWriteOutputFile(target, pm, m, output_c.as_ptr(), file_type); + let result = if let Some(dwo_output) = dwo_output { + let dwo_output_c = path_to_c_string(dwo_output); + llvm::LLVMRustWriteOutputFile( + target, + pm, + m, + output_c.as_ptr(), + dwo_output_c.as_ptr(), + file_type, + ) + } else { + llvm::LLVMRustWriteOutputFile( + target, + pm, + m, + output_c.as_ptr(), + std::ptr::null(), + file_type, + ) + }; result.into_result().map_err(|()| { let msg = format!("could not write output to {}", output.display()); llvm_err(handler, &msg) @@ -62,12 +87,17 @@ pub fn write_output_file( } pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm::TargetMachine { - target_machine_factory(sess, config::OptLevel::No)() + let config = TargetMachineFactoryConfig { split_dwarf_file: None }; + target_machine_factory(sess, config::OptLevel::No)(config) .unwrap_or_else(|err| llvm_err(sess.diagnostic(), &err).raise()) } -pub fn create_target_machine(tcx: TyCtxt<'_>) -> &'static mut llvm::TargetMachine { - target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))() +pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine { + let split_dwarf_file = tcx + .output_filenames(LOCAL_CRATE) + .split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(mod_name)); + let config = TargetMachineFactoryConfig { split_dwarf_file }; + target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))(config) .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise()) } @@ -122,7 +152,7 @@ fn to_llvm_code_model(code_model: Option) -> llvm::CodeModel { pub fn target_machine_factory( sess: &Session, optlvl: config::OptLevel, -) -> Arc Result<&'static mut llvm::TargetMachine, String> + Send + Sync> { +) -> TargetMachineFactoryFn { let reloc_model = to_llvm_relocation_model(sess.relocation_model()); let (opt_level, _) = to_llvm_opt_settings(optlvl); @@ -163,7 +193,10 @@ pub fn target_machine_factory( let use_init_array = !sess.opts.debugging_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section); - Arc::new(move || { + Arc::new(move |config: TargetMachineFactoryConfig| { + let split_dwarf_file = config.split_dwarf_file.unwrap_or_default(); + let split_dwarf_file = CString::new(split_dwarf_file.to_str().unwrap()).unwrap(); + let tm = unsafe { llvm::LLVMRustCreateTargetMachine( triple.as_ptr(), @@ -182,6 +215,7 @@ pub fn target_machine_factory( emit_stack_size_section, relax_elf_relocations, use_init_array, + split_dwarf_file.as_ptr(), ) }; @@ -451,7 +485,7 @@ pub(crate) unsafe fn optimize( diag_handler: &Handler, module: &ModuleCodegen, config: &ModuleConfig, -) -> Result<(), FatalError> { +) { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &module.name[..]); let llmod = module.module_llvm.llmod(); @@ -477,7 +511,7 @@ pub(crate) unsafe fn optimize( _ => llvm::OptStage::PreLinkNoLTO, }; optimize_with_new_llvm_pass_manager(cgcx, module, config, opt_level, opt_stage); - return Ok(()); + return; } if cgcx.prof.llvm_recording_enabled() { @@ -600,7 +634,6 @@ pub(crate) unsafe fn optimize( llvm::LLVMDisposePassManager(fpm); llvm::LLVMDisposePassManager(mpm); } - Ok(()) } unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static mut llvm::Pass>) { @@ -785,7 +818,15 @@ pub(crate) unsafe fn codegen( llmod }; with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(diag_handler, tm, cpm, llmod, &path, llvm::FileType::AssemblyFile) + write_output_file( + diag_handler, + tm, + cpm, + llmod, + &path, + None, + llvm::FileType::AssemblyFile, + ) })?; } @@ -794,6 +835,15 @@ pub(crate) unsafe fn codegen( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]); + + let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name); + let dwo_out = match cgcx.split_dwarf_kind { + // Don't change how DWARF is emitted in single mode (or when disabled). + SplitDwarfKind::None | SplitDwarfKind::Single => None, + // Emit (a subset of the) DWARF into a separate file in split mode. + SplitDwarfKind::Split => Some(dwo_out.as_path()), + }; + with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file( diag_handler, @@ -801,6 +851,7 @@ pub(crate) unsafe fn codegen( cpm, llmod, &obj_out, + dwo_out, llvm::FileType::ObjectFile, ) })?; @@ -828,6 +879,7 @@ pub(crate) unsafe fn codegen( Ok(module.into_compiled_module( config.emit_obj != EmitObj::None, + cgcx.split_dwarf_kind == SplitDwarfKind::Split, config.emit_bc, &cgcx.output_filenames, )) diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 7d01f6a54995..d5be3132dee1 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -1,17 +1,15 @@ -//! Codegen the completed AST to the LLVM IR. -//! -//! Some functions here, such as codegen_block and codegen_expr, return a value -- -//! the result of the codegen to LLVM -- while others, such as codegen_fn -//! and mono_item, are called only for the side effect of adding a -//! particular definition to the LLVM IR output we're producing. +//! Codegen the MIR to the LLVM IR. //! //! Hopefully useful general knowledge about codegen: //! -//! * There's no way to find out the `Ty` type of a Value. Doing so +//! * There's no way to find out the [`Ty`] type of a [`Value`]. Doing so //! would be "trying to get the eggs out of an omelette" (credit: -//! pcwalton). You can, instead, find out its `llvm::Type` by calling `val_ty`, -//! but one `llvm::Type` corresponds to many `Ty`s; for instance, `tup(int, int, -//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`. +//! pcwalton). You can, instead, find out its [`llvm::Type`] by calling [`val_ty`], +//! but one [`llvm::Type`] corresponds to many [`Ty`]s; for instance, `tup(int, int, +//! int)` and `rec(x=int, y=int, z=int)` will have the same [`llvm::Type`]. +//! +//! [`Ty`]: rustc_middle::ty::Ty +//! [`val_ty`]: common::val_ty use super::ModuleLlvm; diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 34e1b7a60451..58af9d4cd04a 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -314,6 +314,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } +/// Get the [LLVM type][Type] of a [`Value`]. pub fn val_ty(v: &Value) -> &Type { unsafe { llvm::LLVMTypeOf(v) } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 96484034da7c..36a21b38c035 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -993,9 +993,15 @@ pub fn compile_unit_metadata( let producer = format!("clang LLVM ({})", rustc_producer); let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); - let work_dir = tcx.sess.working_dir.0.to_string_lossy(); let flags = "\0"; - let split_name = ""; + + let out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory; + let split_name = tcx + .output_filenames(LOCAL_CRATE) + .split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name)) + .unwrap_or_default(); + let out_dir = out_dir.to_str().unwrap(); + let split_name = split_name.to_str().unwrap(); // FIXME(#60020): // @@ -1020,8 +1026,8 @@ pub fn compile_unit_metadata( debug_context.builder, name_in_debuginfo.as_ptr().cast(), name_in_debuginfo.len(), - work_dir.as_ptr().cast(), - work_dir.len(), + out_dir.as_ptr().cast(), + out_dir.len(), llvm::ChecksumKind::None, ptr::null(), 0, @@ -1039,6 +1045,8 @@ pub fn compile_unit_metadata( split_name.as_ptr().cast(), split_name.len(), kind, + 0, + tcx.sess.opts.debugging_opts.split_dwarf_inlining, ); if tcx.sess.opts.debugging_opts.profile { @@ -1409,10 +1417,11 @@ fn generator_layout_and_saved_local_names( let state_arg = mir::Local::new(1); for var in &body.var_debug_info { - if var.place.local != state_arg { + let place = if let mir::VarDebugInfoContents::Place(p) = var.value { p } else { continue }; + if place.local != state_arg { continue; } - match var.place.projection[..] { + match place.projection[..] { [ // Deref of the `Pin<&mut Self>` state argument. mir::ProjectionElem::Field(..), @@ -2313,13 +2322,13 @@ fn set_members_of_composite_type( DIB(cx), composite_type_metadata, Some(type_array), - type_params, + Some(type_params), ); } } /// Computes the type parameters for a type, if any, for the given metadata. -fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&'ll DIArray> { +fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> &'ll DIArray { if let ty::Adt(def, substs) = *ty.kind() { if substs.types().next().is_some() { let generics = cx.tcx.generics_of(def.did); @@ -2349,10 +2358,10 @@ fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&' }) .collect(); - return Some(create_DIArray(DIB(cx), &template_params[..])); + return create_DIArray(DIB(cx), &template_params[..]); } } - return Some(create_DIArray(DIB(cx), &[])); + return create_DIArray(DIB(cx), &[]); fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec { let mut names = generics diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ec557b7a682e..bf0d499e6c49 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -854,8 +854,8 @@ fn generic_simd_intrinsic( )); } - if name_str.starts_with("simd_shuffle") { - let n: u64 = name_str["simd_shuffle".len()..].parse().unwrap_or_else(|_| { + if let Some(stripped) = name_str.strip_prefix("simd_shuffle") { + let n: u64 = stripped.parse().unwrap_or_else(|_| { span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?") }); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5974b59d39e4..92ac770aca55 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -19,7 +19,9 @@ use back::write::{create_informational_target_machine, create_target_machine}; pub use llvm_util::target_features; use rustc_ast::expand::allocator::AllocatorKind; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::{CodegenResults, CompiledModule}; @@ -34,7 +36,6 @@ use rustc_span::symbol::Symbol; use std::any::Any; use std::ffi::CStr; -use std::sync::Arc; mod back { pub mod archive; @@ -109,7 +110,7 @@ impl ExtraBackendMethods for LlvmCodegenBackend { &self, sess: &Session, optlvl: OptLevel, - ) -> Arc Result<&'static mut llvm::TargetMachine, String> + Send + Sync> { + ) -> TargetMachineFactoryFn { back::write::target_machine_factory(sess, optlvl) } fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str { @@ -159,7 +160,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { module: &ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError> { - back::write::optimize(cgcx, diag_handler, module, config) + Ok(back::write::optimize(cgcx, diag_handler, module, config)) } unsafe fn optimize_thin( cgcx: &CodegenContext, @@ -297,21 +298,19 @@ impl CodegenBackend for LlvmCodegenBackend { codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorReported> { + use crate::back::archive::LlvmArchiveBuilder; + use rustc_codegen_ssa::back::link::link_binary; + // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - sess.time("link_crate", || { - use crate::back::archive::LlvmArchiveBuilder; - use rustc_codegen_ssa::back::link::link_binary; - - let target_cpu = crate::llvm_util::target_cpu(sess); - link_binary::>( - sess, - &codegen_results, - outputs, - &codegen_results.crate_name.as_str(), - target_cpu, - ); - }); + let target_cpu = crate::llvm_util::target_cpu(sess); + link_binary::>( + sess, + &codegen_results, + outputs, + &codegen_results.crate_name.as_str(), + target_cpu, + ); Ok(()) } @@ -331,7 +330,7 @@ impl ModuleLlvm { unsafe { let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names()); let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; - ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx) } + ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx, mod_name) } } } @@ -352,7 +351,13 @@ impl ModuleLlvm { unsafe { let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); let llmod_raw = back::lto::parse_module(llcx, name, buffer, handler)?; - let tm = match (cgcx.tm_factory.0)() { + + let split_dwarf_file = cgcx + .output_filenames + .split_dwarf_filename(cgcx.split_dwarf_kind, Some(name.to_str().unwrap())); + let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file }; + + let tm = match (cgcx.tm_factory)(tm_factory_config) { Ok(m) => m, Err(e) => { handler.struct_err(&e).emit(); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 41482d18946a..707aaa2b53ff 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1830,6 +1830,8 @@ extern "C" { SplitName: *const c_char, SplitNameLen: size_t, kind: DebugEmissionKind, + DWOId: u64, + SplitDebugInlining: bool, ) -> &'a DIDescriptor; pub fn LLVMRustDIBuilderCreateFile( @@ -2151,6 +2153,7 @@ extern "C" { EmitStackSizeSection: bool, RelaxELFRelocations: bool, UseInitArray: bool, + SplitDwarfFile: *const c_char, ) -> Option<&'static mut TargetMachine>; pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine); pub fn LLVMRustAddBuilderLibraryInfo( @@ -2179,6 +2182,7 @@ extern "C" { PM: &PassManager<'a>, M: &'a Module, Output: *const c_char, + DwoOutput: *const c_char, FileType: FileType, ) -> LLVMRustResult; pub fn LLVMRustOptimizeWithNewPassManager( diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 8ea4768f77db..0876907e1194 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -40,9 +40,7 @@ fn uncached_llvm_type<'a, 'tcx>( // FIXME(eddyb) producing readable type names for trait objects can result // in problematically distinct types due to HRTB and subtyping (see #47638). // ty::Dynamic(..) | - ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Generator(..) | ty::Str - if !cx.sess().fewer_names() => - { + ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Generator(..) | ty::Str => { let mut name = with_no_trimmed_paths(|| layout.ty.to_string()); if let (&ty::Adt(def, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) @@ -58,12 +56,6 @@ fn uncached_llvm_type<'a, 'tcx>( } Some(name) } - ty::Adt(..) => { - // If `Some` is returned then a named struct is created in LLVM. Name collisions are - // avoided by LLVM (with increasing suffixes). If rustc doesn't generate names then that - // can improve perf. - Some(String::new()) - } _ => None, }; diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 46febf049c39..55fddb38e10b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::CrateNum; -use rustc_middle::middle::cstore::{EncodedMetadata, LibSource, NativeLib}; +use rustc_middle::middle::cstore::{EncodedMetadata, LibSource}; use rustc_middle::middle::dependency_format::Linkage; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo}; use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SanitizerSet}; @@ -21,7 +21,10 @@ use super::archive::ArchiveBuilder; use super::command::Command; use super::linker::{self, Linker}; use super::rpath::{self, RPathConfig}; -use crate::{looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME}; +use crate::{ + looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, + METADATA_FILENAME, +}; use cc::windows_registry; use tempfile::Builder as TempFileBuilder; @@ -96,6 +99,9 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( path.as_ref(), target_cpu, ); + if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Split { + link_dwarf_object(sess, &out_filename); + } } } if sess.opts.json_artifact_notifications { @@ -107,22 +113,30 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( // Remove the temporary object file and metadata if we aren't saving temps sess.time("link_binary_remove_temps", || { if !sess.opts.cg.save_temps { + let remove_temps_from_module = |module: &CompiledModule| { + if let Some(ref obj) = module.object { + remove(sess, obj); + } + + if let Some(ref obj) = module.dwarf_object { + remove(sess, obj); + } + }; + if sess.opts.output_types.should_codegen() && !preserve_objects_for_their_debuginfo(sess) { - for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { - remove(sess, obj); + for module in &codegen_results.modules { + remove_temps_from_module(module); } } + if let Some(ref metadata_module) = codegen_results.metadata_module { - if let Some(ref obj) = metadata_module.object { - remove(sess, obj); - } + remove_temps_from_module(metadata_module); } + if let Some(ref allocator_module) = codegen_results.allocator_module { - if let Some(ref obj) = allocator_module.object { - remove(sess, obj); - } + remove_temps_from_module(allocator_module); } } }); @@ -279,12 +293,12 @@ pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &MaybeT out_filename } -// Create an 'rlib' -// -// An rlib in its current incarnation is essentially a renamed .a file. The -// rlib primarily contains the object file of the crate, but it also contains -// all of the object files from native libraries. This is done by unzipping -// native libraries and inserting all of the contents into this archive. +/// Create an 'rlib'. +/// +/// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains +/// the object file of the crate, but it also contains all of the object files from native +/// libraries. This is done by unzipping native libraries and inserting all of the contents into +/// this archive. fn link_rlib<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, codegen_results: &CodegenResults, @@ -379,18 +393,17 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( ab } -// Create a static archive -// -// This is essentially the same thing as an rlib, but it also involves adding -// all of the upstream crates' objects into the archive. This will slurp in -// all of the native libraries of upstream dependencies as well. -// -// Additionally, there's no way for us to link dynamic libraries, so we warn -// about all dynamic library dependencies that they're not linked in. -// -// There's no need to include metadata in a static archive, so ensure to not -// link in the metadata object file (and also don't prepare the archive with a -// metadata file). +/// Create a static archive. +/// +/// This is essentially the same thing as an rlib, but it also involves adding all of the upstream +/// crates' objects into the archive. This will slurp in all of the native libraries of upstream +/// dependencies as well. +/// +/// Additionally, there's no way for us to link dynamic libraries, so we warn about all dynamic +/// library dependencies that they're not linked in. +/// +/// There's no need to include metadata in a static archive, so ensure to not link in the metadata +/// object file (and also don't prepare the archive with a metadata file). fn link_staticlib<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, codegen_results: &CodegenResults, @@ -447,10 +460,73 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>( } } -// Create a dynamic library or executable -// -// This will invoke the system linker/cc to create the resulting file. This -// links to all upstream files as well. +fn escape_stdout_stderr_string(s: &[u8]) -> String { + str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| { + let mut x = "Non-UTF-8 output: ".to_string(); + x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from)); + x + }) +} + +const LLVM_DWP_EXECUTABLE: &'static str = "rust-llvm-dwp"; + +/// Invoke `llvm-dwp` (shipped alongside rustc) to link `dwo` files from Split DWARF into a `dwp` +/// file. +fn link_dwarf_object<'a>(sess: &'a Session, executable_out_filename: &Path) { + info!("preparing dwp to {}.dwp", executable_out_filename.to_str().unwrap()); + + let dwp_out_filename = executable_out_filename.with_extension("dwp"); + let mut cmd = Command::new(LLVM_DWP_EXECUTABLE); + cmd.arg("-e"); + cmd.arg(executable_out_filename); + cmd.arg("-o"); + cmd.arg(&dwp_out_filename); + + let mut new_path = sess.host_filesearch(PathKind::All).get_tools_search_paths(false); + if let Some(path) = env::var_os("PATH") { + new_path.extend(env::split_paths(&path)); + } + let new_path = env::join_paths(new_path).unwrap(); + cmd.env("PATH", new_path); + + info!("{:?}", &cmd); + match sess.time("run_dwp", || cmd.output()) { + Ok(prog) if !prog.status.success() => { + sess.struct_err(&format!( + "linking dwarf objects with `{}` failed: {}", + LLVM_DWP_EXECUTABLE, prog.status + )) + .note(&format!("{:?}", &cmd)) + .note(&escape_stdout_stderr_string(&prog.stdout)) + .note(&escape_stdout_stderr_string(&prog.stderr)) + .emit(); + info!("linker stderr:\n{}", escape_stdout_stderr_string(&prog.stderr)); + info!("linker stdout:\n{}", escape_stdout_stderr_string(&prog.stdout)); + } + Ok(_) => {} + Err(e) => { + let dwp_not_found = e.kind() == io::ErrorKind::NotFound; + let mut err = if dwp_not_found { + sess.struct_err(&format!("linker `{}` not found", LLVM_DWP_EXECUTABLE)) + } else { + sess.struct_err(&format!("could not exec the linker `{}`", LLVM_DWP_EXECUTABLE)) + }; + + err.note(&e.to_string()); + + if !dwp_not_found { + err.note(&format!("{:?}", &cmd)); + } + + err.emit(); + } + } +} + +/// Create a dynamic library or executable. +/// +/// This will invoke the system linker/cc to create the resulting file. This links to all upstream +/// files as well. fn link_natively<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, crate_type: CrateType, @@ -662,7 +738,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( prog.status )) .note(&format!("{:?}", &cmd)) - .note(&escape_string(&output)) + .note(&escape_stdout_stderr_string(&output)) .emit(); // If MSVC's `link.exe` was expected but the return code @@ -715,8 +791,8 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( sess.abort_if_errors(); } - info!("linker stderr:\n{}", escape_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_string(&prog.stdout)); + info!("linker stderr:\n{}", escape_stdout_stderr_string(&prog.stderr)); + info!("linker stdout:\n{}", escape_stdout_stderr_string(&prog.stdout)); } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; @@ -962,6 +1038,13 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { return false; } + // Single mode keeps debuginfo in the same object file, but in such a way that it it skipped + // by the linker - so it's expected that when codegen units are linked together that this + // debuginfo would be lost without keeping around the temps. + if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Single { + return true; + } + // If we're on OSX then the equivalent of split dwarf is turned on by // default. The final executable won't actually have any debug information // except it'll have pointers to elsewhere. Historically we've always run @@ -1677,17 +1760,15 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.take_cmd() } -// # Native library linking -// -// User-supplied library search paths (-L on the command line). These are -// the same paths used to find Rust crates, so some of them may have been -// added already by the previous crate linking code. This only allows them -// to be found at compile time so it is still entirely up to outside -// forces to make sure that library can be found at runtime. -// -// Also note that the native libraries linked here are only the ones located -// in the current crate. Upstream crates with native library dependencies -// may have their native library pulled in above. +/// # Native library linking +/// +/// User-supplied library search paths (-L on the command line). These are the same paths used to +/// find Rust crates, so some of them may have been added already by the previous crate linking +/// code. This only allows them to be found at compile time so it is still entirely up to outside +/// forces to make sure that library can be found at runtime. +/// +/// Also note that the native libraries linked here are only the ones located in the current crate. +/// Upstream crates with native library dependencies may have their native library pulled in above. fn add_local_native_libraries( cmd: &mut dyn Linker, sess: &Session, @@ -1727,11 +1808,10 @@ fn add_local_native_libraries( } } -// # Rust Crate linking -// -// Rust crates are not considered at all when creating an rlib output. All -// dependencies will be linked when producing the final output (instead of -// the intermediate rlib version) +/// # Rust Crate linking +/// +/// Rust crates are not considered at all when creating an rlib output. All dependencies will be +/// linked when producing the final output (instead of the intermediate rlib version). fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( cmd: &mut dyn Linker, sess: &'a Session, @@ -1996,24 +2076,21 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( } } -// Link in all of our upstream crates' native dependencies. Remember that -// all of these upstream native dependencies are all non-static -// dependencies. We've got two cases then: -// -// 1. The upstream crate is an rlib. In this case we *must* link in the -// native dependency because the rlib is just an archive. -// -// 2. The upstream crate is a dylib. In order to use the dylib, we have to -// have the dependency present on the system somewhere. Thus, we don't -// gain a whole lot from not linking in the dynamic dependency to this -// crate as well. -// -// The use case for this is a little subtle. In theory the native -// dependencies of a crate are purely an implementation detail of the crate -// itself, but the problem arises with generic and inlined functions. If a -// generic function calls a native function, then the generic function must -// be instantiated in the target crate, meaning that the native symbol must -// also be resolved in the target crate. +/// Link in all of our upstream crates' native dependencies. Remember that all of these upstream +/// native dependencies are all non-static dependencies. We've got two cases then: +/// +/// 1. The upstream crate is an rlib. In this case we *must* link in the native dependency because +/// the rlib is just an archive. +/// +/// 2. The upstream crate is a dylib. In order to use the dylib, we have to have the dependency +/// present on the system somewhere. Thus, we don't gain a whole lot from not linking in the +/// dynamic dependency to this crate as well. +/// +/// The use case for this is a little subtle. In theory the native dependencies of a crate are +/// purely an implementation detail of the crate itself, but the problem arises with generic and +/// inlined functions. If a generic function calls a native function, then the generic function +/// must be instantiated in the target crate, meaning that the native symbol must also be resolved +/// in the target crate. fn add_upstream_native_libraries( cmd: &mut dyn Linker, sess: &Session, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7f2bb7b5bcda..c84b87964b84 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -274,17 +274,20 @@ impl ModuleConfig { } } -// HACK(eddyb) work around `#[derive]` producing wrong bounds for `Clone`. -pub struct TargetMachineFactory( - pub Arc Result + Send + Sync>, -); - -impl Clone for TargetMachineFactory { - fn clone(&self) -> Self { - TargetMachineFactory(self.0.clone()) - } +/// Configuration passed to the function returned by the `target_machine_factory`. +pub struct TargetMachineFactoryConfig { + /// Split DWARF is enabled in LLVM by checking that `TM.MCOptions.SplitDwarfFile` isn't empty, + /// so the path to the dwarf object has to be provided when we create the target machine. + /// This can be ignored by backends which do not need it for their Split DWARF support. + pub split_dwarf_file: Option, } +pub type TargetMachineFactoryFn = Arc< + dyn Fn(TargetMachineFactoryConfig) -> Result<::TargetMachine, String> + + Send + + Sync, +>; + pub type ExportedSymbols = FxHashMap>>; /// Additional resources used by optimize_and_codegen (not module specific) @@ -305,12 +308,13 @@ pub struct CodegenContext { pub regular_module_config: Arc, pub metadata_module_config: Arc, pub allocator_module_config: Arc, - pub tm_factory: TargetMachineFactory, + pub tm_factory: TargetMachineFactoryFn, pub msvc_imps_needed: bool, pub is_pe_coff: bool, pub target_pointer_width: u32, pub target_arch: String, pub debuginfo: config::DebugInfo, + pub split_dwarf_kind: config::SplitDwarfKind, // Number of cgus excluding the allocator/metadata modules pub total_cgus: usize, @@ -627,6 +631,12 @@ fn produce_final_output_artifacts( } } + if let Some(ref path) = module.dwarf_object { + if !keep_numbered_objects { + remove(sess, path); + } + } + if let Some(ref path) = module.bytecode { if !keep_numbered_bitcode { remove(sess, path); @@ -849,6 +859,7 @@ fn execute_copy_from_cache_work_item( name: module.name, kind: ModuleKind::Regular, object, + dwarf_object: None, bytecode: None, })) } @@ -1020,13 +1031,14 @@ fn start_executing_work( regular_module_config: regular_config, metadata_module_config: metadata_config, allocator_module_config: allocator_config, - tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)), + tm_factory: backend.target_machine_factory(tcx.sess, ol), total_cgus, msvc_imps_needed: msvc_imps_needed(tcx), is_pe_coff: tcx.sess.target.is_like_windows, target_pointer_width: tcx.sess.target.pointer_width, target_arch: tcx.sess.target.arch.clone(), debuginfo: tcx.sess.opts.debuginfo, + split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf, }; // This is the "main loop" of parallel work happening for parallel codegen. diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 21138f967a27..f47d2ada61a1 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1,18 +1,3 @@ -//! Codegen the completed AST to the LLVM IR. -//! -//! Some functions here, such as `codegen_block` and `codegen_expr`, return a value -- -//! the result of the codegen to LLVM -- while others, such as `codegen_fn` -//! and `mono_item`, are called only for the side effect of adding a -//! particular definition to the LLVM IR output we're producing. -//! -//! Hopefully useful general knowledge about codegen: -//! -//! * There's no way to find out the `Ty` type of a `Value`. Doing so -//! would be "trying to get the eggs out of an omelette" (credit: -//! pcwalton). You can, instead, find out its `llvm::Type` by calling `val_ty`, -//! but one `llvm::Type` corresponds to many `Ty`s; for instance, `tup(int, int, -//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`. - use crate::back::write::{ compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen, @@ -46,7 +31,6 @@ use rustc_session::cgu_reuse_tracker::CguReuse; use rustc_session::config::{self, EntryFnType}; use rustc_session::utils::NativeLibKind; use rustc_session::Session; -use rustc_symbol_mangling::test as symbol_names_test; use rustc_target::abi::{Align, LayoutOf, VariantIdx}; use std::cmp; @@ -486,8 +470,6 @@ pub fn codegen_crate( ongoing_codegen.codegen_finished(tcx); - finalize_tcx(tcx); - ongoing_codegen.check_for_errors(tcx.sess); return ongoing_codegen; @@ -688,14 +670,8 @@ pub fn codegen_crate( total_codegen_time.into_inner(), ); - rustc_incremental::assert_module_sources::assert_module_sources(tcx); - - symbol_names_test::report_symbol_names(tcx); - ongoing_codegen.check_for_errors(tcx.sess); - finalize_tcx(tcx); - ongoing_codegen.into_inner() } @@ -746,18 +722,6 @@ impl Drop for AbortCodegenOnDrop { } } -fn finalize_tcx(tcx: TyCtxt<'_>) { - tcx.sess.time("assert_dep_graph", || rustc_incremental::assert_dep_graph(tcx)); - tcx.sess.time("serialize_dep_graph", || rustc_incremental::save_dep_graph(tcx)); - - // We assume that no queries are run past here. If there are new queries - // after this point, they'll show up as "" in self-profiling data. - { - let _prof_timer = tcx.prof.generic_activity("self_profile_alloc_query_strings"); - tcx.alloc_self_profile_query_strings(); - } -} - impl CrateInfo { pub fn new(tcx: TyCtxt<'_>) -> CrateInfo { let mut info = CrateInfo { @@ -766,7 +730,7 @@ impl CrateInfo { profiler_runtime: None, is_no_builtins: Default::default(), native_libraries: Default::default(), - used_libraries: tcx.native_libraries(LOCAL_CRATE), + used_libraries: tcx.native_libraries(LOCAL_CRATE).iter().map(Into::into).collect(), link_args: tcx.link_args(LOCAL_CRATE), crate_name: Default::default(), used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), @@ -787,7 +751,8 @@ impl CrateInfo { info.missing_lang_items.reserve(n_crates); for &cnum in crates.iter() { - info.native_libraries.insert(cnum, tcx.native_libraries(cnum)); + info.native_libraries + .insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect()); info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum)); if tcx.is_panic_runtime(cnum) { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 8ec1eed44045..e27eac3f69b0 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -21,15 +21,17 @@ extern crate tracing; #[macro_use] extern crate rustc_middle; +use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::CrateNum; use rustc_hir::LangItem; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::middle::cstore::{CrateSource, LibSource, NativeLib}; +use rustc_middle::middle::cstore::{self, CrateSource, LibSource}; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::ty::query::Providers; use rustc_session::config::{OutputFilenames, OutputType, RUST_CGU_EXT}; +use rustc_session::utils::NativeLibKind; use rustc_span::symbol::Symbol; use std::path::{Path, PathBuf}; @@ -64,13 +66,15 @@ impl ModuleCodegen { pub fn into_compiled_module( self, emit_obj: bool, + emit_dwarf_obj: bool, emit_bc: bool, outputs: &OutputFilenames, ) -> CompiledModule { let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name))); + let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo(Some(&self.name))); let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name))); - CompiledModule { name: self.name.clone(), kind: self.kind, object, bytecode } + CompiledModule { name: self.name.clone(), kind: self.kind, object, dwarf_object, bytecode } } } @@ -79,6 +83,7 @@ pub struct CompiledModule { pub name: String, pub kind: ModuleKind, pub object: Option, + pub dwarf_object: Option, pub bytecode: Option, } @@ -102,6 +107,19 @@ bitflags::bitflags! { } } +#[derive(Clone, Debug, Encodable, Decodable, HashStable)] +pub struct NativeLib { + pub kind: NativeLibKind, + pub name: Option, + pub cfg: Option, +} + +impl From<&cstore::NativeLib> for NativeLib { + fn from(lib: &cstore::NativeLib) -> Self { + NativeLib { kind: lib.kind, name: lib.name, cfg: lib.cfg.clone() } + } +} + /// Misc info we load from metadata to persist beyond the tcx. /// /// Note: though `CrateNum` is only meaningful within the same tcx, information within `CrateInfo` @@ -116,9 +134,9 @@ pub struct CrateInfo { pub compiler_builtins: Option, pub profiler_runtime: Option, pub is_no_builtins: FxHashSet, - pub native_libraries: FxHashMap>>, + pub native_libraries: FxHashMap>, pub crate_name: FxHashMap, - pub used_libraries: Lrc>, + pub used_libraries: Vec, pub link_args: Lrc>, pub used_crate_source: FxHashMap>, pub used_crates_static: Vec<(CrateNum, LibSource)>, diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 44bb0deeae97..57e49ba8d1a5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -112,12 +112,12 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { }; // Allow uses of projections that are ZSTs or from scalar fields. - let is_consume = match context { + let is_consume = matches!( + context, PlaceContext::NonMutatingUse( NonMutatingUseContext::Copy | NonMutatingUseContext::Move, - ) => true, - _ => false, - }; + ) + ); if is_consume { let base_ty = mir::Place::ty_from(place_ref.local, proj_base, self.fx.mir, cx.tcx()); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index e59832a8eed5..ecac05fd9557 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -522,7 +522,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mut bx: Bx, terminator: &mir::Terminator<'tcx>, func: &mir::Operand<'tcx>, - args: &Vec>, + args: &[mir::Operand<'tcx>], destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, cleanup: Option, fn_span: Span, @@ -1395,6 +1395,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { dst: PlaceRef<'tcx, Bx::Value>, ) { let src = self.codegen_operand(bx, src); + + // Special-case transmutes between scalars as simple bitcasts. + match (&src.layout.abi, &dst.layout.abi) { + (abi::Abi::Scalar(src_scalar), abi::Abi::Scalar(dst_scalar)) => { + // HACK(eddyb) LLVM doesn't like `bitcast`s between pointers and non-pointers. + if (src_scalar.value == abi::Pointer) == (dst_scalar.value == abi::Pointer) { + assert_eq!(src.layout.size, dst.layout.size); + + // NOTE(eddyb) the `from_immediate` and `to_immediate_scalar` + // conversions allow handling `bool`s the same as `u8`s. + let src = bx.from_immediate(src.immediate()); + let src_as_dst = bx.bitcast(src, bx.backend_type(dst.layout)); + Immediate(bx.to_immediate_scalar(src_as_dst, dst_scalar)).store(bx, dst); + return; + } + } + _ => {} + } + let llty = bx.backend_type(src.layout); let cast_ptr = bx.pointercast(dst.llval, bx.type_ptr_to(llty)); let align = src.layout.align.abi.min(dst.align); diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index c8001b8daf0d..3a85c268e0ea 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -11,7 +11,7 @@ use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn eval_mir_constant_to_operand( - &mut self, + &self, bx: &mut Bx, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { @@ -21,7 +21,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } pub fn eval_mir_constant( - &mut self, + &self, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { match self.monomorphize(constant.literal).val { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index c4191a4e23d9..f1eae605da01 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -8,7 +8,7 @@ use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Span}; use rustc_target::abi::{LayoutOf, Size}; -use super::operand::OperandValue; +use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; @@ -116,6 +116,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { span } + fn spill_operand_to_stack( + operand: &OperandRef<'tcx, Bx::Value>, + name: Option, + bx: &mut Bx, + ) -> PlaceRef<'tcx, Bx::Value> { + // "Spill" the value onto the stack, for debuginfo, + // without forcing non-debuginfo uses of the local + // to also load from the stack every single time. + // FIXME(#68817) use `llvm.dbg.value` instead, + // at least for the cases which LLVM handles correctly. + let spill_slot = PlaceRef::alloca(bx, operand.layout); + if let Some(name) = name { + bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill")); + } + operand.val.store(bx, spill_slot); + spill_slot + } + /// Apply debuginfo and/or name, after creating the `alloca` for a local, /// or initializing the local with an operand (whichever applies). pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { @@ -152,7 +170,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // (after #67586 gets fixed). None } else { - let name = kw::Invalid; + let name = kw::Empty; let decl = &self.mir.local_decls[local]; let dbg_var = if full_debug_info { self.adjusted_span_and_dbg_scope(decl.source_info).map( @@ -186,7 +204,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None } else { Some(match whole_local_var.or(fallback_var) { - Some(var) if var.name != kw::Invalid => var.name.to_string(), + Some(var) if var.name != kw::Empty => var.name.to_string(), _ => format!("{:?}", local), }) }; @@ -226,17 +244,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - // "Spill" the value onto the stack, for debuginfo, - // without forcing non-debuginfo uses of the local - // to also load from the stack every single time. - // FIXME(#68817) use `llvm.dbg.value` instead, - // at least for the cases which LLVM handles correctly. - let spill_slot = PlaceRef::alloca(bx, operand.layout); - if let Some(name) = name { - bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill")); - } - operand.val.store(bx, spill_slot); - spill_slot + Self::spill_operand_to_stack(operand, name, bx) } LocalRef::Place(place) => *place, @@ -308,6 +316,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`. pub fn compute_per_local_var_debug_info( &self, + bx: &mut Bx, ) -> Option>>> { let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full; @@ -322,31 +331,63 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { None }; - let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| { - let place = var.place; - let var_ty = self.monomorphized_place_ty(place.as_ref()); - let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg - && place.projection.is_empty() - && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE - { - let arg_index = place.local.index() - 1; - // FIXME(eddyb) shouldn't `ArgumentVariable` indices be - // offset in closures to account for the hidden environment? - // Also, is this `+ 1` needed at all? - VariableKind::ArgumentVariable(arg_index + 1) - } else { - VariableKind::LocalVariable + let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| { + let (var_ty, var_kind) = match var.value { + mir::VarDebugInfoContents::Place(place) => { + let var_ty = self.monomorphized_place_ty(place.as_ref()); + let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg + && place.projection.is_empty() + && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE + { + let arg_index = place.local.index() - 1; + + // FIXME(eddyb) shouldn't `ArgumentVariable` indices be + // offset in closures to account for the hidden environment? + // Also, is this `+ 1` needed at all? + VariableKind::ArgumentVariable(arg_index + 1) + } else { + VariableKind::LocalVariable + }; + (var_ty, var_kind) + } + mir::VarDebugInfoContents::Const(c) => { + let ty = self.monomorphize(c.literal.ty); + (ty, VariableKind::LocalVariable) + } }; + self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span) }); - per_local[var.place.local].push(PerLocalVarDebugInfo { - name: var.name, - source_info: var.source_info, - dbg_var, - projection: var.place.projection, - }); + match var.value { + mir::VarDebugInfoContents::Place(place) => { + per_local[place.local].push(PerLocalVarDebugInfo { + name: var.name, + source_info: var.source_info, + dbg_var, + projection: place.projection, + }); + } + mir::VarDebugInfoContents::Const(c) => { + if let Some(dbg_var) = dbg_var { + let dbg_loc = match self.dbg_loc(var.source_info) { + Some(dbg_loc) => dbg_loc, + None => continue, + }; + + if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) { + let base = Self::spill_operand_to_stack( + &operand, + Some(var.name.to_string()), + bx, + ); + + bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]); + } + } + } + } } Some(per_local) } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 72a64a8c5103..80e3ed75b858 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -83,9 +83,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - sym::unreachable => { - return; - } sym::va_start => bx.va_start(args[0].immediate()), sym::va_end => bx.va_end(args[0].immediate()), sym::size_of_val => { @@ -106,8 +103,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes()) } } - sym::size_of - | sym::pref_align_of + sym::pref_align_of | sym::min_align_of | sym::needs_drop | sym::type_id @@ -119,10 +115,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { .unwrap(); OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx) } - // Effectively no-op - sym::forget => { - return; - } sym::offset => { let ptr = args[0].immediate(); let offset = args[1].immediate(); @@ -218,9 +210,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow - | sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul | sym::unchecked_div | sym::unchecked_rem | sym::unchecked_shl @@ -254,9 +243,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - sym::wrapping_add => bx.add(args[0].immediate(), args[1].immediate()), - sym::wrapping_sub => bx.sub(args[0].immediate(), args[1].immediate()), - sym::wrapping_mul => bx.mul(args[0].immediate(), args[1].immediate()), sym::exact_div => { if signed { bx.exactsdiv(args[0].immediate(), args[1].immediate()) @@ -538,8 +524,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let ty = substs.type_at(0); - if int_type_width_signed(ty, bx.tcx()).is_some() { - bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order) + if int_type_width_signed(ty, bx.tcx()).is_some() + || (ty.is_unsafe_ptr() && op == "xchg") + { + let mut ptr = args[0].immediate(); + let mut val = args[1].immediate(); + if ty.is_unsafe_ptr() { + // Some platforms do not support atomic operations on pointers, + // so we cast to integer first. + let ptr_llty = bx.type_ptr_to(bx.type_isize()); + ptr = bx.pointercast(ptr, ptr_llty); + val = bx.ptrtoint(val, bx.type_isize()); + } + bx.atomic_rmw(atom_op, ptr, val, order) } else { return invalid_monomorphization(ty); } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 640f805d5e88..285140060be4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -186,7 +186,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( caller_location: None, }; - fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(); + fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut bx); for const_ in &mir.required_consts { if let Err(err) = fx.eval_mir_constant(const_) { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 2ad470c2693d..e3a6cabd6005 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -489,6 +489,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Discriminant(ref place) => { let discr_ty = rvalue.ty(self.mir, bx.tcx()); + let discr_ty = self.monomorphize(discr_ty); let discr = self .codegen_place(&mut bx, place.as_ref()) .codegen_get_discr(&mut bx, discr_ty); diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index b9c555c2eb06..f28db2fe84b6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -1,5 +1,6 @@ use super::write::WriteBackendMethods; use super::CodegenObject; +use crate::back::write::TargetMachineFactoryFn; use crate::{CodegenResults, ModuleCodegen}; use rustc_ast::expand::allocator::AllocatorKind; @@ -21,7 +22,6 @@ use rustc_target::spec::Target; pub use rustc_data_structures::sync::MetadataRef; use std::any::Any; -use std::sync::Arc; pub trait BackendTypes { type Value: CodegenObject; @@ -123,7 +123,7 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se &self, sess: &Session, opt_level: config::OptLevel, - ) -> Arc Result + Send + Sync>; + ) -> TargetMachineFactoryFn; fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str; fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str>; } diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index 1634c5863163..09b91083a634 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -149,8 +149,6 @@ struct Event { /// those successors), we will pop off that node's `Settled` event. /// /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms -/// [`NodeStatus`]: ./enum.NodeStatus.html -/// [`TriColorVisitor::node_examined`]: ./trait.TriColorVisitor.html#method.node_examined pub struct TriColorDepthFirstSearch<'graph, G> where G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 5b3d8233f3da..e2cbb09ce5e6 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -523,7 +523,7 @@ where successors_len: 0, min_depth: depth, min_cycle_root: successor_node, - successor_node: successor_node, + successor_node, }); continue 'recurse; } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index d903a557c7fd..5880bbd3de44 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -15,8 +15,7 @@ #![feature(fn_traits)] #![feature(int_bits_const)] #![feature(min_specialization)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(nll)] #![feature(allow_internal_unstable)] #![feature(hash_raw_entry)] @@ -27,7 +26,7 @@ #![feature(thread_id_value)] #![feature(extend_one)] #![feature(const_panic)] -#![feature(min_const_generics)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![feature(new_uninit)] #![feature(once_cell)] #![feature(maybe_uninit_uninit_array)] diff --git a/compiler/rustc_data_structures/src/sorted_map/index_map.rs b/compiler/rustc_data_structures/src/sorted_map/index_map.rs index 2bb421a47efa..01cd1cec9245 100644 --- a/compiler/rustc_data_structures/src/sorted_map/index_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map/index_map.rs @@ -24,8 +24,7 @@ use rustc_index::vec::{Idx, IndexVec}; /// to insert into the middle of the sorted array. Users should avoid mutating this data structure /// in-place. /// -/// [`IndexVec`]: ../../rustc_index/vec/struct.IndexVec.html -/// [`SortedMap`]: ../sorted_map/struct.SortedMap.html +/// [`SortedMap`]: super::SortedMap #[derive(Clone, Debug)] pub struct SortedIndexMultiMap { /// The elements of the map in insertion order. diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index fe8ae7abf982..06e8442d4753 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -40,7 +40,7 @@ const SSO_ARRAY_SIZE: usize = 8; // into_keys/into_values (unstable) // all raw_entry-related // PartialEq/Eq (requires sorting the array) -// Entry::or_insert_with_key (unstable) +// Entry::or_insert_with_key // Vacant/Occupied entries and related // // FIXME: In HashMap most methods accepting key reference diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index b3466f49b9f2..d57ab2433ad1 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -603,7 +603,7 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { } } -fn show_content_with_pager(content: &String) { +fn show_content_with_pager(content: &str) { let pager_name = env::var_os("PAGER").unwrap_or_else(|| { if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") } }); diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 636c37142ad9..c669f7fed272 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -111,6 +111,7 @@ E0206: include_str!("./error_codes/E0206.md"), E0207: include_str!("./error_codes/E0207.md"), E0210: include_str!("./error_codes/E0210.md"), E0211: include_str!("./error_codes/E0211.md"), +E0212: include_str!("./error_codes/E0212.md"), E0214: include_str!("./error_codes/E0214.md"), E0220: include_str!("./error_codes/E0220.md"), E0221: include_str!("./error_codes/E0221.md"), @@ -463,6 +464,7 @@ E0776: include_str!("./error_codes/E0776.md"), E0777: include_str!("./error_codes/E0777.md"), E0778: include_str!("./error_codes/E0778.md"), E0779: include_str!("./error_codes/E0779.md"), +E0780: include_str!("./error_codes/E0780.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -503,7 +505,6 @@ E0779: include_str!("./error_codes/E0779.md"), // E0196, // cannot determine a type for this closure E0208, // E0209, // builtin traits can only be implemented on structs or enums - E0212, // cannot extract an associated type from a higher-ranked trait bound // E0213, // associated types are not accepted in this context // E0215, // angle-bracket notation is not stable with `Fn` // E0216, // parenthetical notation is only stable with `Fn` diff --git a/compiler/rustc_error_codes/src/error_codes/E0212.md b/compiler/rustc_error_codes/src/error_codes/E0212.md new file mode 100644 index 000000000000..17465414650b --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0212.md @@ -0,0 +1,35 @@ +Cannot use the associated type of +a trait with uninferred generic parameters. + +Erroneous code example: + +```compile_fail,E0212 +pub trait Foo { + type A; + + fn get(&self, t: T) -> Self::A; +} + +fn foo2 Foo<&'x isize>>( + field: I::A) {} // error! +``` + +In this example, we have to instantiate `'x`, and +we don't know what lifetime to instantiate it with. +To fix this, spell out the precise lifetimes involved. +Example: + +``` +pub trait Foo { + type A; + + fn get(&self, t: T) -> Self::A; +} + +fn foo3 Foo<&'x isize>>( + x: >::A) {} // ok! + + +fn foo4<'a, I : for<'x> Foo<&'x isize>>( + x: >::A) {} // ok! +``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0730.md b/compiler/rustc_error_codes/src/error_codes/E0730.md index 016b3f38aa31..56d0e6afa183 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0730.md +++ b/compiler/rustc_error_codes/src/error_codes/E0730.md @@ -3,8 +3,6 @@ An array without a fixed length was pattern-matched. Erroneous code example: ```compile_fail,E0730 -#![feature(const_generics)] - fn is_123(x: [u32; N]) -> bool { match x { [1, 2, ..] => true, // error: cannot pattern-match on an diff --git a/compiler/rustc_error_codes/src/error_codes/E0770.md b/compiler/rustc_error_codes/src/error_codes/E0770.md index 278bf9b907b2..b39163a9de3f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0770.md +++ b/compiler/rustc_error_codes/src/error_codes/E0770.md @@ -10,6 +10,5 @@ fn foo() {} // error! To fix this error, use a concrete type for the const parameter: ``` -#![feature(const_generics)] fn foo() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0780.md b/compiler/rustc_error_codes/src/error_codes/E0780.md new file mode 100644 index 000000000000..704b4ae181bb --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0780.md @@ -0,0 +1,19 @@ +Cannot use `doc(inline)` with anonymous imports + +Erroneous code example: + +```ignore (cannot-doctest-multicrate-project) + +#[doc(inline)] // error: invalid doc argument +pub use foo::Foo as _; +``` + +Anonymous imports are always rendered with `#[doc(no_inline)]`. To fix this +error, remove the `#[doc(inline)]` attribute. + +Example: + +```ignore (cannot-doctest-multicrate-project) + +pub use foo::Foo as _; +``` diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index decbf03b9de8..e61476bf23e1 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -30,7 +30,8 @@ pub enum DiagnosticId { Lint { name: String, has_future_breakage: bool }, } -/// For example a note attached to an error. +/// A "sub"-diagnostic attached to a parent diagnostic. +/// For example, a note attached to an error. #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] pub struct SubDiagnostic { pub level: Level, @@ -124,6 +125,7 @@ impl Diagnostic { self.level = Level::Cancelled; } + /// Check if this diagnostic [was cancelled][Self::cancel()]. pub fn cancelled(&self) -> bool { self.level == Level::Cancelled } @@ -136,8 +138,6 @@ impl Diagnostic { /// /// This span is *not* considered a ["primary span"][`MultiSpan`]; only /// the `Span` supplied when creating the diagnostic is primary. - /// - /// [`MultiSpan`]: ../rustc_span/struct.MultiSpan.html pub fn span_label>(&mut self, span: Span, label: T) -> &mut Self { self.span.push_span_label(span, label.into()); self @@ -164,7 +164,7 @@ impl Diagnostic { self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"") } - pub fn note_unsuccessfull_coercion( + pub fn note_unsuccessful_coercion( &mut self, expected: DiagnosticStyledString, found: DiagnosticStyledString, @@ -241,6 +241,7 @@ impl Diagnostic { self } + /// Add a note attached to this diagnostic. pub fn note(&mut self, msg: &str) -> &mut Self { self.sub(Level::Note, msg, MultiSpan::new(), None); self @@ -252,33 +253,40 @@ impl Diagnostic { } /// Prints the span with a note above it. + /// This is like [`Diagnostic::note()`], but it gets its own span. pub fn span_note>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Note, msg, sp.into(), None); self } + /// Add a warning attached to this diagnostic. pub fn warn(&mut self, msg: &str) -> &mut Self { self.sub(Level::Warning, msg, MultiSpan::new(), None); self } - /// Prints the span with a warn above it. + /// Prints the span with a warning above it. + /// This is like [`Diagnostic::warn()`], but it gets its own span. pub fn span_warn>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Warning, msg, sp.into(), None); self } + /// Add a help message attached to this diagnostic. pub fn help(&mut self, msg: &str) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new(), None); self } /// Prints the span with some help above it. + /// This is like [`Diagnostic::help()`], but it gets its own span. pub fn span_help>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Help, msg, sp.into(), None); self } + /// Show a suggestion that has multiple parts to it. + /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion( &mut self, msg: &str, @@ -299,6 +307,8 @@ impl Diagnostic { self } + /// Show multiple suggestions that have multiple parts. + /// See also [`Diagnostic::multipart_suggestion()`]. pub fn multipart_suggestions( &mut self, msg: &str, @@ -382,6 +392,7 @@ impl Diagnostic { self } + /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn span_suggestion_with_style( &mut self, sp: Span, @@ -401,6 +412,7 @@ impl Diagnostic { self } + /// Always show the suggested change. pub fn span_suggestion_verbose( &mut self, sp: Span, @@ -419,6 +431,7 @@ impl Diagnostic { } /// Prints out a message with multiple suggested edits of the code. + /// See also [`Diagnostic::span_suggestion()`]. pub fn span_suggestions( &mut self, sp: Span, @@ -458,7 +471,7 @@ impl Diagnostic { self } - /// Prints out a message with for a suggestion without showing the suggested code. + /// Prints out a message for a suggestion without showing the suggested code. /// /// This is intended to be used for suggestions that are obvious in what the changes need to /// be from the message, showing the span label inline would be visually unpleasant @@ -481,7 +494,7 @@ impl Diagnostic { self } - /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli. + /// Adds a suggestion to the JSON output that will not be shown in the CLI. /// /// This is intended to be used for suggestions that are *very* obvious in what the changes /// need to be from the message, but we still want other tools to be able to apply them. diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index c9259a1502c8..f165a60336a6 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -30,6 +30,15 @@ struct DiagnosticBuilderInner<'a> { allow_suggestions: bool, } +/// This is a helper macro for [`forward!`] that allows automatically adding documentation +/// that uses tokens from [`forward!`]'s input. +macro_rules! forward_inner_docs { + ($e:expr => $i:item) => { + #[doc = $e] + $i + } +} + /// In general, the `DiagnosticBuilder` uses deref to allow access to /// the fields and methods of the embedded `diagnostic` in a /// transparent way. *However,* many of the methods are intended to @@ -45,10 +54,11 @@ macro_rules! forward { pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self ) => { $(#[$attrs])* + forward_inner_docs!(concat!("See [`Diagnostic::", stringify!($n), "()`].") => pub fn $n(&self, $($name: $ty),*) -> &Self { self.diagnostic.$n($($name),*); self - } + }); }; // Forward pattern for &mut self -> &mut Self @@ -57,10 +67,11 @@ macro_rules! forward { pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self ) => { $(#[$attrs])* + forward_inner_docs!(concat!("See [`Diagnostic::", stringify!($n), "()`].") => pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { self.0.diagnostic.$n($($name),*); self - } + }); }; // Forward pattern for &mut self -> &mut Self, with S: Into @@ -74,10 +85,11 @@ macro_rules! forward { ) -> &mut Self ) => { $(#[$attrs])* + forward_inner_docs!(concat!("See [`Diagnostic::", stringify!($n), "()`].") => pub fn $n>(&mut self, $($name: $ty),*) -> &mut Self { self.0.diagnostic.$n($($name),*); self - } + }); }; } @@ -116,7 +128,7 @@ impl<'a> DiagnosticBuilder<'a> { /// Stashes diagnostic for possible later improvement in a different, /// later stage of the compiler. The diagnostic can be accessed with - /// the provided `span` and `key` through `.steal_diagnostic` on `Handler`. + /// the provided `span` and `key` through [`Handler::steal_diagnostic()`]. /// /// As with `buffer`, this is unless the handler has disabled such buffering. pub fn stash(self, span: Span, key: StashKey) { @@ -202,7 +214,7 @@ impl<'a> DiagnosticBuilder<'a> { } /// Labels all the given spans with the provided label. - /// See `span_label` for more information. + /// See [`Diagnostic::span_label()`] for more information. pub fn span_labels( &mut self, spans: impl IntoIterator, @@ -233,7 +245,7 @@ impl<'a> DiagnosticBuilder<'a> { found_extra: &dyn fmt::Display, ) -> &mut Self); - forward!(pub fn note_unsuccessfull_coercion( + forward!(pub fn note_unsuccessful_coercion( &mut self, expected: DiagnosticStyledString, found: DiagnosticStyledString, @@ -254,6 +266,7 @@ impl<'a> DiagnosticBuilder<'a> { msg: &str, ) -> &mut Self); + /// See [`Diagnostic::multipart_suggestion()`]. pub fn multipart_suggestion( &mut self, msg: &str, @@ -267,6 +280,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::multipart_suggestions()`]. pub fn multipart_suggestions( &mut self, msg: &str, @@ -280,6 +294,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::tool_only_multipart_suggestion()`]. pub fn tool_only_multipart_suggestion( &mut self, msg: &str, @@ -293,6 +308,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::span_suggestion()`]. pub fn span_suggestion( &mut self, sp: Span, @@ -307,6 +323,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::span_suggestions()`]. pub fn span_suggestions( &mut self, sp: Span, @@ -321,6 +338,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::span_suggestion_short()`]. pub fn span_suggestion_short( &mut self, sp: Span, @@ -335,6 +353,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::span_suggestion_verbose()`]. pub fn span_suggestion_verbose( &mut self, sp: Span, @@ -349,6 +368,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::span_suggestion_hidden()`]. pub fn span_suggestion_hidden( &mut self, sp: Span, @@ -363,6 +383,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::tool_only_span_suggestion()`] for more information. pub fn tool_only_span_suggestion( &mut self, sp: Span, @@ -380,19 +401,22 @@ impl<'a> DiagnosticBuilder<'a> { forward!(pub fn set_span>(&mut self, sp: S) -> &mut Self); forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); + /// Allow attaching suggestions this diagnostic. + /// If this is set to `false`, then any suggestions attached with the `span_suggestion_*` + /// methods after this is set to `false` will be ignored. pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self { self.0.allow_suggestions = allow; self } /// Convenience function for internal use, clients should use one of the - /// struct_* methods on Handler. + /// `struct_*` methods on [`Handler`]. crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { DiagnosticBuilder::new_with_code(handler, level, None, message) } /// Convenience function for internal use, clients should use one of the - /// struct_* methods on Handler. + /// `struct_*` methods on [`Handler`]. crate fn new_with_code( handler: &'a Handler, level: Level, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 335f3b7a9a01..774a0764d114 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -2,8 +2,8 @@ use crate::expand::{self, AstFragment, Invocation}; use crate::module::DirectoryOwnership; use rustc_ast::ptr::P; -use rustc_ast::token; -use rustc_ast::tokenstream::TokenStream; +use rustc_ast::token::{self, Nonterminal}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, Attribute, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability}; @@ -119,8 +119,8 @@ impl Annotatable { } } - crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { - let nt = match self { + crate fn into_nonterminal(self) -> Nonterminal { + match self { Annotatable::Item(item) => token::NtItem(item), Annotatable::TraitItem(item) | Annotatable::ImplItem(item) => { token::NtItem(P(item.and_then(ast::AssocItem::into_item))) @@ -137,8 +137,11 @@ impl Annotatable { | Annotatable::Param(..) | Annotatable::StructField(..) | Annotatable::Variant(..) => panic!("unexpected annotatable"), - }; - nt_to_tokenstream(&nt, sess, DUMMY_SP) + } + } + + crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { + nt_to_tokenstream(&self.into_nonterminal(), sess, DUMMY_SP, CanSynthesizeMissingTokens::No) } pub fn expect_item(self) -> P { diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 563783c5b795..1193f66651ce 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -29,6 +29,7 @@ use smallvec::SmallVec; pub struct StripUnconfigured<'a> { pub sess: &'a Session, pub features: Option<&'a Features>, + pub modified: bool, } fn get_features( @@ -199,7 +200,7 @@ fn get_features( // `cfg_attr`-process the crate's attributes and compute the crate's features. pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) { - let mut strip_unconfigured = StripUnconfigured { sess, features: None }; + let mut strip_unconfigured = StripUnconfigured { sess, features: None, modified: false }; let unconfigured_attrs = krate.attrs.clone(); let diag = &sess.parse_sess.span_diagnostic; @@ -243,7 +244,12 @@ const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ impl<'a> StripUnconfigured<'a> { pub fn configure(&mut self, mut node: T) -> Option { self.process_cfg_attrs(&mut node); - self.in_cfg(node.attrs()).then_some(node) + if self.in_cfg(node.attrs()) { + Some(node) + } else { + self.modified = true; + None + } } /// Parse and expand all `cfg_attr` attributes into a list of attributes @@ -270,6 +276,9 @@ impl<'a> StripUnconfigured<'a> { return vec![attr]; } + // A `#[cfg_attr]` either gets removed, or replaced with a new attribute + self.modified = true; + let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { None => return vec![], Some(r) => r, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 2da5bde028fc..5d40d478b963 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -12,7 +12,7 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; -use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path}; +use rustc_ast::{self as ast, AttrItem, AttrStyle, Block, LitKind, NodeId, PatKind, Path}; use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind, Unsafe}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; @@ -522,12 +522,29 @@ impl<'a, 'b> MacroExpander<'a, 'b> { item.visit_attrs(|attrs| attrs.retain(|a| !a.has_name(sym::derive))); (item, Vec::new()) } else { - let mut item = StripUnconfigured { + let mut visitor = StripUnconfigured { sess: self.cx.sess, features: self.cx.ecfg.features, - } - .fully_configure(item); + modified: false, + }; + let mut item = visitor.fully_configure(item); item.visit_attrs(|attrs| attrs.retain(|a| !a.has_name(sym::derive))); + if visitor.modified && !derives.is_empty() { + // Erase the tokens if cfg-stripping modified the item + // This will cause us to synthesize fake tokens + // when `nt_to_tokenstream` is called on this item. + match &mut item { + Annotatable::Item(item) => item.tokens = None, + Annotatable::Stmt(stmt) => { + if let StmtKind::Item(item) = &mut stmt.kind { + item.tokens = None + } else { + panic!("Unexpected stmt {:?}", stmt); + } + } + _ => panic!("Unexpected annotatable {:?}", item), + } + } invocations.reserve(derives.len()); let derive_placeholders = derives @@ -622,7 +639,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let invocations = { let mut collector = InvocationCollector { - cfg: StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features }, + cfg: StripUnconfigured { + sess: &self.cx.sess, + features: self.cx.ecfg.features, + modified: false, + }, cx: self.cx, invocations: Vec::new(), monotonic: self.monotonic, @@ -716,7 +737,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::Attr(expander) => { self.gate_proc_macro_input(&item); self.gate_proc_macro_attr_item(span, &item); - let tokens = item.into_tokens(&self.cx.sess.parse_sess); + let tokens = match attr.style { + AttrStyle::Outer => item.into_tokens(&self.cx.sess.parse_sess), + // FIXME: Properly collect tokens for inner attributes + AttrStyle::Inner => rustc_parse::fake_token_stream( + &self.cx.sess.parse_sess, + &item.into_nonterminal(), + span, + ), + }; let attr_item = attr.unwrap_normal_item(); if let MacArgs::Eq(..) = attr_item.args { self.cx.span_err(span, "key-value macro attributes are not supported"); diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 47247294f5dc..3b722c04cb15 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -36,16 +36,13 @@ crate mod mbe; mod tests; #[cfg(test)] mod parse { - #[cfg(test)] mod tests; } #[cfg(test)] mod tokenstream { - #[cfg(test)] mod tests; } #[cfg(test)] mod mut_visit { - #[cfg(test)] mod tests; } diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index eb4aab116f00..cbc4d14a65a1 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -84,7 +84,7 @@ enum TokenTree { /// e.g., `$var` MetaVar(Span, Ident), /// e.g., `$var:expr`. This is only used in the left hand side of MBE macros. - MetaVarDecl(Span, Ident /* name to bind */, NonterminalKind), + MetaVarDecl(Span, Ident /* name to bind */, Option), } impl TokenTree { diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 92a8f2311267..e76cc6f1fed7 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -1,4 +1,4 @@ -//! This is an NFA-based parser, which calls out to the main rust parser for named non-terminals +//! This is an NFA-based parser, which calls out to the main Rust parser for named non-terminals //! (which it commits to fully when it hits one in a grammar). There's a set of current NFA threads //! and a set of next ones. Instead of NTs, we have a special case for Kleene star. The big-O, in //! pathological cases, is worse than traditional use of NFA or Earley parsing, but it's an easier @@ -378,6 +378,11 @@ fn nameize>( n_rec(sess, next_m, res.by_ref(), ret_val)?; } } + TokenTree::MetaVarDecl(span, _, None) => { + if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { + return Err((span, "missing fragment specifier".to_string())); + } + } TokenTree::MetaVarDecl(sp, bind_name, _) => match ret_val .entry(MacroRulesNormalizedIdent::new(bind_name)) { @@ -422,7 +427,6 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { /// /// # Parameters /// -/// - `sess`: the parsing session into which errors are emitted. /// - `cur_items`: the set of current items to be processed. This should be empty by the end of a /// successful execution of this function. /// - `next_items`: the set of newly generated items. These are used to replenish `cur_items` in @@ -430,13 +434,12 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { /// - `eof_items`: the set of items that would be valid if this was the EOF. /// - `bb_items`: the set of items that are waiting for the black-box parser. /// - `token`: the current token of the parser. -/// - `span`: the `Span` in the source code corresponding to the token trees we are trying to match -/// against the matcher positions in `cur_items`. /// /// # Returns /// /// A `ParseResult`. Note that matches are kept track of through the items generated. fn inner_parse_loop<'root, 'tt>( + sess: &ParseSess, cur_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>, next_items: &mut Vec>, eof_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>, @@ -554,11 +557,21 @@ fn inner_parse_loop<'root, 'tt>( }))); } + // We need to match a metavar (but the identifier is invalid)... this is an error + TokenTree::MetaVarDecl(span, _, None) => { + if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { + return Error(span, "missing fragment specifier".to_string()); + } + } + // We need to match a metavar with a valid ident... call out to the black-box // parser by adding an item to `bb_items`. - TokenTree::MetaVarDecl(_, _, kind) => { - // Built-in nonterminals never start with these tokens, - // so we can eliminate them from consideration. + TokenTree::MetaVarDecl(_, _, Some(kind)) => { + // Built-in nonterminals never start with these tokens, so we can eliminate + // them from consideration. + // + // We use the span of the metavariable declaration to determine any + // edition-specific matching behavior for non-terminals. if Parser::nonterminal_may_begin_with(kind, token) { bb_items.push(item); } @@ -627,6 +640,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na // parsing from the black-box parser done. The result is that `next_items` will contain a // bunch of possible next matcher positions in `next_items`. match inner_parse_loop( + parser.sess, &mut cur_items, &mut next_items, &mut eof_items, @@ -688,7 +702,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na let nts = bb_items .iter() .map(|item| match item.top_elts.get_tt(item.idx) { - TokenTree::MetaVarDecl(_, bind, kind) => format!("{} ('{}')", kind, bind), + TokenTree::MetaVarDecl(_, bind, Some(kind)) => format!("{} ('{}')", kind, bind), _ => panic!(), }) .collect::>() @@ -718,8 +732,10 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na assert_eq!(bb_items.len(), 1); let mut item = bb_items.pop().unwrap(); - if let TokenTree::MetaVarDecl(span, _, kind) = item.top_elts.get_tt(item.idx) { + if let TokenTree::MetaVarDecl(span, _, Some(kind)) = item.top_elts.get_tt(item.idx) { let match_cur = item.match_cur; + // We use the span of the metavariable declaration to determine any + // edition-specific matching behavior for non-terminals. let nt = match parser.to_mut().parse_nonterminal(kind) { Err(mut err) => { err.span_label( diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 66463eeb9071..3d126749d541 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -401,7 +401,7 @@ pub fn compile_declarative_macro( let diag = &sess.parse_sess.span_diagnostic; let lhs_nm = Ident::new(sym::lhs, def.span); let rhs_nm = Ident::new(sym::rhs, def.span); - let tt_spec = NonterminalKind::TT; + let tt_spec = Some(NonterminalKind::TT); // Parse the macro_rules! invocation let (macro_rules, body) = match &def.kind { @@ -476,10 +476,15 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = - mbe::quoted::parse(tt.clone().into(), true, &sess.parse_sess, def.id) - .pop() - .unwrap(); + let tt = mbe::quoted::parse( + tt.clone().into(), + true, + &sess.parse_sess, + def.id, + features, + ) + .pop() + .unwrap(); valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt); return tt; } @@ -501,6 +506,7 @@ pub fn compile_declarative_macro( false, &sess.parse_sess, def.id, + features, ) .pop() .unwrap(); @@ -578,7 +584,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { TokenTree::Sequence(span, ref seq) => { if seq.separator.is_none() && seq.tts.iter().all(|seq_tt| match *seq_tt { - TokenTree::MetaVarDecl(_, _, NonterminalKind::Vis) => true, + TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true, TokenTree::Sequence(_, ref sub_seq) => { sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne @@ -961,7 +967,7 @@ fn check_matcher_core( // Now `last` holds the complete set of NT tokens that could // end the sequence before SUFFIX. Check that every one works with `suffix`. for token in &last.tokens { - if let TokenTree::MetaVarDecl(_, name, kind) = *token { + if let TokenTree::MetaVarDecl(_, name, Some(kind)) = *token { for next_token in &suffix_first.tokens { match is_in_follow(next_token, kind) { IsInFollow::Yes => {} @@ -1019,7 +1025,7 @@ fn check_matcher_core( } fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool { - if let mbe::TokenTree::MetaVarDecl(_, _, kind) = *tok { + if let mbe::TokenTree::MetaVarDecl(_, _, Some(kind)) = *tok { frag_can_be_followed_by_any(kind) } else { // (Non NT's can always be followed by anything in matchers.) @@ -1090,7 +1096,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::Pat => { + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1123,7 +1129,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { } _ => IsInFollow::No(TOKENS), }, - TokenTree::MetaVarDecl(_, _, NonterminalKind::Block) => IsInFollow::Yes, + TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Block)) => IsInFollow::Yes, _ => IsInFollow::No(TOKENS), } } @@ -1158,7 +1164,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { TokenTree::MetaVarDecl( _, _, - NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path, + Some(NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path), ) => IsInFollow::Yes, _ => IsInFollow::No(TOKENS), } @@ -1171,7 +1177,8 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { match *tt { mbe::TokenTree::Token(ref token) => pprust::token_to_string(&token), mbe::TokenTree::MetaVar(_, name) => format!("${}", name), - mbe::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind), + mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${}:{}", name, kind), + mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${}:", name), _ => panic!( "{}", "unexpected mbe::TokenTree::{Sequence or Delimited} \ diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 48db532c78f3..a4b44931fc1a 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -3,10 +3,11 @@ use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream; -use rustc_ast::NodeId; +use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; -use rustc_session::parse::ParseSess; -use rustc_span::symbol::{kw, Ident}; +use rustc_feature::Features; +use rustc_session::parse::{feature_err, ParseSess}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -29,10 +30,8 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ /// `ident` are "matchers". They are not present in the body of a macro rule -- just in the /// pattern, so we pass a parameter to indicate whether to expect them or not. /// - `sess`: the parsing session. Any errors will be emitted to this session. -/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use -/// unstable features or not. -/// - `edition`: which edition are we in. -/// - `macro_node_id`: the NodeId of the macro we are parsing. +/// - `node_id`: the NodeId of the macro we are parsing. +/// - `features`: language features so we can do feature gating. /// /// # Returns /// @@ -42,6 +41,7 @@ pub(super) fn parse( expect_matchers: bool, sess: &ParseSess, node_id: NodeId, + features: &Features, ) -> Vec { // Will contain the final collection of `self::TokenTree` let mut result = Vec::new(); @@ -52,7 +52,7 @@ pub(super) fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id); + let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id, features); match tree { TokenTree::MetaVar(start_sp, ident) if expect_matchers => { let span = match trees.next() { @@ -61,19 +61,40 @@ pub(super) fn parse( Some(tokenstream::TokenTree::Token(token)) => match token.ident() { Some((frag, _)) => { let span = token.span.with_lo(start_sp.lo()); - let kind = token::NonterminalKind::from_symbol(frag.name) - .unwrap_or_else(|| { - let msg = format!( - "invalid fragment specifier `{}`", - frag.name - ); - sess.span_diagnostic - .struct_span_err(span, &msg) - .help(VALID_FRAGMENT_NAMES_MSG) + + match frag.name { + sym::pat2018 | sym::pat2021 => { + if !features.edition_macro_pats { + feature_err( + sess, + sym::edition_macro_pats, + frag.span, + "`pat2018` and `pat2021` are unstable.", + ) .emit(); - token::NonterminalKind::Ident - }); - result.push(TokenTree::MetaVarDecl(span, ident, kind)); + } + } + _ => {} + } + + let kind = + token::NonterminalKind::from_symbol(frag.name, || { + span.edition() + }) + .unwrap_or_else( + || { + let msg = format!( + "invalid fragment specifier `{}`", + frag.name + ); + sess.span_diagnostic + .struct_span_err(span, &msg) + .help(VALID_FRAGMENT_NAMES_MSG) + .emit(); + token::NonterminalKind::Ident + }, + ); + result.push(TokenTree::MetaVarDecl(span, ident, Some(kind))); continue; } _ => token.span, @@ -83,8 +104,11 @@ pub(super) fn parse( } tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp), }; - sess.span_diagnostic.struct_span_err(span, "missing fragment specifier").emit(); - continue; + if node_id != DUMMY_NODE_ID { + // Macros loaded from other crates have dummy node ids. + sess.missing_fragment_specifiers.borrow_mut().insert(span, node_id); + } + result.push(TokenTree::MetaVarDecl(span, ident, None)); } // Not a metavar or no matchers allowed, so just return the tree @@ -107,14 +131,14 @@ pub(super) fn parse( /// converting `tree` /// - `expect_matchers`: same as for `parse` (see above). /// - `sess`: the parsing session. Any errors will be emitted to this session. -/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use -/// unstable features or not. +/// - `features`: language features so we can do feature gating. fn parse_tree( tree: tokenstream::TokenTree, outer_trees: &mut impl Iterator, expect_matchers: bool, sess: &ParseSess, node_id: NodeId, + features: &Features, ) -> TokenTree { // Depending on what `tree` is, we could be parsing different parts of a macro match tree { @@ -142,7 +166,7 @@ fn parse_tree( sess.span_diagnostic.span_err(span.entire(), &msg); } // Parse the contents of the sequence itself - let sequence = parse(tts, expect_matchers, sess, node_id); + let sequence = parse(tts, expect_matchers, sess, node_id, features); // Get the Kleene operator and optional separator let (separator, kleene) = parse_sep_and_kleene_op(&mut trees, span.entire(), sess); @@ -193,7 +217,10 @@ fn parse_tree( // descend into the delimited set and further parse it. tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( span, - Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess, node_id) }), + Lrc::new(Delimited { + delim, + tts: parse(tts, expect_matchers, sess, node_id, features), + }), ), } } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 36707a1ae272..e8e098b62129 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -3,7 +3,7 @@ use crate::proc_macro_server; use rustc_ast::ptr::P; use rustc_ast::token; -use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree}; use rustc_ast::{self as ast, *}; use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_err, Applicability, ErrorReported}; @@ -94,7 +94,12 @@ impl MultiItemModifier for ProcMacroDerive { let input = if item.pretty_printing_compatibility_hack() { TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into() } else { - nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP) + nt_to_tokenstream( + &item, + &ecx.sess.parse_sess, + DUMMY_SP, + CanSynthesizeMissingTokens::Yes, + ) }; let server = proc_macro_server::Rustc::new(ecx); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 4cfb188783ba..02ae842675f3 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -2,7 +2,8 @@ use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token; -use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; +use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens}; +use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::Diagnostic; @@ -178,7 +179,7 @@ impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec)> { TokenTree::Ident(Ident::new(sess, name.name, is_raw, name.span)) } else { - let stream = nt_to_tokenstream(&nt, sess, span); + let stream = nt_to_tokenstream(&nt, sess, span, CanSynthesizeMissingTokens::No); TokenTree::Group(Group { delimiter: Delimiter::None, stream, diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 4401ec0a04ea..aa54ffb132dc 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -273,6 +273,8 @@ declare_features! ( /// Allows patterns with concurrent by-move and by-ref bindings. /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. (accepted, move_ref_pattern, "1.48.0", Some(68354), None), + /// The smallest useful subset of `const_generics`. + (accepted, min_const_generics, "1.51.0", Some(74878), None), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 845e03150d71..c61857a1cd0a 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -578,9 +578,6 @@ declare_features! ( /// Allows calling `transmute` in const fn (active, const_fn_transmute, "1.46.0", Some(53605), None), - /// The smallest useful subset of `const_generics`. - (active, min_const_generics, "1.47.0", Some(74878), None), - /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), @@ -623,6 +620,9 @@ declare_features! ( /// Allows arbitrary expressions in key-value attributes at parse time. (active, extended_key_value_attributes, "1.50.0", Some(78835), None), + /// `:pat2018` and `:pat2021` macro matchers. + (active, edition_macro_pats, "1.51.0", Some(54883), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -651,5 +651,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ /// Some features are not allowed to be used together at the same time, if /// the two are present, produce an error. -pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = - &[(sym::const_generics, sym::min_const_generics)]; +/// +/// Currently empty, but we will probably need this again in the future, +/// so let's keep it in for now. +pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[]; diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index f965f7fdefe4..2a7c2a02fbaf 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -18,6 +18,9 @@ mod active; mod builtin_attrs; mod removed; +#[cfg(test)] +mod tests; + use rustc_span::{edition::Edition, symbol::Symbol, Span}; use std::fmt; use std::num::NonZeroU32; @@ -149,30 +152,3 @@ pub use builtin_attrs::{ AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, }; pub use removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES}; - -#[cfg(test)] -mod test { - use super::UnstableFeatures; - - #[test] - fn rustc_bootstrap_parsing() { - let is_bootstrap = |env, krate| { - std::env::set_var("RUSTC_BOOTSTRAP", env); - matches!(UnstableFeatures::from_environment(krate), UnstableFeatures::Cheat) - }; - assert!(is_bootstrap("1", None)); - assert!(is_bootstrap("1", Some("x"))); - // RUSTC_BOOTSTRAP allows specifying a specific crate - assert!(is_bootstrap("x", Some("x"))); - // RUSTC_BOOTSTRAP allows multiple comma-delimited crates - assert!(is_bootstrap("x,y,z", Some("x"))); - assert!(is_bootstrap("x,y,z", Some("y"))); - // Crate that aren't specified do not get unstable features - assert!(!is_bootstrap("x", Some("a"))); - assert!(!is_bootstrap("x,y,z", Some("a"))); - assert!(!is_bootstrap("x,y,z", None)); - - // this is technically a breaking change, but there are no stability guarantees for RUSTC_BOOTSTRAP - assert!(!is_bootstrap("0", None)); - } -} diff --git a/compiler/rustc_feature/src/tests.rs b/compiler/rustc_feature/src/tests.rs new file mode 100644 index 000000000000..50433e44b135 --- /dev/null +++ b/compiler/rustc_feature/src/tests.rs @@ -0,0 +1,23 @@ +use super::UnstableFeatures; + +#[test] +fn rustc_bootstrap_parsing() { + let is_bootstrap = |env, krate| { + std::env::set_var("RUSTC_BOOTSTRAP", env); + matches!(UnstableFeatures::from_environment(krate), UnstableFeatures::Cheat) + }; + assert!(is_bootstrap("1", None)); + assert!(is_bootstrap("1", Some("x"))); + // RUSTC_BOOTSTRAP allows specifying a specific crate + assert!(is_bootstrap("x", Some("x"))); + // RUSTC_BOOTSTRAP allows multiple comma-delimited crates + assert!(is_bootstrap("x,y,z", Some("x"))); + assert!(is_bootstrap("x,y,z", Some("y"))); + // Crate that aren't specified do not get unstable features + assert!(!is_bootstrap("x", Some("a"))); + assert!(!is_bootstrap("x,y,z", Some("a"))); + assert!(!is_bootstrap("x,y,z", None)); + + // this is technically a breaking change, but there are no stability guarantees for RUSTC_BOOTSTRAP + assert!(!is_bootstrap("0", None)); +} diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 280e863d4744..47a7651e1d41 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -28,7 +28,7 @@ pub struct Lifetime { pub span: Span, /// Either "`'a`", referring to a named lifetime definition, - /// or "``" (i.e., `kw::Invalid`), for elision placeholders. + /// or "``" (i.e., `kw::Empty`), for elision placeholders. /// /// HIR lowering inserts these placeholders in type paths that /// refer to type definitions needing lifetime parameters, @@ -607,7 +607,7 @@ pub struct Crate<'hir> { // over the ids in increasing order. In principle it should not // matter what order we visit things in, but in *practice* it // does, because it can affect the order in which errors are - // detected, which in turn can make compile-fail tests yield + // detected, which in turn can make UI tests yield // slightly different results. pub items: BTreeMap>, @@ -760,9 +760,9 @@ pub struct Pat<'hir> { pub default_binding_modes: bool, } -impl Pat<'_> { +impl<'hir> Pat<'hir> { // FIXME(#19596) this is a workaround, but there should be a better way - fn walk_short_(&self, it: &mut impl FnMut(&Pat<'_>) -> bool) -> bool { + fn walk_short_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) -> bool { if !it(self) { return false; } @@ -785,12 +785,12 @@ impl Pat<'_> { /// Note that when visiting e.g. `Tuple(ps)`, /// if visiting `ps[0]` returns `false`, /// then `ps[1]` will not be visited. - pub fn walk_short(&self, mut it: impl FnMut(&Pat<'_>) -> bool) -> bool { + pub fn walk_short(&self, mut it: impl FnMut(&Pat<'hir>) -> bool) -> bool { self.walk_short_(&mut it) } // FIXME(#19596) this is a workaround, but there should be a better way - fn walk_(&self, it: &mut impl FnMut(&Pat<'_>) -> bool) { + fn walk_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) { if !it(self) { return; } @@ -810,7 +810,7 @@ impl Pat<'_> { /// Walk the pattern in left-to-right order. /// /// If `it(pat)` returns `false`, the children are not visited. - pub fn walk(&self, mut it: impl FnMut(&Pat<'_>) -> bool) { + pub fn walk(&self, mut it: impl FnMut(&Pat<'hir>) -> bool) { self.walk_(&mut it) } @@ -1160,6 +1160,7 @@ pub struct Arm<'hir> { #[derive(Debug, HashStable_Generic)] pub enum Guard<'hir> { If(&'hir Expr<'hir>), + IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>), } #[derive(Debug, HashStable_Generic)] @@ -1721,6 +1722,8 @@ pub enum MatchSource { IfDesugar { contains_else_clause: bool }, /// An `if let _ = _ { .. }` (optionally with `else { .. }`). IfLetDesugar { contains_else_clause: bool }, + /// An `if let _ = _ => { .. }` match guard. + IfLetGuardDesugar, /// A `while _ { .. }` (which was desugared to a `loop { match _ { .. } }`). WhileDesugar, /// A `while let _ = _ { .. }` (which was desugared to a @@ -1739,7 +1742,7 @@ impl MatchSource { use MatchSource::*; match self { Normal => "match", - IfDesugar { .. } | IfLetDesugar { .. } => "if", + IfDesugar { .. } | IfLetDesugar { .. } | IfLetGuardDesugar => "if", WhileDesugar | WhileLetDesugar => "while", ForLoopDesugar => "for", TryDesugar => "?", diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 3c330c5d6c52..03c8b1738853 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1228,6 +1228,10 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) { if let Some(ref g) = arm.guard { match g { Guard::If(ref e) => visitor.visit_expr(e), + Guard::IfLet(ref pat, ref e) => { + visitor.visit_pat(pat); + visitor.visit_expr(e); + } } } visitor.visit_expr(&arm.body); diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index b870e4c6ead8..2774cc9c08e1 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -9,6 +9,13 @@ use crate::{Item, ItemKind, TraitItem, TraitItemKind}; use std::fmt::{self, Display}; +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum GenericParamKind { + Type, + Lifetime, + Const, +} + #[derive(Copy, Clone, PartialEq, Debug)] pub enum MethodKind { Trait { body: bool }, @@ -43,6 +50,7 @@ pub enum Target { ForeignFn, ForeignStatic, ForeignTy, + GenericParam(GenericParamKind), } impl Display for Target { @@ -77,6 +85,11 @@ impl Display for Target { Target::ForeignFn => "foreign function", Target::ForeignStatic => "foreign static item", Target::ForeignTy => "foreign type", + Target::GenericParam(kind) => match kind { + GenericParamKind::Type => "type parameter", + GenericParamKind::Lifetime => "lifetime parameter", + GenericParamKind::Const => "const parameter", + }, } ) } @@ -124,4 +137,14 @@ impl Target { hir::ForeignItemKind::Type => Target::ForeignTy, } } + + pub fn from_generic_param(generic_param: &hir::GenericParam<'_>) -> Target { + match generic_param.kind { + hir::GenericParamKind::Type { .. } => Target::GenericParam(GenericParamKind::Type), + hir::GenericParamKind::Lifetime { .. } => { + Target::GenericParam(GenericParamKind::Lifetime) + } + hir::GenericParamKind::Const { .. } => Target::GenericParam(GenericParamKind::Const), + } + } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 597c55b4bd7f..0b5eb1d82667 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2002,6 +2002,15 @@ impl<'a> State<'a> { self.print_expr(&e); self.s.space(); } + hir::Guard::IfLet(pat, e) => { + self.word_nbsp("if"); + self.word_nbsp("let"); + self.print_pat(&pat); + self.s.space(); + self.word_space("="); + self.print_expr(&e); + self.s.space(); + } } } self.word_space("=>"); diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index e17396422f13..9b4388c911f1 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -12,7 +12,7 @@ //! In this code, we report errors on each `rustc_if_this_changed` //! annotation. If a path exists in all cases, then we would report //! "all path(s) exist". Otherwise, we report: "no path to `foo`" for -//! each case where no path exists. `compile-fail` tests can then be +//! each case where no path exists. `ui` tests can then be //! used to check when paths exist or do not. //! //! The full form of the `rustc_if_this_changed` annotation is diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 8e00e54650df..0b501da7cd97 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -27,7 +27,6 @@ pub const WORD_BITS: usize = WORD_BYTES * 8; /// to or greater than the domain size. All operations that involve two bitsets /// will panic if the bitsets have differing domain sizes. /// -/// [`GrowableBitSet`]: struct.GrowableBitSet.html #[derive(Eq, PartialEq, Decodable, Encodable)] pub struct BitSet { domain_size: usize, diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 8a60b196e5e6..9002d251f123 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -625,7 +625,8 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { let var = self.canonical_var(info, r.into()); - let region = ty::ReLateBound(self.binder_index, ty::BoundRegion::BrAnon(var.as_u32())); + let br = ty::BoundRegion { kind: ty::BrAnon(var.as_u32()) }; + let region = ty::ReLateBound(self.binder_index, br); self.tcx().mk_region(region) } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index c8d66cbb695d..71ce50f74537 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -525,10 +525,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { result_subst: &'a CanonicalVarValues<'tcx>, ) -> impl Iterator> + 'a + Captures<'tcx> { unsubstituted_region_constraints.iter().map(move |&constraint| { - let ty::OutlivesPredicate(k1, r2) = - substitute_value(self.tcx, result_subst, constraint).skip_binder(); + let predicate = substitute_value(self.tcx, result_subst, constraint); + let ty::OutlivesPredicate(k1, r2) = predicate.skip_binder(); - let predicate = match k1.unpack() { + let atom = match k1.unpack() { GenericArgKind::Lifetime(r1) => { ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r1, r2)) } @@ -540,8 +540,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { // encounter this branch. span_bug!(cause.span, "unexpected const outlives {:?}", constraint); } - } - .potentially_quantified(self.tcx, ty::PredicateKind::ForAll); + }; + let predicate = + predicate.rebind(atom).potentially_quantified(self.tcx, ty::PredicateKind::ForAll); Obligation::new(cause.clone(), param_env, predicate) }) diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs index cd4f1fa3bc30..387f480814ae 100644 --- a/compiler/rustc_infer/src/infer/canonical/substitute.rs +++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs @@ -87,6 +87,6 @@ where c => bug!("{:?} is a const but value is {:?}", bound_ct, c), }; - tcx.replace_escaping_bound_vars(value, fld_r, fld_t, fld_c).0 + tcx.replace_escaping_bound_vars(value, fld_r, fld_t, fld_c) } } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 6a1715ef8189..e38eebe23b1e 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -543,6 +543,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { true } + fn visit_ct_substs(&self) -> bool { + true + } + fn binders( &mut self, a: ty::Binder, @@ -551,7 +555,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { where T: Relate<'tcx>, { - Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?)) + Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } fn relate_item_substs( @@ -716,7 +720,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { let variable_table = &mut inner.const_unification_table(); let var_value = variable_table.probe_value(vid); match var_value.val { - ConstVariableValue::Known { value: u } => self.relate(u, u), + ConstVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } ConstVariableValue::Unknown { universe } => { if self.for_universe.can_name(universe) { Ok(c) @@ -815,6 +822,10 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { true } + fn visit_ct_substs(&self) -> bool { + true + } + fn relate_with_variance>( &mut self, _variance: ty::Variance, @@ -833,7 +844,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { where T: Relate<'tcx>, { - Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?)) + Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { @@ -870,6 +881,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { } } } + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => Ok(t), _ => relate::super_relate_tys(self, t, t), } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 183fb314a00d..777107ed8631 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -153,6 +153,7 @@ fn msg_span_from_early_bound_and_free_regions( Some(Node::Item(it)) => item_scope_tag(&it), Some(Node::TraitItem(it)) => trait_item_scope_tag(&it), Some(Node::ImplItem(it)) => impl_item_scope_tag(&it), + Some(Node::ForeignItem(it)) => foreign_item_scope_tag(&it), _ => unreachable!(), }; let (prefix, span) = match *region { @@ -165,7 +166,9 @@ fn msg_span_from_early_bound_and_free_regions( } (format!("the lifetime `{}` as defined on", br.name), sp) } - ty::ReFree(ty::FreeRegion { bound_region: ty::BoundRegion::BrNamed(_, name), .. }) => { + ty::ReFree(ty::FreeRegion { + bound_region: ty::BoundRegionKind::BrNamed(_, name), .. + }) => { let mut sp = sm.guess_head_span(tcx.hir().span(node)); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) @@ -231,6 +234,13 @@ fn impl_item_scope_tag(item: &hir::ImplItem<'_>) -> &'static str { } } +fn foreign_item_scope_tag(item: &hir::ForeignItem<'_>) -> &'static str { + match item.kind { + hir::ForeignItemKind::Fn(..) => "method body", + hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => "associated item", + } +} + fn explain_span(tcx: TyCtxt<'tcx>, heading: &str, span: Span) -> (String, Option) { let lo = tcx.sess.source_map().lookup_char_pos(span.lo()); (format!("the {} at {}:{}", heading, lo.line, lo.col.to_usize() + 1), Some(span)) @@ -415,7 +425,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // obviously it never weeds out ALL errors. fn process_errors( &self, - errors: &Vec>, + errors: &[RegionResolutionError<'tcx>], ) -> Vec> { debug!("process_errors()"); @@ -440,7 +450,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { - errors.clone() + errors.to_owned() } else { errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() }; @@ -496,7 +506,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn print_dyn_existential( self, - _predicates: &'tcx ty::List>, + _predicates: &'tcx ty::List>>, ) -> Result { Err(NonTrivialPath) } @@ -1622,7 +1632,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } (TypeError::ObjectUnsafeCoercion(_), _) => { - diag.note_unsuccessfull_coercion(found, expected); + diag.note_unsuccessful_coercion(found, expected); } (_, _) => { debug!( @@ -2279,7 +2289,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, var_origin: RegionVariableOrigin, ) -> DiagnosticBuilder<'tcx> { - let br_string = |br: ty::BoundRegion| { + let br_string = |br: ty::BoundRegionKind| { let mut s = match br { ty::BrNamed(_, name) => name.to_string(), _ => String::new(), diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index eb1521f05657..b014b9832e78 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -25,7 +25,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) fn find_anon_type( &self, region: Region<'tcx>, - br: &ty::BoundRegion, + br: &ty::BoundRegionKind, ) -> Option<(&hir::Ty<'tcx>, &hir::FnDecl<'tcx>)> { if let Some(anon_reg) = self.tcx().is_suitable_region(region) { let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id); @@ -56,7 +56,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { fn find_component_for_bound_region( &self, arg: &'tcx hir::Ty<'tcx>, - br: &ty::BoundRegion, + br: &ty::BoundRegionKind, ) -> Option<&'tcx hir::Ty<'tcx>> { let mut nested_visitor = FindNestedTypeVisitor { tcx: self.tcx(), @@ -80,7 +80,7 @@ struct FindNestedTypeVisitor<'tcx> { tcx: TyCtxt<'tcx>, // The bound_region corresponding to the Refree(freeregion) // associated with the anonymous region we are looking for. - bound_region: ty::BoundRegion, + bound_region: ty::BoundRegionKind, // The type where the anonymous lifetime appears // for e.g., Vec<`&u8`> and <`&u8`> found_type: Option<&'tcx hir::Ty<'tcx>>, @@ -207,7 +207,7 @@ impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { struct TyPathVisitor<'tcx> { tcx: TyCtxt<'tcx>, found_it: bool, - bound_region: ty::BoundRegion, + bound_region: ty::BoundRegionKind, current_index: ty::DebruijnIndex, } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 4d3217a9c0bd..0958afa03082 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -132,10 +132,7 @@ impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { [segment] if segment .res - .map(|res| match res { - Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _) => true, - _ => false, - }) + .map(|res| matches!(res, Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _))) .unwrap_or(false) => { self.types.push(path.span); diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 61fad8863e7c..17a56046a5cc 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -14,8 +14,8 @@ pub(super) struct AnonymousParamInfo<'tcx> { pub param: &'tcx hir::Param<'tcx>, /// The type corresponding to the anonymous region parameter. pub param_ty: Ty<'tcx>, - /// The ty::BoundRegion corresponding to the anonymous region. - pub bound_region: ty::BoundRegion, + /// The ty::BoundRegionKind corresponding to the anonymous region. + pub bound_region: ty::BoundRegionKind, /// The `Span` of the parameter type. pub param_ty_span: Span, /// Signals that the argument is the first parameter in the declaration. @@ -43,7 +43,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), ty::ReEarlyBound(ebr) => ( self.tcx().parent(ebr.def_id).unwrap(), - ty::BoundRegion::BrNamed(ebr.def_id, ebr.name), + ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name), ), _ => return None, // not a free region }; @@ -145,7 +145,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) fn is_return_type_anon( &self, scope_def_id: LocalDefId, - br: ty::BoundRegion, + br: ty::BoundRegionKind, decl: &hir::FnDecl<'_>, ) -> Option { let ret_ty = self.tcx().type_of(scope_def_id); diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index 32f73237dd41..728dc2de3703 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -93,10 +93,7 @@ impl<'tcx> FreeRegionMap<'tcx> { /// True for free regions other than `'static`. pub fn is_free(&self, r: Region<'_>) -> bool { - match *r { - ty::ReEarlyBound(_) | ty::ReFree(_) => true, - _ => false, - } + matches!(r, ty::ReEarlyBound(_) | ty::ReFree(_)) } /// True if `r` is a free region or static of the sort that this diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index 39043980dc4f..e794903fca3a 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -77,10 +77,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // (i.e., if there are no placeholders). let next_universe = self.universe().next_universe(); - let fld_r = |br| { + let fld_r = |br: ty::BoundRegion| { self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { universe: next_universe, - name: br, + name: br.kind, })) }; diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index d7b2ce7ee208..ab34cda8cc18 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -393,10 +393,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { if self.expand_node(a_region, b_vid, b_data) { changes.push(b_vid); } - match *b_data { - VarValue::Value(ReStatic) | VarValue::ErrorValue => false, - _ => true, - } + !matches!(b_data, VarValue::Value(ReStatic) | VarValue::ErrorValue) }); } } @@ -972,11 +969,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } VerifyBound::IsEmpty => { - if let ty::ReEmpty(_) = min { - true - } else { - false - } + matches!(min, ty::ReEmpty(_)) } VerifyBound::AnyBound(bs) => { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6affe0e5463d..56d9634213ae 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -450,7 +450,7 @@ pub enum RegionVariableOrigin { /// Region variables created for bound regions /// in a function or method that is called - LateBoundRegion(Span, ty::BoundRegion, LateBoundRegionConversionTime), + LateBoundRegion(Span, ty::BoundRegionKind, LateBoundRegionConversionTime), UpvarRegion(ty::UpvarId, Span), @@ -1317,7 +1317,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { T: TypeFoldable<'tcx>, { if !value.needs_infer() { - return value.clone(); // Avoid duplicated subst-folding. + return value; // Avoid duplicated subst-folding. } let mut r = resolve::OpportunisticVarResolver::new(self); value.fold_with(&mut r) @@ -1421,7 +1421,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { where T: TypeFoldable<'tcx>, { - let fld_r = |br| self.next_region_var(LateBoundRegion(span, br, lbrct)); + let fld_r = + |br: ty::BoundRegion| self.next_region_var(LateBoundRegion(span, br.kind, lbrct)); let fld_t = |_| { self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 0b2847658f71..97ef685cf6f8 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -176,7 +176,7 @@ where universe }); - let placeholder = ty::PlaceholderRegion { universe, name: br }; + let placeholder = ty::PlaceholderRegion { universe, name: br.kind }; delegate.next_placeholder_region(placeholder) } else { delegate.next_existential_region_var(true) @@ -1008,6 +1008,6 @@ where self.first_free_index.shift_in(1); let result = self.relate(a.skip_binder(), a.skip_binder())?; self.first_free_index.shift_out(1); - Ok(ty::Binder::bind(result)) + Ok(a.rebind(result)) } } diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index 65284bcee912..33bddf1dedc1 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -90,6 +90,7 @@ impl ProjectionCacheKey<'tcx> { pub enum ProjectionCacheEntry<'tcx> { InProgress, Ambiguous, + Recur, Error, NormalizedTy(NormalizedTy<'tcx>), } @@ -143,7 +144,12 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}", key, value ); - let fresh_key = self.map().insert(key, ProjectionCacheEntry::NormalizedTy(value)); + let mut map = self.map(); + if let Some(ProjectionCacheEntry::Recur) = map.get(&key) { + debug!("Not overwriting Recur"); + return; + } + let fresh_key = map.insert(key, ProjectionCacheEntry::NormalizedTy(value)); assert!(!fresh_key, "never started projecting `{:?}`", key); } @@ -197,6 +203,14 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { assert!(!fresh, "never started projecting `{:?}`", key); } + /// Indicates that while trying to normalize `key`, `key` was required to + /// be normalized again. Selection or evaluation should eventually report + /// an error here. + pub fn recur(&mut self, key: ProjectionCacheKey<'tcx>) { + let fresh = self.map().insert(key, ProjectionCacheEntry::Recur); + assert!(!fresh, "never started projecting `{:?}`", key); + } + /// Indicates that trying to normalize `key` resulted in /// error. pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) { diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index acd49d86c2c8..28eb1fed6a0a 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -25,8 +25,9 @@ use std::sync::{Arc, Mutex}; pub type Result = result::Result; /// Represents a compiler session. +/// /// Can be used to run `rustc_interface` queries. -/// Created by passing `Config` to `run_compiler`. +/// Created by passing [`Config`] to [`run_compiler`]. pub struct Compiler { pub(crate) sess: Lrc, codegen_backend: Lrc>, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index c13d26c79d73..461ee0859227 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -29,6 +29,7 @@ use rustc_passes::{self, hir_stats, layout_test}; use rustc_plugin_impl as plugin; use rustc_resolve::{Resolver, ResolverArenas}; use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType, PpMode, PpSourceMode}; +use rustc_session::lint; use rustc_session::output::{filename_for_input, filename_for_metadata}; use rustc_session::search_paths::PathKind; use rustc_session::Session; @@ -95,7 +96,7 @@ declare_box_region_type!( /// harness if one is to be provided, injection of a dependency on the /// standard library and prelude, and name resolution. /// -/// Returns `None` if we're aborting after handling -W help. +/// Returns [`None`] if we're aborting after handling -W help. pub fn configure_and_expand( sess: Lrc, lint_store: Lrc, @@ -306,11 +307,27 @@ fn configure_and_expand_inner<'a>( ecx.check_unused_macros(); }); + let mut missing_fragment_specifiers: Vec<_> = ecx + .sess + .parse_sess + .missing_fragment_specifiers + .borrow() + .iter() + .map(|(span, node_id)| (*span, *node_id)) + .collect(); + missing_fragment_specifiers.sort_unstable_by_key(|(span, _)| *span); + + let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); + + for (span, node_id) in missing_fragment_specifiers { + let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER; + let msg = "missing fragment specifier"; + resolver.lint_buffer().buffer_lint(lint, node_id, span, msg); + } if cfg!(windows) { env::set_var("PATH", &old_path); } - let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); if recursion_limit_hit { // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed // with a large AST @@ -872,7 +889,7 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { // Avoid overwhelming user with errors if borrow checking failed. // I'm not sure how helpful this is, to be honest, but it avoids a - // lot of annoying errors in the compile-fail tests (basically, + // lot of annoying errors in the ui tests (basically, // lint warnings and so on -- kindck used to do this abort, but // kindck is gone now). -nmatsakis if sess.has_errors() { @@ -996,6 +1013,23 @@ pub fn start_codegen<'tcx>( codegen_backend.codegen_crate(tcx, metadata, need_metadata_module) }); + // Don't run these test assertions when not doing codegen. Compiletest tries to build + // build-fail tests in check mode first and expects it to not give an error in that case. + if tcx.sess.opts.output_types.should_codegen() { + rustc_incremental::assert_module_sources::assert_module_sources(tcx); + rustc_symbol_mangling::test::report_symbol_names(tcx); + } + + tcx.sess.time("assert_dep_graph", || rustc_incremental::assert_dep_graph(tcx)); + tcx.sess.time("serialize_dep_graph", || rustc_incremental::save_dep_graph(tcx)); + + // We assume that no queries are run past here. If there are new queries + // after this point, they'll show up as "" in self-profiling data. + { + let _prof_timer = tcx.prof.generic_activity("self_profile_alloc_query_strings"); + tcx.alloc_self_profile_query_strings(); + } + info!("Post-codegen\n{:?}", tcx.debug_stats()); if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 4c340b3fc1f5..9c49f926d417 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -23,7 +23,11 @@ use std::cell::{Ref, RefCell, RefMut}; use std::rc::Rc; /// Represent the result of a query. -/// This result can be stolen with the `take` method and generated with the `compute` method. +/// +/// This result can be stolen with the [`take`] method and generated with the [`compute`] method. +/// +/// [`take`]: Self::take +/// [`compute`]: Self::compute pub struct Query { result: RefCell>>, } @@ -276,7 +280,7 @@ impl<'tcx> Queries<'tcx> { // Don't do code generation if there were any errors self.session().compile_status()?; - // Hook for compile-fail tests. + // Hook for UI tests. Self::check_for_rustc_errors_attr(tcx); Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek())) @@ -285,7 +289,7 @@ impl<'tcx> Queries<'tcx> { } /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used - /// to write compile-fail tests that actually test that compilation succeeds without reporting + /// to write UI tests that actually test that compilation succeeds without reporting /// an error. fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { let def_id = match tcx.entry_fn(LOCAL_CRATE) { @@ -399,6 +403,7 @@ impl Linker { return Ok(()); } + let _timer = sess.prof.verbose_generic_activity("link_crate"); self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs) } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 2273266a3ff8..3e94f1637734 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -587,7 +587,7 @@ fn test_debugging_options_tracking_hash() { tracked!(share_generics, Some(true)); tracked!(show_span, Some(String::from("abc"))); tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1)); - tracked!(symbol_mangling_version, SymbolManglingVersion::V0); + tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0)); tracked!(teach, true); tracked!(thinlto, Some(true)); tracked!(tune_cpu, Some(String::from("abc"))); diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 697d25fdb585..b4dd0fc2449e 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -201,7 +201,7 @@ fn scan_escape(first_char: char, chars: &mut Chars<'_>, mode: Mode) -> Result return Err(EscapeError::LeadingUnderscoreUnicodeEscape), diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 676c85e4afdc..a8371274f61f 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -868,7 +868,7 @@ impl EarlyLintPass for AnonymousParameters { if let ast::AssocItemKind::Fn(_, ref sig, _, _) = it.kind { for arg in sig.decl.inputs.iter() { if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { - if ident.name == kw::Invalid { + if ident.name == kw::Empty { cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| { let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); @@ -2299,7 +2299,7 @@ impl EarlyLintPass for IncompleteFeatures { } } -const HAS_MIN_FEATURES: &[Symbol] = &[sym::const_generics, sym::specialization]; +const HAS_MIN_FEATURES: &[Symbol] = &[sym::specialization]; declare_lint! { /// The `invalid_value` lint detects creating a value that is not valid, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 16563d21ff13..c82fe50af870 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -261,6 +261,7 @@ impl LintStore { } } + #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { let target = match self.by_name.get(new_name) { Some(&Id(lint_id)) => lint_id, @@ -728,7 +729,7 @@ impl<'tcx> LateContext<'tcx> { /// Check if a `DefId`'s path matches the given absolute type path usage. /// - /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`; + /// Anonymous scopes such as `extern` imports are matched with `kw::Empty`; /// inherent `impl` blocks are matched with the name of the type. /// /// Instead of using this method, it is often preferable to instead use @@ -786,7 +787,7 @@ impl<'tcx> LateContext<'tcx> { fn print_dyn_existential( self, - _predicates: &'tcx ty::List>, + _predicates: &'tcx ty::List>>, ) -> Result { Ok(()) } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 3e22eba15aae..5cece569903c 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -12,7 +12,9 @@ use rustc_hir::{intravisit, HirId}; use rustc_middle::hir::map::Map; use rustc_middle::lint::LevelSource; use rustc_middle::lint::LintDiagnosticBuilder; -use rustc_middle::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; +use rustc_middle::lint::{ + struct_lint_level, LintLevelMap, LintLevelSets, LintLevelSource, LintSet, +}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{builtin, Level, Lint, LintId}; @@ -91,7 +93,7 @@ impl<'s> LintLevelsBuilder<'s> { }; for id in ids { self.check_gated_lint(id, DUMMY_SP); - let src = LintSource::CommandLine(lint_flag_val, orig_level); + let src = LintLevelSource::CommandLine(lint_flag_val, orig_level); specs.insert(id, (level, src)); } } @@ -128,19 +130,19 @@ impl<'s> LintLevelsBuilder<'s> { ); diag_builder.span_label(src.span(), "overruled by previous forbid"); match old_src { - LintSource::Default => { + LintLevelSource::Default => { diag_builder.note(&format!( "`forbid` lint level is the default for {}", id.to_string() )); } - LintSource::Node(_, forbid_source_span, reason) => { + LintLevelSource::Node(_, forbid_source_span, reason) => { diag_builder.span_label(forbid_source_span, "`forbid` level set here"); if let Some(rationale) = reason { diag_builder.note(&rationale.as_str()); } } - LintSource::CommandLine(_, _) => { + LintLevelSource::CommandLine(_, _) => { diag_builder.note("`forbid` lint level was set on command line"); } } @@ -276,7 +278,7 @@ impl<'s> LintLevelsBuilder<'s> { let name = meta_item.path.segments.last().expect("empty lint name").ident.name; match store.check_lint_name(&name.as_str(), tool_name) { CheckLintNameResult::Ok(ids) => { - let src = LintSource::Node(name, li.span(), reason); + let src = LintLevelSource::Node(name, li.span(), reason); for &id in ids { self.check_gated_lint(id, attr.span); self.insert_spec(&mut specs, id, (level, src)); @@ -287,7 +289,7 @@ impl<'s> LintLevelsBuilder<'s> { match result { Ok(ids) => { let complete_name = &format!("{}::{}", tool_name.unwrap(), name); - let src = LintSource::Node( + let src = LintLevelSource::Node( Symbol::intern(complete_name), li.span(), reason, @@ -324,7 +326,7 @@ impl<'s> LintLevelsBuilder<'s> { }, ); - let src = LintSource::Node( + let src = LintLevelSource::Node( Symbol::intern(&new_lint_name), li.span(), reason, @@ -403,7 +405,7 @@ impl<'s> LintLevelsBuilder<'s> { } let (lint_attr_name, lint_attr_span) = match *src { - LintSource::Node(name, span, _) => (name, span), + LintLevelSource::Node(name, span, _) => (name, span), _ => continue, }; @@ -460,7 +462,7 @@ impl<'s> LintLevelsBuilder<'s> { } /// Find the lint level for a lint. - pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintSource) { + pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintLevelSource) { self.sets.get_lint_level(lint, self.cur, None, self.sess) } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 80ef855c3859..2336b52619ab 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -283,7 +283,6 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { UNUSED_MUT, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, - OVERLAPPING_PATTERNS, UNUSED_MUST_USE, UNUSED_UNSAFE, PATH_STATEMENTS, @@ -335,6 +334,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { store.register_renamed("exceeding_bitshifts", "arithmetic_overflow"); store.register_renamed("redundant_semicolon", "redundant_semicolons"); store.register_renamed("intra_doc_link_resolution_failure", "broken_intra_doc_links"); + store.register_renamed("overlapping_patterns", "overlapping_range_endpoints"); store.register_removed("unknown_features", "replaced by an error"); store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); store.register_removed("negate_unsigned", "cast a signed value instead"); diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index 428198cae891..0fe6564880f0 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -28,27 +28,19 @@ declare_lint_pass!(RedundantSemicolons => [REDUNDANT_SEMICOLONS]); impl EarlyLintPass for RedundantSemicolons { fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { - let mut after_item_stmt = false; let mut seq = None; for stmt in block.stmts.iter() { match (&stmt.kind, &mut seq) { (StmtKind::Empty, None) => seq = Some((stmt.span, false)), (StmtKind::Empty, Some(seq)) => *seq = (seq.0.to(stmt.span), true), - (_, seq) => { - maybe_lint_redundant_semis(cx, seq, after_item_stmt); - after_item_stmt = matches!(stmt.kind, StmtKind::Item(_)); - } + (_, seq) => maybe_lint_redundant_semis(cx, seq), } } - maybe_lint_redundant_semis(cx, &mut seq, after_item_stmt); + maybe_lint_redundant_semis(cx, &mut seq); } } -fn maybe_lint_redundant_semis( - cx: &EarlyContext<'_>, - seq: &mut Option<(Span, bool)>, - after_item_stmt: bool, -) { +fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) { if let Some((span, multiple)) = seq.take() { // FIXME: Find a better way of ignoring the trailing // semicolon from macro expansion @@ -56,12 +48,6 @@ fn maybe_lint_redundant_semis( return; } - // FIXME: Lint on semicolons after item statements - // once doing so doesn't break bootstrapping - if after_item_stmt { - return; - } - cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| { let (msg, rem) = if multiple { ("unnecessary trailing semicolons", "remove these semicolons") diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 2a5ad5e6c98a..5e1f94c071c6 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -218,8 +218,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } ty::Dynamic(binder, _) => { let mut has_emitted = false; - for predicate in binder.skip_binder().iter() { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate { + for predicate in binder.iter() { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = + predicate.skip_binder() + { let def_id = trait_ref.def_id; let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post,); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a9358c9610a5..1c692d4f2076 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -588,8 +588,8 @@ declare_lint! { } declare_lint! { - /// The `overlapping_patterns` lint detects `match` arms that have - /// [range patterns] that overlap. + /// The `overlapping_range_endpoints` lint detects `match` arms that have [range patterns] that + /// overlap on their endpoints. /// /// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns /// @@ -607,13 +607,12 @@ declare_lint! { /// /// ### Explanation /// - /// It is likely a mistake to have range patterns in a match expression - /// that overlap. Check that the beginning and end values are what you - /// expect, and keep in mind that with `..=` the left and right bounds are - /// inclusive. - pub OVERLAPPING_PATTERNS, + /// It is likely a mistake to have range patterns in a match expression that overlap in this + /// way. Check that the beginning and end values are what you expect, and keep in mind that + /// with `..=` the left and right bounds are inclusive. + pub OVERLAPPING_RANGE_ENDPOINTS, Warn, - "detects overlapping patterns" + "detects range patterns with overlapping endpoints" } declare_lint! { @@ -1228,6 +1227,50 @@ declare_lint! { }; } +declare_lint! { + /// The `missing_fragment_specifier` lint is issued when an unused pattern in a + /// `macro_rules!` macro definition has a meta-variable (e.g. `$e`) that is not + /// followed by a fragment specifier (e.g. `:expr`). + /// + /// This warning can always be fixed by removing the unused pattern in the + /// `macro_rules!` macro definition. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// macro_rules! foo { + /// () => {}; + /// ($name) => { }; + /// } + /// + /// fn main() { + /// foo!(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// To fix this, remove the unused pattern from the `macro_rules!` macro definition: + /// + /// ```rust + /// macro_rules! foo { + /// () => {}; + /// } + /// fn main() { + /// foo!(); + /// } + /// ``` + pub MISSING_FRAGMENT_SPECIFIER, + Deny, + "detects missing fragment specifiers in unused `macro_rules!` patterns", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #40107 ", + edition: None, + }; +} + declare_lint! { /// The `late_bound_lifetime_arguments` lint detects generic lifetime /// arguments in path segments with late bound lifetime parameters. @@ -2809,7 +2852,7 @@ declare_lint_pass! { DEAD_CODE, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, - OVERLAPPING_PATTERNS, + OVERLAPPING_RANGE_ENDPOINTS, BINDINGS_WITH_VARIANT_NAME, UNUSED_MACROS, WARNINGS, @@ -2828,6 +2871,7 @@ declare_lint_pass! { CONST_ITEM_MUTATION, SAFE_PACKED_BORROWS, PATTERNS_IN_FNS_WITHOUT_BODY, + MISSING_FRAGMENT_SPECIFIER, LATE_BOUND_LIFETIME_ARGUMENTS, ORDER_DEPENDENT_TRAIT_OBJECTS, COHERENCE_LEAK_CHECK, diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 54b22ca49a29..621363bed80e 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -201,10 +201,10 @@ fn main() { cmd.args(&components); for lib in output(&mut cmd).split_whitespace() { - let name = if lib.starts_with("-l") { - &lib[2..] - } else if lib.starts_with('-') { - &lib[1..] + let name = if let Some(stripped) = lib.strip_prefix("-l") { + stripped + } else if let Some(stripped) = lib.strip_prefix('-') { + stripped } else if Path::new(lib).exists() { // On MSVC llvm-config will print the full name to libraries, but // we're only interested in the name part @@ -241,17 +241,17 @@ fn main() { cmd.arg(llvm_link_arg).arg("--ldflags"); for lib in output(&mut cmd).split_whitespace() { if is_crossed { - if lib.starts_with("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target)); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target)); + if let Some(stripped) = lib.strip_prefix("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", stripped.replace(&host, &target)); + } else if let Some(stripped) = lib.strip_prefix("-L") { + println!("cargo:rustc-link-search=native={}", stripped.replace(&host, &target)); } - } else if lib.starts_with("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", &lib[9..]); - } else if lib.starts_with("-l") { - println!("cargo:rustc-link-lib={}", &lib[2..]); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", &lib[2..]); + } else if let Some(stripped) = lib.strip_prefix("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", stripped); + } else if let Some(stripped) = lib.strip_prefix("-l") { + println!("cargo:rustc-link-lib={}", stripped); + } else if let Some(stripped) = lib.strip_prefix("-L") { + println!("cargo:rustc-link-search=native={}", stripped); } } @@ -262,10 +262,10 @@ fn main() { let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); if let Some(s) = llvm_linker_flags { for lib in s.into_string().unwrap().split_whitespace() { - if lib.starts_with("-l") { - println!("cargo:rustc-link-lib={}", &lib[2..]); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", &lib[2..]); + if let Some(stripped) = lib.strip_prefix("-l") { + println!("cargo:rustc-link-lib={}", stripped); + } else if let Some(stripped) = lib.strip_prefix("-L") { + println!("cargo:rustc-link-search=native={}", stripped); } } } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 01d76bb3e94f..2264908995bb 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -450,7 +450,8 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool AsmComments, bool EmitStackSizeSection, bool RelaxELFRelocations, - bool UseInitArray) { + bool UseInitArray, + const char *SplitDwarfFile) { auto OptLevel = fromRust(RustOptLevel); auto RM = fromRust(RustReloc); @@ -476,6 +477,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.MCOptions.AsmVerbose = AsmComments; Options.MCOptions.PreserveAsmComments = AsmComments; Options.MCOptions.ABIName = ABIStr; + if (SplitDwarfFile) { + Options.MCOptions.SplitDwarfFile = SplitDwarfFile; + } Options.RelaxELFRelocations = RelaxELFRelocations; Options.UseInitArray = UseInitArray; @@ -610,7 +614,7 @@ static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) { extern "C" LLVMRustResult LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, - LLVMModuleRef M, const char *Path, + LLVMModuleRef M, const char *Path, const char *DwoPath, LLVMRustFileType RustFileType) { llvm::legacy::PassManager *PM = unwrap(PMR); auto FileType = fromRust(RustFileType); @@ -626,8 +630,22 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, } buffer_ostream BOS(OS); - unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); - PM->run(*unwrap(M)); + if (DwoPath) { + raw_fd_ostream DOS(DwoPath, EC, sys::fs::F_None); + EC.clear(); + if (EC) + ErrorInfo = EC.message(); + if (ErrorInfo != "") { + LLVMRustSetLastError(ErrorInfo.c_str()); + return LLVMRustResult::Failure; + } + buffer_ostream DBOS(DOS); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, false); + PM->run(*unwrap(M)); + } else { + unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); + PM->run(*unwrap(M)); + } // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output // stream (OS), so the only real safe place to delete this is here? Don't we diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index e17f933932e6..c0ff62c17beb 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -690,13 +690,14 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( const char *Producer, size_t ProducerLen, bool isOptimized, const char *Flags, unsigned RuntimeVer, const char *SplitName, size_t SplitNameLen, - LLVMRustDebugEmissionKind Kind) { + LLVMRustDebugEmissionKind Kind, + uint64_t DWOId, bool SplitDebugInlining) { auto *File = unwrapDI(FileRef); return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen), isOptimized, Flags, RuntimeVer, StringRef(SplitName, SplitNameLen), - fromRust(Kind))); + fromRust(Kind), DWOId, SplitDebugInlining)); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile( diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 5c28839c9b7e..152ae159aed4 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -21,7 +21,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { #[proc_macro] pub fn symbols(input: TokenStream) -> TokenStream { - symbols::symbols(input) + symbols::symbols(input.into()).into() } decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive); diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 12990ae2d94a..6d876784be65 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -5,8 +5,8 @@ use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - braced, parenthesized, parse_macro_input, AttrStyle, Attribute, Block, Error, Expr, Ident, - ReturnType, Token, Type, + braced, parenthesized, parse_macro_input, parse_quote, AttrStyle, Attribute, Block, Error, + Expr, Ident, ReturnType, Token, Type, }; mod kw { @@ -272,6 +272,40 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers { if desc.is_some() { panic!("duplicate modifier `desc` for query `{}`", query.name); } + // If there are no doc-comments, give at least some idea of what + // it does by showing the query description. + if query.doc_comments.is_empty() { + use ::syn::*; + let mut list = list.iter(); + let format_str: String = match list.next() { + Some(&Expr::Lit(ExprLit { lit: Lit::Str(ref lit_str), .. })) => { + lit_str.value().replace("`{}`", "{}") // We add them later anyways for consistency + } + _ => panic!("Expected a string literal"), + }; + let mut fmt_fragments = format_str.split("{}"); + let mut doc_string = fmt_fragments.next().unwrap().to_string(); + list.map(::quote::ToTokens::to_token_stream).zip(fmt_fragments).for_each( + |(tts, next_fmt_fragment)| { + use ::core::fmt::Write; + write!( + &mut doc_string, + " `{}` {}", + tts.to_string().replace(" . ", "."), + next_fmt_fragment, + ) + .unwrap(); + }, + ); + let doc_string = format!( + "[query description - consider adding a doc-comment!] {}", + doc_string + ); + let comment = parse_quote! { + #[doc = #doc_string] + }; + query.doc_comments.push(comment); + } desc = Some((tcx, list)); } QueryModifier::FatalCycle => { diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs index 610b9155cfc1..5c061a9d3c79 100644 --- a/compiler/rustc_macros/src/session_diagnostic.rs +++ b/compiler/rustc_macros/src/session_diagnostic.rs @@ -574,7 +574,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> { /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y) /// ``` /// This function builds the entire call to format!. - fn build_format(&self, input: &String, span: proc_macro2::Span) -> proc_macro2::TokenStream { + fn build_format(&self, input: &str, span: proc_macro2::Span) -> proc_macro2::TokenStream { // This set is used later to generate the final format string. To keep builds reproducible, // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead // of a HashSet. diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 94d4ad78e8d9..5b932864dff5 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -1,8 +1,35 @@ -use proc_macro::TokenStream; +//! Proc macro which builds the Symbol table +//! +//! # Debugging +//! +//! Since this proc-macro does some non-trivial work, debugging it is important. +//! This proc-macro can be invoked as an ordinary unit test, like so: +//! +//! ```bash +//! cd compiler/rustc_macros +//! cargo test symbols::test_symbols -- --nocapture +//! ``` +//! +//! This unit test finds the `symbols!` invocation in `compiler/rustc_span/src/symbol.rs` +//! and runs it. It verifies that the output token stream can be parsed as valid module +//! items and that no errors were produced. +//! +//! You can also view the generated code by using `cargo expand`: +//! +//! ```bash +//! cargo install cargo-expand # this is necessary only once +//! cd compiler/rustc_span +//! cargo expand > /tmp/rustc_span.rs # it's a big file +//! ``` + +use proc_macro2::{Span, TokenStream}; use quote::quote; -use std::collections::HashSet; +use std::collections::HashMap; use syn::parse::{Parse, ParseStream, Result}; -use syn::{braced, parse_macro_input, Ident, LitStr, Token}; +use syn::{braced, punctuated::Punctuated, Ident, LitStr, Token}; + +#[cfg(test)] +mod tests; mod kw { syn::custom_keyword!(Keywords); @@ -19,7 +46,6 @@ impl Parse for Keyword { let name = input.parse()?; input.parse::()?; let value = input.parse()?; - input.parse::()?; Ok(Keyword { name, value }) } @@ -37,28 +63,14 @@ impl Parse for Symbol { Ok(_) => Some(input.parse()?), Err(_) => None, }; - input.parse::()?; Ok(Symbol { name, value }) } } -/// A type used to greedily parse another type until the input is empty. -struct List(Vec); - -impl Parse for List { - fn parse(input: ParseStream<'_>) -> Result { - let mut list = Vec::new(); - while !input.is_empty() { - list.push(input.parse()?); - } - Ok(List(list)) - } -} - struct Input { - keywords: List, - symbols: List, + keywords: Punctuated, + symbols: Punctuated, } impl Parse for Input { @@ -66,115 +78,141 @@ impl Parse for Input { input.parse::()?; let content; braced!(content in input); - let keywords = content.parse()?; + let keywords = Punctuated::parse_terminated(&content)?; input.parse::()?; let content; braced!(content in input); - let symbols = content.parse()?; + let symbols = Punctuated::parse_terminated(&content)?; Ok(Input { keywords, symbols }) } } +#[derive(Default)] +struct Errors { + list: Vec, +} + +impl Errors { + fn error(&mut self, span: Span, message: String) { + self.list.push(syn::Error::new(span, message)); + } +} + pub fn symbols(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as Input); + let (mut output, errors) = symbols_with_errors(input); + + // If we generated any errors, then report them as compiler_error!() macro calls. + // This lets the errors point back to the most relevant span. It also allows us + // to report as many errors as we can during a single run. + output.extend(errors.into_iter().map(|e| e.to_compile_error())); + + output +} + +fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { + let mut errors = Errors::default(); + + let input: Input = match syn::parse2(input) { + Ok(input) => input, + Err(e) => { + // This allows us to display errors at the proper span, while minimizing + // unrelated errors caused by bailing out (and not generating code). + errors.list.push(e); + Input { keywords: Default::default(), symbols: Default::default() } + } + }; let mut keyword_stream = quote! {}; let mut symbols_stream = quote! {}; - let mut digits_stream = quote! {}; let mut prefill_stream = quote! {}; let mut counter = 0u32; - let mut keys = HashSet::::new(); - let mut prev_key: Option = None; - let mut errors = Vec::::new(); + let mut keys = + HashMap::::with_capacity(input.keywords.len() + input.symbols.len() + 10); + let mut prev_key: Option<(Span, String)> = None; - let mut check_dup = |str: &str, errors: &mut Vec| { - if !keys.insert(str.to_string()) { - errors.push(format!("Symbol `{}` is duplicated", str)); + let mut check_dup = |span: Span, str: &str, errors: &mut Errors| { + if let Some(prev_span) = keys.get(str) { + errors.error(span, format!("Symbol `{}` is duplicated", str)); + errors.error(*prev_span, format!("location of previous definition")); + } else { + keys.insert(str.to_string(), span); } }; - let mut check_order = |str: &str, errors: &mut Vec| { - if let Some(ref prev_str) = prev_key { + let mut check_order = |span: Span, str: &str, errors: &mut Errors| { + if let Some((prev_span, ref prev_str)) = prev_key { if str < prev_str { - errors.push(format!("Symbol `{}` must precede `{}`", str, prev_str)); + errors.error(span, format!("Symbol `{}` must precede `{}`", str, prev_str)); + errors.error(prev_span, format!("location of previous symbol `{}`", prev_str)); } } - prev_key = Some(str.to_string()); + prev_key = Some((span, str.to_string())); }; // Generate the listed keywords. - for keyword in &input.keywords.0 { + for keyword in input.keywords.iter() { let name = &keyword.name; let value = &keyword.value; - check_dup(&value.value(), &mut errors); + let value_string = value.value(); + check_dup(keyword.name.span(), &value_string, &mut errors); prefill_stream.extend(quote! { #value, }); keyword_stream.extend(quote! { - #[allow(non_upper_case_globals)] pub const #name: Symbol = Symbol::new(#counter); }); counter += 1; } // Generate the listed symbols. - for symbol in &input.symbols.0 { + for symbol in input.symbols.iter() { let name = &symbol.name; let value = match &symbol.value { Some(value) => value.value(), None => name.to_string(), }; - check_dup(&value, &mut errors); - check_order(&name.to_string(), &mut errors); + check_dup(symbol.name.span(), &value, &mut errors); + check_order(symbol.name.span(), &name.to_string(), &mut errors); + prefill_stream.extend(quote! { #value, }); symbols_stream.extend(quote! { - #[allow(rustc::default_hash_types)] - #[allow(non_upper_case_globals)] pub const #name: Symbol = Symbol::new(#counter); }); counter += 1; } // Generate symbols for the strings "0", "1", ..., "9". + let digits_base = counter; + counter += 10; for n in 0..10 { let n = n.to_string(); - check_dup(&n, &mut errors); + check_dup(Span::call_site(), &n, &mut errors); prefill_stream.extend(quote! { #n, }); - digits_stream.extend(quote! { - Symbol::new(#counter), - }); - counter += 1; } + let _ = counter; // for future use - if !errors.is_empty() { - for error in errors.into_iter() { - eprintln!("error: {}", error) - } - panic!("errors in `Keywords` and/or `Symbols`"); - } + let output = quote! { + const SYMBOL_DIGITS_BASE: u32 = #digits_base; - let tt = TokenStream::from(quote! { - macro_rules! keywords { - () => { - #keyword_stream - } + #[doc(hidden)] + #[allow(non_upper_case_globals)] + mod kw_generated { + use super::Symbol; + #keyword_stream } - macro_rules! define_symbols { - () => { - #symbols_stream - - #[allow(non_upper_case_globals)] - pub const digits_array: &[Symbol; 10] = &[ - #digits_stream - ]; - } + #[allow(rustc::default_hash_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + pub mod sym_generated { + use super::Symbol; + #symbols_stream } impl Interner { @@ -184,11 +222,16 @@ pub fn symbols(input: TokenStream) -> TokenStream { ]) } } - }); + }; - // To see the generated code generated, uncomment this line, recompile, and - // run the resulting output through `rustfmt`. - //eprintln!("{}", tt); + (output, errors.list) - tt + // To see the generated code, use the "cargo expand" command. + // Do this once to install: + // cargo install cargo-expand + // + // Then, cd to rustc_span and run: + // cargo expand > /tmp/rustc_span_expanded.rs + // + // and read that file. } diff --git a/compiler/rustc_macros/src/symbols/tests.rs b/compiler/rustc_macros/src/symbols/tests.rs new file mode 100644 index 000000000000..82b4b876978f --- /dev/null +++ b/compiler/rustc_macros/src/symbols/tests.rs @@ -0,0 +1,102 @@ +use super::*; + +// This test is mainly here for interactive development. Use this test while +// you're working on the proc-macro defined in this file. +#[test] +fn test_symbols() { + // We textually include the symbol.rs file, which contains the list of all + // symbols, keywords, and common words. Then we search for the + // `symbols! { ... }` call. + + static SYMBOL_RS_FILE: &str = include_str!("../../../rustc_span/src/symbol.rs"); + + let file = syn::parse_file(SYMBOL_RS_FILE).unwrap(); + let symbols_path: syn::Path = syn::parse_quote!(symbols); + + let m: &syn::ItemMacro = file + .items + .iter() + .filter_map(|i| { + if let syn::Item::Macro(m) = i { + if m.mac.path == symbols_path { Some(m) } else { None } + } else { + None + } + }) + .next() + .expect("did not find `symbols!` macro invocation."); + + let body_tokens = m.mac.tokens.clone(); + + test_symbols_macro(body_tokens, &[]); +} + +fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) { + let (output, found_errors) = symbols_with_errors(input); + + // It should always parse. + let _parsed_file = syn::parse2::(output).unwrap(); + + assert_eq!( + found_errors.len(), + expected_errors.len(), + "Macro generated a different number of errors than expected" + ); + + for (found_error, &expected_error) in found_errors.iter().zip(expected_errors.iter()) { + let found_error_str = format!("{}", found_error); + assert_eq!(found_error_str, expected_error); + } +} + +#[test] +fn check_dup_keywords() { + let input = quote! { + Keywords { + Crate: "crate", + Crate: "crate", + } + Symbols {} + }; + test_symbols_macro(input, &["Symbol `crate` is duplicated", "location of previous definition"]); +} + +#[test] +fn check_dup_symbol() { + let input = quote! { + Keywords {} + Symbols { + splat, + splat, + } + }; + test_symbols_macro(input, &["Symbol `splat` is duplicated", "location of previous definition"]); +} + +#[test] +fn check_dup_symbol_and_keyword() { + let input = quote! { + Keywords { + Splat: "splat", + } + Symbols { + splat, + } + }; + test_symbols_macro(input, &["Symbol `splat` is duplicated", "location of previous definition"]); +} + +#[test] +fn check_symbol_order() { + let input = quote! { + Keywords {} + Symbols { + zebra, + aardvark, + } + }; + test_symbols_macro( + input, + &["Symbol `aardvark` must precede `zebra`", "location of previous symbol `zebra`"], + ); +} diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 33cbf0fb2345..019ca5174a22 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -706,7 +706,7 @@ impl<'a> CrateLoader<'a> { self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime()); } - fn inject_profiler_runtime(&mut self) { + fn inject_profiler_runtime(&mut self, krate: &ast::Crate) { if (self.sess.opts.debugging_opts.instrument_coverage || self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled()) @@ -714,6 +714,13 @@ impl<'a> CrateLoader<'a> { { info!("loading profiler"); + if self.sess.contains_name(&krate.attrs, sym::no_core) { + self.sess.err( + "`profiler_builtins` crate (required by compiler options) \ + is not compatible with crate attribute `#![no_core]`", + ); + } + let name = sym::profiler_builtins; let cnum = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit, None); let data = self.cstore.get_crate_data(cnum); @@ -879,7 +886,7 @@ impl<'a> CrateLoader<'a> { } pub fn postprocess(&mut self, krate: &ast::Crate) { - self.inject_profiler_runtime(); + self.inject_profiler_runtime(krate); self.inject_allocator_crate(krate); self.inject_panic_runtime(krate); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index fe29f9d177f9..744fdc83a91e 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -132,7 +132,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { impl Collector<'tcx> { fn register_native_lib(&mut self, span: Option, lib: NativeLib) { - if lib.name.as_ref().map(|&s| s == kw::Invalid).unwrap_or(false) { + if lib.name.as_ref().map(|&s| s == kw::Empty).unwrap_or(false) { match span { Some(span) => { struct_span_err!( diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 46dd0df65e06..4dfe3e848776 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -319,10 +319,6 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { self.opaque.position() } - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - fn type_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.type_shorthands } @@ -663,7 +659,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { no_builtins: tcx.sess.contains_name(&attrs, sym::no_builtins), panic_runtime: tcx.sess.contains_name(&attrs, sym::panic_runtime), profiler_runtime: tcx.sess.contains_name(&attrs, sym::profiler_runtime), - symbol_mangling_version: tcx.sess.opts.debugging_opts.symbol_mangling_version, + symbol_mangling_version: tcx.sess.opts.debugging_opts.get_symbol_mangling_version(), crate_deps, dylib_dependency_formats, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 3250f1830de1..47b7768b410a 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -30,3 +30,4 @@ chalk-ir = "0.36.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } measureme = "9.0.0" rustc_session = { path = "../rustc_session" } +rustc_type_ir = { path = "../rustc_type_ir" } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index a94f6d25fc71..728bfef9f467 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -5,7 +5,7 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; -use rustc_hir::def_id::{DefPathHash, LocalDefId}; +use rustc_hir::def_id::LocalDefId; mod dep_node; @@ -91,9 +91,9 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { type DepKind = DepKind; type StableHashingContext = StableHashingContext<'tcx>; - fn register_reused_dep_path_hash(&self, hash: DefPathHash) { + fn register_reused_dep_node(&self, dep_node: &DepNode) { if let Some(cache) = self.queries.on_disk_cache.as_ref() { - cache.register_reused_dep_path_hash(hash) + cache.register_reused_dep_node(*self, dep_node) } } diff --git a/compiler/rustc_middle/src/hir/map/blocks.rs b/compiler/rustc_middle/src/hir/map/blocks.rs index 6f572a4875f8..9d392c7b26bf 100644 --- a/compiler/rustc_middle/src/hir/map/blocks.rs +++ b/compiler/rustc_middle/src/hir/map/blocks.rs @@ -42,37 +42,25 @@ trait MaybeFnLike { impl MaybeFnLike for hir::Item<'_> { fn is_fn_like(&self) -> bool { - match self.kind { - hir::ItemKind::Fn(..) => true, - _ => false, - } + matches!(self.kind, hir::ItemKind::Fn(..)) } } impl MaybeFnLike for hir::ImplItem<'_> { fn is_fn_like(&self) -> bool { - match self.kind { - hir::ImplItemKind::Fn(..) => true, - _ => false, - } + matches!(self.kind, hir::ImplItemKind::Fn(..)) } } impl MaybeFnLike for hir::TraitItem<'_> { fn is_fn_like(&self) -> bool { - match self.kind { - hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => true, - _ => false, - } + matches!(self.kind, hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_))) } } impl MaybeFnLike for hir::Expr<'_> { fn is_fn_like(&self) -> bool { - match self.kind { - hir::ExprKind::Closure(..) => true, - _ => false, - } + matches!(self.kind, hir::ExprKind::Closure(..)) } } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 598e28c1a3ab..09d5b1021031 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -379,7 +379,7 @@ impl<'hir> Map<'hir> { pub fn body_param_names(&self, id: BodyId) -> impl Iterator + 'hir { self.body(id).params.iter().map(|arg| match arg.pat.kind { PatKind::Binding(_, _, ident, _) => ident, - _ => Ident::new(kw::Invalid, rustc_span::DUMMY_SP), + _ => Ident::new(kw::Empty, rustc_span::DUMMY_SP), }) } diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 5da4be4e9827..00db19019c48 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -4,21 +4,43 @@ use crate::ty::Ty; use rustc_hir::HirId; use rustc_target::abi::VariantIdx; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] pub enum PlaceBase { - /// A temporary variable + /// A temporary variable. Rvalue, - /// A named `static` item + /// A named `static` item. StaticItem, - /// A named local variable + /// A named local variable. Local(HirId), - /// An upvar referenced by closure env + /// An upvar referenced by closure env. Upvar(ty::UpvarId), } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] pub enum ProjectionKind { - /// A dereference of a pointer, reference or `Box` of the given type + /// A dereference of a pointer, reference or `Box` of the given type. Deref, /// `B.F` where `B` is the base expression and `F` is @@ -36,19 +58,30 @@ pub enum ProjectionKind { Subslice, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] pub struct Projection<'tcx> { - /// Type after the projection is being applied. + /// Type after the projection is applied. pub ty: Ty<'tcx>, - /// Defines the type of access + /// Defines the kind of access made by the projection. pub kind: ProjectionKind, } /// A `Place` represents how a value is located in memory. /// -/// This is an HIR version of `mir::Place` -#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +/// This is an HIR version of [`rustc_middle::mir::Place`]. +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct Place<'tcx> { /// The type of the `PlaceBase` pub base_ty: Ty<'tcx>, @@ -60,13 +93,13 @@ pub struct Place<'tcx> { /// A `PlaceWithHirId` represents how a value is located in memory. /// -/// This is an HIR version of `mir::Place` -#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +/// This is an HIR version of [`rustc_middle::mir::Place`]. +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct PlaceWithHirId<'tcx> { /// `HirId` of the expression or pattern producing this value. pub hir_id: HirId, - /// Information about the `Place` + /// Information about the `Place`. pub place: Place<'tcx>, } @@ -77,10 +110,7 @@ impl<'tcx> PlaceWithHirId<'tcx> { base: PlaceBase, projections: Vec>, ) -> PlaceWithHirId<'tcx> { - PlaceWithHirId { - hir_id: hir_id, - place: Place { base_ty: base_ty, base: base, projections: projections }, - } + PlaceWithHirId { hir_id, place: Place { base_ty, base, projections } } } } diff --git a/compiler/rustc_middle/src/ich/impls_ty.rs b/compiler/rustc_middle/src/ich/impls_ty.rs index 69bb4e23c4c0..573b514e8445 100644 --- a/compiler/rustc_middle/src/ich/impls_ty.rs +++ b/compiler/rustc_middle/src/ich/impls_ty.rs @@ -70,16 +70,16 @@ impl<'a> HashStable> for ty::RegionKind { ty::ReEmpty(universe) => { universe.hash_stable(hcx, hasher); } - ty::ReLateBound(db, ty::BrAnon(i)) => { + ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrAnon(i) }) => { db.hash_stable(hcx, hasher); i.hash_stable(hcx, hasher); } - ty::ReLateBound(db, ty::BrNamed(def_id, name)) => { + ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrNamed(def_id, name) }) => { db.hash_stable(hcx, hasher); def_id.hash_stable(hcx, hasher); name.hash_stable(hcx, hasher); } - ty::ReLateBound(db, ty::BrEnv) => { + ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrEnv }) => { db.hash_stable(hcx, hasher); } ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => { diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 6e5f95c45274..e106db38b2c9 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -323,9 +323,10 @@ impl<'tcx> CanonicalVarValues<'tcx> { GenericArgKind::Type(..) => { tcx.mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i).into())).into() } - GenericArgKind::Lifetime(..) => tcx - .mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(i))) - .into(), + GenericArgKind::Lifetime(..) => { + let br = ty::BoundRegion { kind: ty::BrAnon(i) }; + tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into() + } GenericArgKind::Const(ct) => tcx .mk_const(ty::Const { ty: ct.ty, diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index cdc5940d9bae..6ae83a7f6675 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -8,7 +8,7 @@ //! - **MIR.** The "mid-level (M) intermediate representation (IR)" is //! defined in the `mir` module. This module contains only the //! *definition* of the MIR; the passes that transform and operate -//! on MIR are found in `librustc_mir` crate. +//! on MIR are found in `rustc_mir` crate. //! - **Types.** The internal representation of types used in rustc is //! defined in the `ty` module. This includes the **type context** //! (or `tcx`), which is the central context during most of diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index a61d37cc90eb..64d850192f44 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -13,7 +13,7 @@ use rustc_span::{symbol, Span, Symbol, DUMMY_SP}; /// How a lint level was set. #[derive(Clone, Copy, PartialEq, Eq, HashStable)] -pub enum LintSource { +pub enum LintLevelSource { /// Lint is at the default level as declared /// in rustc or a plugin. Default, @@ -22,30 +22,31 @@ pub enum LintSource { Node(Symbol, Span, Option /* RFC 2383 reason */), /// Lint level was set by a command-line flag. - /// The provided `Level` is the level specified on the command line - - /// the actual level may be lower due to `--cap-lints` + /// The provided `Level` is the level specified on the command line. + /// (The actual level may be lower due to `--cap-lints`.) CommandLine(Symbol, Level), } -impl LintSource { +impl LintLevelSource { pub fn name(&self) -> Symbol { match *self { - LintSource::Default => symbol::kw::Default, - LintSource::Node(name, _, _) => name, - LintSource::CommandLine(name, _) => name, + LintLevelSource::Default => symbol::kw::Default, + LintLevelSource::Node(name, _, _) => name, + LintLevelSource::CommandLine(name, _) => name, } } pub fn span(&self) -> Span { match *self { - LintSource::Default => DUMMY_SP, - LintSource::Node(_, span, _) => span, - LintSource::CommandLine(_, _) => DUMMY_SP, + LintLevelSource::Default => DUMMY_SP, + LintLevelSource::Node(_, span, _) => span, + LintLevelSource::CommandLine(_, _) => DUMMY_SP, } } } -pub type LevelSource = (Level, LintSource); +/// A tuple of a lint level and its source. +pub type LevelSource = (Level, LintLevelSource); pub struct LintLevelSets { pub list: Vec, @@ -113,7 +114,7 @@ impl LintLevelSets { id: LintId, mut idx: u32, aux: Option<&FxHashMap>, - ) -> (Option, LintSource) { + ) -> (Option, LintLevelSource) { if let Some(specs) = aux { if let Some(&(level, src)) = specs.get(&id) { return (Some(level), src); @@ -125,7 +126,7 @@ impl LintLevelSets { if let Some(&(level, src)) = specs.get(&id) { return (Some(level), src); } - return (None, LintSource::Default); + return (None, LintLevelSource::Default); } LintSet::Node { ref specs, parent } => { if let Some(&(level, src)) = specs.get(&id) { @@ -213,7 +214,7 @@ pub fn struct_lint_level<'s, 'd>( sess: &'s Session, lint: &'static Lint, level: Level, - src: LintSource, + src: LintLevelSource, span: Option, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + 'd, ) { @@ -223,7 +224,7 @@ pub fn struct_lint_level<'s, 'd>( sess: &'s Session, lint: &'static Lint, level: Level, - src: LintSource, + src: LintLevelSource, span: Option, decorate: Box FnOnce(LintDiagnosticBuilder<'b>) + 'd>, ) { @@ -274,14 +275,14 @@ pub fn struct_lint_level<'s, 'd>( let name = lint.name_lower(); match src { - LintSource::Default => { + LintLevelSource::Default => { sess.diag_note_once( &mut err, DiagnosticMessageId::from(lint), &format!("`#[{}({})]` on by default", level.as_str(), name), ); } - LintSource::CommandLine(lint_flag_val, orig_level) => { + LintLevelSource::CommandLine(lint_flag_val, orig_level) => { let flag = match orig_level { Level::Warn => "-W", Level::Deny => "-D", @@ -310,7 +311,7 @@ pub fn struct_lint_level<'s, 'd>( ); } } - LintSource::Node(lint_attr_name, src, reason) => { + LintLevelSource::Node(lint_attr_name, src, reason) => { if let Some(rationale) = reason { err.note(&rationale.as_str()); } diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 254b57a005e8..54188985d7c5 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -8,7 +8,9 @@ use rustc_macros::HashStable; use std::fmt; use std::hash::Hash; -// Accessibility levels, sorted in ascending order +/// Represents the levels of accessibility an item can have. +/// +/// The variants are sorted in ascending order of accessibility. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, HashStable)] pub enum AccessLevel { /// Superset of `AccessLevel::Reachable` used to mark impl Trait items. @@ -18,13 +20,13 @@ pub enum AccessLevel { /// public, then type `T` is reachable. Its values can be obtained by other crates /// even if the type itself is not nameable. Reachable, - /// Public items + items accessible to other crates with help of `pub use` re-exports + /// Public items + items accessible to other crates with the help of `pub use` re-exports. Exported, - /// Items accessible to other crates directly, without help of re-exports + /// Items accessible to other crates directly, without the help of re-exports. Public, } -// Accessibility levels for reachable HIR nodes +/// Holds a map of accessibility levels for reachable HIR nodes. #[derive(Clone)] pub struct AccessLevels { pub map: FxHashMap, diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index d060549ca813..eb48198991c2 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -332,7 +332,7 @@ pub struct ScopeTree { pub struct YieldData { /// The `Span` of the yield. pub span: Span, - /// The number of expressions and patterns appearing before the `yield` in the body plus one. + /// The number of expressions and patterns appearing before the `yield` in the body, plus one. pub expr_and_pat_count: usize, pub source: hir::YieldSource, } @@ -449,9 +449,7 @@ impl ScopeTree { } /// Checks whether the given scope contains a `yield`. If so, - /// returns `Some((span, expr_count))` with the span of a yield we found and - /// the number of expressions and patterns appearing before the `yield` in the body + 1. - /// If there a are multiple yields in a scope, the one with the highest number is returned. + /// returns `Some(YieldData)`. If not, returns `None`. pub fn yield_in_scope(&self, scope: Scope) -> Option { self.yield_in_scope.get(&scope).cloned() } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 47c140e0b188..4f08057a7e32 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -132,37 +132,37 @@ pub fn report_unstable( /// Checks whether an item marked with `deprecated(since="X")` is currently /// deprecated (i.e., whether X is not greater than the current rustc version). pub fn deprecation_in_effect(is_since_rustc_version: bool, since: Option<&str>) -> bool { - let since = if let Some(since) = since { - if is_since_rustc_version { - since - } else { - // We assume that the deprecation is in effect if it's not a - // rustc version. - return true; - } - } else { - // If since attribute is not set, then we're definitely in effect. - return true; - }; fn parse_version(ver: &str) -> Vec { // We ignore non-integer components of the version (e.g., "nightly"). ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect() } - if let Some(rustc) = option_env!("CFG_RELEASE") { - let since: Vec = parse_version(&since); - let rustc: Vec = parse_version(rustc); - // We simply treat invalid `since` attributes as relating to a previous - // Rust version, thus always displaying the warning. - if since.len() != 3 { - return true; - } - since <= rustc - } else { - // By default, a deprecation warning applies to - // the current version of the compiler. - true + if !is_since_rustc_version { + // The `since` field doesn't have semantic purpose in the stable `deprecated` + // attribute, only in `rustc_deprecated`. + return true; } + + if let Some(since) = since { + if since == "TBD" { + return false; + } + + if let Some(rustc) = option_env!("CFG_RELEASE") { + let since: Vec = parse_version(&since); + let rustc: Vec = parse_version(rustc); + // We simply treat invalid `since` attributes as relating to a previous + // Rust version, thus always displaying the warning. + if since.len() != 3 { + return true; + } + return since <= rustc; + } + }; + + // Assume deprecation is in effect if "since" field is missing + // or if we can't determine the current Rust version. + true } pub fn deprecation_suggestion( @@ -182,19 +182,24 @@ pub fn deprecation_suggestion( } pub fn deprecation_message(depr: &Deprecation, kind: &str, path: &str) -> (String, &'static Lint) { - let (message, lint) = if deprecation_in_effect( - depr.is_since_rustc_version, - depr.since.map(Symbol::as_str).as_deref(), - ) { + let since = depr.since.map(Symbol::as_str); + let (message, lint) = if deprecation_in_effect(depr.is_since_rustc_version, since.as_deref()) { (format!("use of deprecated {} `{}`", kind, path), DEPRECATED) } else { ( - format!( - "use of {} `{}` that will be deprecated in future version {}", - kind, - path, - depr.since.unwrap() - ), + if since.as_deref() == Some("TBD") { + format!( + "use of {} `{}` that will be deprecated in a future Rust version", + kind, path + ) + } else { + format!( + "use of {} `{}` that will be deprecated in future version {}", + kind, + path, + since.unwrap() + ) + }, DEPRECATED_IN_FUTURE, ) }; diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 8a6bf9dff7b6..95096d0fb719 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -118,17 +118,11 @@ impl CoverageKind { } pub fn is_counter(&self) -> bool { - match self { - Self::Counter { .. } => true, - _ => false, - } + matches!(self, Self::Counter { .. }) } pub fn is_expression(&self) -> bool { - match self { - Self::Expression { .. } => true, - _ => false, - } + matches!(self, Self::Expression { .. }) } pub fn is_unreachable(&self) -> bool { diff --git a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs new file mode 100644 index 000000000000..5f028975bd0e --- /dev/null +++ b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs @@ -0,0 +1,62 @@ +use rustc_data_structures::graph::{ + self, DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors, +}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::OnceCell; +use rustc_serialize as serialize; + +/// Helper type to cache the result of `graph::is_cyclic`. +#[derive(Clone, Debug)] +pub(super) struct GraphIsCyclicCache { + cache: OnceCell, +} + +impl GraphIsCyclicCache { + #[inline] + pub(super) fn new() -> Self { + GraphIsCyclicCache { cache: OnceCell::new() } + } + + pub(super) fn is_cyclic(&self, graph: &G) -> bool + where + G: ?Sized + DirectedGraph + WithStartNode + WithSuccessors + WithNumNodes, + { + *self.cache.get_or_init(|| graph::is_cyclic(graph)) + } + + /// Invalidates the cache. + #[inline] + pub(super) fn invalidate(&mut self) { + // Invalidating the cache requires mutating the MIR, which in turn requires a unique + // reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all + // callers of `invalidate` have a unique reference to the MIR and thus to the + // cache. This means we never need to do synchronization when `invalidate` is called, + // we can simply reinitialize the `OnceCell`. + self.cache = OnceCell::new(); + } +} + +impl serialize::Encodable for GraphIsCyclicCache { + #[inline] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + serialize::Encodable::encode(&(), s) + } +} + +impl serialize::Decodable for GraphIsCyclicCache { + #[inline] + fn decode(d: &mut D) -> Result { + serialize::Decodable::decode(d).map(|_v: ()| Self::new()) + } +} + +impl HashStable for GraphIsCyclicCache { + #[inline] + fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) { + // do nothing + } +} + +TrivialTypeFoldableAndLiftImpls! { + GraphIsCyclicCache, +} diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index bcf85797313f..80b58642136e 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -132,7 +132,6 @@ pub use self::pointer::{Pointer, PointerArithmetic}; /// Uniquely identifies one of the following: /// - A constant /// - A static -/// - A const fn where all arguments (if any) are zero-sized types #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, Lift)] pub struct GlobalId<'tcx> { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 814f91b04310..a69555fd1a8c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -35,11 +35,13 @@ use std::ops::{ControlFlow, Index, IndexMut}; use std::slice; use std::{iter, mem, option}; +use self::graph_cyclic_cache::GraphIsCyclicCache; use self::predecessors::{PredecessorCache, Predecessors}; pub use self::query::*; pub mod abstract_const; pub mod coverage; +mod graph_cyclic_cache; pub mod interpret; pub mod mono; mod predecessors; @@ -227,6 +229,7 @@ pub struct Body<'tcx> { pub is_polymorphic: bool, predecessor_cache: PredecessorCache, + is_cyclic: GraphIsCyclicCache, } impl<'tcx> Body<'tcx> { @@ -267,6 +270,7 @@ impl<'tcx> Body<'tcx> { required_consts: Vec::new(), is_polymorphic: false, predecessor_cache: PredecessorCache::new(), + is_cyclic: GraphIsCyclicCache::new(), }; body.is_polymorphic = body.has_param_types_or_consts(); body @@ -296,6 +300,7 @@ impl<'tcx> Body<'tcx> { var_debug_info: Vec::new(), is_polymorphic: false, predecessor_cache: PredecessorCache::new(), + is_cyclic: GraphIsCyclicCache::new(), }; body.is_polymorphic = body.has_param_types_or_consts(); body @@ -309,11 +314,12 @@ impl<'tcx> Body<'tcx> { #[inline] pub fn basic_blocks_mut(&mut self) -> &mut IndexVec> { // Because the user could mutate basic block terminators via this reference, we need to - // invalidate the predecessor cache. + // invalidate the caches. // // FIXME: Use a finer-grained API for this, so only transformations that alter terminators - // invalidate the predecessor cache. + // invalidate the caches. self.predecessor_cache.invalidate(); + self.is_cyclic.invalidate(); &mut self.basic_blocks } @@ -322,6 +328,7 @@ impl<'tcx> Body<'tcx> { &mut self, ) -> (&mut IndexVec>, &mut LocalDecls<'tcx>) { self.predecessor_cache.invalidate(); + self.is_cyclic.invalidate(); (&mut self.basic_blocks, &mut self.local_decls) } @@ -334,13 +341,14 @@ impl<'tcx> Body<'tcx> { &mut Vec>, ) { self.predecessor_cache.invalidate(); + self.is_cyclic.invalidate(); (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) } /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the /// `START_BLOCK`. pub fn is_cfg_cyclic(&self) -> bool { - graph::is_cyclic(self) + self.is_cyclic.is_cyclic(self) } #[inline] @@ -1060,6 +1068,23 @@ impl<'tcx> LocalDecl<'tcx> { } } +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum VarDebugInfoContents<'tcx> { + /// NOTE(eddyb) There's an unenforced invariant that this `Place` is + /// based on a `Local`, not a `Static`, and contains no indexing. + Place(Place<'tcx>), + Const(Constant<'tcx>), +} + +impl<'tcx> Debug for VarDebugInfoContents<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + match self { + VarDebugInfoContents::Const(c) => write!(fmt, "{}", c), + VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p), + } + } +} + /// Debug information pertaining to a user variable. #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct VarDebugInfo<'tcx> { @@ -1071,9 +1096,7 @@ pub struct VarDebugInfo<'tcx> { pub source_info: SourceInfo, /// Where the data for this user variable is to be found. - /// NOTE(eddyb) There's an unenforced invariant that this `Place` is - /// based on a `Local`, not a `Static`, and contains no indexing. - pub place: Place<'tcx>, + pub value: VarDebugInfoContents<'tcx>, } /////////////////////////////////////////////////////////////////////////// @@ -1741,6 +1764,21 @@ impl<'tcx> Place<'tcx> { pub fn as_ref(&self) -> PlaceRef<'tcx> { PlaceRef { local: self.local, projection: &self.projection } } + + /// Iterate over the projections in evaluation order, i.e., the first element is the base with + /// its projection and then subsequently more projections are added. + /// As a concrete example, given the place a.b.c, this would yield: + /// - (a, .b) + /// - (a.b, .c) + /// Given a place without projections, the iterator is empty. + pub fn iter_projections( + self, + ) -> impl Iterator, PlaceElem<'tcx>)> + DoubleEndedIterator { + self.projection.iter().enumerate().map(move |(i, proj)| { + let base = PlaceRef { local: self.local, projection: &self.projection[..i] }; + (base, proj) + }) + } } impl From for Place<'_> { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index db0056e482be..89a93096f1c2 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -46,7 +46,7 @@ pub enum UnsafetyViolationDetails { UseOfMutableStatic, UseOfExternStatic, DerefOfRawPointer, - AssignToNonCopyUnionField, + AssignToDroppingUnionField, AccessToUnionField, MutationOfLayoutConstrainedField, BorrowOfLayoutConstrainedField, @@ -94,8 +94,8 @@ impl UnsafetyViolationDetails { "raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules \ and cause data races: all of these are undefined behavior", ), - AssignToNonCopyUnionField => ( - "assignment to non-`Copy` union field", + AssignToDroppingUnionField => ( + "assignment to union field that might need dropping", "the previous content of the field will be dropped, which causes undefined \ behavior if the field was not properly initialized", ), diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index f0bfdae261c6..1b2c1076a688 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -136,6 +136,15 @@ impl<'tcx> Place<'tcx> { } } +impl<'tcx> PlaceRef<'tcx> { + pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx> + where + D: HasLocalDecls<'tcx>, + { + Place::ty_from(self.local, &self.projection, local_decls, tcx) + } +} + pub enum RvalueInitializationState { Shallow, Deep, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 7538818b8afc..023555d91cc9 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -306,13 +306,13 @@ macro_rules! make_mir_visitor { let mut index = 0; for statement in statements { - let location = Location { block: block, statement_index: index }; + let location = Location { block, statement_index: index }; self.visit_statement(statement, location); index += 1; } if let Some(terminator) = terminator { - let location = Location { block: block, statement_index: index }; + let location = Location { block, statement_index: index }; self.visit_terminator(terminator, location); } } @@ -829,16 +829,20 @@ macro_rules! make_mir_visitor { let VarDebugInfo { name: _, source_info, - place, + value, } = var_debug_info; self.visit_source_info(source_info); let location = START_BLOCK.start_location(); - self.visit_place( - place, - PlaceContext::NonUse(NonUseContext::VarDebugInfo), - location, - ); + match value { + VarDebugInfoContents::Const(c) => self.visit_constant(c, location), + VarDebugInfoContents::Place(place) => + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ), + } } fn super_source_scope(&mut self, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1b5f7a2c12e7..1e836d0a8425 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -576,11 +576,13 @@ rustc_queries! { desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } } - query impl_trait_ref(key: DefId) -> Option> { - desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(key) } + /// Given an `impl_id`, return the trait it implements. + /// Return `None` if this is an inherent impl. + query impl_trait_ref(impl_id: DefId) -> Option> { + desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } } - query impl_polarity(key: DefId) -> ty::ImplPolarity { - desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(key) } + query impl_polarity(impl_id: DefId) -> ty::ImplPolarity { + desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(impl_id) } } query issue33140_self_ty(key: DefId) -> Option> { @@ -917,8 +919,10 @@ rustc_queries! { } TypeChecking { - query trait_of_item(def_id: DefId) -> Option { - desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(def_id) } + /// Given an `associated_item`, find the trait it belongs to. + /// Return `None` if the `DefId` is not an associated item. + query trait_of_item(associated_item: DefId) -> Option { + desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(associated_item) } } } @@ -948,20 +952,29 @@ rustc_queries! { } TypeChecking { - query all_local_trait_impls(key: CrateNum) -> &'tcx BTreeMap> { + /// Return all `impl` blocks in the current crate. + /// + /// To allow caching this between crates, you must pass in [`LOCAL_CRATE`] as the crate number. + /// Passing in any other crate will cause an ICE. + /// + /// [`LOCAL_CRATE`]: rustc_hir::def_id::LOCAL_CRATE + query all_local_trait_impls(local_crate: CrateNum) -> &'tcx BTreeMap> { desc { "local trait impls" } } - query trait_impls_of(key: DefId) -> ty::trait_def::TraitImpls { + + /// Given a trait `trait_id`, return all known `impl` blocks. + query trait_impls_of(trait_id: DefId) -> ty::trait_def::TraitImpls { storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) } + desc { |tcx| "trait impls of `{}`", tcx.def_path_str(trait_id) } } - query specialization_graph_of(key: DefId) -> specialization_graph::Graph { + + query specialization_graph_of(trait_id: DefId) -> specialization_graph::Graph { storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(key) } + desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } cache_on_disk_if { true } } - query object_safety_violations(key: DefId) -> &'tcx [traits::ObjectSafetyViolation] { - desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(key) } + query object_safety_violations(trait_id: DefId) -> &'tcx [traits::ObjectSafetyViolation] { + desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(trait_id) } } /// Gets the ParameterEnvironment for a given item; this environment @@ -969,6 +982,7 @@ rustc_queries! { /// type-checking etc, and it does not normalize specializable /// associated types. This is almost always what you want, /// unless you are doing MIR optimizations, in which case you + /// might want to use `reveal_all()` method to change modes. query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } } @@ -1229,10 +1243,15 @@ rustc_queries! { } TypeChecking { + /// Given a crate and a trait, look up all impls of that trait in the crate. + /// Return `(impl_id, self_ty)`. query implementations_of_trait(_: (CrateNum, DefId)) -> &'tcx [(DefId, Option)] { desc { "looking up implementations of a trait in a crate" } } + + /// Given a crate, look up all trait impls in that crate. + /// Return `(impl_id, self_ty)`. query all_trait_implementations(_: CrateNum) -> &'tcx [(DefId, Option)] { desc { "looking up all (?) trait implementations" } diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs index 27bccc0bcafa..a5962e3b3ba5 100644 --- a/compiler/rustc_middle/src/ty/_match.rs +++ b/compiler/rustc_middle/src/ty/_match.rs @@ -118,6 +118,6 @@ impl TypeRelation<'tcx> for Match<'tcx> { where T: Relate<'tcx>, { - Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?)) + Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } } diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index b2fc3710cd67..df594690215b 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -69,7 +69,6 @@ impl OpaqueEncoder for rustc_serialize::opaque::Encoder { pub trait TyEncoder<'tcx>: Encoder { const CLEAR_CROSS_CRATE: bool; - fn tcx(&self) -> TyCtxt<'tcx>; fn position(&self) -> usize; fn type_shorthands(&mut self) -> &mut FxHashMap, usize>; fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; @@ -321,10 +320,14 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { } } -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> + for ty::List>> +{ fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { let len = decoder.read_usize()?; - Ok(decoder.tcx().mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) + Ok(decoder + .tcx() + .mk_poly_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) } } @@ -373,7 +376,7 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::N impl_decodable_via_ref! { &'tcx ty::TypeckResults<'tcx>, &'tcx ty::List>, - &'tcx ty::List>, + &'tcx ty::List>>, &'tcx Allocation, &'tcx mir::Body<'tcx>, &'tcx mir::UnsafetyCheckResult, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1b3416e112ba..b2db09cbc806 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -5,7 +5,7 @@ use crate::dep_graph::{self, DepGraph, DepKind, DepNode, DepNodeExt}; use crate::hir::exports::ExportMap; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; -use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintSource}; +use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; use crate::middle; use crate::middle::cstore::{CrateStoreDyn, EncodedMetadata}; use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault}; @@ -87,7 +87,7 @@ pub struct CtxtInterners<'tcx> { substs: InternedSet<'tcx, InternalSubsts<'tcx>>, canonical_var_infos: InternedSet<'tcx, List>>, region: InternedSet<'tcx, RegionKind>, - existential_predicates: InternedSet<'tcx, List>>, + poly_existential_predicates: InternedSet<'tcx, List>>>, predicate: InternedSet<'tcx, PredicateInner<'tcx>>, predicates: InternedSet<'tcx, List>>, projs: InternedSet<'tcx, List>, @@ -103,7 +103,7 @@ impl<'tcx> CtxtInterners<'tcx> { type_list: Default::default(), substs: Default::default(), region: Default::default(), - existential_predicates: Default::default(), + poly_existential_predicates: Default::default(), canonical_var_infos: Default::default(), predicate: Default::default(), predicates: Default::default(), @@ -299,6 +299,7 @@ pub struct ResolvedOpaqueTy<'tcx> { /// Here, we would store the type `T`, the span of the value `x`, the "scope-span" for /// the scope that contains `x`, the expr `T` evaluated from, and the span of `foo.await`. #[derive(TyEncodable, TyDecodable, Clone, Debug, Eq, Hash, PartialEq, HashStable)] +#[derive(TypeFoldable)] pub struct GeneratorInteriorTypeCause<'tcx> { /// Type of the captured binding. pub ty: Ty<'tcx>, @@ -423,7 +424,7 @@ pub struct TypeckResults<'tcx> { /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). - pub generator_interior_types: Vec>, + pub generator_interior_types: ty::Binder>>, /// We sometimes treat byte string literals (which are of type `&[u8; N]`) /// as `&[u8]`, depending on the pattern in which they are used. @@ -455,7 +456,7 @@ impl<'tcx> TypeckResults<'tcx> { concrete_opaque_types: Default::default(), closure_captures: Default::default(), closure_min_captures: Default::default(), - generator_interior_types: Default::default(), + generator_interior_types: ty::Binder::dummy(Default::default()), treat_byte_string_as_slice: Default::default(), } } @@ -624,6 +625,19 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } + /// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured + /// by the closure. + pub fn closure_min_captures_flattened( + &self, + closure_def_id: DefId, + ) -> impl Iterator> { + self.closure_min_captures + .get(&closure_def_id) + .map(|closure_min_captures| closure_min_captures.values().flat_map(|v| v.iter())) + .into_iter() + .flatten() + } + pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { self.upvar_capture_map[&upvar_id] } @@ -876,7 +890,7 @@ pub struct FreeRegionInfo { // `LocalDefId` corresponding to FreeRegion pub def_id: LocalDefId, // the bound region corresponding to FreeRegion - pub boundregion: ty::BoundRegion, + pub boundregion: ty::BoundRegionKind, // checks if bound region is in Impl Item pub is_impl_item: bool, } @@ -1372,7 +1386,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn lazy_normalization(self) -> bool { let features = self.features(); - // Note: We do not enable lazy normalization for `features.min_const_generics`. + // Note: We do not enable lazy normalization for `min_const_generics`. features.const_generics || features.lazy_normalization_consts } @@ -1398,7 +1412,7 @@ impl<'tcx> TyCtxt<'tcx> { }) } - // Returns the `DefId` and the `BoundRegion` corresponding to the given region. + // Returns the `DefId` and the `BoundRegionKind` corresponding to the given region. pub fn is_suitable_region(self, region: Region<'tcx>) -> Option { let (suitable_region_binding_scope, bound_region) = match *region { ty::ReFree(ref free_region) => { @@ -1406,7 +1420,7 @@ impl<'tcx> TyCtxt<'tcx> { } ty::ReEarlyBound(ref ebr) => ( self.parent(ebr.def_id).unwrap().expect_local(), - ty::BoundRegion::BrNamed(ebr.def_id, ebr.name), + ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name), ), _ => return None, // not a free region }; @@ -1610,7 +1624,7 @@ nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>} nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>} nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>} -nop_list_lift! {existential_predicates; ExistentialPredicate<'a> => ExistentialPredicate<'tcx>} +nop_list_lift! {poly_existential_predicates; ty::Binder> => ty::Binder>} nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>} nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>} nop_list_lift! {projs; ProjectionKind => ProjectionKind} @@ -2051,7 +2065,8 @@ slice_interners!( type_list: _intern_type_list(Ty<'tcx>), substs: _intern_substs(GenericArg<'tcx>), canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>), - existential_predicates: _intern_existential_predicates(ExistentialPredicate<'tcx>), + poly_existential_predicates: + _intern_poly_existential_predicates(ty::Binder>), predicates: _intern_predicates(Predicate<'tcx>), projs: _intern_projs(ProjectionKind), place_elems: _intern_place_elems(PlaceElem<'tcx>), @@ -2282,7 +2297,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn mk_dynamic( self, - obj: ty::Binder<&'tcx List>>, + obj: &'tcx List>>, reg: ty::Region<'tcx>, ) -> Ty<'tcx> { self.mk_ty(Dynamic(obj, reg)) @@ -2412,13 +2427,17 @@ impl<'tcx> TyCtxt<'tcx> { Place { local: place.local, projection: self.intern_place_elems(&projection) } } - pub fn intern_existential_predicates( + pub fn intern_poly_existential_predicates( self, - eps: &[ExistentialPredicate<'tcx>], - ) -> &'tcx List> { + eps: &[ty::Binder>], + ) -> &'tcx List>> { assert!(!eps.is_empty()); - assert!(eps.array_windows().all(|[a, b]| a.stable_cmp(self, b) != Ordering::Greater)); - self._intern_existential_predicates(eps) + assert!( + eps.array_windows() + .all(|[a, b]| a.skip_binder().stable_cmp(self, &b.skip_binder()) + != Ordering::Greater) + ); + self._intern_poly_existential_predicates(eps) } pub fn intern_predicates(self, preds: &[Predicate<'tcx>]) -> &'tcx List> { @@ -2475,13 +2494,16 @@ impl<'tcx> TyCtxt<'tcx> { }) } - pub fn mk_existential_predicates< - I: InternAs<[ExistentialPredicate<'tcx>], &'tcx List>>, + pub fn mk_poly_existential_predicates< + I: InternAs< + [ty::Binder>], + &'tcx List>>, + >, >( self, iter: I, ) -> I::Output { - iter.intern_with(|xs| self.intern_existential_predicates(xs)) + iter.intern_with(|xs| self.intern_poly_existential_predicates(xs)) } pub fn mk_predicates], &'tcx List>>>( @@ -2537,7 +2559,7 @@ impl<'tcx> TyCtxt<'tcx> { self, lint: &'static Lint, mut id: hir::HirId, - ) -> (Level, LintSource) { + ) -> (Level, LintLevelSource) { let sets = self.lint_levels(LOCAL_CRATE); loop { if let Some(pair) = sets.level_and_source(lint, id, self.sess) { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 5ec0ec0c56ad..fe20925b3879 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -1,6 +1,6 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty::diagnostics::suggest_constraining_type_param; -use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt}; +use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; use rustc_ast as ast; use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; use rustc_errors::{pluralize, DiagnosticBuilder}; @@ -42,8 +42,8 @@ pub enum TypeError<'tcx> { ArgCount, RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), - RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>), - RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>), + RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>), + RegionsOverlyPolymorphic(BoundRegionKind, Region<'tcx>), RegionsPlaceholderMismatch, Sorts(ExpectedFound>), @@ -58,7 +58,7 @@ pub enum TypeError<'tcx> { CyclicTy(Ty<'tcx>), CyclicConst(&'tcx ty::Const<'tcx>), ProjectionMismatched(ExpectedFound), - ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), + ExistentialMismatch(ExpectedFound<&'tcx ty::List>>>), ObjectUnsafeCoercion(DefId), ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), @@ -94,7 +94,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { } } - let br_string = |br: ty::BoundRegion| match br { + let br_string = |br: ty::BoundRegionKind| match br { ty::BrNamed(_, name) => format!(" {}", name), _ => String::new(), }; @@ -647,14 +647,11 @@ impl Trait for X { let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. - let callable_scope = match body_owner { - Some( + let callable_scope = matches!(body_owner, Some( hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), - ) => true, - _ => false, - }; + )); let impl_comparison = matches!( cause_code, ObligationCauseCode::CompareImplMethodObligation { .. } diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 8b97a87f214b..4de3d1592486 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -160,19 +160,15 @@ impl FlagComputation { } &ty::Dynamic(obj, r) => { - self.bound_computation(obj, |computation, obj| { - for predicate in obj.iter() { - match predicate { - ty::ExistentialPredicate::Trait(tr) => { - computation.add_substs(tr.substs) - } - ty::ExistentialPredicate::Projection(p) => { - computation.add_existential_projection(&p); - } - ty::ExistentialPredicate::AutoTrait(_) => {} + for predicate in obj.iter() { + self.bound_computation(predicate, |computation, predicate| match predicate { + ty::ExistentialPredicate::Trait(tr) => computation.add_substs(tr.substs), + ty::ExistentialPredicate::Projection(p) => { + computation.add_existential_projection(&p); } - } - }); + ty::ExistentialPredicate::AutoTrait(_) => {} + }); + } self.add_region(r); } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 13c8d6b2bccb..382f3708c3d4 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -534,8 +534,8 @@ impl<'tcx> TyCtxt<'tcx> { /// results returned by the closure; the closure is expected to /// return a free region (relative to this binder), and hence the /// binder is removed in the return type. The closure is invoked - /// once for each unique `BoundRegion`; multiple references to the - /// same `BoundRegion` will reuse the previous result. A map is + /// once for each unique `BoundRegionKind`; multiple references to the + /// same `BoundRegionKind` will reuse the previous result. A map is /// returned at the end with each bound region and the free region /// that replaced it. /// @@ -544,7 +544,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn replace_late_bound_regions( self, value: Binder, - fld_r: F, + mut fld_r: F, ) -> (T, BTreeMap>) where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, @@ -555,7 +555,10 @@ impl<'tcx> TyCtxt<'tcx> { let fld_c = |bound_ct, ty| { self.mk_const(ty::Const { val: ty::ConstKind::Bound(ty::INNERMOST, bound_ct), ty }) }; - self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t, fld_c) + let mut region_map = BTreeMap::new(); + let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br)); + let value = self.replace_escaping_bound_vars(value.skip_binder(), real_fld_r, fld_t, fld_c); + (value, region_map) } /// Replaces all escaping bound vars. The `fld_r` closure replaces escaping @@ -567,34 +570,18 @@ impl<'tcx> TyCtxt<'tcx> { mut fld_r: F, mut fld_t: G, mut fld_c: H, - ) -> (T, BTreeMap>) + ) -> T where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, G: FnMut(ty::BoundTy) -> Ty<'tcx>, H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>, T: TypeFoldable<'tcx>, { - use rustc_data_structures::fx::FxHashMap; - - let mut region_map = BTreeMap::new(); - let mut type_map = FxHashMap::default(); - let mut const_map = FxHashMap::default(); - if !value.has_escaping_bound_vars() { - (value, region_map) + value } else { - let mut real_fld_r = |br| *region_map.entry(br).or_insert_with(|| fld_r(br)); - - let mut real_fld_t = - |bound_ty| *type_map.entry(bound_ty).or_insert_with(|| fld_t(bound_ty)); - - let mut real_fld_c = - |bound_ct, ty| *const_map.entry(bound_ct).or_insert_with(|| fld_c(bound_ct, ty)); - - let mut replacer = - BoundVarReplacer::new(self, &mut real_fld_r, &mut real_fld_t, &mut real_fld_c); - let result = value.fold_with(&mut replacer); - (result, region_map) + let mut replacer = BoundVarReplacer::new(self, &mut fld_r, &mut fld_t, &mut fld_c); + value.fold_with(&mut replacer) } } @@ -604,7 +591,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn replace_bound_vars( self, value: Binder, - fld_r: F, + mut fld_r: F, fld_t: G, fld_c: H, ) -> (T, BTreeMap>) @@ -614,7 +601,10 @@ impl<'tcx> TyCtxt<'tcx> { H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>, T: TypeFoldable<'tcx>, { - self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t, fld_c) + let mut region_map = BTreeMap::new(); + let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br)); + let value = self.replace_escaping_bound_vars(value.skip_binder(), real_fld_r, fld_t, fld_c); + (value, region_map) } /// Replaces any late-bound regions bound in `value` with @@ -626,7 +616,7 @@ impl<'tcx> TyCtxt<'tcx> { self.replace_late_bound_regions(value, |br| { self.mk_region(ty::ReFree(ty::FreeRegion { scope: all_outlive_scope, - bound_region: br, + bound_region: br.kind, })) }) .0 @@ -639,7 +629,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn collect_constrained_late_bound_regions( self, value: &Binder, - ) -> FxHashSet + ) -> FxHashSet where T: TypeFoldable<'tcx>, { @@ -650,7 +640,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn collect_referenced_late_bound_regions( self, value: &Binder, - ) -> FxHashSet + ) -> FxHashSet where T: TypeFoldable<'tcx>, { @@ -661,7 +651,7 @@ impl<'tcx> TyCtxt<'tcx> { self, value: &Binder, just_constraint: bool, - ) -> FxHashSet + ) -> FxHashSet where T: TypeFoldable<'tcx>, { @@ -695,7 +685,8 @@ impl<'tcx> TyCtxt<'tcx> { let mut counter = 0; Binder::bind( self.replace_late_bound_regions(sig, |_| { - let r = self.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(counter))); + let br = ty::BoundRegion { kind: ty::BrAnon(counter) }; + let r = self.mk_region(ty::ReLateBound(ty::INNERMOST, br)); counter += 1; r }) @@ -955,7 +946,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { /// into a hash set. struct LateBoundRegionsCollector { current_index: ty::DebruijnIndex, - regions: FxHashSet, + regions: FxHashSet, /// `true` if we only want regions that are known to be /// "constrained" when you equate this type with another type. In @@ -1014,7 +1005,7 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { if let ty::ReLateBound(debruijn, br) = *r { if debruijn == self.current_index { - self.regions.insert(br); + self.regions.insert(br.kind); } } ControlFlow::CONTINUE diff --git a/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs b/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs index ee6b06a1cc80..d9aebfc8293b 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs @@ -17,7 +17,7 @@ pub struct DefIdForest { /// If A and B are DefIds in the `DefIdForest`, and A is a descendant /// of B, then only B will be in `root_ids`. /// We use a `SmallVec` here because (for its use for caching inhabitedness) - /// its rare that this will contain even two IDs. + /// it's rare that this will contain even two IDs. root_ids: SmallVec<[DefId; 1]>, } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d6b3afb3be3d..4475d4e9f2de 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1634,7 +1634,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let layout = tcx.intern_layout(Layout { variants: Variants::Multiple { - tag: tag, + tag, tag_encoding: TagEncoding::Direct, tag_field: tag_index, variants, @@ -2455,7 +2455,8 @@ impl<'tcx> ty::Instance<'tcx> { ty::Generator(_, substs, _) => { let sig = substs.as_generator().poly_sig(); - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let br = ty::BoundRegion { kind: ty::BrEnv }; + let env_region = ty::ReLateBound(ty::INNERMOST, br); let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); let pin_did = tcx.require_lang_item(LangItem::Pin, None); diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 83a2bdf90f9a..e657088a5e46 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -24,7 +24,7 @@ extern "C" { /// This means we can use pointer for both /// equality comparisons and hashing. /// -/// Unlike slices, The types contained in `List` are expected to be `Copy` +/// Unlike slices, the types contained in `List` are expected to be `Copy` /// and iterating over a `List` returns `T` instead of a reference. /// /// Note: `Slice` was already taken by the `Ty`. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2d7f417b64a1..1fe1400fabec 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1,3 +1,14 @@ +//! Defines how the compiler represents types internally. +//! +//! Two important entities in this module are: +//! +//! - [`rustc_middle::ty::Ty`], used to represent the semantics of a type. +//! - [`rustc_middle::ty::TyCtxt`], the central data structure in the compiler. +//! +//! For more information, see ["The `ty` module: representing types"] in the ructc-dev-guide. +//! +//! ["The `ty` module: representing types"]: https://rustc-dev-guide.rust-lang.org/ty.html + // ignore-tidy-filelength pub use self::fold::{TypeFoldable, TypeFolder, TypeVisitor}; pub use self::AssocItemContainer::*; @@ -51,13 +62,13 @@ use std::ops::{ControlFlow, Range}; use std::ptr; use std::str; -pub use self::sty::BoundRegion::*; +pub use self::sty::BoundRegionKind::*; pub use self::sty::InferTy::*; pub use self::sty::RegionKind; pub use self::sty::RegionKind::*; pub use self::sty::TyKind::*; -pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar, DebruijnIndex, INNERMOST}; -pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; +pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar}; +pub use self::sty::{BoundRegion, BoundRegionKind, EarlyBoundRegion, FreeRegion, Region}; pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; pub use self::sty::{ClosureSubstsParts, GeneratorSubstsParts}; @@ -67,6 +78,7 @@ pub use self::sty::{ExistentialProjection, PolyExistentialProjection}; pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; pub use crate::ty::diagnostics::*; +pub use rustc_type_ir::{DebruijnIndex, TypeFlags, INNERMOST}; pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; @@ -497,91 +509,6 @@ pub struct CReaderCacheKey { pub pos: usize, } -bitflags! { - /// Flags that we track on types. These flags are propagated upwards - /// through the type during type construction, so that we can quickly check - /// whether the type has various kinds of types in it without recursing - /// over the type itself. - pub struct TypeFlags: u32 { - // Does this have parameters? Used to determine whether substitution is - // required. - /// Does this have [Param]? - const HAS_TY_PARAM = 1 << 0; - /// Does this have [ReEarlyBound]? - const HAS_RE_PARAM = 1 << 1; - /// Does this have [ConstKind::Param]? - const HAS_CT_PARAM = 1 << 2; - - const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_RE_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits; - - /// Does this have [Infer]? - const HAS_TY_INFER = 1 << 3; - /// Does this have [ReVar]? - const HAS_RE_INFER = 1 << 4; - /// Does this have [ConstKind::Infer]? - const HAS_CT_INFER = 1 << 5; - - /// Does this have inference variables? Used to determine whether - /// inference is required. - const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_RE_INFER.bits - | TypeFlags::HAS_CT_INFER.bits; - - /// Does this have [Placeholder]? - const HAS_TY_PLACEHOLDER = 1 << 6; - /// Does this have [RePlaceholder]? - const HAS_RE_PLACEHOLDER = 1 << 7; - /// Does this have [ConstKind::Placeholder]? - const HAS_CT_PLACEHOLDER = 1 << 8; - - /// `true` if there are "names" of regions and so forth - /// that are local to a particular fn/inferctxt - const HAS_FREE_LOCAL_REGIONS = 1 << 9; - - /// `true` if there are "names" of types and regions and so forth - /// that are local to a particular fn - const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits - | TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_CT_INFER.bits - | TypeFlags::HAS_TY_PLACEHOLDER.bits - | TypeFlags::HAS_CT_PLACEHOLDER.bits - | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; - - /// Does this have [Projection]? - const HAS_TY_PROJECTION = 1 << 10; - /// Does this have [Opaque]? - const HAS_TY_OPAQUE = 1 << 11; - /// Does this have [ConstKind::Unevaluated]? - const HAS_CT_PROJECTION = 1 << 12; - - /// Could this type be normalized further? - const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits - | TypeFlags::HAS_TY_OPAQUE.bits - | TypeFlags::HAS_CT_PROJECTION.bits; - - /// Is an error type/const reachable? - const HAS_ERROR = 1 << 13; - - /// Does this have any region that "appears free" in the type? - /// Basically anything but [ReLateBound] and [ReErased]. - const HAS_FREE_REGIONS = 1 << 14; - - /// Does this have any [ReLateBound] regions? Used to check - /// if a global bound is safe to evaluate. - const HAS_RE_LATE_BOUND = 1 << 15; - - /// Does this have any [ReErased] regions? - const HAS_RE_ERASED = 1 << 16; - - /// Does this value have parameters/placeholders/inference variables which could be - /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 17; - } -} - #[allow(rustc::usage_of_ty_tykind)] pub struct TyS<'tcx> { /// This field shouldn't be used directly and may be removed in the future. @@ -672,7 +599,18 @@ impl<'a, 'tcx> HashStable> for TyS<'tcx> { #[rustc_diagnostic_item = "Ty"] pub type Ty<'tcx> = &'tcx TyS<'tcx>; -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] pub struct UpvarPath { pub hir_id: hir::HirId, } @@ -680,7 +618,7 @@ pub struct UpvarPath { /// Upvars do not get their own `NodeId`. Instead, we use the pair of /// the original var ID (that is, the root variable that is referenced /// by the upvar) and the ID of the closure expression. -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct UpvarId { pub var_path: UpvarPath, pub closure_expr_id: LocalDefId, @@ -692,7 +630,7 @@ impl UpvarId { } } -#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] +#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, TypeFoldable, Copy, HashStable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. ImmBorrow, @@ -746,7 +684,7 @@ pub enum BorrowKind { /// Information describing the capture of an upvar. This is computed /// during `typeck`, specifically by `regionck`. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub enum UpvarCapture<'tcx> { /// Upvar is captured by value. This is always true when the /// closure is labeled `move`, but can also be true in other cases @@ -763,7 +701,7 @@ pub enum UpvarCapture<'tcx> { ByRef(UpvarBorrow<'tcx>), } -#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct UpvarBorrow<'tcx> { /// The kind of borrow: by-ref upvars have access to shared /// immutable borrows, which are not part of the normal language @@ -790,7 +728,7 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap = Vec>; /// A `Place` and the corresponding `CaptureInfo`. -#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct CapturedPlace<'tcx> { pub place: HirPlace<'tcx>, pub info: CaptureInfo<'tcx>, @@ -799,7 +737,7 @@ pub struct CapturedPlace<'tcx> { /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) /// for a particular capture as well as identifying the part of the source code /// that triggered this capture to occur. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct CaptureInfo<'tcx> { /// Expr Id pointing to use that resulted in selecting the current capture kind /// @@ -1222,17 +1160,16 @@ pub enum PredicateAtom<'tcx> { TypeWellFormedFromEnv(Ty<'tcx>), } -impl<'tcx> PredicateAtom<'tcx> { +impl<'tcx> Binder> { /// Wraps `self` with the given qualifier if this predicate has any unbound variables. pub fn potentially_quantified( self, tcx: TyCtxt<'tcx>, qualifier: impl FnOnce(Binder>) -> PredicateKind<'tcx>, ) -> Predicate<'tcx> { - if self.has_escaping_bound_vars() { - qualifier(Binder::bind(self)) - } else { - PredicateKind::Atom(self) + match self.no_bound_vars() { + Some(atom) => PredicateKind::Atom(atom), + None => qualifier(self), } .to_predicate(tcx) } @@ -1325,7 +1262,11 @@ impl<'tcx> Predicate<'tcx> { let substs = trait_ref.skip_binder().substs; let pred = self.skip_binders(); let new = pred.subst(tcx, substs); - if new != pred { new.potentially_quantified(tcx, PredicateKind::ForAll) } else { self } + if new != pred { + ty::Binder::bind(new).potentially_quantified(tcx, PredicateKind::ForAll) + } else { + self + } } } @@ -1352,6 +1293,10 @@ impl<'tcx> PolyTraitPredicate<'tcx> { // Ok to skip binder since trait `DefId` does not care about regions. self.skip_binder().def_id() } + + pub fn self_ty(self) -> ty::Binder> { + self.map_bound(|trait_ref| trait_ref.self_ty()) + } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] @@ -1476,37 +1421,39 @@ impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - PredicateAtom::Trait(self.value.skip_binder(), self.constness) + self.value + .map_bound(|value| PredicateAtom::Trait(value, self.constness)) .potentially_quantified(tcx, PredicateKind::ForAll) } } impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - PredicateAtom::RegionOutlives(self.skip_binder()) + self.map_bound(|value| PredicateAtom::RegionOutlives(value)) .potentially_quantified(tcx, PredicateKind::ForAll) } } impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - PredicateAtom::TypeOutlives(self.skip_binder()) + self.map_bound(|value| PredicateAtom::TypeOutlives(value)) .potentially_quantified(tcx, PredicateKind::ForAll) } } impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - PredicateAtom::Projection(self.skip_binder()) + self.map_bound(|value| PredicateAtom::Projection(value)) .potentially_quantified(tcx, PredicateKind::ForAll) } } impl<'tcx> Predicate<'tcx> { pub fn to_opt_poly_trait_ref(self) -> Option>> { - match self.skip_binders() { + let predicate = self.bound_atom(); + match predicate.skip_binder() { PredicateAtom::Trait(t, constness) => { - Some(ConstnessAnd { constness, value: ty::Binder::bind(t.trait_ref) }) + Some(ConstnessAnd { constness, value: predicate.rebind(t.trait_ref) }) } PredicateAtom::Projection(..) | PredicateAtom::Subtype(..) @@ -1522,8 +1469,9 @@ impl<'tcx> Predicate<'tcx> { } pub fn to_opt_type_outlives(self) -> Option> { - match self.skip_binders() { - PredicateAtom::TypeOutlives(data) => Some(ty::Binder::bind(data)), + let predicate = self.bound_atom(); + match predicate.skip_binder() { + PredicateAtom::TypeOutlives(data) => Some(predicate.rebind(data)), PredicateAtom::Trait(..) | PredicateAtom::Projection(..) | PredicateAtom::Subtype(..) @@ -1670,7 +1618,7 @@ where } } -pub type PlaceholderRegion = Placeholder; +pub type PlaceholderRegion = Placeholder; pub type PlaceholderType = Placeholder; @@ -1690,8 +1638,6 @@ pub type PlaceholderConst<'tcx> = Placeholder>; /// which cause cycle errors. /// /// ```rust -/// #![feature(const_generics)] -/// /// struct A; /// impl A { /// fn foo(&self) -> [u8; N] { [0; N] } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 2e00be2395b8..c79e06b7fdd3 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -63,7 +63,7 @@ pub trait Printer<'tcx>: Sized { fn print_dyn_existential( self, - predicates: &'tcx ty::List>, + predicates: &'tcx ty::List>>, ) -> Result; fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result; @@ -343,7 +343,9 @@ impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { } } -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List> { +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> + for &'tcx ty::List>> +{ type Output = P::DynExistential; type Error = P::Error; fn print(&self, cx: P) -> Result { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 38f8e779f6a9..893572785f76 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -125,13 +125,13 @@ pub struct RegionHighlightMode { highlight_regions: [Option<(ty::RegionKind, usize)>; 3], /// If enabled, when printing a "free region" that originated from - /// the given `ty::BoundRegion`, print it as "`'1`". Free regions that would ordinarily + /// the given `ty::BoundRegionKind`, print it as "`'1`". Free regions that would ordinarily /// have names print as normal. /// /// This is used when you have a signature like `fn foo(x: &u32, /// y: &'a u32)` and we want to give a name to the region of the /// reference `x`. - highlight_bound_region: Option<(ty::BoundRegion, usize)>, + highlight_bound_region: Option<(ty::BoundRegionKind, usize)>, } impl RegionHighlightMode { @@ -175,7 +175,7 @@ impl RegionHighlightMode { /// Highlight the given bound region. /// We can only highlight one bound region at a time. See /// the field `highlight_bound_region` for more detailed notes. - pub fn highlighting_bound_region(&mut self, br: ty::BoundRegion, number: usize) { + pub fn highlighting_bound_region(&mut self, br: ty::BoundRegionKind, number: usize) { assert!(self.highlight_bound_region.is_none()); self.highlight_bound_region = Some((br, number)); } @@ -209,6 +209,17 @@ pub trait PrettyPrinter<'tcx>: value.as_ref().skip_binder().print(self) } + fn wrap_binder Result>( + self, + value: &ty::Binder, + f: F, + ) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, + { + f(value.as_ref().skip_binder(), self) + } + /// Prints comma-separated elements. fn comma_sep(mut self, mut elems: impl Iterator) -> Result where @@ -753,72 +764,77 @@ pub trait PrettyPrinter<'tcx>: fn pretty_print_dyn_existential( mut self, - predicates: &'tcx ty::List>, + predicates: &'tcx ty::List>>, ) -> Result { - define_scoped_cx!(self); - // Generate the main trait ref, including associated types. let mut first = true; if let Some(principal) = predicates.principal() { - p!(print_def_path(principal.def_id, &[])); + self = self.wrap_binder(&principal, |principal, mut cx| { + define_scoped_cx!(cx); + p!(print_def_path(principal.def_id, &[])); - let mut resugared = false; + let mut resugared = false; - // Special-case `Fn(...) -> ...` and resugar it. - let fn_trait_kind = self.tcx().fn_trait_kind_from_lang_item(principal.def_id); - if !self.tcx().sess.verbose() && fn_trait_kind.is_some() { - if let ty::Tuple(ref args) = principal.substs.type_at(0).kind() { - let mut projections = predicates.projection_bounds(); - if let (Some(proj), None) = (projections.next(), projections.next()) { - let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); - p!(pretty_fn_sig(&tys, false, proj.ty)); - resugared = true; + // Special-case `Fn(...) -> ...` and resugar it. + let fn_trait_kind = cx.tcx().fn_trait_kind_from_lang_item(principal.def_id); + if !cx.tcx().sess.verbose() && fn_trait_kind.is_some() { + if let ty::Tuple(ref args) = principal.substs.type_at(0).kind() { + let mut projections = predicates.projection_bounds(); + if let (Some(proj), None) = (projections.next(), projections.next()) { + let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); + p!(pretty_fn_sig(&tys, false, proj.skip_binder().ty)); + resugared = true; + } } } - } - // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, - // in order to place the projections inside the `<...>`. - if !resugared { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = self.tcx().mk_ty_infer(ty::FreshTy(0)); - let principal = principal.with_self_ty(self.tcx(), dummy_self); + // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, + // in order to place the projections inside the `<...>`. + if !resugared { + // Use a type that can't appear in defaults of type parameters. + let dummy_cx = cx.tcx().mk_ty_infer(ty::FreshTy(0)); + let principal = principal.with_self_ty(cx.tcx(), dummy_cx); - let args = self.generic_args_to_print( - self.tcx().generics_of(principal.def_id), - principal.substs, - ); + let args = cx.generic_args_to_print( + cx.tcx().generics_of(principal.def_id), + principal.substs, + ); - // Don't print `'_` if there's no unerased regions. - let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, - _ => false, - }); - let mut args = args.iter().cloned().filter(|arg| match arg.unpack() { - GenericArgKind::Lifetime(_) => print_regions, - _ => true, - }); - let mut projections = predicates.projection_bounds(); + // Don't print `'_` if there's no unerased regions. + let print_regions = args.iter().any(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => *r != ty::ReErased, + _ => false, + }); + let mut args = args.iter().cloned().filter(|arg| match arg.unpack() { + GenericArgKind::Lifetime(_) => print_regions, + _ => true, + }); + let mut projections = predicates.projection_bounds(); - let arg0 = args.next(); - let projection0 = projections.next(); - if arg0.is_some() || projection0.is_some() { - let args = arg0.into_iter().chain(args); - let projections = projection0.into_iter().chain(projections); + let arg0 = args.next(); + let projection0 = projections.next(); + if arg0.is_some() || projection0.is_some() { + let args = arg0.into_iter().chain(args); + let projections = projection0.into_iter().chain(projections); - p!(generic_delimiters(|mut cx| { - cx = cx.comma_sep(args)?; - if arg0.is_some() && projection0.is_some() { - write!(cx, ", ")?; - } - cx.comma_sep(projections) - })); + p!(generic_delimiters(|mut cx| { + cx = cx.comma_sep(args)?; + if arg0.is_some() && projection0.is_some() { + write!(cx, ", ")?; + } + cx.comma_sep(projections) + })); + } } - } + Ok(cx) + })?; + first = false; } + define_scoped_cx!(self); + // Builtin bounds. // FIXME(eddyb) avoid printing twice (needed to ensure // that the auto traits are sorted *and* printed via cx). @@ -1391,7 +1407,7 @@ impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { fn print_dyn_existential( self, - predicates: &'tcx ty::List>, + predicates: &'tcx ty::List>>, ) -> Result { self.pretty_print_dyn_existential(predicates) } @@ -1465,7 +1481,7 @@ impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { // FIXME(eddyb) `name` should never be empty, but it // currently is for `extern { ... }` "foreign modules". let name = disambiguated_data.data.name(); - if name != DefPathDataName::Named(kw::Invalid) { + if name != DefPathDataName::Named(kw::Empty) { if !self.empty_path { write!(self, "::")?; } @@ -1537,6 +1553,17 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { self.pretty_in_binder(value) } + fn wrap_binder Result>( + self, + value: &ty::Binder, + f: C, + ) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, + { + self.pretty_wrap_binder(value, f) + } + fn typed_value( mut self, f: impl FnOnce(Self) -> Result, @@ -1581,14 +1608,14 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { match *region { ty::ReEarlyBound(ref data) => { - data.name != kw::Invalid && data.name != kw::UnderscoreLifetime + data.name != kw::Empty && data.name != kw::UnderscoreLifetime } - ty::ReLateBound(_, br) + ty::ReLateBound(_, ty::BoundRegion { kind: br }) | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { if let ty::BrNamed(_, name) = br { - if name != kw::Invalid && name != kw::UnderscoreLifetime { + if name != kw::Empty && name != kw::UnderscoreLifetime { return true; } } @@ -1658,16 +1685,16 @@ impl FmtPrinter<'_, '_, F> { // `explain_region()` or `note_and_explain_region()`. match *region { ty::ReEarlyBound(ref data) => { - if data.name != kw::Invalid { + if data.name != kw::Empty { p!(write("{}", data.name)); return Ok(self); } } - ty::ReLateBound(_, br) + ty::ReLateBound(_, ty::BoundRegion { kind: br }) | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { if let ty::BrNamed(_, name) = br { - if name != kw::Invalid && name != kw::UnderscoreLifetime { + if name != kw::Empty && name != kw::UnderscoreLifetime { p!(write("{}", name)); return Ok(self); } @@ -1752,10 +1779,10 @@ impl FmtPrinter<'_, 'tcx, F> { let mut region_index = self.region_index; let new_value = self.tcx.replace_late_bound_regions(value.clone(), |br| { let _ = start_or_continue(&mut self, "for<", ", "); - let br = match br { + let kind = match br.kind { ty::BrNamed(_, name) => { let _ = write!(self, "{}", name); - br + br.kind } ty::BrAnon(_) | ty::BrEnv => { let name = loop { @@ -1769,7 +1796,7 @@ impl FmtPrinter<'_, 'tcx, F> { ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) } }; - self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)) + self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind })) }); start_or_continue(&mut self, "", "> ")?; @@ -1790,6 +1817,22 @@ impl FmtPrinter<'_, 'tcx, F> { Ok(inner) } + pub fn pretty_wrap_binder Result>( + self, + value: &ty::Binder, + f: C, + ) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, + { + let old_region_index = self.region_index; + let (new, new_value) = self.name_all_regions(value)?; + let mut inner = f(&new_value.0, new)?; + inner.region_index = old_region_index; + inner.binder_depth -= 1; + Ok(inner) + } + fn prepare_late_bound_region_info(&mut self, value: &ty::Binder) where T: TypeFoldable<'tcx>, @@ -1797,7 +1840,7 @@ impl FmtPrinter<'_, 'tcx, F> { struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet); impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - if let ty::ReLateBound(_, ty::BrNamed(_, name)) = *r { + if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name) }) = *r { self.0.insert(name); } r.super_visit_with(self) @@ -1906,12 +1949,12 @@ impl ty::Binder> { forward_display_to_print! { Ty<'tcx>, - &'tcx ty::List>, + &'tcx ty::List>>, &'tcx ty::Const<'tcx>, // HACK(eddyb) these are exhaustive instead of generic, // because `for<'tcx>` isn't possible yet. - ty::Binder<&'tcx ty::List>>, + ty::Binder>, ty::Binder>, ty::Binder>, ty::Binder>, diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index 898cc24992ba..8a1165bbd647 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -1,4 +1,4 @@ -use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; +use crate::dep_graph::{DepNode, DepNodeIndex, SerializedDepNodeIndex}; use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use crate::mir::{self, interpret}; use crate::ty::codec::{OpaqueEncoder, RefDecodable, TyDecoder, TyEncoder}; @@ -264,6 +264,13 @@ impl<'sess> OnDiskCache<'sess> { (file_to_file_index, file_index_to_stable_id) }; + // Register any dep nodes that we reused from the previous session, + // but didn't `DepNode::construct` in this session. This ensures + // that their `DefPathHash` to `RawDefId` mappings are registered + // in 'latest_foreign_def_path_hashes' if necessary, since that + // normally happens in `DepNode::construct`. + tcx.dep_graph.register_reused_dep_nodes(tcx); + // Load everything into memory so we can write it out to the on-disk // cache. The vast majority of cacheable query results should already // be in memory, so this should be a cheap operation. @@ -454,6 +461,7 @@ impl<'sess> OnDiskCache<'sess> { fn try_remap_cnum(&self, tcx: TyCtxt<'_>, cnum: u32) -> Option { let cnum_map = self.cnum_map.get_or_init(|| Self::compute_cnum_map(tcx, &self.prev_cnums[..])); + debug!("try_remap_cnum({}): cnum_map={:?}", cnum, cnum_map); cnum_map[CrateNum::from_u32(cnum)] } @@ -466,9 +474,33 @@ impl<'sess> OnDiskCache<'sess> { .insert(hash, RawDefId { krate: def_id.krate.as_u32(), index: def_id.index.as_u32() }); } - pub fn register_reused_dep_path_hash(&self, hash: DefPathHash) { - if let Some(old_id) = self.foreign_def_path_hashes.get(&hash) { - self.latest_foreign_def_path_hashes.lock().insert(hash, *old_id); + /// If the given `dep_node`'s hash still exists in the current compilation, + /// and its current `DefId` is foreign, calls `store_foreign_def_id` with it. + /// + /// Normally, `store_foreign_def_id_hash` can be called directly by + /// the dependency graph when we construct a `DepNode`. However, + /// when we re-use a deserialized `DepNode` from the previous compilation + /// session, we only have the `DefPathHash` available. This method is used + /// to that any `DepNode` that we re-use has a `DefPathHash` -> `RawId` written + /// out for usage in the next compilation session. + pub fn register_reused_dep_node(&self, tcx: TyCtxt<'tcx>, dep_node: &DepNode) { + // For reused dep nodes, we only need to store the mapping if the node + // is one whose query key we can reconstruct from the hash. We use the + // mapping to aid that reconstruction in the next session. While we also + // use it to decode `DefId`s we encoded in the cache as `DefPathHashes`, + // they're already registered during `DefId` encoding. + if dep_node.kind.can_reconstruct_query_key() { + let hash = DefPathHash(dep_node.hash.into()); + + // We can't simply copy the `RawDefId` from `foreign_def_path_hashes` to + // `latest_foreign_def_path_hashes`, since the `RawDefId` might have + // changed in the current compilation session (e.g. we've added/removed crates, + // or added/removed definitions before/after the target definition). + if let Some(def_id) = self.def_path_hash_to_def_id(tcx, hash) { + if !def_id.is_local() { + self.store_foreign_def_id_hash(def_id, hash); + } + } } } @@ -592,6 +624,7 @@ impl<'sess> OnDiskCache<'sess> { match cache.entry(hash) { Entry::Occupied(e) => *e.get(), Entry::Vacant(e) => { + debug!("def_path_hash_to_def_id({:?})", hash); // Check if the `DefPathHash` corresponds to a definition in the current // crate if let Some(def_id) = self.local_def_path_hash_to_def_id.get(&hash).cloned() { @@ -605,9 +638,11 @@ impl<'sess> OnDiskCache<'sess> { // current compilation session, the crate is guaranteed to be the same // (otherwise, we would compute a different `DefPathHash`). let raw_def_id = self.get_raw_def_id(&hash)?; + debug!("def_path_hash_to_def_id({:?}): raw_def_id = {:?}", hash, raw_def_id); // If the owning crate no longer exists, the corresponding definition definitely // no longer exists. let krate = self.try_remap_cnum(tcx, raw_def_id.krate)?; + debug!("def_path_hash_to_def_id({:?}): krate = {:?}", hash, krate); // If our `DefPathHash` corresponded to a definition in the local crate, // we should have either found it in `local_def_path_hash_to_def_id`, or // never attempted to load it in the first place. Any query result or `DepNode` @@ -621,6 +656,7 @@ impl<'sess> OnDiskCache<'sess> { // Try to find a definition in the current session, using the previous `DefIndex` // as an initial guess. let opt_def_id = tcx.cstore.def_path_hash_to_def_id(krate, raw_def_id.index, hash); + debug!("def_path_to_def_id({:?}): opt_def_id = {:?}", hash, opt_def_id); e.insert(opt_def_id); opt_def_id } @@ -630,7 +666,7 @@ impl<'sess> OnDiskCache<'sess> { //- DECODING ------------------------------------------------------------------- -/// A decoder that can read from the incr. comp. cache. It is similar to the one +/// A decoder that can read from the incremental compilation cache. It is similar to the one /// we use for crate metadata decoding in that it can rebase spans and eventually /// will also handle things that contain `Ty` instances. crate struct CacheDecoder<'a, 'tcx> { @@ -918,7 +954,7 @@ impl<'a, 'tcx> Decodable> for &'tcx [Span] { //- ENCODING ------------------------------------------------------------------- -/// An encoder that can write the incr. comp. cache. +/// An encoder that can write to the incremental compilation cache. struct CacheEncoder<'a, 'tcx, E: OpaqueEncoder> { tcx: TyCtxt<'tcx>, encoder: &'a mut E, @@ -1026,9 +1062,6 @@ where { const CLEAR_CROSS_CRATE: bool = false; - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } fn position(&self) -> usize { self.encoder.encoder_position() } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index ef5034e218da..293b3c6b0470 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -33,6 +33,15 @@ pub trait TypeRelation<'tcx>: Sized { /// relation. Just affects error messages. fn a_is_expected(&self) -> bool; + /// Whether we should look into the substs of unevaluated constants + /// even if `feature(const_evaluatable_checked)` is active. + /// + /// This is needed in `combine` to prevent accidentially creating + /// infinite types as we abuse `TypeRelation` to walk a type there. + fn visit_ct_substs(&self) -> bool { + false + } + fn with_cause(&mut self, _cause: Cause, f: F) -> R where F: FnOnce(&mut Self) -> R, @@ -579,7 +588,7 @@ pub fn super_relate_consts>( ( ty::ConstKind::Unevaluated(a_def, a_substs, None), ty::ConstKind::Unevaluated(b_def, b_substs, None), - ) if tcx.features().const_evaluatable_checked => { + ) if tcx.features().const_evaluatable_checked && !relation.visit_ct_substs() => { if tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) { Ok(a.val) } else { @@ -603,7 +612,7 @@ pub fn super_relate_consts>( new_const_val.map(|val| tcx.mk_const(ty::Const { val, ty: a.ty })) } -impl<'tcx> Relate<'tcx> for &'tcx ty::List> { +impl<'tcx> Relate<'tcx> for &'tcx ty::List>> { fn relate>( relation: &mut R, a: Self, @@ -616,9 +625,10 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List> { // in `a`. let mut a_v: Vec<_> = a.into_iter().collect(); let mut b_v: Vec<_> = b.into_iter().collect(); - a_v.sort_by(|a, b| a.stable_cmp(tcx, b)); + // `skip_binder` here is okay because `stable_cmp` doesn't look at binders + a_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); a_v.dedup(); - b_v.sort_by(|a, b| a.stable_cmp(tcx, b)); + b_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); b_v.dedup(); if a_v.len() != b_v.len() { return Err(TypeError::ExistentialMismatch(expected_found(relation, a, b))); @@ -626,14 +636,18 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List> { let v = a_v.into_iter().zip(b_v.into_iter()).map(|(ep_a, ep_b)| { use crate::ty::ExistentialPredicate::*; - match (ep_a, ep_b) { - (Trait(a), Trait(b)) => Ok(Trait(relation.relate(a, b)?)), - (Projection(a), Projection(b)) => Ok(Projection(relation.relate(a, b)?)), - (AutoTrait(a), AutoTrait(b)) if a == b => Ok(AutoTrait(a)), + match (ep_a.skip_binder(), ep_b.skip_binder()) { + (Trait(a), Trait(b)) => Ok(ty::Binder::bind(Trait( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))), + (Projection(a), Projection(b)) => Ok(ty::Binder::bind(Projection( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))), + (AutoTrait(a), AutoTrait(b)) if a == b => Ok(ep_a.rebind(AutoTrait(a))), _ => Err(TypeError::ExistentialMismatch(expected_found(relation, a, b))), } }); - Ok(tcx.mk_existential_predicates(v)?) + Ok(tcx.mk_poly_existential_predicates(v)?) } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 94e69a93a6b1..7a1ca6a6c2bf 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -65,7 +65,7 @@ impl fmt::Debug for ty::adjustment::Adjustment<'tcx> { } } -impl fmt::Debug for ty::BoundRegion { +impl fmt::Debug for ty::BoundRegionKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { ty::BrAnon(n) => write!(f, "BrAnon({:?})", n), @@ -308,13 +308,13 @@ TrivialTypeFoldableAndLiftImpls! { crate::traits::Reveal, crate::ty::adjustment::AutoBorrowMutability, crate::ty::AdtKind, - // Including `BoundRegion` is a *bit* dubious, but direct + // Including `BoundRegionKind` is a *bit* dubious, but direct // references to bound region appear in `ty::Error`, and aren't // really meant to be folded. In general, we can only fold a fully // general `Region`. - crate::ty::BoundRegion, + crate::ty::BoundRegionKind, crate::ty::AssocItem, - crate::ty::Placeholder, + crate::ty::Placeholder, crate::ty::ClosureKind, crate::ty::FreeRegion, crate::ty::InferTy, @@ -843,9 +843,9 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { } } -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List>> { fn super_fold_with>(self, folder: &mut F) -> Self { - ty::util::fold_list(self, folder, |tcx, v| tcx.intern_existential_predicates(v)) + ty::util::fold_list(self, folder, |tcx, v| tcx.intern_poly_existential_predicates(v)) } fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 78994c6e1c77..744c7a541a53 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -40,12 +40,12 @@ pub struct TypeAndMut<'tcx> { /// at least as big as the scope `fr.scope`". pub struct FreeRegion { pub scope: DefId, - pub bound_region: BoundRegion, + pub bound_region: BoundRegionKind, } #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] -pub enum BoundRegion { +pub enum BoundRegionKind { /// An anonymous region parameter for a given fn (&T) BrAnon(u32), @@ -60,26 +60,36 @@ pub enum BoundRegion { BrEnv, } -impl BoundRegion { - pub fn is_named(&self) -> bool { - match *self { - BoundRegion::BrNamed(_, name) => name != kw::UnderscoreLifetime, - _ => false, - } - } +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, PartialOrd, Ord)] +#[derive(HashStable)] +pub struct BoundRegion { + pub kind: BoundRegionKind, +} +impl BoundRegion { /// When canonicalizing, we replace unbound inference variables and free /// regions with anonymous late bound regions. This method asserts that /// we have an anonymous late bound region, which hence may refer to /// a canonical variable. pub fn assert_bound_var(&self) -> BoundVar { - match *self { - BoundRegion::BrAnon(var) => BoundVar::from_u32(var), + match self.kind { + BoundRegionKind::BrAnon(var) => BoundVar::from_u32(var), _ => bug!("bound region is not anonymous"), } } } +impl BoundRegionKind { + pub fn is_named(&self) -> bool { + match *self { + BoundRegionKind::BrNamed(_, name) => name != kw::UnderscoreLifetime, + _ => false, + } + } +} + +/// Defines the kinds of types. +/// /// N.B., if you change this, you'll probably want to change the corresponding /// AST structure in `librustc_ast/ast.rs` as well. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable, Debug)] @@ -102,7 +112,7 @@ pub enum TyKind<'tcx> { /// A primitive floating-point type. For example, `f64`. Float(ast::FloatTy), - /// Structures, enumerations and unions. + /// Algebraic data types (ADT). For example: structures, enumerations and unions. /// /// InternalSubsts here, possibly against intuition, *may* contain `Param`s. /// That is, even after substitution it is possible that there are type @@ -152,7 +162,7 @@ pub enum TyKind<'tcx> { FnPtr(PolyFnSig<'tcx>), /// A trait, defined with `trait`. - Dynamic(Binder<&'tcx List>>, ty::Region<'tcx>), + Dynamic(&'tcx List>>, ty::Region<'tcx>), /// The anonymous type of a closure. Used to represent the type of /// `|a| a`. @@ -162,11 +172,11 @@ pub enum TyKind<'tcx> { /// `|a| yield a`. Generator(DefId, SubstsRef<'tcx>, hir::Movability), - /// A type representin the types stored inside a generator. + /// A type representing the types stored inside a generator. /// This should only appear in GeneratorInteriors. GeneratorWitness(Binder<&'tcx List>>), - /// The never type `!` + /// The never type `!`. Never, /// A tuple type. For example, `(i32, bool)`. @@ -205,10 +215,7 @@ pub enum TyKind<'tcx> { impl TyKind<'tcx> { #[inline] pub fn is_primitive(&self) -> bool { - match self { - Bool | Char | Int(_) | Uint(_) | Float(_) => true, - _ => false, - } + matches!(self, Bool | Char | Int(_) | Uint(_) | Float(_)) } /// Get the article ("a" or "an") to use with this type. @@ -762,7 +769,7 @@ impl<'tcx> Binder> { } } -impl<'tcx> List> { +impl<'tcx> List>> { /// Returns the "principal `DefId`" of this set of existential predicates. /// /// A Rust trait object type consists (in addition to a lifetime bound) @@ -788,64 +795,42 @@ impl<'tcx> List> { /// is `{Send, Sync}`, while there is no principal. These trait objects /// have a "trivial" vtable consisting of just the size, alignment, /// and destructor. - pub fn principal(&self) -> Option> { - match self[0] { - ExistentialPredicate::Trait(tr) => Some(tr), - _ => None, - } + pub fn principal(&self) -> Option>> { + self[0] + .map_bound(|this| match this { + ExistentialPredicate::Trait(tr) => Some(tr), + _ => None, + }) + .transpose() } pub fn principal_def_id(&self) -> Option { - self.principal().map(|trait_ref| trait_ref.def_id) + self.principal().map(|trait_ref| trait_ref.skip_binder().def_id) } #[inline] pub fn projection_bounds<'a>( &'a self, - ) -> impl Iterator> + 'a { - self.iter().filter_map(|predicate| match predicate { - ExistentialPredicate::Projection(projection) => Some(projection), - _ => None, + ) -> impl Iterator>> + 'a { + self.iter().filter_map(|predicate| { + predicate + .map_bound(|pred| match pred { + ExistentialPredicate::Projection(projection) => Some(projection), + _ => None, + }) + .transpose() }) } #[inline] pub fn auto_traits<'a>(&'a self) -> impl Iterator + 'a { - self.iter().filter_map(|predicate| match predicate { + self.iter().filter_map(|predicate| match predicate.skip_binder() { ExistentialPredicate::AutoTrait(did) => Some(did), _ => None, }) } } -impl<'tcx> Binder<&'tcx List>> { - pub fn principal(&self) -> Option>> { - self.map_bound(|b| b.principal()).transpose() - } - - pub fn principal_def_id(&self) -> Option { - self.skip_binder().principal_def_id() - } - - #[inline] - pub fn projection_bounds<'a>( - &'a self, - ) -> impl Iterator> + 'a { - self.skip_binder().projection_bounds().map(Binder::bind) - } - - #[inline] - pub fn auto_traits<'a>(&'a self) -> impl Iterator + 'a { - self.skip_binder().auto_traits() - } - - pub fn iter<'a>( - &'a self, - ) -> impl DoubleEndedIterator>> + 'tcx { - self.skip_binder().iter().map(Binder::bind) - } -} - /// A complete reference to a trait. These take numerous guises in syntax, /// but perhaps the most recognizable form is in a where-clause: /// @@ -1289,53 +1274,6 @@ impl<'tcx> ParamConst { } } -rustc_index::newtype_index! { - /// A [De Bruijn index][dbi] is a standard means of representing - /// regions (and perhaps later types) in a higher-ranked setting. In - /// particular, imagine a type like this: - /// - /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) - /// ^ ^ | | | - /// | | | | | - /// | +------------+ 0 | | - /// | | | - /// +----------------------------------+ 1 | - /// | | - /// +----------------------------------------------+ 0 - /// - /// In this type, there are two binders (the outer fn and the inner - /// fn). We need to be able to determine, for any given region, which - /// fn type it is bound by, the inner or the outer one. There are - /// various ways you can do this, but a De Bruijn index is one of the - /// more convenient and has some nice properties. The basic idea is to - /// count the number of binders, inside out. Some examples should help - /// clarify what I mean. - /// - /// Let's start with the reference type `&'b isize` that is the first - /// argument to the inner function. This region `'b` is assigned a De - /// Bruijn index of 0, meaning "the innermost binder" (in this case, a - /// fn). The region `'a` that appears in the second argument type (`&'a - /// isize`) would then be assigned a De Bruijn index of 1, meaning "the - /// second-innermost binder". (These indices are written on the arrays - /// in the diagram). - /// - /// What is interesting is that De Bruijn index attached to a particular - /// variable will vary depending on where it appears. For example, - /// the final type `&'a char` also refers to the region `'a` declared on - /// the outermost fn. But this time, this reference is not nested within - /// any other binders (i.e., it is not an argument to the inner fn, but - /// rather the outer one). Therefore, in this case, it is assigned a - /// De Bruijn index of 0, because the innermost binder in that location - /// is the outer fn. - /// - /// [dbi]: https://en.wikipedia.org/wiki/De_Bruijn_index - #[derive(HashStable)] - pub struct DebruijnIndex { - DEBUG_FORMAT = "DebruijnIndex({})", - const INNERMOST = 0, - } -} - pub type Region<'tcx> = &'tcx RegionKind; /// Representation of regions. Note that the NLL checker uses a distinct @@ -1450,7 +1388,7 @@ pub enum RegionKind { /// Region bound in a function scope, which will be substituted when the /// function is called. - ReLateBound(DebruijnIndex, BoundRegion), + ReLateBound(ty::DebruijnIndex, BoundRegion), /// When checking a function body, the types of all arguments and so forth /// that refer to bound region parameters are modified to refer to free @@ -1486,28 +1424,33 @@ pub struct EarlyBoundRegion { pub name: Symbol, } +/// A **ty**pe **v**ariable **ID**. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct TyVid { pub index: u32, } +/// A **`const`** **v**ariable **ID**. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct ConstVid<'tcx> { pub index: u32, pub phantom: PhantomData<&'tcx ()>, } +/// An **int**egral (`u32`, `i32`, `usize`, etc.) type **v**ariable **ID**. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct IntVid { pub index: u32, } +/// An **float**ing-point (`f32` or `f64`) type **v**ariable **ID**. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct FloatVid { pub index: u32, } rustc_index::newtype_index! { + /// A **region** (lifetime) **v**ariable **ID**. pub struct RegionVid { DEBUG_FORMAT = custom, } @@ -1519,18 +1462,40 @@ impl Atom for RegionVid { } } +/// A placeholder for a type that hasn't been inferred yet. +/// +/// E.g., if we have an empty array (`[]`), then we create a fresh +/// type variable for the element type since we won't know until it's +/// used what the element type is supposed to be. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub enum InferTy { + /// A type variable. TyVar(TyVid), + /// An integral type variable (`{integer}`). + /// + /// These are created when the compiler sees an integer literal like + /// `1` that could be several different types (`u8`, `i32`, `u32`, etc.). + /// We don't know until it's used what type it's supposed to be, so + /// we create a fresh type variable. IntVar(IntVid), + /// A floating-point type variable (`{float}`). + /// + /// These are created when the compiler sees an float literal like + /// `1.0` that could be either an `f32` or an `f64`. + /// We don't know until it's used what type it's supposed to be, so + /// we create a fresh type variable. FloatVar(FloatVid), - /// A `FreshTy` is one that is generated as a replacement for an - /// unbound type variable. This is convenient for caching etc. See - /// `infer::freshen` for more details. + /// A [`FreshTy`][Self::FreshTy] is one that is generated as a replacement + /// for an unbound type variable. This is convenient for caching etc. See + /// `rustc_infer::infer::freshen` for more details. + /// + /// Compare with [`TyVar`][Self::TyVar]. FreshTy(u32), + /// Like [`FreshTy`][Self::FreshTy], but as a replacement for [`IntVar`][Self::IntVar]. FreshIntTy(u32), + /// Like [`FreshTy`][Self::FreshTy], but as a replacement for [`FloatVar`][Self::FloatVar]. FreshFloatTy(u32), } @@ -1614,72 +1579,13 @@ impl<'tcx> PolyExistentialProjection<'tcx> { } } -impl DebruijnIndex { - /// Returns the resulting index when this value is moved into - /// `amount` number of new binders. So, e.g., if you had - /// - /// for<'a> fn(&'a x) - /// - /// and you wanted to change it to - /// - /// for<'a> fn(for<'b> fn(&'a x)) - /// - /// you would need to shift the index for `'a` into a new binder. - #[must_use] - pub fn shifted_in(self, amount: u32) -> DebruijnIndex { - DebruijnIndex::from_u32(self.as_u32() + amount) - } - - /// Update this index in place by shifting it "in" through - /// `amount` number of binders. - pub fn shift_in(&mut self, amount: u32) { - *self = self.shifted_in(amount); - } - - /// Returns the resulting index when this value is moved out from - /// `amount` number of new binders. - #[must_use] - pub fn shifted_out(self, amount: u32) -> DebruijnIndex { - DebruijnIndex::from_u32(self.as_u32() - amount) - } - - /// Update in place by shifting out from `amount` binders. - pub fn shift_out(&mut self, amount: u32) { - *self = self.shifted_out(amount); - } - - /// Adjusts any De Bruijn indices so as to make `to_binder` the - /// innermost binder. That is, if we have something bound at `to_binder`, - /// it will now be bound at INNERMOST. This is an appropriate thing to do - /// when moving a region out from inside binders: - /// - /// ``` - /// for<'a> fn(for<'b> for<'c> fn(&'a u32), _) - /// // Binder: D3 D2 D1 ^^ - /// ``` - /// - /// Here, the region `'a` would have the De Bruijn index D3, - /// because it is the bound 3 binders out. However, if we wanted - /// to refer to that region `'a` in the second argument (the `_`), - /// those two binders would not be in scope. In that case, we - /// might invoke `shift_out_to_binder(D3)`. This would adjust the - /// De Bruijn index of `'a` to D1 (the innermost binder). - /// - /// If we invoke `shift_out_to_binder` and the region is in fact - /// bound by one of the binders we are shifting out of, that is an - /// error (and should fail an assertion failure). - pub fn shifted_out_to_binder(self, to_binder: DebruijnIndex) -> Self { - self.shifted_out(to_binder.as_u32() - INNERMOST.as_u32()) - } -} - /// Region utilities impl RegionKind { /// Is this region named by the user? pub fn has_name(&self) -> bool { match *self { RegionKind::ReEarlyBound(ebr) => ebr.has_name(), - RegionKind::ReLateBound(_, br) => br.is_named(), + RegionKind::ReLateBound(_, br) => br.kind.is_named(), RegionKind::ReFree(fr) => fr.bound_region.is_named(), RegionKind::ReStatic => true, RegionKind::ReVar(..) => false, @@ -1690,20 +1596,14 @@ impl RegionKind { } pub fn is_late_bound(&self) -> bool { - match *self { - ty::ReLateBound(..) => true, - _ => false, - } + matches!(*self, ty::ReLateBound(..)) } pub fn is_placeholder(&self) -> bool { - match *self { - ty::RePlaceholder(..) => true, - _ => false, - } + matches!(*self, ty::RePlaceholder(..)) } - pub fn bound_at_or_above_binder(&self, index: DebruijnIndex) -> bool { + pub fn bound_at_or_above_binder(&self, index: ty::DebruijnIndex) -> bool { match *self { ty::ReLateBound(debruijn, _) => debruijn >= index, _ => false, @@ -2174,6 +2074,15 @@ impl<'tcx> TyS<'tcx> { } } + /// Get the `i`-th element of a tuple. + /// Panics when called on anything but a tuple. + pub fn tuple_element_ty(&self, i: usize) -> Option> { + match self.kind() { + Tuple(substs) => substs.iter().nth(i).map(|field| field.expect_ty()), + _ => bug!("tuple_fields called on non-tuple"), + } + } + /// If the type contains variants, returns the valid range of variant indices. // // FIXME: This requires the optimized MIR in the case of generators. @@ -2213,13 +2122,44 @@ impl<'tcx> TyS<'tcx> { } /// Returns the type of the discriminant of this type. - pub fn discriminant_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + pub fn discriminant_ty(&'tcx self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match self.kind() { ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), - _ => { - // This can only be `0`, for now, so `u8` will suffice. - tcx.types.u8 + + ty::Param(_) | ty::Projection(_) | ty::Opaque(..) | ty::Infer(ty::TyVar(_)) => { + let assoc_items = + tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); + let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + tcx.mk_projection(discriminant_def_id, tcx.mk_substs([self.into()].iter())) + } + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Foreign(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::GeneratorWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Error(_) + | ty::Infer(IntVar(_) | FloatVar(_)) => tcx.types.u8, + + ty::Bound(..) + | ty::Placeholder(_) + | ty::Infer(FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("`discriminant_ty` applied to unexpected type: {:?}", self) } } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 25787f005aa2..a64580336ad6 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -503,7 +503,8 @@ impl<'tcx> TyCtxt<'tcx> { closure_substs: SubstsRef<'tcx>, ) -> Option>> { let closure_ty = self.mk_closure(closure_def_id, closure_substs); - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let br = ty::BoundRegion { kind: ty::BrEnv }; + let env_region = ty::ReLateBound(ty::INNERMOST, br); let closure_kind_ty = closure_substs.as_closure().kind_ty(); let closure_kind = closure_kind_ty.to_opt_closure_kind()?; let env_ty = match closure_kind { diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs index 0903ef508986..e79adcdb5459 100644 --- a/compiler/rustc_middle/src/util/bug.rs +++ b/compiler/rustc_middle/src/util/bug.rs @@ -21,6 +21,7 @@ pub fn span_bug_fmt>(span: S, args: fmt::Arguments<'_>) -> ! opt_span_bug_fmt(Some(span), args, Location::caller()); } +#[track_caller] fn opt_span_bug_fmt>( span: Option, args: fmt::Arguments<'_>, diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml index 9bfd1da03912..10dbf35fedcc 100644 --- a/compiler/rustc_mir/Cargo.toml +++ b/compiler/rustc_mir/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [dependencies] either = "1.5.0" rustc_graphviz = { path = "../rustc_graphviz" } +gsgdt = "0.1.2" itertools = "0.9" tracing = "0.1" polonius-engine = "0.12.0" diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 41f3edaa4138..4ebc1cdca605 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -496,7 +496,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // lifetimes without names with the value `'0`. match ty.kind() { ty::Ref( - ty::RegionKind::ReLateBound(_, br) + ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br }) | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), _, _, @@ -517,7 +517,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let region = match ty.kind() { ty::Ref(region, _, _) => { match region { - ty::RegionKind::ReLateBound(_, br) + ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br }) | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { printer.region_highlight_mode.highlighting_bound_region(*br, counter) } @@ -954,7 +954,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, def_id: DefId, target_place: PlaceRef<'tcx>, - places: &Vec>, + places: &[Operand<'tcx>], ) -> Option<(Span, Option, Span)> { debug!( "closure_span: def_id={:?} target_place={:?} places={:?}", diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs index e22dab015170..78da43c31c0f 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs @@ -138,7 +138,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// Returns `true` if a closure is inferred to be an `FnMut` closure. fn is_closure_fn_mut(&self, fr: RegionVid) -> bool { if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) { - if let ty::BoundRegion::BrEnv = free_region.bound_region { + if let ty::BoundRegionKind::BrEnv = free_region.bound_region { if let DefiningTy::Closure(_, substs) = self.regioncx.universal_regions().defining_ty { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs index 6211cf8a9da8..cbca012824f8 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs @@ -281,7 +281,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } ty::ReFree(free_region) => match free_region.bound_region { - ty::BoundRegion::BrNamed(region_def_id, name) => { + ty::BoundRegionKind::BrNamed(region_def_id, name) => { // Get the span to point to, even if we don't use the name. let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP); debug!( @@ -307,7 +307,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } } - ty::BoundRegion::BrEnv => { + ty::BoundRegionKind::BrEnv => { let def_ty = self.regioncx.universal_regions().defining_ty; if let DefiningTy::Closure(_, substs) = def_ty { @@ -349,7 +349,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } } - ty::BoundRegion::BrAnon(_) => None, + ty::BoundRegionKind::BrAnon(_) => None, }, ty::ReLateBound(..) diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index de54c5582e04..44044d55532d 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -9,9 +9,10 @@ use rustc_hir::{HirId, Node}; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, - PlaceRef, + PlaceRef, VarDebugInfoContents, }; use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; @@ -75,6 +76,7 @@ crate use region_infer::RegionInferenceContext; crate struct Upvar { name: Symbol, + // FIXME(project-rfc-2229#8): This should use Place or something similar var_hir_id: HirId, /// If true, the capture is behind a reference. @@ -133,19 +135,21 @@ fn do_mir_borrowck<'a, 'tcx>( let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); for var_debug_info in &input_body.var_debug_info { - if let Some(local) = var_debug_info.place.as_local() { - if let Some(prev_name) = local_names[local] { - if var_debug_info.name != prev_name { - span_bug!( - var_debug_info.source_info.span, - "local {:?} has many names (`{}` vs `{}`)", - local, - prev_name, - var_debug_info.name - ); + if let VarDebugInfoContents::Place(place) = var_debug_info.value { + if let Some(local) = place.as_local() { + if let Some(prev_name) = local_names[local] { + if var_debug_info.name != prev_name { + span_bug!( + var_debug_info.source_info.span, + "local {:?} has many names (`{}` vs `{}`)", + local, + prev_name, + var_debug_info.name + ); + } } + local_names[local] = Some(var_debug_info.name); } - local_names[local] = Some(var_debug_info.name); } } @@ -155,13 +159,13 @@ fn do_mir_borrowck<'a, 'tcx>( infcx.set_tainted_by_errors(); } let upvars: Vec<_> = tables - .closure_captures - .get(&def.did.to_def_id()) - .into_iter() - .flat_map(|v| v.values()) - .map(|upvar_id| { - let var_hir_id = upvar_id.var_path.hir_id; - let capture = tables.upvar_capture(*upvar_id); + .closure_min_captures_flattened(def.did.to_def_id()) + .map(|captured_place| { + let var_hir_id = match captured_place.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + _ => bug!("Expected upvar"), + }; + let capture = captured_place.info.capture_kind; let by_ref = match capture { ty::UpvarCapture::ByValue(_) => false, ty::UpvarCapture::ByRef(..) => true, diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs index 3586be2804d6..9d45f6fd0d34 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs @@ -1145,8 +1145,24 @@ impl<'tcx> RegionInferenceContext<'tcx> { for ur in self.scc_values.universal_regions_outlived_by(r_scc) { let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur); debug!("approx_universal_upper_bound: ur={:?} lub={:?} new_lub={:?}", ur, lub, new_lub); + // The upper bound of two non-static regions is static: this + // means we know nothing about the relationship between these + // two regions. Pick a 'better' one to use when constructing + // a diagnostic if ur != static_r && lub != static_r && new_lub == static_r { - lub = std::cmp::min(ur, lub); + // Prefer the region with an `external_name` - this + // indicates that the region is early-bound, so working with + // it can produce a nicer error. + if self.region_definition(ur).external_name.is_some() { + lub = ur; + } else if self.region_definition(lub).external_name.is_some() { + // Leave lub unchanged + } else { + // If we get here, we don't have any reason to prefer + // one region over the other. Just pick the + // one with the lower index for now. + lub = std::cmp::min(ur, lub); + } } else { lub = new_lub; } diff --git a/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs b/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs index 995e3a60a0c7..7e8a33efe114 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs @@ -58,11 +58,7 @@ impl vll::LinkElem for Appearance { } impl LocalUseMap { - crate fn build( - live_locals: &Vec, - elements: &RegionValueElements, - body: &Body<'_>, - ) -> Self { + crate fn build(live_locals: &[Local], elements: &RegionValueElements, body: &Body<'_>) -> Self { let nones = IndexVec::from_elem_n(None, body.local_decls.len()); let mut local_use_map = LocalUseMap { first_def_at: nones.clone(), diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index a5c45452dec8..42cd050abc5d 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -749,7 +749,11 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { (&adt_def.variants[VariantIdx::new(0)], substs) } ty::Closure(_, substs) => { - return match substs.as_closure().upvar_tys().nth(field.index()) { + return match substs + .as_closure() + .tupled_upvars_ty() + .tuple_element_ty(field.index()) + { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { field_count: substs.as_closure().upvar_tys().count(), @@ -1984,44 +1988,48 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // If the length is larger than 1, the repeat expression will need to copy the // element, so we require the `Copy` trait. if len.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) { - if let Operand::Move(_) = operand { - // While this is located in `nll::typeck` this error is not an NLL error, it's - // a required check to make sure that repeated elements implement `Copy`. - let span = body.source_info(location).span; - let ty = operand.ty(body, tcx); - if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { - let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env); - // To determine if `const_in_array_repeat_expressions` feature gate should - // be mentioned, need to check if the rvalue is promotable. - let should_suggest = - should_suggest_const_in_array_repeat_expressions_attribute( - &ccx, operand, - ); - debug!("check_rvalue: should_suggest={:?}", should_suggest); + match operand { + Operand::Copy(..) | Operand::Constant(..) => { + // These are always okay: direct use of a const, or a value that can evidently be copied. + } + Operand::Move(_) => { + // Make sure that repeated elements implement `Copy`. + let span = body.source_info(location).span; + let ty = operand.ty(body, tcx); + if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { + let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env); + // To determine if `const_in_array_repeat_expressions` feature gate should + // be mentioned, need to check if the rvalue is promotable. + let should_suggest = + should_suggest_const_in_array_repeat_expressions_attribute( + &ccx, operand, + ); + debug!("check_rvalue: should_suggest={:?}", should_suggest); - let def_id = body.source.def_id().expect_local(); - self.infcx.report_selection_error( - &traits::Obligation::new( - ObligationCause::new( - span, - self.tcx().hir().local_def_id_to_hir_id(def_id), - traits::ObligationCauseCode::RepeatVec(should_suggest), - ), - self.param_env, - ty::Binder::bind(ty::TraitRef::new( - self.tcx().require_lang_item( - LangItem::Copy, - Some(self.last_span), + let def_id = body.source.def_id().expect_local(); + self.infcx.report_selection_error( + &traits::Obligation::new( + ObligationCause::new( + span, + self.tcx().hir().local_def_id_to_hir_id(def_id), + traits::ObligationCauseCode::RepeatVec(should_suggest), ), - tcx.mk_substs_trait(ty, &[]), - )) - .without_const() - .to_predicate(self.tcx()), - ), - &traits::SelectionError::Unimplemented, - false, - false, - ); + self.param_env, + ty::Binder::bind(ty::TraitRef::new( + self.tcx().require_lang_item( + LangItem::Copy, + Some(self.last_span), + ), + tcx.mk_substs_trait(ty, &[]), + )) + .without_const() + .to_predicate(self.tcx()), + ), + &traits::SelectionError::Unimplemented, + false, + false, + ); + } } } } diff --git a/compiler/rustc_mir/src/borrow_check/universal_regions.rs b/compiler/rustc_mir/src/borrow_check/universal_regions.rs index 7ad38a1f82cd..c1a0d9856b7e 100644 --- a/compiler/rustc_mir/src/borrow_check/universal_regions.rs +++ b/compiler/rustc_mir/src/borrow_check/universal_regions.rs @@ -700,7 +700,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { debug!("replace_bound_regions_with_nll_infer_vars: br={:?}", br); let liberated_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion { scope: all_outlive_scope.to_def_id(), - bound_region: br, + bound_region: br.kind, })); let region_vid = self.next_nll_region_var(origin); indices.insert_late_bound_region(liberated_region, region_vid.to_region_vid()); @@ -795,7 +795,7 @@ fn for_each_late_bound_region_defined_on<'tcx>( let region_def_id = tcx.hir().local_def_id(hir_id); let liberated_region = tcx.mk_region(ty::ReFree(ty::FreeRegion { scope: fn_def_id, - bound_region: ty::BoundRegion::BrNamed(region_def_id.to_def_id(), name), + bound_region: ty::BoundRegionKind::BrNamed(region_def_id.to_def_id(), name), })); f(liberated_region); } diff --git a/compiler/rustc_mir/src/const_eval/error.rs b/compiler/rustc_mir/src/const_eval/error.rs index 39358e03e759..0e610e375522 100644 --- a/compiler/rustc_mir/src/const_eval/error.rs +++ b/compiler/rustc_mir/src/const_eval/error.rs @@ -20,6 +20,7 @@ pub enum ConstEvalErrKind { ModifiedGlobal, AssertFailure(AssertKind), Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, + Abort(String), } // The errors become `MachineStop` with plain strings when being raised. @@ -46,6 +47,7 @@ impl fmt::Display for ConstEvalErrKind { Panic { msg, line, col, file } => { write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col) } + Abort(ref msg) => write!(f, "{}", msg), } } } diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index 6e09ae434064..df163f656284 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -31,6 +31,19 @@ fn eval_body_using_ecx<'mir, 'tcx>( ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env); let tcx = *ecx.tcx; + assert!( + cid.promoted.is_some() + || matches!( + ecx.tcx.def_kind(cid.instance.def_id()), + DefKind::Const + | DefKind::Static + | DefKind::ConstParam + | DefKind::AnonConst + | DefKind::AssocConst + ), + "Unexpected DefKind: {:?}", + ecx.tcx.def_kind(cid.instance.def_id()) + ); let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?; assert!(!layout.is_unsized()); let ret = ecx.allocate(layout, MemoryKind::Stack); @@ -40,15 +53,6 @@ fn eval_body_using_ecx<'mir, 'tcx>( let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom); - // Assert all args (if any) are zero-sized types; `eval_body_using_ecx` doesn't - // make sense if the body is expecting nontrivial arguments. - // (The alternative would be to use `eval_fn_call` with an args slice.) - for arg in body.args_iter() { - let decl = body.local_decls.get(arg).expect("arg missing from local_decls"); - let layout = ecx.layout_of(decl.ty.subst(tcx, cid.instance.substs))?; - assert!(layout.is_zst()) - } - ecx.push_stack_frame( cid.instance, body, @@ -379,25 +383,19 @@ pub fn eval_to_allocation_raw_provider<'tcx>( Ok(mplace) => { // Since evaluation had no errors, valiate the resulting constant: let validation = try { - // FIXME do not validate promoteds until a decision on - // https://github.com/rust-lang/rust/issues/67465 and - // https://github.com/rust-lang/rust/issues/67534 is made. - // Promoteds can contain unexpected `UnsafeCell` and reference `static`s, but their - // otherwise restricted form ensures that this is still sound. We just lose the - // extra safety net of some of the dynamic checks. They can also contain invalid - // values, but since we do not usually check intermediate results of a computation - // for validity, it might be surprising to do that here. - if cid.promoted.is_none() { - let mut ref_tracking = RefTracking::new(mplace); - let mut inner = false; - while let Some((mplace, path)) = ref_tracking.todo.pop() { - let mode = match tcx.static_mutability(cid.instance.def_id()) { - Some(_) => CtfeValidationMode::Regular, // a `static` - None => CtfeValidationMode::Const { inner }, - }; - ecx.const_validate_operand(mplace.into(), path, &mut ref_tracking, mode)?; - inner = true; - } + let mut ref_tracking = RefTracking::new(mplace); + let mut inner = false; + while let Some((mplace, path)) = ref_tracking.todo.pop() { + let mode = match tcx.static_mutability(cid.instance.def_id()) { + Some(_) if cid.promoted.is_some() => { + // Promoteds in statics are allowed to point to statics. + CtfeValidationMode::Const { inner, allow_static_ptrs: true } + } + Some(_) => CtfeValidationMode::Regular, // a `static` + None => CtfeValidationMode::Const { inner, allow_static_ptrs: false }, + }; + ecx.const_validate_operand(mplace.into(), path, &mut ref_tracking, mode)?; + inner = true; } }; if let Err(error) = validation { diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index 187f6fab5181..72912dd76ff5 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -1,6 +1,4 @@ use rustc_middle::mir; -use rustc_middle::ty::layout::HasTyCtxt; -use rustc_middle::ty::InstanceDef; use rustc_middle::ty::{self, Ty}; use std::borrow::Borrow; use std::collections::hash_map::Entry; @@ -17,60 +15,13 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{Align, Size}; use crate::interpret::{ - self, compile_time_machine, AllocId, Allocation, Frame, GlobalId, ImmTy, InterpCx, - InterpResult, Memory, OpTy, PlaceTy, Pointer, Scalar, + self, compile_time_machine, AllocId, Allocation, Frame, ImmTy, InterpCx, InterpResult, Memory, + OpTy, PlaceTy, Pointer, Scalar, }; use super::error::*; impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { - /// Evaluate a const function where all arguments (if any) are zero-sized types. - /// The evaluation is memoized thanks to the query system. - /// - /// Returns `true` if the call has been evaluated. - fn try_eval_const_fn_call( - &mut self, - instance: ty::Instance<'tcx>, - ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, - args: &[OpTy<'tcx>], - ) -> InterpResult<'tcx, bool> { - trace!("try_eval_const_fn_call: {:?}", instance); - // Because `#[track_caller]` adds an implicit non-ZST argument, we also cannot - // perform this optimization on items tagged with it. - if instance.def.requires_caller_location(self.tcx()) { - return Ok(false); - } - // Only memoize instrinsics. This was added in #79594 while adding the `const_allocate` intrinsic. - // We only memoize intrinsics because it would be unsound to memoize functions - // which might interact with the heap. - // Additionally, const_allocate intrinsic is impure and thus should not be memoized; - // it will not be memoized because it has non-ZST args - if !matches!(instance.def, InstanceDef::Intrinsic(_)) { - return Ok(false); - } - // For the moment we only do this for functions which take no arguments - // (or all arguments are ZSTs) so that we don't memoize too much. - if args.iter().any(|a| !a.layout.is_zst()) { - return Ok(false); - } - - let dest = match ret { - Some((dest, _)) => dest, - // Don't memoize diverging function calls. - None => return Ok(false), - }; - - let gid = GlobalId { instance, promoted: None }; - - let place = self.eval_to_allocation(gid)?; - - self.copy_op(place.into(), dest)?; - - self.return_to_block(ret.map(|r| r.1))?; - trace!("{:?}", self.dump_place(*dest)); - Ok(true) - } - /// "Intercept" a function call to a panic-related function /// because we have something special to do for it. /// If this returns successfully (`Ok`), the function should just be evaluated normally. @@ -253,7 +204,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, + _ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, _unwind: Option, // unwinding is not supported in consts ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> { debug!("find_mir_or_eval_fn: {:?}", instance); @@ -263,13 +214,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, // Execution might have wandered off into other crates, so we cannot do a stability- // sensitive check here. But we can at least rule out functions that are not const // at all. - if ecx.tcx.is_const_fn_raw(def.did) { - // If this function is a `const fn` then under certain circumstances we - // can evaluate call via the query system, thus memoizing all future calls. - if ecx.try_eval_const_fn_call(instance, ret, args)? { - return Ok(None); - } - } else { + if !ecx.tcx.is_const_fn_raw(def.did) { // Some functions we support even if they are non-const -- but avoid testing // that for const fn! ecx.hook_panic_fn(instance, args)?; @@ -384,6 +329,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, Err(ConstEvalErrKind::AssertFailure(err).into()) } + fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: String) -> InterpResult<'tcx, !> { + Err(ConstEvalErrKind::Abort(msg).into()) + } + fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> { Err(ConstEvalErrKind::NeedsRfc("pointer-to-integer cast".to_string()).into()) } diff --git a/compiler/rustc_mir/src/dataflow/impls/liveness.rs b/compiler/rustc_mir/src/dataflow/impls/liveness.rs index a2b0713cd7d0..85aaff5ab729 100644 --- a/compiler/rustc_mir/src/dataflow/impls/liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/liveness.rs @@ -11,7 +11,7 @@ use crate::dataflow::{AnalysisDomain, Backward, GenKill, GenKillAnalysis}; /// exist. See [this `mir-dataflow` test][flow-test] for an example. You almost never want to use /// this analysis without also looking at the results of [`MaybeBorrowedLocals`]. /// -/// [`MaybeBorrowedLocals`]: ../struct.MaybeBorrowedLocals.html +/// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals /// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis pub struct MaybeLiveLocals; diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index d48b7fb3b924..3d955576f0ff 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -840,36 +840,31 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } - /// Mark a storage as live, killing the previous content and returning it. - /// Remember to deallocate that! - pub fn storage_live( - &mut self, - local: mir::Local, - ) -> InterpResult<'tcx, LocalValue> { + /// Mark a storage as live, killing the previous content. + pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> { assert!(local != mir::RETURN_PLACE, "Cannot make return place live"); trace!("{:?} is now live", local); let local_val = LocalValue::Uninitialized; - // StorageLive *always* kills the value that's currently stored. - // However, we do not error if the variable already is live; - // see . - Ok(mem::replace(&mut self.frame_mut().locals[local].value, local_val)) + // StorageLive expects the local to be dead, and marks it live. + let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); + if !matches!(old, LocalValue::Dead) { + throw_ub_format!("StorageLive on a local that was already live"); + } + Ok(()) } - /// Returns the old value of the local. - /// Remember to deallocate that! - pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue { + pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> { assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); trace!("{:?} is now dead", local); - mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead) + // It is entirely okay for this local to be already dead (at least that's how we currently generate MIR) + let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); + self.deallocate_local(old)?; + Ok(()) } - pub(super) fn deallocate_local( - &mut self, - local: LocalValue, - ) -> InterpResult<'tcx> { - // FIXME: should we tell the user that there was a local which was never written to? + fn deallocate_local(&mut self, local: LocalValue) -> InterpResult<'tcx> { if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { // All locals have a backing allocation, even if the allocation is empty // due to the local having ZST type. diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index f666a89ca56d..58858c09f44e 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -61,12 +61,11 @@ crate fn eval_nullary_intrinsic<'tcx>( ConstValue::Slice { data: alloc, start: 0, end: alloc.len() } } sym::needs_drop => ConstValue::from_bool(tp_ty.needs_drop(tcx, param_env)), - sym::size_of | sym::min_align_of | sym::pref_align_of => { + sym::min_align_of | sym::pref_align_of => { let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?; let n = match name { sym::pref_align_of => layout.align.pref.bytes(), sym::min_align_of => layout.align.abi.bytes(), - sym::size_of => layout.size.bytes(), _ => bug!(), }; ConstValue::from_machine_usize(n, &tcx) @@ -125,8 +124,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (dest, ret) = match ret { None => match intrinsic_name { sym::transmute => throw_ub_format!("transmuting to uninhabited type"), - sym::unreachable => throw_ub!(Unreachable), - sym::abort => M::abort(self)?, + sym::abort => M::abort(self, "the program aborted execution".to_owned())?, // Unsupported diverging intrinsic. _ => return Ok(false), }, @@ -143,9 +141,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::min_align_of_val | sym::size_of_val => { - let place = self.deref_operand(args[0])?; + // Avoid `deref_operand` -- this is not a deref, the ptr does not have to be + // dereferencable! + let place = self.ref_to_mplace(self.read_immediate(args[0])?)?; let (size, align) = self - .size_and_align_of(place.meta, place.layout)? + .size_and_align_of_mplace(place)? .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?; let result = match intrinsic_name { @@ -160,13 +160,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::min_align_of | sym::pref_align_of | sym::needs_drop - | sym::size_of | sym::type_id | sym::type_name | sym::variant_count => { let gid = GlobalId { instance, promoted: None }; let ty = match intrinsic_name { - sym::min_align_of | sym::pref_align_of | sym::size_of | sym::variant_count => { + sym::min_align_of | sym::pref_align_of | sym::variant_count => { self.tcx.types.usize } sym::needs_drop => self.tcx.types.bool, @@ -212,28 +211,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let out_val = numeric_intrinsic(intrinsic_name, bits, kind)?; self.write_scalar(out_val, dest)?; } - sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul - | sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow => { + sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { let lhs = self.read_immediate(args[0])?; let rhs = self.read_immediate(args[1])?; - let (bin_op, ignore_overflow) = match intrinsic_name { - sym::wrapping_add => (BinOp::Add, true), - sym::wrapping_sub => (BinOp::Sub, true), - sym::wrapping_mul => (BinOp::Mul, true), - sym::add_with_overflow => (BinOp::Add, false), - sym::sub_with_overflow => (BinOp::Sub, false), - sym::mul_with_overflow => (BinOp::Mul, false), + let bin_op = match intrinsic_name { + sym::add_with_overflow => BinOp::Add, + sym::sub_with_overflow => BinOp::Sub, + sym::mul_with_overflow => BinOp::Mul, _ => bug!("Already checked for int ops"), }; - if ignore_overflow { - self.binop_ignore_overflow(bin_op, lhs, rhs, dest)?; - } else { - self.binop_with_overflow(bin_op, lhs, rhs, dest)?; - } + self.binop_with_overflow(bin_op, lhs, rhs, dest)?; } sym::saturating_add | sym::saturating_sub => { let l = self.read_immediate(args[0])?; @@ -337,6 +324,29 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = Scalar::from_uint(truncated_bits, layout.size); self.write_scalar(result, dest)?; } + sym::copy | sym::copy_nonoverlapping => { + let elem_ty = instance.substs.type_at(0); + let elem_layout = self.layout_of(elem_ty)?; + let count = self.read_scalar(args[2])?.to_machine_usize(self)?; + let elem_align = elem_layout.align.abi; + + let size = elem_layout.size.checked_mul(count, self).ok_or_else(|| { + err_ub_format!("overflow computing total size of `{}`", intrinsic_name) + })?; + let src = self.read_scalar(args[0])?.check_init()?; + let src = self.memory.check_ptr_access(src, size, elem_align)?; + let dest = self.read_scalar(args[1])?.check_init()?; + let dest = self.memory.check_ptr_access(dest, size, elem_align)?; + + if let (Some(src), Some(dest)) = (src, dest) { + self.memory.copy( + src, + dest, + size, + intrinsic_name == sym::copy_nonoverlapping, + )?; + } + } sym::offset => { let ptr = self.read_scalar(args[0])?.check_init()?; let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; @@ -407,6 +417,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::transmute => { self.copy_op_transmute(args[0], dest)?; } + sym::assert_inhabited => { + let ty = instance.substs.type_at(0); + let layout = self.layout_of(ty)?; + + if layout.abi.is_uninhabited() { + // The run-time intrinsic panics just to get a good backtrace; here we abort + // since there is no problem showing a backtrace even for aborts. + M::abort( + self, + format!( + "aborted execution: attempted to instantiate uninhabited type `{}`", + ty + ), + )?; + } + } sym::simd_insert => { let index = u64::from(self.read_scalar(args[1])?.to_u32()?); let elem = args[2]; diff --git a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs index 554ada1ab254..e1ec4cc5e973 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs @@ -74,7 +74,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { fn print_dyn_existential( mut self, - predicates: &'tcx ty::List>, + predicates: &'tcx ty::List>>, ) -> Result { let mut first = true; for p in predicates { diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index 0bba02737722..f50cc6c16ea1 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -9,6 +9,7 @@ use std::hash::Hash; use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefId; +use rustc_target::abi::Size; use super::{ AllocId, Allocation, AllocationExtra, CheckInAllocMsg, Frame, ImmTy, InterpCx, InterpResult, @@ -176,7 +177,7 @@ pub trait Machine<'mir, 'tcx>: Sized { ) -> InterpResult<'tcx>; /// Called to evaluate `Abort` MIR terminator. - fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, !> { + fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _msg: String) -> InterpResult<'tcx, !> { throw_unsup_format!("aborting execution is not supported") } @@ -299,6 +300,15 @@ pub trait Machine<'mir, 'tcx>: Sized { Ok(()) } + /// Called after initializing static memory using the interpreter. + fn after_static_mem_initialized( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: Pointer, + _size: Size, + ) -> InterpResult<'tcx> { + Ok(()) + } + /// Executes a retagging operation #[inline] fn retag( diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs index 156da84f2910..95738db1f553 100644 --- a/compiler/rustc_mir/src/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -95,14 +95,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Mark locals as alive StorageLive(local) => { - let old_val = self.storage_live(*local)?; - self.deallocate_local(old_val)?; + self.storage_live(*local)?; } // Mark locals as dead StorageDead(local) => { - let old_val = self.storage_dead(*local); - self.deallocate_local(old_val)?; + self.storage_dead(*local)?; } // No dynamic semantics attached to `FakeRead`; MIR diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index bb11c2a23bd8..a2931325a286 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -110,7 +110,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Abort => { - M::abort(self)?; + M::abort(self, "the program aborted execution".to_owned())?; } // When we encounter Resume, we've finished unwinding diff --git a/compiler/rustc_mir/src/interpret/traits.rs b/compiler/rustc_mir/src/interpret/traits.rs index fa7036f4e5b0..09ce6bc0fb75 100644 --- a/compiler/rustc_mir/src/interpret/traits.rs +++ b/compiler/rustc_mir/src/interpret/traits.rs @@ -56,11 +56,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // If you touch this code, be sure to also make the corresponding changes to // `get_vtable` in `rust_codegen_llvm/meth.rs`. // ///////////////////////////////////////////////////////////////////////////////////////// - let vtable = self.memory.allocate( - ptr_size * u64::try_from(methods.len()).unwrap().checked_add(3).unwrap(), - ptr_align, - MemoryKind::Vtable, - ); + let vtable_size = ptr_size * u64::try_from(methods.len()).unwrap().checked_add(3).unwrap(); + let vtable = self.memory.allocate(vtable_size, ptr_align, MemoryKind::Vtable); let drop = Instance::resolve_drop_in_place(tcx, ty); let drop = self.memory.create_fn_alloc(FnVal::Instance(drop)); @@ -93,6 +90,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + M::after_static_mem_initialized(self, vtable, vtable_size)?; + self.memory.mark_immutable(vtable.alloc_id)?; assert!(self.vtables.insert((ty, poly_trait_ref), vtable).is_none()); diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 2d235d65c4d3..423d1270ac86 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -117,11 +117,12 @@ pub enum PathElem { pub enum CtfeValidationMode { /// Regular validation, nothing special happening. Regular, - /// Validation of a `const`. `inner` says if this is an inner, indirect allocation (as opposed - /// to the top-level const allocation). - /// Being an inner allocation makes a difference because the top-level allocation of a `const` - /// is copied for each use, but the inner allocations are implicitly shared. - Const { inner: bool }, + /// Validation of a `const`. + /// `inner` says if this is an inner, indirect allocation (as opposed to the top-level const + /// allocation). Being an inner allocation makes a difference because the top-level allocation + /// of a `const` is copied for each use, but the inner allocations are implicitly shared. + /// `allow_static_ptrs` says if pointers to statics are permitted (which is the case for promoteds in statics). + Const { inner: bool, allow_static_ptrs: bool }, } /// State for tracking recursive validation of references @@ -152,7 +153,7 @@ impl RefTracking } /// Format a path -fn write_path(out: &mut String, path: &Vec) { +fn write_path(out: &mut String, path: &[PathElem]) { use self::PathElem::*; for elem in path.iter() { @@ -390,7 +391,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } // Make sure this is dereferenceable and all. let size_and_align = try_validation!( - self.ecx.size_and_align_of(place.meta, place.layout), + self.ecx.size_and_align_of_mplace(place), self.path, err_ub!(InvalidMeta(msg)) => { "invalid {} metadata: {}", kind, msg }, ); @@ -437,7 +438,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' if let Some(GlobalAlloc::Static(did)) = alloc_kind { assert!(!self.ecx.tcx.is_thread_local_static(did)); assert!(self.ecx.tcx.is_static(did)); - if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) { + if matches!( + self.ctfe_mode, + Some(CtfeValidationMode::Const { allow_static_ptrs: false, .. }) + ) { // See const_eval::machine::MemoryExtra::can_access_statics for why // this check is so important. // This check is reachable when the const just referenced the static, @@ -742,9 +746,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // Sanity check: `builtin_deref` does not know any pointers that are not primitive. assert!(op.layout.ty.builtin_deref(true).is_none()); - // Special check preventing `UnsafeCell` in constants + // Special check preventing `UnsafeCell` in the inner part of constants if let Some(def) = op.layout.ty.ty_adt_def() { - if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true })) + if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) && Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() { throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" }); diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs index 2ed115b12971..e6d822086f52 100644 --- a/compiler/rustc_mir/src/lib.rs +++ b/compiler/rustc_mir/src/lib.rs @@ -28,6 +28,7 @@ Rust MIR: a lowered representation of Rust. #![feature(or_patterns)] #![feature(once_cell)] #![feature(control_flow_enum)] +#![feature(str_split_once)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs index a84570432786..e2d50ba034ad 100644 --- a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -66,12 +66,14 @@ impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> { location: Location, decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>, ) { - // Don't lint on borrowing/assigning to a dereference - // e.g: + // Don't lint on borrowing/assigning when a dereference is involved. + // If we 'leave' the temporary via a dereference, we must + // be modifying something else // // `unsafe { *FOO = 0; *BAR.field = 1; }` // `unsafe { &mut *FOO }` - if !matches!(place.projection.last(), Some(PlaceElem::Deref)) { + // `unsafe { (*ARRAY)[0] = val; } + if !place.projection.iter().any(|p| matches!(p, PlaceElem::Deref)) { let source_info = self.body.source_info(location); let lint_root = self.body.source_scopes[source_info.scope] .local_data diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index d00038f345c9..90688ebbd0ac 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -722,17 +722,16 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { trace!("visit_statement: statement={:?} location={:?}", statement, location); - match statement.kind { - StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } => { - self.super_statement(statement, location); - } + self.super_statement(statement, location); + match statement.kind { StatementKind::LlvmInlineAsm { .. } => { - self.super_statement(statement, location); self.check_op(ops::InlineAsm); } - StatementKind::FakeRead(..) + StatementKind::Assign(..) + | StatementKind::SetDiscriminant { .. } + | StatementKind::FakeRead(..) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Retag { .. } diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs index acec3e8f82f4..e64955c4986c 100644 --- a/compiler/rustc_mir/src/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -181,6 +181,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use()); } + // Check for borrows to packed fields. + // `is_disaligned` already traverses the place to consider all projections after the last + // `Deref`, so this only needs to be called once at the top level. if context.is_borrow() { if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { self.require_unsafe( @@ -190,87 +193,105 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } } - for (i, elem) in place.projection.iter().enumerate() { - let proj_base = &place.projection[..i]; - if context.is_borrow() { - if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { + // Some checks below need the extra metainfo of the local declaration. + let decl = &self.body.local_decls[place.local]; + + // Check the base local: it might be an unsafe-to-access static. We only check derefs of the + // temporary holding the static pointer to avoid duplicate errors + // . + if decl.internal && place.projection.first() == Some(&ProjectionElem::Deref) { + // If the projection root is an artifical local that we introduced when + // desugaring `static`, give a more specific error message + // (avoid the general "raw pointer" clause below, that would only be confusing). + if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { + if self.tcx.is_mutable_static(def_id) { self.require_unsafe( - UnsafetyViolationKind::BorrowPacked, - UnsafetyViolationDetails::BorrowOfPackedField, + UnsafetyViolationKind::General, + UnsafetyViolationDetails::UseOfMutableStatic, ); + return; + } else if self.tcx.is_foreign_item(def_id) { + self.require_unsafe( + UnsafetyViolationKind::General, + UnsafetyViolationDetails::UseOfExternStatic, + ); + return; } } - let source_info = self.source_info; - if let [] = proj_base { - let decl = &self.body.local_decls[place.local]; - if decl.internal { - // If the projection root is an artifical local that we introduced when - // desugaring `static`, give a more specific error message - // (avoid the general "raw pointer" clause below, that would only be confusing). - if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { - if self.tcx.is_mutable_static(def_id) { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfMutableStatic, - ); - return; - } else if self.tcx.is_foreign_item(def_id) { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfExternStatic, - ); - return; - } + } + + // Check for raw pointer `Deref`. + for (base, proj) in place.iter_projections() { + if proj == ProjectionElem::Deref { + let source_info = self.source_info; // Backup source_info so we can restore it later. + if base.projection.is_empty() && decl.internal { + // Internal locals are used in the `move_val_init` desugaring. + // We want to check unsafety against the source info of the + // desugaring, rather than the source info of the RHS. + self.source_info = self.body.local_decls[place.local].source_info; + } + let base_ty = base.ty(self.body, self.tcx).ty; + if base_ty.is_unsafe_ptr() { + self.require_unsafe( + UnsafetyViolationKind::GeneralAndConstFn, + UnsafetyViolationDetails::DerefOfRawPointer, + ) + } + self.source_info = source_info; // Restore backed-up source_info. + } + } + + // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes + // whether we *read* the union field or potentially *write* to it (if this place is being assigned to). + let mut saw_deref = false; + for (base, proj) in place.iter_projections().rev() { + if proj == ProjectionElem::Deref { + saw_deref = true; + continue; + } + + let base_ty = base.ty(self.body, self.tcx).ty; + if base_ty.ty_adt_def().map_or(false, |adt| adt.is_union()) { + // If we did not hit a `Deref` yet and the overall place use is an assignment, the + // rules are different. + let assign_to_field = !saw_deref + && matches!( + context, + PlaceContext::MutatingUse( + MutatingUseContext::Store + | MutatingUseContext::Drop + | MutatingUseContext::AsmOutput + ) + ); + // If this is just an assignment, determine if the assigned type needs dropping. + if assign_to_field { + // We have to check the actual type of the assignment, as that determines if the + // old value is being dropped. + let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty; + // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping. + let manually_drop = assigned_ty + .ty_adt_def() + .map_or(false, |adt_def| adt_def.is_manually_drop()); + let nodrop = manually_drop + || assigned_ty.is_copy_modulo_regions( + self.tcx.at(self.source_info.span), + self.param_env, + ); + if !nodrop { + self.require_unsafe( + UnsafetyViolationKind::GeneralAndConstFn, + UnsafetyViolationDetails::AssignToDroppingUnionField, + ); } else { - // Internal locals are used in the `move_val_init` desugaring. - // We want to check unsafety against the source info of the - // desugaring, rather than the source info of the RHS. - self.source_info = self.body.local_decls[place.local].source_info; + // write to non-drop union field, safe } + } else { + self.require_unsafe( + UnsafetyViolationKind::GeneralAndConstFn, + UnsafetyViolationDetails::AccessToUnionField, + ) } } - let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; - match base_ty.kind() { - ty::RawPtr(..) => self.require_unsafe( - UnsafetyViolationKind::GeneralAndConstFn, - UnsafetyViolationDetails::DerefOfRawPointer, - ), - ty::Adt(adt, _) => { - if adt.is_union() { - if context == PlaceContext::MutatingUse(MutatingUseContext::Store) - || context == PlaceContext::MutatingUse(MutatingUseContext::Drop) - || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) - { - let elem_ty = match elem { - ProjectionElem::Field(_, ty) => ty, - _ => span_bug!( - self.source_info.span, - "non-field projection {:?} from union?", - place - ), - }; - if !elem_ty.is_copy_modulo_regions( - self.tcx.at(self.source_info.span), - self.param_env, - ) { - self.require_unsafe( - UnsafetyViolationKind::GeneralAndConstFn, - UnsafetyViolationDetails::AssignToNonCopyUnionField, - ) - } else { - // write to non-move union, safe - } - } else { - self.require_unsafe( - UnsafetyViolationKind::GeneralAndConstFn, - UnsafetyViolationDetails::AccessToUnionField, - ) - } - } - } - _ => {} - } - self.source_info = source_info; } } } diff --git a/compiler/rustc_mir/src/transform/const_debuginfo.rs b/compiler/rustc_mir/src/transform/const_debuginfo.rs new file mode 100644 index 000000000000..3cdaf4c7dcd4 --- /dev/null +++ b/compiler/rustc_mir/src/transform/const_debuginfo.rs @@ -0,0 +1,102 @@ +//! Finds locals which are assigned once to a const and unused except for debuginfo and converts +//! their debuginfo to use the const directly, allowing the local to be removed. + +use rustc_middle::{ + mir::{ + visit::{PlaceContext, Visitor}, + Body, Constant, Local, Location, Operand, Rvalue, StatementKind, VarDebugInfoContents, + }, + ty::TyCtxt, +}; + +use crate::transform::MirPass; +use rustc_index::{bit_set::BitSet, vec::IndexVec}; + +pub struct ConstDebugInfo; + +impl<'tcx> MirPass<'tcx> for ConstDebugInfo { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + if !tcx.sess.opts.debugging_opts.unsound_mir_opts { + return; + } + + trace!("running ConstDebugInfo on {:?}", body.source); + + for (local, constant) in find_optimization_oportunities(body) { + for debuginfo in &mut body.var_debug_info { + if let VarDebugInfoContents::Place(p) = debuginfo.value { + if p.local == local && p.projection.is_empty() { + trace!( + "changing debug info for {:?} from place {:?} to constant {:?}", + debuginfo.name, + p, + constant + ); + debuginfo.value = VarDebugInfoContents::Const(constant); + } + } + } + } + } +} + +struct LocalUseVisitor { + local_mutating_uses: IndexVec, + local_assignment_locations: IndexVec>, +} + +fn find_optimization_oportunities<'tcx>(body: &Body<'tcx>) -> Vec<(Local, Constant<'tcx>)> { + let mut visitor = LocalUseVisitor { + local_mutating_uses: IndexVec::from_elem(0, &body.local_decls), + local_assignment_locations: IndexVec::from_elem(None, &body.local_decls), + }; + + visitor.visit_body(body); + + let mut locals_to_debuginfo = BitSet::new_empty(body.local_decls.len()); + for debuginfo in &body.var_debug_info { + if let VarDebugInfoContents::Place(p) = debuginfo.value { + if let Some(l) = p.as_local() { + locals_to_debuginfo.insert(l); + } + } + } + + let mut eligable_locals = Vec::new(); + for (local, mutating_uses) in visitor.local_mutating_uses.drain_enumerated(..) { + if mutating_uses != 1 || !locals_to_debuginfo.contains(local) { + continue; + } + + if let Some(location) = visitor.local_assignment_locations[local] { + let bb = &body[location.block]; + + // The value is assigned as the result of a call, not a constant + if bb.statements.len() == location.statement_index { + continue; + } + + if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(box c)))) = + &bb.statements[location.statement_index].kind + { + if let Some(local) = p.as_local() { + eligable_locals.push((local, *c)); + } + } + } + } + + eligable_locals +} + +impl<'tcx> Visitor<'tcx> for LocalUseVisitor { + fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) { + if context.is_mutating_use() { + self.local_mutating_uses[*local] = self.local_mutating_uses[*local].saturating_add(1); + + if context.is_place_assignment() { + self.local_assignment_locations[*local] = Some(location); + } + } + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs index 20f6a16e0f75..b5921aac5614 100644 --- a/compiler/rustc_mir/src/transform/coverage/counters.rs +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -140,7 +140,7 @@ impl<'a> BcbCounters<'a> { /// message for subsequent debugging. fn make_bcb_counters( &mut self, - coverage_spans: &Vec, + coverage_spans: &[CoverageSpan], ) -> Result, Error> { debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); let num_bcbs = self.basic_coverage_blocks.num_nodes(); @@ -465,7 +465,7 @@ impl<'a> BcbCounters<'a> { fn choose_preferred_expression_branch( &self, traversal: &TraverseCoverageGraphWithLoops, - branches: &Vec, + branches: &[BcbBranch], ) -> BcbBranch { let branch_needs_a_counter = |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); @@ -509,7 +509,7 @@ impl<'a> BcbCounters<'a> { fn find_some_reloop_branch( &self, traversal: &TraverseCoverageGraphWithLoops, - branches: &Vec, + branches: &[BcbBranch], ) -> Option { let branch_needs_a_counter = |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index e9528557b333..b66e37436a66 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -148,40 +148,46 @@ impl DebugOptions { if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(',') { - let mut setting = setting_str.splitn(2, '='); - match setting.next() { - Some(option) if option == "allow_unused_expressions" => { - allow_unused_expressions = bool_option_val(option, setting.next()); + let (option, value) = match setting_str.split_once('=') { + None => (setting_str, None), + Some((k, v)) => (k, Some(v)), + }; + match option { + "allow_unused_expressions" => { + allow_unused_expressions = bool_option_val(option, value); debug!( "{} env option `allow_unused_expressions` is set to {}", RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions ); } - Some(option) if option == "counter_format" => { - if let Some(strval) = setting.next() { - counter_format = counter_format_option_val(strval); - debug!( - "{} env option `counter_format` is set to {:?}", - RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format - ); - } else { - bug!( - "`{}` option in environment variable {} requires one or more \ - plus-separated choices (a non-empty subset of \ - `id+block+operation`)", - option, - RUSTC_COVERAGE_DEBUG_OPTIONS - ); - } + "counter_format" => { + match value { + None => { + bug!( + "`{}` option in environment variable {} requires one or more \ + plus-separated choices (a non-empty subset of \ + `id+block+operation`)", + option, + RUSTC_COVERAGE_DEBUG_OPTIONS + ); + } + Some(val) => { + counter_format = counter_format_option_val(val); + debug!( + "{} env option `counter_format` is set to {:?}", + RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format + ); + } + }; } - Some("") => {} - Some(invalid) => bug!( - "Unsupported setting `{}` in environment variable {}", - invalid, - RUSTC_COVERAGE_DEBUG_OPTIONS - ), - None => {} - } + _ => { + bug!( + "Unsupported setting `{}` in environment variable {}", + option, + RUSTC_COVERAGE_DEBUG_OPTIONS + ) + } + }; } } @@ -279,7 +285,7 @@ impl DebugCounters { ), }; counters - .insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)) + .insert(id, DebugCounter::new(counter_kind.clone(), some_block_label)) .expect_none( "attempt to add the same counter_kind to DebugCounters more than once", ); @@ -334,7 +340,7 @@ impl DebugCounters { if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { let counters = self.some_counters.as_ref().unwrap(); if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = - counters.get(&id.into()) + counters.get(&id) { return if counter_format.id { format!("{}#{}", block_label, id.index()) diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index 2408a999c05a..b1a1bb957e79 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -32,24 +32,28 @@ impl CoverageGraph { // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the - // each predecessor of a BCB leader_bb should be in a unique BCB, and each successor of a - // BCB last_bb should be in its own unique BCB. Therefore, collecting the BCBs using - // `bb_to_bcb` should work without requiring a deduplication step. + // each predecessor of a BCB leader_bb should be in a unique BCB. It is possible for a + // `SwitchInt` to have multiple targets to the same destination `BasicBlock`, so + // de-duplication is required. This is done without reordering the successors. + let bcbs_len = bcbs.len(); + let mut seen = IndexVec::from_elem_n(false, bcbs_len); let successors = IndexVec::from_fn_n( |bcb| { + for b in seen.iter_mut() { + *b = false; + } let bcb_data = &bcbs[bcb]; - let bcb_successors = + let mut bcb_successors = Vec::new(); + for successor in bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind) .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) - .collect::>(); - debug_assert!({ - let mut sorted = bcb_successors.clone(); - sorted.sort_unstable(); - let initial_len = sorted.len(); - sorted.dedup(); - sorted.len() == initial_len - }); + { + if !seen[successor] { + seen[successor] = true; + bcb_successors.push(successor); + } + } bcb_successors }, bcbs.len(), diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index f69748db238c..93133e9b7f06 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -30,6 +30,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; +use rustc_span::source_map::SourceMap; use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; /// A simple error message wrapper for `coverage::Error`s. @@ -78,6 +79,14 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { return; } + match mir_body.basic_blocks()[mir::START_BLOCK].terminator().kind { + TerminatorKind::Unreachable => { + trace!("InstrumentCoverage skipped for unreachable `START_BLOCK`"); + return; + } + _ => {} + } + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); Instrumentor::new(&self.name(), tcx, mir_body).inject_counters(); trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); @@ -302,8 +311,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { inject_statement( self.mir_body, counter_kind, - self.bcb_last_bb(bcb), - Some(make_code_region(file_name, &self.source_file, span, body_span)), + self.bcb_leader_bb(bcb), + Some(make_code_region(source_map, file_name, &self.source_file, span, body_span)), ); } } @@ -462,7 +471,7 @@ fn inject_statement( code_region: some_code_region, }), }; - data.statements.push(statement); + data.statements.insert(0, statement); } // Non-code expressions are injected into the coverage map, without generating executable code. @@ -481,6 +490,7 @@ fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: Co /// Convert the Span into its file name, start line and column, and end line and column fn make_code_region( + source_map: &SourceMap, file_name: Symbol, source_file: &Lrc, span: Span, @@ -500,6 +510,8 @@ fn make_code_region( } else { source_file.lookup_file_pos(span.hi()) }; + let start_line = source_map.doctest_offset_line(&source_file.name, start_line); + let end_line = source_map.doctest_offset_line(&source_file.name, end_line); CodeRegion { file_name, start_line: start_line as u32, diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml b/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml index a9d6f0c803d2..eda1ced001ca 100644 --- a/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml @@ -7,6 +7,3 @@ edition = "2018" [lib] proc-macro = true doctest = false - -[dependencies] -proc-macro2 = "1" diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs index f91477911a48..b16a99d7f0dc 100644 --- a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs +++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs @@ -216,9 +216,10 @@ impl<'a, 'tcx> Helper<'a, 'tcx> { let discr = self.find_switch_discriminant_info(bb, switch)?; // go through each target, finding a discriminant read, and a switch - let results = discr.targets_with_values.iter().map(|(value, target)| { - self.find_discriminant_switch_pairing(&discr, target.clone(), value.clone()) - }); + let results = discr + .targets_with_values + .iter() + .map(|(value, target)| self.find_discriminant_switch_pairing(&discr, *target, *value)); // if the optimization did not apply for one of the targets, then abort if results.clone().any(|x| x.is_none()) || results.len() == 0 { @@ -283,6 +284,33 @@ impl<'a, 'tcx> Helper<'a, 'tcx> { return None; } + // when the second place is a projection of the first one, it's not safe to calculate their discriminant values sequentially. + // for example, this should not be optimized: + // + // ```rust + // enum E<'a> { Empty, Some(&'a E<'a>), } + // let Some(Some(_)) = e; + // ``` + // + // ```mir + // bb0: { + // _2 = discriminant(*_1) + // switchInt(_2) -> [...] + // } + // bb1: { + // _3 = discriminant(*(((*_1) as Some).0: &E)) + // switchInt(_3) -> [...] + // } + // ``` + let discr_place = discr_info.place_of_adt_discr_read; + let this_discr_place = this_bb_discr_info.place_of_adt_discr_read; + if discr_place.local == this_discr_place.local + && this_discr_place.projection.starts_with(discr_place.projection) + { + trace!("NO: one target is the projection of another"); + return None; + } + // if we reach this point, the optimization applies, and we should be able to optimize this case // store the info that is needed to apply the optimization diff --git a/compiler/rustc_mir/src/transform/function_item_references.rs b/compiler/rustc_mir/src/transform/function_item_references.rs index d592580af9ce..7c8c349da1d6 100644 --- a/compiler/rustc_mir/src/transform/function_item_references.rs +++ b/compiler/rustc_mir/src/transform/function_item_references.rs @@ -99,7 +99,7 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> { &self, def_id: DefId, substs_ref: SubstsRef<'tcx>, - args: &Vec>, + args: &[Operand<'tcx>], source_info: SourceInfo, ) { let param_env = self.tcx.param_env(def_id); @@ -162,7 +162,7 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> { .unwrap_or(None) } - fn nth_arg_span(&self, args: &Vec>, n: usize) -> Span { + fn nth_arg_span(&self, args: &[Operand<'tcx>], n: usize) -> Span { match &args[n] { Operand::Copy(place) | Operand::Move(place) => { self.body.local_decls[place.local].source_info.span diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 4eeb8969bb11..6e7575c1d71b 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -41,14 +41,6 @@ impl<'tcx> MirPass<'tcx> for Inline { return; } - if tcx.sess.opts.debugging_opts.instrument_coverage { - // The current implementation of source code coverage injects code region counters - // into the MIR, and assumes a 1-to-1 correspondence between MIR and source-code- - // based function. - debug!("function inlining is disabled when compiling with `instrument_coverage`"); - return; - } - if inline(tcx, body) { debug!("running simplify cfg on {:?}", body.source); CfgSimplifier::new(body).simplify(); diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs index 3eb2b500d662..990ca313c5dd 100644 --- a/compiler/rustc_mir/src/transform/instcombine.rs +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -29,8 +29,10 @@ impl<'tcx> MirPass<'tcx> for InstCombine { optimization_finder.optimizations }; - // Then carry out those optimizations. - MutVisitor::visit_body(&mut InstCombineVisitor { optimizations, tcx }, body); + if !optimizations.is_empty() { + // Then carry out those optimizations. + MutVisitor::visit_body(&mut InstCombineVisitor { optimizations, tcx }, body); + } } } @@ -95,7 +97,7 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { } } - self.super_rvalue(rvalue, location) + // We do not call super_rvalue as we are not interested in any other parts of the tree } } @@ -299,7 +301,7 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { self.find_unneeded_equality_comparison(rvalue, location); - self.super_rvalue(rvalue, location) + // We do not call super_rvalue as we are not interested in any other parts of the tree } } @@ -310,3 +312,21 @@ struct OptimizationList<'tcx> { unneeded_equality_comparison: FxHashMap>, unneeded_deref: FxHashMap>, } + +impl<'tcx> OptimizationList<'tcx> { + fn is_empty(&self) -> bool { + match self { + OptimizationList { + and_stars, + arrays_lengths, + unneeded_equality_comparison, + unneeded_deref, + } => { + and_stars.is_empty() + && arrays_lengths.is_empty() + && unneeded_equality_comparison.is_empty() + && unneeded_deref.is_empty() + } + } + } +} diff --git a/compiler/rustc_mir/src/transform/lower_intrinsics.rs b/compiler/rustc_mir/src/transform/lower_intrinsics.rs index 543acb74acbc..f5968532eb39 100644 --- a/compiler/rustc_mir/src/transform/lower_intrinsics.rs +++ b/compiler/rustc_mir/src/transform/lower_intrinsics.rs @@ -83,6 +83,21 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Goto { target }; } } + sym::discriminant_value => { + if let (Some((destination, target)), Some(arg)) = + (*destination, args[0].place()) + { + let arg = tcx.mk_place_deref(arg); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(box ( + destination, + Rvalue::Discriminant(arg), + )), + }); + terminator.kind = TerminatorKind::Goto { target }; + } + } _ => {} } } diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index e86d11e248fc..7f3b421cf76f 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -21,6 +21,7 @@ pub mod check_consts; pub mod check_packed_ref; pub mod check_unsafety; pub mod cleanup_post_borrowck; +pub mod const_debuginfo; pub mod const_prop; pub mod coverage; pub mod deaggregator; @@ -363,6 +364,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, // but before optimizations begin. &add_retag::AddRetag, + &lower_intrinsics::LowerIntrinsics, &simplify::SimplifyCfg::new("elaborate-drops"), // `Deaggregator` is conceptually part of MIR building, some backends rely on it happening // and it can help optimizations. @@ -391,7 +393,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // The main optimizations that we do on MIR. let optimizations: &[&dyn MirPass<'tcx>] = &[ - &lower_intrinsics::LowerIntrinsics, &remove_unneeded_drops::RemoveUnneededDrops, &match_branches::MatchBranchSimplification, // inst combine is after MatchBranchSimplification to clean up Ne(_1, false) @@ -408,6 +409,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::new("final"), &nrvo::RenameReturnPlace, + &const_debuginfo::ConstDebugInfo, &simplify::SimplifyLocals, &multiple_return_terminators::MultipleReturnTerminators, ]; diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index 8d5ed747c3f8..ea92e23e9bff 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -90,7 +90,7 @@ pub enum TempState { impl TempState { pub fn is_promotable(&self) -> bool { debug!("is_promotable: self={:?}", self); - matches!(self, TempState::Defined { .. } ) + matches!(self, TempState::Defined { .. }) } } @@ -309,50 +309,26 @@ impl<'tcx> Validator<'_, 'tcx> { let statement = &self.body[loc.block].statements[loc.statement_index]; match &statement.kind { StatementKind::Assign(box (_, Rvalue::Ref(_, kind, place))) => { - match kind { - BorrowKind::Shared | BorrowKind::Mut { .. } => {} - - // FIXME(eddyb) these aren't promoted here but *could* - // be promoted as part of a larger value because - // `validate_rvalue` doesn't check them, need to - // figure out what is the intended behavior. - BorrowKind::Shallow | BorrowKind::Unique => return Err(Unpromotable), - } - // We can only promote interior borrows of promotable temps (non-temps // don't get promoted anyway). self.validate_local(place.local)?; + // The reference operation itself must be promotable. + // (Needs to come after `validate_local` to avoid ICEs.) + self.validate_ref(*kind, place)?; + + // We do not check all the projections (they do not get promoted anyway), + // but we do stay away from promoting anything involving a dereference. if place.projection.contains(&ProjectionElem::Deref) { return Err(Unpromotable); } + + // We cannot promote things that need dropping, since the promoted value + // would not get dropped. if self.qualif_local::(place.local) { return Err(Unpromotable); } - // FIXME(eddyb) this duplicates part of `validate_rvalue`. - let has_mut_interior = - self.qualif_local::(place.local); - if has_mut_interior { - return Err(Unpromotable); - } - - if let BorrowKind::Mut { .. } = kind { - let ty = place.ty(self.body, self.tcx).ty; - - // In theory, any zero-sized value could be borrowed - // mutably without consequences. However, only &mut [] - // is allowed right now. - if let ty::Array(_, len) = ty.kind() { - match len.try_eval_usize(self.tcx, self.param_env) { - Some(0) => {} - _ => return Err(Unpromotable), - } - } else { - return Err(Unpromotable); - } - } - Ok(()) } _ => bug!(), @@ -572,58 +548,115 @@ impl<'tcx> Validator<'_, 'tcx> { } } - fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { - match *rvalue { - Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { - let operand_ty = operand.ty(self.body, self.tcx); - let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); - let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { - // ptr-to-int casts are not possible in consts and thus not promotable + fn validate_ref(&self, kind: BorrowKind, place: &Place<'tcx>) -> Result<(), Unpromotable> { + match kind { + // Reject these borrow types just to be safe. + // FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase. + BorrowKind::Shallow | BorrowKind::Unique => return Err(Unpromotable), + + BorrowKind::Shared => { + let has_mut_interior = self.qualif_local::(place.local); + if has_mut_interior { return Err(Unpromotable); } } - Rvalue::BinaryOp(op, ref lhs, _) => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() { - assert!( - op == BinOp::Eq - || op == BinOp::Ne - || op == BinOp::Le - || op == BinOp::Lt - || op == BinOp::Ge - || op == BinOp::Gt - || op == BinOp::Offset - ); + BorrowKind::Mut { .. } => { + let ty = place.ty(self.body, self.tcx).ty; - // raw pointer operations are not allowed inside consts and thus not promotable + // In theory, any zero-sized value could be borrowed + // mutably without consequences. However, only &mut [] + // is allowed right now. + if let ty::Array(_, len) = ty.kind() { + match len.try_eval_usize(self.tcx, self.param_env) { + Some(0) => {} + _ => return Err(Unpromotable), + } + } else { return Err(Unpromotable); } } - - Rvalue::NullaryOp(NullOp::Box, _) => return Err(Unpromotable), - - // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous. - _ => {} } + Ok(()) + } + + fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match rvalue { - Rvalue::ThreadLocalRef(_) => Err(Unpromotable), - - Rvalue::NullaryOp(..) => Ok(()), - - Rvalue::Discriminant(place) | Rvalue::Len(place) => self.validate_place(place.as_ref()), - Rvalue::Use(operand) | Rvalue::Repeat(operand, _) - | Rvalue::UnaryOp(_, operand) - | Rvalue::Cast(_, operand, _) => self.validate_operand(operand), - - Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { - self.validate_operand(lhs)?; - self.validate_operand(rhs) + | Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, operand) => { + self.validate_operand(operand)?; } + Rvalue::Discriminant(place) | Rvalue::Len(place) => { + self.validate_place(place.as_ref())? + } + + Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), + + Rvalue::Cast(kind, operand, cast_ty) => { + if matches!(kind, CastKind::Misc) { + let operand_ty = operand.ty(self.body, self.tcx); + let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); + let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); + if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { + // ptr-to-int casts are not possible in consts and thus not promotable + return Err(Unpromotable); + } + // int-to-ptr casts are fine, they just use the integer value at pointer type. + } + + self.validate_operand(operand)?; + } + + Rvalue::BinaryOp(op, lhs, rhs) | Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + let op = *op; + if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() { + // raw pointer operations are not allowed inside consts and thus not promotable + assert!(matches!( + op, + BinOp::Eq + | BinOp::Ne + | BinOp::Le + | BinOp::Lt + | BinOp::Ge + | BinOp::Gt + | BinOp::Offset + )); + return Err(Unpromotable); + } + + match op { + // FIXME: reject operations that can fail -- namely, division and modulo. + BinOp::Eq + | BinOp::Ne + | BinOp::Le + | BinOp::Lt + | BinOp::Ge + | BinOp::Gt + | BinOp::Offset + | BinOp::Add + | BinOp::Sub + | BinOp::Mul + | BinOp::Div + | BinOp::Rem + | BinOp::BitXor + | BinOp::BitAnd + | BinOp::BitOr + | BinOp::Shl + | BinOp::Shr => {} + } + + self.validate_operand(lhs)?; + self.validate_operand(rhs)?; + } + + Rvalue::NullaryOp(op, _) => match op { + NullOp::Box => return Err(Unpromotable), + NullOp::SizeOf => {} + }, + Rvalue::AddressOf(_, place) => { // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is // no problem, only using it is. @@ -636,53 +669,36 @@ impl<'tcx> Validator<'_, 'tcx> { }); } } - Err(Unpromotable) + return Err(Unpromotable); } Rvalue::Ref(_, kind, place) => { - if let BorrowKind::Mut { .. } = kind { - let ty = place.ty(self.body, self.tcx).ty; - - // In theory, any zero-sized value could be borrowed - // mutably without consequences. However, only &mut [] - // is allowed right now. - if let ty::Array(_, len) = ty.kind() { - match len.try_eval_usize(self.tcx, self.param_env) { - Some(0) => {} - _ => return Err(Unpromotable), - } - } else { - return Err(Unpromotable); - } - } - // Special-case reborrows to be more like a copy of the reference. - let mut place = place.as_ref(); - if let [proj_base @ .., ProjectionElem::Deref] = &place.projection { - let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + let mut place_simplified = place.as_ref(); + if let [proj_base @ .., ProjectionElem::Deref] = &place_simplified.projection { + let base_ty = + Place::ty_from(place_simplified.local, proj_base, self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - place = PlaceRef { local: place.local, projection: proj_base }; + place_simplified = + PlaceRef { local: place_simplified.local, projection: proj_base }; } } - self.validate_place(place)?; + self.validate_place(place_simplified)?; - let has_mut_interior = self.qualif_local::(place.local); - if has_mut_interior { - return Err(Unpromotable); - } - - Ok(()) + // Check that the reference is fine (using the original place!). + // (Needs to come after `validate_place` to avoid ICEs.) + self.validate_ref(*kind, place)?; } - Rvalue::Aggregate(_, ref operands) => { + Rvalue::Aggregate(_, operands) => { for o in operands { self.validate_operand(o)?; } - - Ok(()) } } + + Ok(()) } fn validate_call( diff --git a/compiler/rustc_mir/src/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs index 205f718d6e44..7598be4e4a11 100644 --- a/compiler/rustc_mir/src/transform/rustc_peek.rs +++ b/compiler/rustc_mir/src/transform/rustc_peek.rs @@ -92,7 +92,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { /// "rustc_peek: bit not set". /// /// The intention is that one can write unit tests for dataflow by -/// putting code into a compile-fail test and using `rustc_peek` to +/// putting code into an UI test and using `rustc_peek` to /// make observations about the results of dataflow static analyses. /// /// (If there are any calls to `rustc_peek` that do not match the diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs index 27bb1def726e..a3459887a9a7 100644 --- a/compiler/rustc_mir/src/transform/simplify_try.rs +++ b/compiler/rustc_mir/src/transform/simplify_try.rs @@ -246,14 +246,19 @@ fn get_arm_identity_info<'a, 'tcx>( tmp_assigned_vars.insert(*r); } - let dbg_info_to_adjust: Vec<_> = - debug_info - .iter() - .enumerate() - .filter_map(|(i, var_info)| { - if tmp_assigned_vars.contains(var_info.place.local) { Some(i) } else { None } - }) - .collect(); + 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, @@ -301,7 +306,7 @@ fn optimization_applies<'tcx>( return false; } - // Verify the assigment chain consists of the form b = a; c = b; d = c; etc... + // 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; @@ -340,9 +345,11 @@ fn optimization_applies<'tcx>( // 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 !dbg_info.place.projection.is_empty() { - trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, dbg_info.place); - return false; + 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; + } } } @@ -423,9 +430,15 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { // Fix the debug info to point to the right local for dbg_index in opt_info.dbg_info_to_adjust { let dbg_info = &mut debug_info[dbg_index]; - assert!(dbg_info.place.projection.is_empty()); - dbg_info.place.local = opt_info.local_0; - dbg_info.place.projection = opt_info.dbg_projection; + 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); diff --git a/compiler/rustc_mir/src/util/borrowck_errors.rs b/compiler/rustc_mir/src/util/borrowck_errors.rs index 83bf7584f2e2..56d8045813c4 100644 --- a/compiler/rustc_mir/src/util/borrowck_errors.rs +++ b/compiler/rustc_mir/src/util/borrowck_errors.rs @@ -68,9 +68,10 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { err.span_label( new_loan_span, format!( - "mutable borrow starts here in previous \ - iteration of loop{}", - opt_via + "{}{} was mutably borrowed here in the previous iteration of the loop{}", + desc, + via(opt_via), + opt_via, ), ); if let Some(old_load_end_span) = old_load_end_span { diff --git a/compiler/rustc_mir/src/util/generic_graph.rs b/compiler/rustc_mir/src/util/generic_graph.rs new file mode 100644 index 000000000000..6ce305a48211 --- /dev/null +++ b/compiler/rustc_mir/src/util/generic_graph.rs @@ -0,0 +1,70 @@ +use gsgdt::{Edge, Graph, Node, NodeStyle}; +use rustc_hir::def_id::DefId; +use rustc_index::vec::Idx; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; + +/// Convert an MIR function into a gsgdt Graph +pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Graph { + let def_id = body.source.def_id(); + let def_name = graphviz_safe_def_name(def_id); + let graph_name = format!("Mir_{}", def_name); + let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + + // Nodes + let nodes: Vec = body + .basic_blocks() + .iter_enumerated() + .map(|(block, _)| bb_to_graph_node(block, body, dark_mode)) + .collect(); + + // Edges + let mut edges = Vec::new(); + for (source, _) in body.basic_blocks().iter_enumerated() { + let def_id = body.source.def_id(); + let terminator = body[source].terminator(); + let labels = terminator.kind.fmt_successor_labels(); + + for (&target, label) in terminator.successors().zip(labels) { + let src = node(def_id, source); + let trg = node(def_id, target); + edges.push(Edge::new(src, trg, label.to_string())); + } + } + + Graph::new(graph_name, nodes, edges) +} + +fn bb_to_graph_node(block: BasicBlock, body: &Body<'_>, dark_mode: bool) -> Node { + let def_id = body.source.def_id(); + let data = &body[block]; + let label = node(def_id, block); + + let (title, bgcolor) = if data.is_cleanup { + let color = if dark_mode { "royalblue" } else { "lightblue" }; + (format!("{} (cleanup)", block.index()), color) + } else { + let color = if dark_mode { "dimgray" } else { "gray" }; + (format!("{}", block.index()), color) + }; + + let style = NodeStyle { title_bg: Some(bgcolor.to_owned()), ..Default::default() }; + let mut stmts: Vec = data.statements.iter().map(|x| format!("{:?}", x)).collect(); + + // add the terminator to the stmts, gsgdt can print it out seperately + let mut terminator_head = String::new(); + data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); + stmts.push(terminator_head); + + Node::new(stmts, label, title, style) +} + +// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so +// it does not have to be user friendly. +pub fn graphviz_safe_def_name(def_id: DefId) -> String { + format!("{}_{}", def_id.krate.index(), def_id.index.index(),) +} + +fn node(def_id: DefId, block: BasicBlock) -> String { + format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id)) +} diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index 625f1a3e6844..37498e50c0eb 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -1,11 +1,12 @@ +use gsgdt::GraphvizSettings; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; -use rustc_index::vec::Idx; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use std::fmt::Debug; use std::io::{self, Write}; +use super::generic_graph::mir_fn_to_generic_graph; use super::pretty::dump_mir_def_ids; /// Write a graphviz DOT graph of a list of MIRs. @@ -32,12 +33,6 @@ where Ok(()) } -// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so -// it does not have to be user friendly. -pub fn graphviz_safe_def_name(def_id: DefId) -> String { - format!("{}_{}", def_id.krate.index(), def_id.index.index(),) -} - /// Write a graphviz DOT graph of the MIR. pub fn write_mir_fn_graphviz<'tcx, W>( tcx: TyCtxt<'tcx>, @@ -48,12 +43,6 @@ pub fn write_mir_fn_graphviz<'tcx, W>( where W: Write, { - let def_id = body.source.def_id(); - let kind = if subgraph { "subgraph" } else { "digraph" }; - let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR - let def_name = graphviz_safe_def_name(def_id); - writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?; - // Global graph properties let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font); let mut graph_attrs = vec![&font[..]]; @@ -67,131 +56,31 @@ where content_attrs.push(r#"fontcolor="white""#); } - writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; - let content_attrs_str = content_attrs.join(" "); - writeln!(w, r#" node [{}];"#, content_attrs_str)?; - writeln!(w, r#" edge [{}];"#, content_attrs_str)?; - // Graph label - write_graph_label(tcx, body, w)?; - - // Nodes - for (block, _) in body.basic_blocks().iter_enumerated() { - write_node(block, body, dark_mode, w)?; - } - - // Edges - for (source, _) in body.basic_blocks().iter_enumerated() { - write_edges(source, body, w)?; - } - writeln!(w, "}}") -} - -/// Write a graphviz HTML-styled label for the given basic block, with -/// all necessary escaping already performed. (This is suitable for -/// emitting directly, as is done in this module, or for use with the -/// LabelText::HtmlStr from librustc_graphviz.) -/// -/// `init` and `fini` are callbacks for emitting additional rows of -/// data (using HTML enclosed with `` in the emitted text). -pub fn write_node_label( - block: BasicBlock, - body: &Body<'_>, - dark_mode: bool, - w: &mut W, - num_cols: u32, - init: INIT, - fini: FINI, -) -> io::Result<()> -where - INIT: Fn(&mut W) -> io::Result<()>, - FINI: Fn(&mut W) -> io::Result<()>, -{ - let data = &body[block]; - - write!(w, r#""#)?; - - // Basic block number at the top. - let (blk, bgcolor) = if data.is_cleanup { - let color = if dark_mode { "royalblue" } else { "lightblue" }; - (format!("{} (cleanup)", block.index()), color) - } else { - let color = if dark_mode { "dimgray" } else { "gray" }; - (format!("{}", block.index()), color) + let mut label = String::from(""); + // FIXME: remove this unwrap + write_graph_label(tcx, body, &mut label).unwrap(); + let g = mir_fn_to_generic_graph(tcx, body); + let settings = GraphvizSettings { + graph_attrs: Some(graph_attrs.join(" ")), + node_attrs: Some(content_attrs.join(" ")), + edge_attrs: Some(content_attrs.join(" ")), + graph_label: Some(label), }; - write!( - w, - r#""#, - attrs = r#"align="center""#, - colspan = num_cols, - blk = blk, - bgcolor = bgcolor - )?; - - init(w)?; - - // List of statements in the middle. - if !data.statements.is_empty() { - write!(w, r#"")?; - } - - // Terminator head at the bottom, not including the list of successor blocks. Those will be - // displayed as labels on the edges between blocks. - let mut terminator_head = String::new(); - data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); - write!(w, r#""#, dot::escape_html(&terminator_head))?; - - fini(w)?; - - // Close the table - write!(w, "
{blk}
"#)?; - for statement in &data.statements { - write!(w, "{}
", escape(statement))?; - } - write!(w, "
{}
") -} - -/// Write a graphviz DOT node for the given basic block. -fn write_node( - block: BasicBlock, - body: &Body<'_>, - dark_mode: bool, - w: &mut W, -) -> io::Result<()> { - let def_id = body.source.def_id(); - // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. - write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?; - write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?; - // Close the node label and the node itself. - writeln!(w, ">];") -} - -/// Write graphviz DOT edges with labels between the given basic block and all of its successors. -fn write_edges(source: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> { - let def_id = body.source.def_id(); - let terminator = body[source].terminator(); - let labels = terminator.kind.fmt_successor_labels(); - - for (&target, label) in terminator.successors().zip(labels) { - let src = node(def_id, source); - let trg = node(def_id, target); - writeln!(w, r#" {} -> {} [label="{}"];"#, src, trg, label)?; - } - - Ok(()) + g.to_dot(w, &settings, subgraph) } /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of /// all the variables and temporaries. -fn write_graph_label<'tcx, W: Write>( +fn write_graph_label<'tcx, W: std::fmt::Write>( tcx: TyCtxt<'tcx>, body: &Body<'_>, w: &mut W, -) -> io::Result<()> { +) -> std::fmt::Result { let def_id = body.source.def_id(); - write!(w, " label=( w, r#"debug {} => {};
"#, var_debug_info.name, - escape(&var_debug_info.place) + escape(&var_debug_info.value), )?; } - writeln!(w, ">;") -} - -fn node(def_id: DefId, block: BasicBlock) -> String { - format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id)) + Ok(()) } fn escape(t: &T) -> String { diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs index aaee0bc526db..b7b702431bc2 100644 --- a/compiler/rustc_mir/src/util/mod.rs +++ b/compiler/rustc_mir/src/util/mod.rs @@ -7,6 +7,7 @@ pub mod storage; mod alignment; pub mod collect_writes; mod find_self_call; +mod generic_graph; pub(crate) mod generic_graphviz; mod graphviz; pub(crate) mod pretty; @@ -15,6 +16,6 @@ pub(crate) mod spanview; pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; pub use self::find_self_call::find_self_call; -pub use self::graphviz::write_node_label as write_graphviz_node_label; -pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz}; +pub use self::generic_graph::graphviz_safe_def_name; +pub use self::graphviz::write_mir_graphviz; pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index cd60602b088f..89ce29bd1012 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::interpret::{ }; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitor}; +use rustc_middle::ty::{self, TyCtxt, TyS, TypeFoldable, TypeVisitor}; use rustc_target::abi::Size; use std::ops::ControlFlow; @@ -408,6 +408,18 @@ impl ExtraComments<'tcx> { } } +fn use_verbose(ty: &&TyS<'tcx>) -> bool { + match ty.kind() { + ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false, + // Unit type + ty::Tuple(g_args) if g_args.is_empty() => false, + ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty())), + ty::Array(ty, _) => use_verbose(ty), + ty::FnDef(..) => false, + _ => true, + } +} + impl Visitor<'tcx> for ExtraComments<'tcx> { fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { self.super_constant(constant, location); @@ -430,16 +442,10 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { self.super_const(constant); let ty::Const { ty, val, .. } = constant; - match ty.kind() { - ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => {} - // Unit type - ty::Tuple(tys) if tys.is_empty() => {} - ty::FnDef(..) => {} - _ => { - self.push("ty::Const"); - self.push(&format!("+ ty: {:?}", ty)); - self.push(&format!("+ val: {:?}", val)); - } + if use_verbose(ty) { + self.push("ty::Const"); + self.push(&format!("+ ty: {:?}", ty)); + self.push(&format!("+ val: {:?}", val)); } } @@ -495,7 +501,7 @@ fn write_scope_tree( let indented_debug_info = format!( "{0:1$}debug {2} => {3:?};", - INDENT, indent, var_debug_info.name, var_debug_info.place, + INDENT, indent, var_debug_info.name, var_debug_info.value, ); writeln!( diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index e6263e5d6cf9..cf2e4e8916d0 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -4,14 +4,62 @@ use crate::build::expr::category::Category; use crate::build::ForGuard::{OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::thir::*; +use rustc_hir::def_id::DefId; +use rustc_hir::HirId; use rustc_middle::middle::region; +use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance}; use rustc_span::Span; +use rustc_target::abi::VariantIdx; use rustc_index::vec::Idx; +/// The "outermost" place that holds this value. +#[derive(Copy, Clone)] +crate enum PlaceBase { + /// Denotes the start of a `Place`. + Local(Local), + + /// When building place for an expression within a closure, the place might start off a + /// captured path. When `capture_disjoint_fields` is enabled, we might not know the capture + /// index (within the desugared closure) of the captured path until most of the projections + /// are applied. We use `PlaceBase::Upvar` to keep track of the root variable off of which the + /// captured path starts, the closure the capture belongs to and the trait the closure + /// implements. + /// + /// Once we have figured out the capture index, we can convert the place builder to start from + /// `PlaceBase::Local`. + /// + /// Consider the following example + /// ```rust + /// let t = (10, (10, (10, 10))); + /// + /// let c = || { + /// println!("{}", t.0.0.0); + /// }; + /// ``` + /// Here the THIR expression for `t.0.0.0` will be something like + /// + /// ``` + /// * Field(0) + /// * Field(0) + /// * Field(0) + /// * UpvarRef(t) + /// ``` + /// + /// When `capture_disjoint_fields` is enabled, `t.0.0.0` is captured and we won't be able to + /// figure out that it is captured until all the `Field` projections are applied. + Upvar { + /// HirId of the upvar + var_hir_id: HirId, + /// DefId of the closure + closure_def_id: DefId, + /// The trait closure implements, `Fn`, `FnMut`, `FnOnce` + closure_kind: ty::ClosureKind }, +} + /// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a /// place by pushing more and more projections onto the end, and then convert the final set into a /// place using the `into_place` method. @@ -19,14 +67,240 @@ use rustc_index::vec::Idx; /// This is used internally when building a place for an expression like `a.b.c`. The fields `b` /// and `c` can be progressively pushed onto the place builder that is created when converting `a`. #[derive(Clone)] -struct PlaceBuilder<'tcx> { - local: Local, +crate struct PlaceBuilder<'tcx> { + base: PlaceBase, projection: Vec>, } +/// Given a list of MIR projections, convert them to list of HIR ProjectionKind. +/// The projections are truncated to represent a path that might be captured by a +/// closure/generator. This implies the vector returned from this function doesn't contain +/// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be +/// part of a path that is captued by a closure. We stop applying projections once we see the first +/// projection that isn't captured by a closure. +fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( + mir_projections: &[PlaceElem<'tcx>], +) -> Vec { + + let mut hir_projections = Vec::new(); + + for mir_projection in mir_projections { + let hir_projection = match mir_projection { + ProjectionElem::Deref => HirProjectionKind::Deref, + ProjectionElem::Field(field, _) => { + // We will never encouter this for multivariant enums, + // read the comment for `Downcast`. + HirProjectionKind::Field(field.index() as u32, VariantIdx::new(0)) + }, + ProjectionElem::Downcast(..) => { + // This projections exist only for enums that have + // multiple variants. Since such enums that are captured + // completely, we can stop here. + break + }, + ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { + // We don't capture array-access projections. + // We can stop here as arrays are captured completely. + break + }, + }; + + hir_projections.push(hir_projection); + } + + hir_projections +} + +/// Return true if the `proj_possible_ancestor` represents an ancestor path +/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`, +/// assuming they both start off of the same root variable. +/// +/// **Note:** It's the caller's responsibility to ensure that both lists of projections +/// start off of the same root variable. +/// +/// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of +/// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`. +/// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`. +/// 2. Since we only look at the projections here function will return `bar.x` as an a valid +/// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections +/// list are being applied to the same root variable. +fn is_ancestor_or_same_capture( + proj_possible_ancestor: &Vec, + proj_capture: &[HirProjectionKind], +) -> bool { + // We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false. + // Therefore we can't just check if all projections are same in the zipped iterator below. + if proj_possible_ancestor.len() > proj_capture.len() { + return false; + } + + proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b) +} + +/// Computes the index of a capture within the desugared closure provided the closure's +/// `closure_min_captures` and the capture's index of the capture in the +/// `ty::MinCaptureList` of the root variable `var_hir_id`. +fn compute_capture_idx<'tcx>( + closure_min_captures: &ty::RootVariableMinCaptureList<'tcx>, + var_hir_id: HirId, + root_var_idx: usize, +) -> usize { + let mut res = 0; + for (var_id, capture_list) in closure_min_captures { + if *var_id == var_hir_id { + res += root_var_idx; + break; + } else { + res += capture_list.len(); + } + } + + res +} + +/// Given a closure, returns the index of a capture within the desugared closure struct and the +/// `ty::CapturedPlace` which is the ancestor of the Place represented using the `var_hir_id` +/// and `projection`. +/// +/// Note there will be at most one ancestor for any given Place. +/// +/// Returns None, when the ancestor is not found. +fn find_capture_matching_projections<'a, 'tcx>( + typeck_results: &'a ty::TypeckResults<'tcx>, + var_hir_id: HirId, + closure_def_id: DefId, + projections: &[PlaceElem<'tcx>], +) -> Option<(usize, &'a ty::CapturedPlace<'tcx>)> { + let closure_min_captures = typeck_results.closure_min_captures.get(&closure_def_id)?; + let root_variable_min_captures = closure_min_captures.get(&var_hir_id)?; + + let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections); + + // If an ancestor is found, `idx` is the index within the list of captured places + // for root variable `var_hir_id` and `capture` is the `ty::CapturedPlace` itself. + let (idx, capture) = root_variable_min_captures.iter().enumerate().find(|(_, capture)| { + let possible_ancestor_proj_kinds = + capture.place.projections.iter().map(|proj| proj.kind).collect(); + is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections) + })?; + + // Convert index to be from the presepective of the entire closure_min_captures map + // instead of just the root variable capture list + Some((compute_capture_idx(closure_min_captures, var_hir_id, idx), capture)) +} + +/// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the +/// `PlaceBuilder` now starts from `PlaceBase::Local`. +/// +/// Returns a Result with the error being the HirId of the Upvar that was not found. +fn to_upvars_resolved_place_builder<'a, 'tcx>( + from_builder: PlaceBuilder<'tcx>, + tcx: TyCtxt<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, +) -> Result, HirId> { + match from_builder.base { + PlaceBase::Local(_) => Ok(from_builder), + PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind } => { + // Captures are represented using fields inside a structure. + // This represents accessing self in the closure structure + let mut upvar_resolved_place_builder = PlaceBuilder::from(Local::new(1)); + match closure_kind { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { + upvar_resolved_place_builder = upvar_resolved_place_builder.deref(); + } + ty::ClosureKind::FnOnce => {} + } + + let (capture_index, capture) = + if let Some(capture_details) = find_capture_matching_projections( + typeck_results, + var_hir_id, + closure_def_id, + &from_builder.projection, + ) { + capture_details + } else { + if !tcx.features().capture_disjoint_fields { + bug!( + "No associated capture found for {:?}[{:#?}] even though \ + capture_disjoint_fields isn't enabled", + var_hir_id, + from_builder.projection + ) + } else { + // FIXME(project-rfc-2229#24): Handle this case properly + debug!( + "No associated capture found for {:?}[{:#?}]", + var_hir_id, + from_builder.projection, + ); + } + return Err(var_hir_id); + }; + + let closure_ty = + typeck_results.node_type(tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local())); + + let substs = match closure_ty.kind() { + ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), + ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), + _ => bug!("Lowering capture for non-closure type {:?}", closure_ty), + }; + + // Access the capture by accessing the field within the Closure struct. + // + // We must have inferred the capture types since we are building MIR, therefore + // it's safe to call `tuple_element_ty` and we can unwrap here because + // we know that the capture exists and is the `capture_index`-th capture. + let var_ty = substs.tupled_upvars_ty().tuple_element_ty(capture_index).unwrap(); + + upvar_resolved_place_builder = upvar_resolved_place_builder.field(Field::new(capture_index), var_ty); + + // If the variable is captured via ByRef(Immutable/Mutable) Borrow, + // we need to deref it + upvar_resolved_place_builder = match capture.info.capture_kind { + ty::UpvarCapture::ByRef(_) => upvar_resolved_place_builder.deref(), + ty::UpvarCapture::ByValue(_) => upvar_resolved_place_builder, + }; + + let next_projection = capture.place.projections.len(); + let mut curr_projections = from_builder.projection; + + // We used some of the projections to build the capture itself, + // now we apply the remaining to the upvar resolved place. + upvar_resolved_place_builder.projection.extend( + curr_projections.drain(next_projection..)); + + Ok(upvar_resolved_place_builder) + } + } +} + impl<'tcx> PlaceBuilder<'tcx> { - fn into_place(self, tcx: TyCtxt<'tcx>) -> Place<'tcx> { - Place { local: self.local, projection: tcx.intern_place_elems(&self.projection) } + crate fn into_place<'a>( + self, + tcx: TyCtxt<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + ) -> Place<'tcx> { + if let PlaceBase::Local(local) = self.base { + Place { local, projection: tcx.intern_place_elems(&self.projection) } + } else { + self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results) + } + } + + fn expect_upvars_resolved<'a>( + self, + tcx: TyCtxt<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + ) -> PlaceBuilder<'tcx> { + to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap() + } + + crate fn base(&self) -> PlaceBase { + self.base } fn field(self, f: Field, ty: Ty<'tcx>) -> Self { @@ -49,7 +323,13 @@ impl<'tcx> PlaceBuilder<'tcx> { impl<'tcx> From for PlaceBuilder<'tcx> { fn from(local: Local) -> Self { - Self { local, projection: Vec::new() } + Self { base: PlaceBase::Local(local), projection: Vec::new() } + } +} + +impl<'tcx> From for PlaceBuilder<'tcx> { + fn from(base: PlaceBase) -> Self { + Self { base, projection: Vec::new() } } } @@ -71,12 +351,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { M: Mirror<'tcx, Output = Expr<'tcx>>, { let place_builder = unpack!(block = self.as_place_builder(block, expr)); - block.and(place_builder.into_place(self.hir.tcx())) + block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results())) } /// This is used when constructing a compound `Place`, so that we can avoid creating /// intermediate `Place` values until we know the full set of projections. - fn as_place_builder(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + crate fn as_place_builder(&mut self, block: BasicBlock, expr: M) -> BlockAnd> where M: Mirror<'tcx, Output = Expr<'tcx>>, { @@ -98,7 +378,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { M: Mirror<'tcx, Output = Expr<'tcx>>, { let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr)); - block.and(place_builder.into_place(self.hir.tcx())) + block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results())) } /// This is used when constructing a compound `Place`, so that we can avoid creating @@ -161,27 +441,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, ), ExprKind::UpvarRef { closure_def_id, var_hir_id } => { - let capture = this - .hir - .typeck_results - .closure_captures - .get(&closure_def_id) - .and_then(|captures| captures.get_full(&var_hir_id)); - - if capture.is_none() { - if !this.hir.tcx().features().capture_disjoint_fields { - bug!( - "No associated capture found for {:?} even though \ - capture_disjoint_fields isn't enabled", - expr.kind - ) - } - // FIXME(project-rfc-2229#24): Handle this case properly - } - - // Unwrap until the FIXME has been resolved - let (capture_index, _, upvar_id) = capture.unwrap(); - this.lower_closure_capture(block, capture_index, *upvar_id) + let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id.expect_local()); + this.lower_captured_upvar(block, upvar_id) } ExprKind::VarRef { id } => { @@ -208,7 +469,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { inferred_ty: expr.ty, }); - let place = place_builder.clone().into_place(this.hir.tcx()); + let place = + place_builder.clone().into_place(this.hir.tcx(), this.hir.typeck_results()); this.cfg.push( block, Statement { @@ -293,59 +555,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - /// Lower a closure/generator capture by representing it as a field - /// access within the desugared closure/generator. - /// - /// `capture_index` is the index of the capture within the desugared - /// closure/generator. - fn lower_closure_capture( + /// Lower a captured upvar. Note we might not know the actual capture index, + /// so we create a place starting from `PlaceBase::Upvar`, which will be resolved + /// once all projections that allow us to indentify a capture have been applied. + fn lower_captured_upvar( &mut self, block: BasicBlock, - capture_index: usize, upvar_id: ty::UpvarId, - ) -> BlockAnd> { + ) -> BlockAnd> { let closure_ty = self .hir .typeck_results() .node_type(self.hir.tcx().hir().local_def_id_to_hir_id(upvar_id.closure_expr_id)); - // Captures are represented using fields inside a structure. - // This represents accessing self in the closure structure - let mut place_builder = PlaceBuilder::from(Local::new(1)); - - // In case of Fn/FnMut closures we must deref to access the fields - // Generators are considered FnOnce, so we ignore this step for them. - if let ty::Closure(_, closure_substs) = closure_ty.kind() { - match self.hir.infcx().closure_kind(closure_substs).unwrap() { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { - place_builder = place_builder.deref(); - } - ty::ClosureKind::FnOnce => {} - } - } - - let substs = match closure_ty.kind() { - ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), - ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), - _ => bug!("Lowering capture for non-closure type {:?}", closure_ty) + let closure_kind = if let ty::Closure(_, closure_substs) = closure_ty.kind() { + self.hir.infcx().closure_kind(closure_substs).unwrap() + } else { + // Generators are considered FnOnce. + ty::ClosureKind::FnOnce }; - // Access the capture by accessing the field within the Closure struct. - // - // We must have inferred the capture types since we are building MIR, therefore - // it's safe to call `upvar_tys` and we can unwrap here because - // we know that the capture exists and is the `capture_index`-th capture. - let var_ty = substs.upvar_tys().nth(capture_index).unwrap(); - place_builder = place_builder.field(Field::new(capture_index), var_ty); - - // If the variable is captured via ByRef(Immutable/Mutable) Borrow, - // we need to deref it - match self.hir.typeck_results.upvar_capture(upvar_id) { - ty::UpvarCapture::ByRef(_) => { - block.and(place_builder.deref()) - } - ty::UpvarCapture::ByValue(_) => block.and(place_builder), - } + block.and(PlaceBuilder::from(PlaceBase::Upvar { + var_hir_id: upvar_id.var_path.hir_id, + closure_def_id: upvar_id.closure_expr_id.to_def_id(), + closure_kind, + })) } /// Lower an index expression @@ -373,7 +607,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let is_outermost_index = fake_borrow_temps.is_none(); let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps); - let base_place = + let mut base_place = unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),)); // Making this a *fresh* temporary means we do not have to worry about @@ -383,7 +617,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block = self.bounds_check( block, - base_place.clone().into_place(self.hir.tcx()), + base_place.clone().into_place(self.hir.tcx(), self.hir.typeck_results()), idx, expr_span, source_info, @@ -392,6 +626,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if is_outermost_index { self.read_fake_borrows(block, fake_borrow_temps, source_info) } else { + base_place = base_place.expect_upvars_resolved(self.hir.tcx(), self.hir.typeck_results()); self.add_fake_borrows_of_base( &base_place, block, @@ -441,8 +676,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo, ) { let tcx = self.hir.tcx(); - let place_ty = - Place::ty_from(base_place.local, &base_place.projection, &self.local_decls, tcx); + let local = match base_place.base { + PlaceBase::Local(local) => local, + PlaceBase::Upvar { .. } => bug!("Expected PlacseBase::Local found Upvar") + }; + + let place_ty = Place::ty_from(local, &base_place.projection, &self.local_decls, tcx); if let ty::Slice(_) = place_ty.ty.kind() { // We need to create fake borrows to ensure that the bounds // check that we just did stays valid. Since we can't assign to @@ -452,7 +691,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match elem { ProjectionElem::Deref => { let fake_borrow_deref_ty = Place::ty_from( - base_place.local, + local, &base_place.projection[..idx], &self.local_decls, tcx, @@ -470,14 +709,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Rvalue::Ref( tcx.lifetimes.re_erased, BorrowKind::Shallow, - Place { local: base_place.local, projection }, + Place { local, projection }, ), ); fake_borrow_temps.push(fake_borrow_temp); } ProjectionElem::Index(_) => { let index_ty = Place::ty_from( - base_place.local, + local, &base_place.projection[..idx], &self.local_decls, tcx, diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 6537ba745c17..3f381f3f15e8 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -4,6 +4,7 @@ use rustc_index::vec::Idx; use crate::build::expr::category::{Category, RvalueFunc}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; +use crate::build::expr::as_place::PlaceBase; use crate::thir::*; use rustc_middle::middle::region; use rustc_middle::mir::AssertKind; @@ -393,44 +394,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) }); - let arg_place = unpack!(block = this.as_place(block, arg)); + let arg_place_builder = unpack!(block = this.as_place_builder(block, arg)); - let mutability = match arg_place.as_ref() { - PlaceRef { local, projection: &[] } => this.local_decls[local].mutability, - PlaceRef { local, projection: &[ProjectionElem::Deref] } => { - debug_assert!( - this.local_decls[local].is_ref_for_guard(), - "Unexpected capture place", - ); - this.local_decls[local].mutability - } - PlaceRef { - local, - projection: &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _)], - } - | PlaceRef { - local, - projection: - &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _), ProjectionElem::Deref], - } => { - let place = PlaceRef { local, projection: proj_base }; + let mutability = match arg_place_builder.base() { + // We are capturing a path that starts off a local variable in the parent. + // The mutability of the current capture is same as the mutability + // of the local declaration in the parent. + PlaceBase::Local(local) => this.local_decls[local].mutability, + // Parent is a closure and we are capturing a path that is captured + // by the parent itself. The mutability of the current capture + // is same as that of the capture in the parent closure. + PlaceBase::Upvar { .. } => { + let enclosing_upvars_resolved = arg_place_builder.clone().into_place( + this.hir.tcx(), + this.hir.typeck_results()); - // Not projected from the implicit `self` in a closure. - debug_assert!( - match place.local_or_deref_local() { - Some(local) => local == Local::new(1), - None => false, - }, - "Unexpected capture place" - ); - // Not in a closure - debug_assert!( - this.upvar_mutbls.len() > upvar_index.index(), - "Unexpected capture place" - ); - this.upvar_mutbls[upvar_index.index()] + match enclosing_upvars_resolved.as_ref() { + PlaceRef { local, projection: &[ProjectionElem::Field(upvar_index, _), ..] } + | PlaceRef { + local, + projection: &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..] } => { + // Not in a closure + debug_assert!( + local == Local::new(1), + "Expected local to be Local(1), found {:?}", + local + ); + // Not in a closure + debug_assert!( + this.upvar_mutbls.len() > upvar_index.index(), + "Unexpected capture place, upvar_mutbls={:#?}, upvar_index={:?}", + this.upvar_mutbls, upvar_index + ); + this.upvar_mutbls[upvar_index.index()] + } + _ => bug!("Unexpected capture place"), + } } - _ => bug!("Unexpected capture place"), }; let borrow_kind = match mutability { @@ -438,6 +438,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, }; + let arg_place = arg_place_builder.into_place( + this.hir.tcx(), + this.hir.typeck_results()); + this.cfg.push_assign( block, source_info, diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 1f70fdb5ae30..09281799041e 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -40,11 +40,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let expr_span = expr.span; let source_info = this.source_info(expr_span); - let expr_is_block_or_scope = match expr.kind { - ExprKind::Block { .. } => true, - ExprKind::Scope { .. } => true, - _ => false, - }; + let expr_is_block_or_scope = matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. }); let schedule_drop = move |this: &mut Self| { if let Some(drop_scope) = scope { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 7ffdb7e33fb1..2e108d480932 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -228,6 +228,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard: Option<&Guard<'tcx>>, fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, + arm_span: Option, arm_scope: Option, ) -> BasicBlock { if candidate.subcandidates.is_empty() { @@ -239,6 +240,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard, fake_borrow_temps, scrutinee_span, + arm_span, true, ) } else { @@ -274,6 +276,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard, &fake_borrow_temps, scrutinee_span, + arm_span, schedule_drops, ); if arm_scope.is_none() { @@ -436,6 +439,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &fake_borrow_temps, irrefutable_pat.span, None, + None, ) .unit() } @@ -817,11 +821,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// For an example of a case where we set `otherwise_block`, even for an /// exhaustive match consider: /// + /// ```rust /// match x { /// (true, true) => (), /// (_, false) => (), /// (false, true) => (), /// } + /// ``` /// /// For this match, we check if `x.0` matches `true` (for the first /// arm). If that's false, we check `x.1`. If it's `true` we check if @@ -935,11 +941,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Link up matched candidates. For example, if we have something like /// this: /// + /// ```rust /// ... /// Some(x) if cond => ... /// Some(x) => ... /// Some(x) if cond => ... /// ... + /// ``` /// /// We generate real edges from: /// * `start_block` to the `prebinding_block` of the first pattern, @@ -1517,7 +1525,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Initializes each of the bindings from the candidate by /// moving/copying/ref'ing the source as appropriate. Tests the guard, if /// any, and then branches to the arm. Returns the block for the case where - /// the guard fails. + /// the guard succeeds. /// /// Note: we do not check earlier that if there is a guard, /// there cannot be move bindings. We avoid a use-after-move by only @@ -1529,6 +1537,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard: Option<&Guard<'tcx>>, fake_borrows: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, + arm_span: Option, schedule_drops: bool, ) -> BasicBlock { debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate); @@ -1659,15 +1668,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); } - // the block to branch to if the guard fails; if there is no - // guard, this block is simply unreachable - let guard = match guard { - Guard::If(e) => self.hir.mirror(e.clone()), + let (guard_span, (post_guard_block, otherwise_post_guard_block)) = match guard { + Guard::If(e) => { + let e = self.hir.mirror(e.clone()); + let source_info = self.source_info(e.span); + (e.span, self.test_bool(block, e, source_info)) + }, + Guard::IfLet(pat, scrutinee) => { + let scrutinee_span = scrutinee.span(); + let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee.clone(), scrutinee_span)); + let mut guard_candidate = Candidate::new(scrutinee_place, &pat, false); + let wildcard = Pat::wildcard_from_ty(pat.ty); + let mut otherwise_candidate = Candidate::new(scrutinee_place, &wildcard, false); + let fake_borrow_temps = + self.lower_match_tree(block, pat.span, false, &mut [&mut guard_candidate, &mut otherwise_candidate]); + self.declare_bindings( + None, + pat.span.to(arm_span.unwrap()), + pat, + ArmHasGuard(false), + Some((Some(&scrutinee_place), scrutinee.span())), + ); + let post_guard_block = self.bind_pattern( + self.source_info(pat.span), + guard_candidate, + None, + &fake_borrow_temps, + scrutinee.span(), + None, + None, + ); + let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap(); + (scrutinee_span, (post_guard_block, otherwise_post_guard_block)) + } }; - let source_info = self.source_info(guard.span); - let guard_end = self.source_info(tcx.sess.source_map().end_point(guard.span)); - let (post_guard_block, otherwise_post_guard_block) = - self.test_bool(block, guard, source_info); + let source_info = self.source_info(guard_span); + let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span)); let guard_frame = self.guard_context.pop().unwrap(); debug!("Exiting guard building context with locals: {:?}", guard_frame); @@ -1925,7 +1961,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - place: for_arm_body.into(), + value: VarDebugInfoContents::Place(for_arm_body.into()), }); let locals = if has_guard.0 { let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { @@ -1944,7 +1980,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - place: ref_for_guard.into(), + value: VarDebugInfoContents::Place(ref_for_guard.into()), }); LocalsForNode::ForGuard { ref_for_guard, for_arm_body } } else { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 34ef4ed78fd0..996615995259 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{GeneratorKind, HirIdMap, Node}; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::ty::subst::Subst; @@ -808,7 +809,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name: ident.name, source_info, - place: arg_local.into(), + value: VarDebugInfoContents::Place(arg_local.into()), }); } } @@ -823,7 +824,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // with the closure's DefId. Here, we run through that vec of UpvarIds for // the given closure and use the necessary information to create upvar // debuginfo and to fill `self.upvar_mutbls`. - if let Some(upvars) = hir_typeck_results.closure_captures.get(&fn_def_id) { + if hir_typeck_results.closure_min_captures.get(&fn_def_id).is_some() { let closure_env_arg = Local::new(1); let mut closure_env_projs = vec![]; let mut closure_ty = self.local_decls[closure_env_arg].ty; @@ -836,15 +837,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty), }; - let upvar_tys = upvar_substs.upvar_tys(); - let upvars_with_tys = upvars.iter().zip(upvar_tys); - self.upvar_mutbls = upvars_with_tys + let capture_tys = upvar_substs.upvar_tys(); + let captures_with_tys = hir_typeck_results + .closure_min_captures_flattened(fn_def_id) + .zip(capture_tys); + + self.upvar_mutbls = captures_with_tys .enumerate() - .map(|(i, ((&var_id, &upvar_id), ty))| { - let capture = hir_typeck_results.upvar_capture(upvar_id); + .map(|(i, (captured_place, ty))| { + let capture = captured_place.info.capture_kind; + let var_id = match captured_place.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + _ => bug!("Expected an upvar") + }; let mut mutability = Mutability::Not; - let mut name = kw::Invalid; + + // FIXME(project-rfc-2229#8): Store more precise information + let mut name = kw::Empty; if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) { if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { name = ident.name; @@ -872,10 +882,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: SourceInfo::outermost(tcx_hir.span(var_id)), - place: Place { + value: VarDebugInfoContents::Place(Place { local: closure_env_arg, projection: tcx.intern_place_elems(&projs), - }, + }), }); mutability diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index e137f77ffbb0..62d2212d1096 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -616,8 +616,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("stmt_expr Break val block_context.push(SubExpr)"); self.block_context.push(BlockFrame::SubExpr); unpack!(block = self.into(destination, dest_scope, block, value)); - dest_scope - .map(|scope| self.unschedule_drop(scope, destination.as_local().unwrap())); + if let Some(scope) = dest_scope { + self.unschedule_drop(scope, destination.as_local().unwrap()) + }; self.block_context.pop(); } else { self.cfg.push_assign_unit(block, source_info, destination, self.hir.tcx()) @@ -1196,6 +1197,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { arm.guard.as_ref(), &fake_borrow_temps, scrutinee_span, + Some(arm.span), Some(arm.scope), ); diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index e934d8ed9da5..417f9bded098 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -6,6 +6,8 @@ use crate::thir::*; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_index::vec::Idx; +use rustc_middle::hir::place::PlaceBase as HirPlaceBase; +use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::BorrowKind; use rustc_middle::ty::adjustment::{ @@ -386,14 +388,12 @@ fn make_mirror_unadjusted<'a, 'tcx>( span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty); } }; + let upvars = cx .typeck_results() - .closure_captures - .get(&def_id) - .iter() - .flat_map(|upvars| upvars.iter()) + .closure_min_captures_flattened(def_id) .zip(substs.upvar_tys()) - .map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty)) + .map(|(captured_place, ty)| capture_upvar(cx, expr, captured_place, ty)) .collect(); ExprKind::Closure { closure_id: def_id, substs, upvars, movability } } @@ -776,10 +776,10 @@ impl ToBorrowKind for hir::Mutability { fn convert_arm<'tcx>(cx: &mut Cx<'_, 'tcx>, arm: &'tcx hir::Arm<'tcx>) -> Arm<'tcx> { Arm { pattern: cx.pattern_from_hir(&arm.pat), - guard: match arm.guard { - Some(hir::Guard::If(ref e)) => Some(Guard::If(e.to_ref())), - _ => None, - }, + guard: arm.guard.as_ref().map(|g| match g { + hir::Guard::If(ref e) => Guard::If(e.to_ref()), + hir::Guard::IfLet(ref pat, ref e) => Guard::IfLet(cx.pattern_from_hir(pat), e.to_ref()), + }), body: arm.body.to_ref(), lint_level: LintLevel::Explicit(arm.hir_id), scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node }, @@ -981,27 +981,55 @@ fn overloaded_place<'a, 'tcx>( ExprKind::Deref { arg: ref_expr.to_ref() } } -fn capture_upvar<'tcx>( +fn capture_upvar<'a, 'tcx>( cx: &mut Cx<'_, 'tcx>, closure_expr: &'tcx hir::Expr<'tcx>, - var_hir_id: hir::HirId, + captured_place: &'a ty::CapturedPlace<'tcx>, upvar_ty: Ty<'tcx>, ) -> ExprRef<'tcx> { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id), - }; - let upvar_capture = cx.typeck_results().upvar_capture(upvar_id); + let upvar_capture = captured_place.info.capture_kind; let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); - let var_ty = cx.typeck_results().node_type(var_hir_id); - let captured_var = Expr { + let var_ty = captured_place.place.base_ty; + + // The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path + // as it's seen for use within the closure and not at the time of closure creation. + // + // That is we see expect to see it start from a captured upvar and not something that is local + // to the closure's parent. + let var_hir_id = match captured_place.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + base => bug!("Expected an upvar, found {:?}", base), + }; + + let mut captured_place_expr = Expr { temp_lifetime, ty: var_ty, span: closure_expr.span, kind: convert_var(cx, var_hir_id), }; + + for proj in captured_place.place.projections.iter() { + let kind = match proj.kind { + HirProjectionKind::Deref => ExprKind::Deref { arg: captured_place_expr.to_ref() }, + HirProjectionKind::Field(field, ..) => { + // Variant index will always be 0, because for multi-variant + // enums, we capture the enum entirely. + ExprKind::Field { + lhs: captured_place_expr.to_ref(), + name: Field::new(field as usize), + } + } + HirProjectionKind::Index | HirProjectionKind::Subslice => { + // We don't capture these projections, so we can ignore them here + continue; + } + }; + + captured_place_expr = Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind }; + } + match upvar_capture { - ty::UpvarCapture::ByValue(_) => captured_var.to_ref(), + ty::UpvarCapture::ByValue(_) => captured_place_expr.to_ref(), ty::UpvarCapture::ByRef(upvar_borrow) => { let borrow_kind = match upvar_borrow.kind { ty::BorrowKind::ImmBorrow => BorrowKind::Shared, @@ -1012,7 +1040,7 @@ fn capture_upvar<'tcx>( temp_lifetime, ty: upvar_ty, span: closure_expr.span, - kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() }, + kind: ExprKind::Borrow { borrow_kind, arg: captured_place_expr.to_ref() }, } .to_ref() } diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index 1a901746d508..ace9cad4d299 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -344,6 +344,7 @@ crate struct Arm<'tcx> { #[derive(Clone, Debug)] crate enum Guard<'tcx> { If(ExprRef<'tcx>), + IfLet(Pat<'tcx>, ExprRef<'tcx>), } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 97edbd83b89c..db817b378f97 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -164,10 +164,20 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { for arm in arms { // Check the arm for some things unrelated to exhaustiveness. self.check_patterns(&arm.pat); + if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard { + self.check_patterns(pat); + } } let mut cx = self.new_cx(scrut.hir_id); + for arm in arms { + if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard { + let tpat = self.lower_pattern(&mut cx, pat, &mut false).0; + check_if_let_guard(&mut cx, &tpat, pat.hir_id); + } + } + let mut have_errors = false; let arms: Vec<_> = arms @@ -360,12 +370,28 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir:: let msg = match source { hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern", hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern", + hir::MatchSource::IfLetGuardDesugar => "irrefutable if-let guard", _ => bug!(), }; lint.build(msg).emit() }); } +fn check_if_let_guard<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, + pat: &'p super::Pat<'tcx>, + pat_id: HirId, +) { + let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }]; + let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty); + report_arm_reachability(&cx, &report, hir::MatchSource::IfLetGuardDesugar); + + if report.non_exhaustiveness_witnesses.is_empty() { + // The match is exhaustive, i.e. the if let pattern is irrefutable. + irrefutable_let_pattern(cx.tcx, pat.span, pat_id, hir::MatchSource::IfLetGuardDesugar) + } +} + /// Report unreachable arms, if any. fn report_arm_reachability<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, @@ -390,6 +416,11 @@ fn report_arm_reachability<'p, 'tcx>( } } + hir::MatchSource::IfLetGuardDesugar => { + assert_eq!(arm_index, 0); + unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None); + } + hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall); } @@ -402,7 +433,7 @@ fn report_arm_reachability<'p, 'tcx>( Useful(unreachables) if unreachables.is_empty() => {} // The arm is reachable, but contains unreachable subpatterns (from or-patterns). Useful(unreachables) => { - let mut unreachables: Vec<_> = unreachables.iter().flatten().copied().collect(); + let mut unreachables: Vec<_> = unreachables.iter().collect(); // Emit lints in the order in which they occur in the file. unreachables.sort_unstable(); for span in unreachables { diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 8b21a9b24e6e..db2fa5730a33 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -1,6 +1,47 @@ -//! This module provides functions to deconstruct and reconstruct patterns into a constructor -//! applied to some fields. This is used by the `_match` module to compute pattern -//! usefulness/exhaustiveness. +//! [`super::usefulness`] explains most of what is happening in this file. As explained there, +//! values and patterns are made from constructors applied to fields. This file defines a +//! `Constructor` enum, a `Fields` struct, and various operations to manipulate them and convert +//! them from/to patterns. +//! +//! There's one idea that is not detailed in [`super::usefulness`] because the details are not +//! needed there: _constructor splitting_. +//! +//! # Constructor splitting +//! +//! The idea is as follows: given a constructor `c` and a matrix, we want to specialize in turn +//! with all the value constructors that are covered by `c`, and compute usefulness for each. +//! Instead of listing all those constructors (which is intractable), we group those value +//! constructors together as much as possible. Example: +//! +//! ``` +//! match (0, false) { +//! (0 ..=100, true) => {} // `p_1` +//! (50..=150, false) => {} // `p_2` +//! (0 ..=200, _) => {} // `q` +//! } +//! ``` +//! +//! The naive approach would try all numbers in the range `0..=200`. But we can be a lot more +//! clever: `0` and `1` for example will match the exact same rows, and return equivalent +//! witnesses. In fact all of `0..50` would. We can thus restrict our exploration to 4 +//! constructors: `0..50`, `50..=100`, `101..=150` and `151..=200`. That is enough and infinitely +//! more tractable. +//! +//! We capture this idea in a function `split(p_1 ... p_n, c)` which returns a list of constructors +//! `c'` covered by `c`. Given such a `c'`, we require that all value ctors `c''` covered by `c'` +//! return an equivalent set of witnesses after specializing and computing usefulness. +//! In the example above, witnesses for specializing by `c''` covered by `0..50` will only differ +//! in their first element. +//! +//! We usually also ask that the `c'` together cover all of the original `c`. However we allow +//! skipping some constructors as long as it doesn't change whether the resulting list of witnesses +//! is empty of not. We use this in the wildcard `_` case. +//! +//! Splitting is implemented in the [`Constructor::split`] function. We don't do splitting for +//! or-patterns; instead we just try the alternatives one-by-one. For details on splitting +//! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]; for slices, see +//! [`SplitVarLenSlice`]. + use self::Constructor::*; use self::SliceKind::*; @@ -24,7 +65,7 @@ use rustc_target::abi::{Integer, Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; use std::cmp::{self, max, min, Ordering}; -use std::iter::IntoIterator; +use std::iter::{once, IntoIterator}; use std::ops::RangeInclusive; /// An inclusive interval, used for precise integer exhaustiveness checking. @@ -161,7 +202,7 @@ impl IntRange { // 2 -------- // 2 ------- let (lo, hi) = self.boundaries(); let (other_lo, other_hi) = other.boundaries(); - lo == other_hi || hi == other_lo + (lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton() } fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { @@ -183,143 +224,56 @@ impl IntRange { Pat { ty, span: DUMMY_SP, kind: Box::new(kind) } } - /// For exhaustive integer matching, some constructors are grouped within other constructors - /// (namely integer typed values are grouped within ranges). However, when specialising these - /// constructors, we want to be specialising for the underlying constructors (the integers), not - /// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would - /// mean creating a separate constructor for every single value in the range, which is clearly - /// impractical. However, observe that for some ranges of integers, the specialisation will be - /// identical across all values in that range (i.e., there are equivalence classes of ranges of - /// constructors based on their `U(S(c, P), S(c, p))` outcome). These classes are grouped by - /// the patterns that apply to them (in the matrix `P`). We can split the range whenever the - /// patterns that apply to that range (specifically: the patterns that *intersect* with that range) - /// change. - /// Our solution, therefore, is to split the range constructor into subranges at every single point - /// the group of intersecting patterns changes (using the method described below). - /// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching - /// on actual integers. The nice thing about this is that the number of subranges is linear in the - /// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't - /// need to be worried about matching over gargantuan ranges. - /// - /// Essentially, given the first column of a matrix representing ranges, looking like the following: - /// - /// |------| |----------| |-------| || - /// |-------| |-------| |----| || - /// |---------| - /// - /// We split the ranges up into equivalence classes so the ranges are no longer overlapping: - /// - /// |--|--|||-||||--||---|||-------| |-|||| || - /// - /// The logic for determining how to split the ranges is fairly straightforward: we calculate - /// boundaries for each interval range, sort them, then create constructors for each new interval - /// between every pair of boundary points. (This essentially sums up to performing the intuitive - /// merging operation depicted above.) - fn split<'p, 'tcx>( + /// Lint on likely incorrect range patterns (#63987) + pub(super) fn lint_overlapping_range_endpoints<'a, 'tcx: 'a>( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - hir_id: Option, - ) -> SmallVec<[Constructor<'tcx>; 1]> { - /// Represents a border between 2 integers. Because the intervals spanning borders - /// must be able to cover every integer, we need to be able to represent - /// 2^128 + 1 such borders. - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] - enum Border { - JustBefore(u128), - AfterMax, - } - - // A function for extracting the borders of an integer interval. - fn range_borders(r: IntRange) -> impl Iterator { - let (lo, hi) = r.range.into_inner(); - let from = Border::JustBefore(lo); - let to = match hi.checked_add(1) { - Some(m) => Border::JustBefore(m), - None => Border::AfterMax, - }; - vec![from, to].into_iter() - } - - // Collect the span and range of all the intersecting ranges to lint on likely - // incorrect range patterns. (#63987) - let mut overlaps = vec![]; - let row_len = pcx.matrix.column_count().unwrap_or(0); - // `borders` is the set of borders between equivalence classes: each equivalence - // class lies between 2 borders. - let row_borders = pcx - .matrix - .head_ctors_and_spans(pcx.cx) - .filter_map(|(ctor, span)| Some((ctor.as_int_range()?, span))) - .filter_map(|(range, span)| { - let intersection = self.intersection(&range); - let should_lint = self.suspicious_intersection(&range); - if let (Some(range), 1, true) = (&intersection, row_len, should_lint) { - // FIXME: for now, only check for overlapping ranges on simple range - // patterns. Otherwise with the current logic the following is detected - // as overlapping: - // ``` - // match (0u8, true) { - // (0 ..= 125, false) => {} - // (125 ..= 255, true) => {} - // _ => {} - // } - // ``` - overlaps.push((range.clone(), span)); - } - intersection - }) - .flat_map(range_borders); - let self_borders = range_borders(self.clone()); - let mut borders: Vec<_> = row_borders.chain(self_borders).collect(); - borders.sort_unstable(); - - self.lint_overlapping_patterns(pcx, hir_id, overlaps); - - // We're going to iterate through every adjacent pair of borders, making sure that - // each represents an interval of nonnegative length, and convert each such - // interval into a constructor. - borders - .array_windows() - .filter_map(|&pair| match pair { - [Border::JustBefore(n), Border::JustBefore(m)] => { - if n < m { - Some(n..=(m - 1)) - } else { - None - } - } - [Border::JustBefore(n), Border::AfterMax] => Some(n..=u128::MAX), - [Border::AfterMax, _] => None, - }) - .map(|range| IntRange { range }) - .map(IntRange) - .collect() - } - - fn lint_overlapping_patterns( - &self, - pcx: PatCtxt<'_, '_, '_>, - hir_id: Option, - overlaps: Vec<(IntRange, Span)>, + pcx: PatCtxt<'_, '_, 'tcx>, + ctors: impl Iterator, Span)>, + column_count: usize, + hir_id: HirId, ) { - if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { + if self.is_singleton() { + return; + } + + if column_count != 1 { + // FIXME: for now, only check for overlapping ranges on simple range + // patterns. Otherwise with the current logic the following is detected + // as overlapping: + // ``` + // match (0u8, true) { + // (0 ..= 125, false) => {} + // (125 ..= 255, true) => {} + // _ => {} + // } + // ``` + return; + } + + let overlaps: Vec<_> = ctors + .filter_map(|(ctor, span)| Some((ctor.as_int_range()?, span))) + .filter(|(range, _)| self.suspicious_intersection(range)) + .map(|(range, span)| (self.intersection(&range).unwrap(), span)) + .collect(); + + if !overlaps.is_empty() { pcx.cx.tcx.struct_span_lint_hir( - lint::builtin::OVERLAPPING_PATTERNS, + lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, hir_id, pcx.span, |lint| { - let mut err = lint.build("multiple patterns covering the same range"); - err.span_label(pcx.span, "overlapping patterns"); + let mut err = lint.build("multiple patterns overlap on their endpoints"); for (int_range, span) in overlaps { - // Use the real type for user display of the ranges: err.span_label( span, &format!( - "this range overlaps on `{}`", - int_range.to_pat(pcx.cx.tcx, pcx.ty), + "this range overlaps on `{}`...", + int_range.to_pat(pcx.cx.tcx, pcx.ty) ), ); } + err.span_label(pcx.span, "... with this range"); + err.note("you likely meant to write mutually exclusive ranges"); err.emit(); }, ); @@ -339,6 +293,101 @@ impl IntRange { } } +/// Represents a border between 2 integers. Because the intervals spanning borders must be able to +/// cover every integer, we need to be able to represent 2^128 + 1 such borders. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +enum IntBorder { + JustBefore(u128), + AfterMax, +} + +/// A range of integers that is partitioned into disjoint subranges. This does constructor +/// splitting for integer ranges as explained at the top of the file. +/// +/// This is fed multiple ranges, and returns an output that covers the input, but is split so that +/// the only intersections between an output range and a seen range are inclusions. No output range +/// straddles the boundary of one of the inputs. +/// +/// The following input: +/// ``` +/// |-------------------------| // `self` +/// |------| |----------| |----| +/// |-------| |-------| +/// ``` +/// would be iterated over as follows: +/// ``` +/// ||---|--||-|---|---|---|--| +/// ``` +#[derive(Debug, Clone)] +struct SplitIntRange { + /// The range we are splitting + range: IntRange, + /// The borders of ranges we have seen. They are all contained within `range`. This is kept + /// sorted. + borders: Vec, +} + +impl SplitIntRange { + fn new(range: IntRange) -> Self { + SplitIntRange { range, borders: Vec::new() } + } + + /// Internal use + fn to_borders(r: IntRange) -> [IntBorder; 2] { + use IntBorder::*; + let (lo, hi) = r.boundaries(); + let lo = JustBefore(lo); + let hi = match hi.checked_add(1) { + Some(m) => JustBefore(m), + None => AfterMax, + }; + [lo, hi] + } + + /// Add ranges relative to which we split. + fn split(&mut self, ranges: impl Iterator) { + let this_range = &self.range; + let included_ranges = ranges.filter_map(|r| this_range.intersection(&r)); + let included_borders = included_ranges.flat_map(|r| { + let borders = Self::to_borders(r); + once(borders[0]).chain(once(borders[1])) + }); + self.borders.extend(included_borders); + self.borders.sort_unstable(); + } + + /// Iterate over the contained ranges. + fn iter<'a>(&'a self) -> impl Iterator + Captures<'a> { + use IntBorder::*; + + let self_range = Self::to_borders(self.range.clone()); + // Start with the start of the range. + let mut prev_border = self_range[0]; + self.borders + .iter() + .copied() + // End with the end of the range. + .chain(once(self_range[1])) + // List pairs of adjacent borders. + .map(move |border| { + let ret = (prev_border, border); + prev_border = border; + ret + }) + // Skip duplicates. + .filter(|(prev_border, border)| prev_border != border) + // Finally, convert to ranges. + .map(|(prev_border, border)| { + let range = match (prev_border, border) { + (JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1), + (JustBefore(n), AfterMax) => n..=u128::MAX, + _ => unreachable!(), // Ruled out by the sorting and filtering we did + }; + IntRange { range } + }) + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum SliceKind { /// Patterns of length `n` (`[x, y]`). @@ -391,132 +440,144 @@ impl Slice { self.kind.arity() } - /// The exhaustiveness-checking paper does not include any details on - /// checking variable-length slice patterns. However, they may be - /// matched by an infinite collection of fixed-length array patterns. - /// - /// Checking the infinite set directly would take an infinite amount - /// of time. However, it turns out that for each finite set of - /// patterns `P`, all sufficiently large array lengths are equivalent: - /// - /// Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies - /// to exactly the subset `Pₜ` of `P` can be transformed to a slice - /// `sₘ` for each sufficiently-large length `m` that applies to exactly - /// the same subset of `P`. - /// - /// Because of that, each witness for reachability-checking of one - /// of the sufficiently-large lengths can be transformed to an - /// equally-valid witness of any other length, so we only have - /// to check slices of the "minimal sufficiently-large length" - /// and less. - /// - /// Note that the fact that there is a *single* `sₘ` for each `m` - /// not depending on the specific pattern in `P` is important: if - /// you look at the pair of patterns - /// `[true, ..]` - /// `[.., false]` - /// Then any slice of length ≥1 that matches one of these two - /// patterns can be trivially turned to a slice of any - /// other length ≥1 that matches them and vice-versa, - /// but the slice of length 2 `[false, true]` that matches neither - /// of these patterns can't be turned to a slice from length 1 that - /// matches neither of these patterns, so we have to consider - /// slices from length 2 there. - /// - /// Now, to see that that length exists and find it, observe that slice - /// patterns are either "fixed-length" patterns (`[_, _, _]`) or - /// "variable-length" patterns (`[_, .., _]`). - /// - /// For fixed-length patterns, all slices with lengths *longer* than - /// the pattern's length have the same outcome (of not matching), so - /// as long as `L` is greater than the pattern's length we can pick - /// any `sₘ` from that length and get the same result. - /// - /// For variable-length patterns, the situation is more complicated, - /// because as seen above the precise value of `sₘ` matters. - /// - /// However, for each variable-length pattern `p` with a prefix of length - /// `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last - /// `slₚ` elements are examined. - /// - /// Therefore, as long as `L` is positive (to avoid concerns about empty - /// types), all elements after the maximum prefix length and before - /// the maximum suffix length are not examined by any variable-length - /// pattern, and therefore can be added/removed without affecting - /// them - creating equivalent patterns from any sufficiently-large - /// length. - /// - /// Of course, if fixed-length patterns exist, we must be sure - /// that our length is large enough to miss them all, so - /// we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))` - /// - /// for example, with the above pair of patterns, all elements - /// but the first and last can be added/removed, so any - /// witness of length ≥2 (say, `[false, false, true]`) can be - /// turned to a witness from any other length ≥2. - fn split<'p, 'tcx>(self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { - let (self_prefix, self_suffix) = match self.kind { - VarLen(self_prefix, self_suffix) => (self_prefix, self_suffix), - _ => return smallvec![Slice(self)], - }; - - let head_ctors = pcx.matrix.head_ctors(pcx.cx).filter(|c| !c.is_wildcard()); - - let mut max_prefix_len = self_prefix; - let mut max_suffix_len = self_suffix; - let mut max_fixed_len = 0; - - for ctor in head_ctors { - if let Slice(slice) = ctor { - match slice.kind { - FixedLen(len) => { - max_fixed_len = cmp::max(max_fixed_len, len); - } - VarLen(prefix, suffix) => { - max_prefix_len = cmp::max(max_prefix_len, prefix); - max_suffix_len = cmp::max(max_suffix_len, suffix); - } - } - } else { - bug!("unexpected ctor for slice type: {:?}", ctor); - } - } - - // For diagnostics, we keep the prefix and suffix lengths separate, so in the case - // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly, - // so that `L = max_prefix_len + max_suffix_len`. - if max_fixed_len + 1 >= max_prefix_len + max_suffix_len { - // The subtraction can't overflow thanks to the above check. - // The new `max_prefix_len` is also guaranteed to be larger than its previous - // value. - max_prefix_len = max_fixed_len + 1 - max_suffix_len; - } - - let final_slice = VarLen(max_prefix_len, max_suffix_len); - let final_slice = Slice::new(self.array_len, final_slice); - match self.array_len { - Some(_) => smallvec![Slice(final_slice)], - None => { - // `self` originally covered the range `(self.arity()..infinity)`. We split that - // range into two: lengths smaller than `final_slice.arity()` are treated - // independently as fixed-lengths slices, and lengths above are captured by - // `final_slice`. - let smaller_lengths = (self.arity()..final_slice.arity()).map(FixedLen); - smaller_lengths - .map(|kind| Slice::new(self.array_len, kind)) - .chain(Some(final_slice)) - .map(Slice) - .collect() - } - } - } - /// See `Constructor::is_covered_by` fn is_covered_by(self, other: Self) -> bool { other.kind.covers_length(self.arity()) } } +/// This computes constructor splitting for variable-length slices, as explained at the top of the +/// file. +/// +/// A slice pattern `[x, .., y]` behaves like the infinite or-pattern `[x, y] | [x, _, y] | [x, _, +/// _, y] | ...`. The corresponding value constructors are fixed-length array constructors above a +/// given minimum length. We obviously can't list this infinitude of constructors. Thankfully, +/// it turns out that for each finite set of slice patterns, all sufficiently large array lengths +/// are equivalent. +/// +/// Let's look at an example, where we are trying to split the last pattern: +/// ``` +/// match x { +/// [true, true, ..] => {} +/// [.., false, false] => {} +/// [..] => {} +/// } +/// ``` +/// Here are the results of specialization for the first few lengths: +/// ``` +/// // length 0 +/// [] => {} +/// // length 1 +/// [_] => {} +/// // length 2 +/// [true, true] => {} +/// [false, false] => {} +/// [_, _] => {} +/// // length 3 +/// [true, true, _ ] => {} +/// [_, false, false] => {} +/// [_, _, _ ] => {} +/// // length 4 +/// [true, true, _, _ ] => {} +/// [_, _, false, false] => {} +/// [_, _, _, _ ] => {} +/// // length 5 +/// [true, true, _, _, _ ] => {} +/// [_, _, _, false, false] => {} +/// [_, _, _, _, _ ] => {} +/// ``` +/// +/// If we went above length 5, we would simply be inserting more columns full of wildcards in the +/// middle. This means that the set of witnesses for length `l >= 5` if equivalent to the set for +/// any other `l' >= 5`: simply add or remove wildcards in the middle to convert between them. +/// +/// This applies to any set of slice patterns: there will be a length `L` above which all lengths +/// behave the same. This is exactly what we need for constructor splitting. Therefore a +/// variable-length slice can be split into a variable-length slice of minimal length `L`, and many +/// fixed-length slices of lengths `< L`. +/// +/// For each variable-length pattern `p` with a prefix of length `plₚ` and suffix of length `slₚ`, +/// only the first `plₚ` and the last `slₚ` elements are examined. Therefore, as long as `L` is +/// positive (to avoid concerns about empty types), all elements after the maximum prefix length +/// and before the maximum suffix length are not examined by any variable-length pattern, and +/// therefore can be added/removed without affecting them - creating equivalent patterns from any +/// sufficiently-large length. +/// +/// Of course, if fixed-length patterns exist, we must be sure that our length is large enough to +/// miss them all, so we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))` +/// +/// `max_slice` below will be made to have arity `L`. +#[derive(Debug)] +struct SplitVarLenSlice { + /// If the type is an array, this is its size. + array_len: Option, + /// The arity of the input slice. + arity: u64, + /// The smallest slice bigger than any slice seen. `max_slice.arity()` is the length `L` + /// described above. + max_slice: SliceKind, +} + +impl SplitVarLenSlice { + fn new(prefix: u64, suffix: u64, array_len: Option) -> Self { + SplitVarLenSlice { array_len, arity: prefix + suffix, max_slice: VarLen(prefix, suffix) } + } + + /// Pass a set of slices relative to which to split this one. + fn split(&mut self, slices: impl Iterator) { + let (max_prefix_len, max_suffix_len) = match &mut self.max_slice { + VarLen(prefix, suffix) => (prefix, suffix), + FixedLen(_) => return, // No need to split + }; + // We grow `self.max_slice` to be larger than all slices encountered, as described above. + // For diagnostics, we keep the prefix and suffix lengths separate, but grow them so that + // `L = max_prefix_len + max_suffix_len`. + let mut max_fixed_len = 0; + for slice in slices { + match slice { + FixedLen(len) => { + max_fixed_len = cmp::max(max_fixed_len, len); + } + VarLen(prefix, suffix) => { + *max_prefix_len = cmp::max(*max_prefix_len, prefix); + *max_suffix_len = cmp::max(*max_suffix_len, suffix); + } + } + } + // We want `L = max(L, max_fixed_len + 1)`, modulo the fact that we keep prefix and + // suffix separate. + if max_fixed_len + 1 >= *max_prefix_len + *max_suffix_len { + // The subtraction can't overflow thanks to the above check. + // The new `max_prefix_len` is larger than its previous value. + *max_prefix_len = max_fixed_len + 1 - *max_suffix_len; + } + + // We cap the arity of `max_slice` at the array size. + match self.array_len { + Some(len) if self.max_slice.arity() >= len => self.max_slice = FixedLen(len), + _ => {} + } + } + + /// Iterate over the partition of this slice. + fn iter<'a>(&'a self) -> impl Iterator + Captures<'a> { + let smaller_lengths = match self.array_len { + // The only admissible fixed-length slice is one of the array size. Whether `max_slice` + // is fixed-length or variable-length, it will be the only relevant slice to output + // here. + Some(_) => (0..0), // empty range + // We cover all arities in the range `(self.arity..infinity)`. We split that range into + // two: lengths smaller than `max_slice.arity()` are treated independently as + // fixed-lengths slices, and lengths above are captured by `max_slice`. + None => self.arity..self.max_slice.arity(), + }; + smaller_lengths + .map(FixedLen) + .chain(once(self.max_slice)) + .map(move |kind| Slice::new(self.array_len, kind)) + } +} + /// A value can be decomposed into a constructor applied to some fields. This struct represents /// the constructor. See also `Fields`. /// @@ -546,6 +607,9 @@ pub(super) enum Constructor<'tcx> { /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. NonExhaustive, + /// Stands for constructors that are not seen in the matrix, as explained in the documentation + /// for [`SplitWildcard`]. + Missing, /// Wildcard pattern. Wildcard, } @@ -652,48 +716,41 @@ impl<'tcx> Constructor<'tcx> { /// This function may discard some irrelevant constructors if this preserves behavior and /// diagnostics. Eg. for the `_` case, we ignore the constructors already present in the /// matrix, unless all of them are. - /// - /// `hir_id` is `None` when we're evaluating the wildcard pattern. In that case we do not want - /// to lint for overlapping ranges. - pub(super) fn split<'p>( + pub(super) fn split<'a>( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - hir_id: Option, - ) -> SmallVec<[Self; 1]> { - debug!("Constructor::split({:#?}, {:#?})", self, pcx.matrix); + pcx: PatCtxt<'_, '_, 'tcx>, + ctors: impl Iterator> + Clone, + ) -> SmallVec<[Self; 1]> + where + 'tcx: 'a, + { + debug!("Constructor::split({:#?})", self); match self { - Wildcard => Constructor::split_wildcard(pcx), + Wildcard => { + let mut split_wildcard = SplitWildcard::new(pcx); + split_wildcard.split(pcx, ctors); + split_wildcard.into_ctors(pcx) + } // Fast-track if the range is trivial. In particular, we don't do the overlapping // ranges check. - IntRange(ctor_range) if !ctor_range.is_singleton() => ctor_range.split(pcx, hir_id), - Slice(slice @ Slice { kind: VarLen(..), .. }) => slice.split(pcx), + IntRange(ctor_range) if !ctor_range.is_singleton() => { + let mut split_range = SplitIntRange::new(ctor_range.clone()); + let int_ranges = ctors.filter_map(|ctor| ctor.as_int_range()); + split_range.split(int_ranges.cloned()); + split_range.iter().map(IntRange).collect() + } + &Slice(Slice { kind: VarLen(self_prefix, self_suffix), array_len }) => { + let mut split_self = SplitVarLenSlice::new(self_prefix, self_suffix, array_len); + let slices = ctors.filter_map(|c| c.as_slice()).map(|s| s.kind); + split_self.split(slices); + split_self.iter().map(Slice).collect() + } // Any other constructor can be used unchanged. _ => smallvec![self.clone()], } } - /// For wildcards, there are two groups of constructors: there are the constructors actually - /// present in the matrix (`head_ctors`), and the constructors not present (`missing_ctors`). - /// Two constructors that are not in the matrix will either both be caught (by a wildcard), or - /// both not be caught. Therefore we can keep the missing constructors grouped together. - fn split_wildcard<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Self; 1]> { - // Missing constructors are those that are not matched by any non-wildcard patterns in the - // current column. We only fully construct them on-demand, because they're rarely used and - // can be big. - let missing_ctors = MissingConstructors::new(pcx); - if missing_ctors.is_empty(pcx) { - // All the constructors are present in the matrix, so we just go through them all. - // We must also split them first. - missing_ctors.all_ctors - } else { - // Some constructors are missing, thus we can specialize with the wildcard constructor, - // which will stand for those constructors that are missing, and behaves like any of - // them. - smallvec![Wildcard] - } - } - /// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`. /// For the simple cases, this is simply checking for equality. For the "grouped" constructors, /// this checks for inclusion. @@ -704,8 +761,8 @@ impl<'tcx> Constructor<'tcx> { match (self, other) { // Wildcards cover anything (_, Wildcard) => true, - // Wildcards are only covered by wildcards - (Wildcard, _) => false, + // The missing ctors are not covered by anything in the matrix except wildcards. + (Missing | Wildcard, _) => false, (Single, Single) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, @@ -778,247 +835,253 @@ impl<'tcx> Constructor<'tcx> { .any(|other| slice.is_covered_by(other)), // This constructor is never covered by anything else NonExhaustive => false, - Str(..) | FloatRange(..) | Opaque | Wildcard => { + Str(..) | FloatRange(..) | Opaque | Missing | Wildcard => { span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self) } } } } -/// This determines the set of all possible constructors of a pattern matching -/// values of type `left_ty`. For vectors, this would normally be an infinite set -/// but is instead bounded by the maximum fixed length of slice patterns in -/// the column of patterns being analyzed. +/// A wildcard constructor that we split relative to the constructors in the matrix, as explained +/// at the top of the file. /// -/// We make sure to omit constructors that are statically impossible. E.g., for -/// `Option`, we do not include `Some(_)` in the returned list of constructors. -/// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by -/// `cx.is_uninhabited()`). -fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec> { - debug!("all_constructors({:?})", pcx.ty); - let cx = pcx.cx; - let make_range = |start, end| { - IntRange( - // `unwrap()` is ok because we know the type is an integer. - IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(), - ) - }; - match pcx.ty.kind() { - ty::Bool => vec![make_range(0, 1)], - ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { - let len = len.eval_usize(cx.tcx, cx.param_env); - if len != 0 && cx.is_uninhabited(sub_ty) { - vec![] - } else { - vec![Slice(Slice::new(Some(len), VarLen(0, 0)))] - } - } - // Treat arrays of a constant but unknown length like slices. - ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { - let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - vec![Slice(Slice::new(None, kind))] - } - ty::Adt(def, substs) if def.is_enum() => { - // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an - // additional "unknown" constructor. - // There is no point in enumerating all possible variants, because the user can't - // actually match against them all themselves. So we always return only the fictitious - // constructor. - // E.g., in an example like: - // - // ``` - // let err: io::ErrorKind = ...; - // match err { - // io::ErrorKind::NotFound => {}, - // } - // ``` - // - // we don't want to show every possible IO error, but instead have only `_` as the - // witness. - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); - - // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it - // as though it had an "unknown" constructor to avoid exposing its emptiness. The - // exception is if the pattern is at the top level, because we want empty matches to be - // considered exhaustive. - let is_secretly_empty = def.variants.is_empty() - && !cx.tcx.features().exhaustive_patterns - && !pcx.is_top_level; - - if is_secretly_empty || is_declared_nonexhaustive { - vec![NonExhaustive] - } else if cx.tcx.features().exhaustive_patterns { - // If `exhaustive_patterns` is enabled, we exclude variants known to be - // uninhabited. - def.variants - .iter() - .filter(|v| { - !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) - .contains(cx.tcx, cx.module) - }) - .map(|v| Variant(v.def_id)) - .collect() - } else { - def.variants.iter().map(|v| Variant(v.def_id)).collect() - } - } - ty::Char => { - vec![ - // The valid Unicode Scalar Value ranges. - make_range('\u{0000}' as u128, '\u{D7FF}' as u128), - make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), - ] - } - ty::Int(_) | ty::Uint(_) - if pcx.ty.is_ptr_sized_integral() - && !cx.tcx.features().precise_pointer_size_matching => - { - // `usize`/`isize` are not allowed to be matched exhaustively unless the - // `precise_pointer_size_matching` feature is enabled. So we treat those types like - // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. - vec![NonExhaustive] - } - &ty::Int(ity) => { - let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; - let min = 1u128 << (bits - 1); - let max = min - 1; - vec![make_range(min, max)] - } - &ty::Uint(uty) => { - let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); - let max = size.truncate(u128::MAX); - vec![make_range(0, max)] - } - // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot - // expose its emptiness. The exception is if the pattern is at the top level, because we - // want empty matches to be considered exhaustive. - ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => { - vec![NonExhaustive] - } - ty::Never => vec![], - _ if cx.is_uninhabited(pcx.ty) => vec![], - ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single], - // This type is one for which we cannot list constructors, like `str` or `f64`. - _ => vec![NonExhaustive], - } -} - -// A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. +/// A constructor that is not present in the matrix rows will only be covered by the rows that have +/// wildcards. Thus we can group all of those constructors together; we call them "missing +/// constructors". Splitting a wildcard would therefore list all present constructors individually +/// (or grouped if they are integers or slices), and then all missing constructors together as a +/// group. +/// +/// However we can go further: since any constructor will match the wildcard rows, and having more +/// rows can only reduce the amount of usefulness witnesses, we can skip the present constructors +/// and only try the missing ones. +/// This will not preserve the whole list of witnesses, but will preserve whether the list is empty +/// or not. In fact this is quite natural from the point of view of diagnostics too. This is done +/// in `to_ctors`: in some cases we only return `Missing`. #[derive(Debug)] -pub(super) struct MissingConstructors<'tcx> { +pub(super) struct SplitWildcard<'tcx> { + /// Constructors seen in the matrix. + matrix_ctors: Vec>, + /// All the constructors for this type all_ctors: SmallVec<[Constructor<'tcx>; 1]>, - used_ctors: Vec>, } -impl<'tcx> MissingConstructors<'tcx> { +impl<'tcx> SplitWildcard<'tcx> { pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self { - let used_ctors: Vec> = - pcx.matrix.head_ctors(pcx.cx).cloned().filter(|c| !c.is_wildcard()).collect(); + debug!("SplitWildcard::new({:?})", pcx.ty); + let cx = pcx.cx; + let make_range = |start, end| { + IntRange( + // `unwrap()` is ok because we know the type is an integer. + IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(), + ) + }; + // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, + // arrays and slices we use ranges and variable-length slices when appropriate. + // + // If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that + // are statically impossible. E.g., for `Option`, we do not include `Some(_)` in the + // returned list of constructors. + // Invariant: this is empty if and only if the type is uninhabited (as determined by + // `cx.is_uninhabited()`). + let all_ctors = match pcx.ty.kind() { + ty::Bool => smallvec![make_range(0, 1)], + ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { + let len = len.eval_usize(cx.tcx, cx.param_env); + if len != 0 && cx.is_uninhabited(sub_ty) { + smallvec![] + } else { + smallvec![Slice(Slice::new(Some(len), VarLen(0, 0)))] + } + } + // Treat arrays of a constant but unknown length like slices. + ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { + let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + smallvec![Slice(Slice::new(None, kind))] + } + ty::Adt(def, substs) if def.is_enum() => { + // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an + // additional "unknown" constructor. + // There is no point in enumerating all possible variants, because the user can't + // actually match against them all themselves. So we always return only the fictitious + // constructor. + // E.g., in an example like: + // + // ``` + // let err: io::ErrorKind = ...; + // match err { + // io::ErrorKind::NotFound => {}, + // } + // ``` + // + // we don't want to show every possible IO error, but instead have only `_` as the + // witness. + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); + + // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it + // as though it had an "unknown" constructor to avoid exposing its emptiness. The + // exception is if the pattern is at the top level, because we want empty matches to be + // considered exhaustive. + let is_secretly_empty = def.variants.is_empty() + && !cx.tcx.features().exhaustive_patterns + && !pcx.is_top_level; + + if is_secretly_empty || is_declared_nonexhaustive { + smallvec![NonExhaustive] + } else if cx.tcx.features().exhaustive_patterns { + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + def.variants + .iter() + .filter(|v| { + !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) + .contains(cx.tcx, cx.module) + }) + .map(|v| Variant(v.def_id)) + .collect() + } else { + def.variants.iter().map(|v| Variant(v.def_id)).collect() + } + } + ty::Char => { + smallvec![ + // The valid Unicode Scalar Value ranges. + make_range('\u{0000}' as u128, '\u{D7FF}' as u128), + make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), + ] + } + ty::Int(_) | ty::Uint(_) + if pcx.ty.is_ptr_sized_integral() + && !cx.tcx.features().precise_pointer_size_matching => + { + // `usize`/`isize` are not allowed to be matched exhaustively unless the + // `precise_pointer_size_matching` feature is enabled. So we treat those types like + // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. + smallvec![NonExhaustive] + } + &ty::Int(ity) => { + let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; + let min = 1u128 << (bits - 1); + let max = min - 1; + smallvec![make_range(min, max)] + } + &ty::Uint(uty) => { + let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); + let max = size.truncate(u128::MAX); + smallvec![make_range(0, max)] + } + // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot + // expose its emptiness. The exception is if the pattern is at the top level, because we + // want empty matches to be considered exhaustive. + ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => { + smallvec![NonExhaustive] + } + ty::Never => smallvec![], + _ if cx.is_uninhabited(pcx.ty) => smallvec![], + ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => smallvec![Single], + // This type is one for which we cannot list constructors, like `str` or `f64`. + _ => smallvec![NonExhaustive], + }; + SplitWildcard { matrix_ctors: Vec::new(), all_ctors } + } + + /// Pass a set of constructors relative to which to split this one. Don't call twice, it won't + /// do what you want. + pub(super) fn split<'a>( + &mut self, + pcx: PatCtxt<'_, '_, 'tcx>, + ctors: impl Iterator> + Clone, + ) where + 'tcx: 'a, + { // Since `all_ctors` never contains wildcards, this won't recurse further. - let all_ctors = - all_constructors(pcx).into_iter().flat_map(|ctor| ctor.split(pcx, None)).collect(); - - MissingConstructors { all_ctors, used_ctors } + self.all_ctors = + self.all_ctors.iter().flat_map(|ctor| ctor.split(pcx, ctors.clone())).collect(); + self.matrix_ctors = ctors.filter(|c| !c.is_wildcard()).cloned().collect(); } - fn is_empty<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> bool { - self.iter(pcx).next().is_none() + /// Whether there are any value constructors for this type that are not present in the matrix. + fn any_missing(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> bool { + self.iter_missing(pcx).next().is_some() } - /// Iterate over all_ctors \ used_ctors - fn iter<'a, 'p>( + /// Iterate over the constructors for this type that are not present in the matrix. + pub(super) fn iter_missing<'a, 'p>( &'a self, pcx: PatCtxt<'a, 'p, 'tcx>, ) -> impl Iterator> + Captures<'p> { - self.all_ctors.iter().filter(move |ctor| !ctor.is_covered_by_any(pcx, &self.used_ctors)) + self.all_ctors.iter().filter(move |ctor| !ctor.is_covered_by_any(pcx, &self.matrix_ctors)) } - /// List the patterns corresponding to the missing constructors. In some cases, instead of - /// listing all constructors of a given type, we prefer to simply report a wildcard. - pub(super) fn report_patterns<'p>( - &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - ) -> SmallVec<[Pat<'tcx>; 1]> { - // There are 2 ways we can report a witness here. - // Commonly, we can report all the "free" - // constructors as witnesses, e.g., if we have: - // - // ``` - // enum Direction { N, S, E, W } - // let Direction::N = ...; - // ``` - // - // we can report 3 witnesses: `S`, `E`, and `W`. - // - // However, there is a case where we don't want - // to do this and instead report a single `_` witness: - // if the user didn't actually specify a constructor - // in this arm, e.g., in - // - // ``` - // let x: (Direction, Direction, bool) = ...; - // let (_, _, false) = x; - // ``` - // - // we don't want to show all 16 possible witnesses - // `(, , true)` - we are - // satisfied with `(_, _, true)`. In this case, - // `used_ctors` is empty. - // The exception is: if we are at the top-level, for example in an empty match, we - // sometimes prefer reporting the list of constructors instead of just `_`. - let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty); - if self.used_ctors.is_empty() && !report_when_all_missing { - // All constructors are unused. Report only a wildcard - // rather than each individual constructor. - smallvec![Pat::wildcard_from_ty(pcx.ty)] - } else { - // Construct for each missing constructor a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, we get the pattern `Some(_)`. - self.iter(pcx) - .map(|missing_ctor| Fields::wildcards(pcx, &missing_ctor).apply(pcx, missing_ctor)) - .collect() + /// Return the set of constructors resulting from splitting the wildcard. As explained at the + /// top of the file, if any constructors are missing we can ignore the present ones. + fn into_ctors(self, pcx: PatCtxt<'_, '_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { + if self.any_missing(pcx) { + // Some constructors are missing, thus we can specialize with the special `Missing` + // constructor, which stands for those constructors that are not seen in the matrix, + // and matches the same rows as any of them (namely the wildcard rows). See the top of + // the file for details. + // However, when all constructors are missing we can also specialize with the full + // `Wildcard` constructor. The difference will depend on what we want in diagnostics. + + // If some constructors are missing, we typically want to report those constructors, + // e.g.: + // ``` + // enum Direction { N, S, E, W } + // let Direction::N = ...; + // ``` + // we can report 3 witnesses: `S`, `E`, and `W`. + // + // However, if the user didn't actually specify a constructor + // in this arm, e.g., in + // ``` + // let x: (Direction, Direction, bool) = ...; + // let (_, _, false) = x; + // ``` + // we don't want to show all 16 possible witnesses `(, , + // true)` - we are satisfied with `(_, _, true)`. So if all constructors are missing we + // prefer to report just a wildcard `_`. + // + // The exception is: if we are at the top-level, for example in an empty match, we + // sometimes prefer reporting the list of constructors instead of just `_`. + let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty); + let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing { + Missing + } else { + Wildcard + }; + return smallvec![ctor]; } + + // All the constructors are present in the matrix, so we just go through them all. + self.all_ctors } } /// Some fields need to be explicitly hidden away in certain cases; see the comment above the -/// `Fields` struct. This struct represents such a potentially-hidden field. When a field is hidden -/// we still keep its type around. +/// `Fields` struct. This struct represents such a potentially-hidden field. #[derive(Debug, Copy, Clone)] pub(super) enum FilteredField<'p, 'tcx> { Kept(&'p Pat<'tcx>), - Hidden(Ty<'tcx>), + Hidden, } impl<'p, 'tcx> FilteredField<'p, 'tcx> { fn kept(self) -> Option<&'p Pat<'tcx>> { match self { FilteredField::Kept(p) => Some(p), - FilteredField::Hidden(_) => None, - } - } - - fn to_pattern(self) -> Pat<'tcx> { - match self { - FilteredField::Kept(p) => p.clone(), - FilteredField::Hidden(ty) => Pat::wildcard_from_ty(ty), + FilteredField::Hidden => None, } } } /// A value can be decomposed into a constructor applied to some fields. This struct represents /// those fields, generalized to allow patterns in each field. See also `Constructor`. +/// This is constructed from a constructor using [`Fields::wildcards()`]. /// /// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is -/// uninhabited. For that, we filter these fields out of the matrix. This is subtle because we -/// still need to have those fields back when going to/from a `Pat`. Most of this is handled -/// automatically in `Fields`, but when constructing or deconstructing `Fields` you need to be -/// careful. As a rule, when going to/from the matrix, use the filtered field list; when going -/// to/from `Pat`, use the full field list. -/// This filtering is uncommon in practice, because uninhabited fields are rarely used, so we avoid -/// it when possible to preserve performance. +/// uninhabited. For that, we filter these fields out of the matrix. This is handled automatically +/// in `Fields`. This filtering is uncommon in practice, because uninhabited fields are rarely used, +/// so we avoid it when possible to preserve performance. #[derive(Debug, Clone)] pub(super) enum Fields<'p, 'tcx> { /// Lists of patterns that don't contain any filtered fields. @@ -1027,21 +1090,19 @@ pub(super) enum Fields<'p, 'tcx> { /// have not measured if it really made a difference. Slice(&'p [Pat<'tcx>]), Vec(SmallVec<[&'p Pat<'tcx>; 2]>), - /// Patterns where some of the fields need to be hidden. `kept_count` caches the number of - /// non-hidden fields. + /// Patterns where some of the fields need to be hidden. For all intents and purposes we only + /// care about the non-hidden fields. We need to keep the real field index for those fields; + /// we're morally storing a `Vec<(usize, &Pat)>` but what we do is more convenient. + /// `len` counts the number of non-hidden fields Filtered { fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>, - kept_count: usize, + len: usize, }, } impl<'p, 'tcx> Fields<'p, 'tcx> { - fn empty() -> Self { - Fields::Slice(&[]) - } - - /// Construct a new `Fields` from the given pattern. Must not be used if the pattern is a field - /// of a struct/tuple/variant. + /// Internal use. Use `Fields::wildcards()` instead. + /// Must not be used if the pattern is a field of a struct/tuple/variant. fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self { Fields::Slice(std::slice::from_ref(pat)) } @@ -1086,7 +1147,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { if has_no_hidden_fields { Fields::wildcards_from_tys(cx, field_tys) } else { - let mut kept_count = 0; + let mut len = 0; let fields = variant .fields .iter() @@ -1101,14 +1162,14 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { // order not to reveal the uninhabitedness of the whole // variant. if is_uninhabited && (!is_visible || is_non_exhaustive) { - FilteredField::Hidden(ty) + FilteredField::Hidden } else { - kept_count += 1; + len += 1; FilteredField::Kept(wildcard_from_ty(ty)) } }) .collect(); - Fields::Filtered { fields, kept_count } + Fields::Filtered { fields, len } } } } @@ -1121,9 +1182,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, - Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Wildcard => { - Fields::empty() - } + Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Missing + | Wildcard => Fields::Slice(&[]), }; debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); ret @@ -1145,14 +1205,16 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// `self`: `[false]` /// returns `Some(false)` pub(super) fn apply(self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Pat<'tcx> { - let mut subpatterns = self.all_patterns(); + let subpatterns_and_indices = self.patterns_and_indices(); + let mut subpatterns = subpatterns_and_indices.iter().map(|&(_, p)| p).cloned(); let pat = match ctor { Single | Variant(_) => match pcx.ty.kind() { ty::Adt(..) | ty::Tuple(..) => { - let subpatterns = subpatterns - .enumerate() - .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + // We want the real indices here. + let subpatterns = subpatterns_and_indices + .iter() + .map(|&(field, p)| FieldPat { field, pattern: p.clone() }) .collect(); if let ty::Adt(adt, substs) = pcx.ty.kind() { @@ -1207,48 +1269,52 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty), NonExhaustive => PatKind::Wild, + Wildcard => return Pat::wildcard_from_ty(pcx.ty), Opaque => bug!("we should not try to apply an opaque constructor"), - Wildcard => bug!( - "trying to apply a wildcard constructor; this should have been done in `apply_constructors`" + Missing => bug!( + "trying to apply the `Missing` constructor; this should have been done in `apply_constructors`" ), }; Pat { ty: pcx.ty, span: DUMMY_SP, kind: Box::new(pat) } } - /// Returns the number of patterns from the viewpoint of match-checking, i.e. excluding hidden - /// fields. This is what we want in most cases in this file, the only exception being - /// conversion to/from `Pat`. + /// Returns the number of patterns. This is the same as the arity of the constructor used to + /// construct `self`. pub(super) fn len(&self) -> usize { match self { Fields::Slice(pats) => pats.len(), Fields::Vec(pats) => pats.len(), - Fields::Filtered { kept_count, .. } => *kept_count, + Fields::Filtered { len, .. } => *len, } } - /// Returns the complete list of patterns, including hidden fields. - fn all_patterns(self) -> impl Iterator> { - let pats: SmallVec<[_; 2]> = match self { - Fields::Slice(pats) => pats.iter().cloned().collect(), - Fields::Vec(pats) => pats.into_iter().cloned().collect(), - Fields::Filtered { fields, .. } => { - // We don't skip any fields here. - fields.into_iter().map(|p| p.to_pattern()).collect() + /// Returns the list of patterns along with the corresponding field indices. + fn patterns_and_indices(&self) -> SmallVec<[(Field, &'p Pat<'tcx>); 2]> { + match self { + Fields::Slice(pats) => { + pats.iter().enumerate().map(|(i, p)| (Field::new(i), p)).collect() } - }; - pats.into_iter() + Fields::Vec(pats) => { + pats.iter().copied().enumerate().map(|(i, p)| (Field::new(i), p)).collect() + } + Fields::Filtered { fields, .. } => { + // Indices must be relative to the full list of patterns + fields + .iter() + .enumerate() + .filter_map(|(i, p)| Some((Field::new(i), p.kept()?))) + .collect() + } + } } - /// Returns the filtered list of patterns, not including hidden fields. - pub(super) fn filtered_patterns(self) -> SmallVec<[&'p Pat<'tcx>; 2]> { + /// Returns the list of patterns. + pub(super) fn into_patterns(self) -> SmallVec<[&'p Pat<'tcx>; 2]> { match self { Fields::Slice(pats) => pats.iter().collect(), Fields::Vec(pats) => pats, - Fields::Filtered { fields, .. } => { - // We skip hidden fields here - fields.into_iter().filter_map(|p| p.kept()).collect() - } + Fields::Filtered { fields, .. } => fields.iter().filter_map(|p| p.kept()).collect(), } } @@ -1264,10 +1330,10 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } /// Overrides some of the fields with the provided patterns. This is used when a pattern - /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start with a - /// `Fields` that is just one wildcard per field of the `Foo` struct, and override the entry - /// corresponding to `field1` with the pattern `Some(_)`. This is also used for slice patterns - /// for the same reason. + /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start + /// with a `Fields` that is just one wildcard per field of the `Foo` struct, and override the + /// entry corresponding to `field1` with the pattern `Some(_)`. This is also used for slice + /// patterns for the same reason. fn replace_fields_indexed( &self, new_pats: impl IntoIterator)>, @@ -1295,8 +1361,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { fields } - /// Replaces contained fields with the given filtered list of patterns, e.g. taken from the - /// matrix. There must be `len()` patterns in `pats`. + /// Replaces contained fields with the given list of patterns. There must be `len()` patterns + /// in `pats`. pub(super) fn replace_fields( &self, cx: &MatchCheckCtxt<'p, 'tcx>, @@ -1305,7 +1371,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats); match self { - Fields::Filtered { fields, kept_count } => { + Fields::Filtered { fields, len } => { let mut pats = pats.iter(); let mut fields = fields.clone(); for f in &mut fields { @@ -1314,7 +1380,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { *p = pats.next().unwrap(); } } - Fields::Filtered { fields, kept_count: *kept_count } + Fields::Filtered { fields, len: *len } } _ => Fields::Slice(pats), } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index f3e1507b37ae..83fee380ccce 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -12,306 +12,282 @@ //! //! ----- //! -//! This file includes the logic for exhaustiveness and usefulness checking for -//! pattern-matching. Specifically, given a list of patterns for a type, we can -//! tell whether: -//! (a) the patterns cover every possible constructor for the type (exhaustiveness) -//! (b) each pattern is necessary (usefulness) +//! This file includes the logic for exhaustiveness and reachability checking for pattern-matching. +//! Specifically, given a list of patterns for a type, we can tell whether: +//! (a) each pattern is reachable (reachability) +//! (b) the patterns cover every possible value for the type (exhaustiveness) //! -//! The algorithm implemented here is a modified version of the one described in -//! [this paper](http://moscova.inria.fr/~maranget/papers/warn/index.html). -//! However, to save future implementors from reading the original paper, we -//! summarise the algorithm here to hopefully save time and be a little clearer -//! (without being so rigorous). -//! -//! # Premise -//! -//! The core of the algorithm revolves about a "usefulness" check. In particular, we -//! are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as -//! a matrix). `U(P, p)` represents whether, given an existing list of patterns -//! `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously- -//! uncovered values of the type). -//! -//! If we have this predicate, then we can easily compute both exhaustiveness of an -//! entire set of patterns and the individual usefulness of each one. -//! (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard -//! match doesn't increase the number of values we're matching) -//! (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a -//! pattern to those that have come before it doesn't increase the number of values -//! we're matching). -//! -//! # Core concept -//! -//! The idea that powers everything that is done in this file is the following: a value is made -//! from a constructor applied to some fields. Examples of constructors are `Some`, `None`, `(,)` -//! (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), and `2` (the -//! constructor for the number `2`). Fields are just a (possibly empty) list of values. -//! -//! Some of the constructors listed above might feel weird: `None` and `2` don't take any -//! arguments. This is part of what makes constructors so general: we will consider plain values -//! like numbers and string literals to be constructors that take no arguments, also called "0-ary -//! constructors"; they are the simplest case of constructors. This allows us to see any value as -//! made up from a tree of constructors, each having a given number of children. For example: -//! `(None, Ok(0))` is made from 4 different constructors. -//! -//! This idea can be extended to patterns: a pattern captures a set of possible values, and we can -//! describe this set using constructors. For example, `Err(_)` captures all values of the type -//! `Result` that start with the `Err` constructor (for some choice of `T` and `E`). The -//! wildcard `_` captures all values of the given type starting with any of the constructors for -//! that type. -//! -//! We use this to compute whether different patterns might capture a same value. Do the patterns -//! `Ok("foo")` and `Err(_)` capture a common value? The answer is no, because the first pattern -//! captures only values starting with the `Ok` constructor and the second only values starting -//! with the `Err` constructor. Do the patterns `Some(42)` and `Some(1..10)` intersect? They might, -//! since they both capture values starting with `Some`. To be certain, we need to dig under the -//! `Some` constructor and continue asking the question. This is the main idea behind the -//! exhaustiveness algorithm: by looking at patterns constructor-by-constructor, we can efficiently -//! figure out if some new pattern might capture a value that hadn't been captured by previous -//! patterns. -//! -//! Constructors are represented by the `Constructor` enum, and its fields by the `Fields` enum. -//! Most of the complexity of this file resides in transforming between patterns and -//! (`Constructor`, `Fields`) pairs, handling all the special cases correctly. -//! -//! Caveat: this constructors/fields distinction doesn't quite cover every Rust value. For example -//! a value of type `Rc` doesn't fit this idea very well, nor do various other things. -//! However, this idea covers most of the cases that are relevant to exhaustiveness checking. +//! The algorithm implemented here is a modified version of the one described in [this +//! paper](http://moscova.inria.fr/~maranget/papers/warn/index.html). We have however generalized +//! it to accommodate the variety of patterns that Rust supports. We thus explain our version here, +//! without being as rigorous. //! //! -//! # Algorithm +//! # Summary //! -//! Recall that `U(P, p)` represents whether, given an existing list of patterns (aka matrix) `P`, -//! adding a new pattern `p` will cover previously-uncovered values of the type. -//! During the course of the algorithm, the rows of the matrix won't just be individual patterns, -//! but rather partially-deconstructed patterns in the form of a list of fields. The paper -//! calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the -//! new pattern `p`. +//! The core of the algorithm is the notion of "usefulness". A pattern `q` is said to be *useful* +//! relative to another pattern `p` of the same type if there is a value that is matched by `q` and +//! not matched by `p`. This generalizes to many `p`s: `q` is useful w.r.t. a list of patterns +//! `p_1 .. p_n` if there is a value that is matched by `q` and by none of the `p_i`. We write +//! `usefulness(p_1 .. p_n, q)` for a function that returns a list of such values. The aim of this +//! file is to compute it efficiently. //! -//! For example, say we have the following: -//! -//! ``` -//! // x: (Option, Result<()>) +//! This is enough to compute reachability: a pattern in a `match` expression is reachable iff it +//! is useful w.r.t. the patterns above it: +//! ```rust //! match x { -//! (Some(true), _) => {} -//! (None, Err(())) => {} -//! (None, Err(_)) => {} +//! Some(_) => ..., +//! None => ..., // reachable: `None` is matched by this but not the branch above +//! Some(0) => ..., // unreachable: all the values this matches are already matched by +//! // `Some(_)` above //! } //! ``` //! -//! Here, the matrix `P` starts as: -//! -//! ``` -//! [ -//! [(Some(true), _)], -//! [(None, Err(()))], -//! [(None, Err(_))], -//! ] +//! This is also enough to compute exhaustiveness: a match is exhaustive iff the wildcard `_` +//! pattern is _not_ useful w.r.t. the patterns in the match. The values returned by `usefulness` +//! are used to tell the user which values are missing. +//! ```rust +//! match x { +//! Some(0) => ..., +//! None => ..., +//! // not exhaustive: `_` is useful because it matches `Some(1)` +//! } //! ``` //! -//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering -//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because -//! all the values it covers are already covered by row 2. -//! -//! A list of patterns can be thought of as a stack, because we are mainly interested in the top of -//! the stack at any given point, and we can pop or apply constructors to get new pattern-stacks. -//! To match the paper, the top of the stack is at the beginning / on the left. -//! -//! There are two important operations on pattern-stacks necessary to understand the algorithm: -//! -//! 1. We can pop a given constructor off the top of a stack. This operation is called -//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or -//! `None`) and `p` a pattern-stack. -//! If the pattern on top of the stack can cover `c`, this removes the constructor and -//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns. -//! Otherwise the pattern-stack is discarded. -//! This essentially filters those pattern-stacks whose top covers the constructor `c` and -//! discards the others. -//! -//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we -//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the -//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get -//! nothing back. -//! -//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` -//! on top of the stack, and we have four cases: -//! -//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We -//! push onto the stack the arguments of this constructor, and return the result: -//! `r_1, .., r_a, p_2, .., p_n` -//! -//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and -//! return nothing. -//! -//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has -//! arguments (its arity), and return the resulting stack: -//! `_, .., _, p_2, .., p_n` -//! -//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting -//! stack: -//! - `S(c, (r_1, p_2, .., p_n))` -//! - `S(c, (r_2, p_2, .., p_n))` -//! -//! 2. We can pop a wildcard off the top of the stack. This is called `S(_, p)`, where `p` is -//! a pattern-stack. Note: the paper calls this `D(p)`. -//! This is used when we know there are missing constructor cases, but there might be -//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check -//! all its *other* components. -//! -//! It is computed as follows. We look at the pattern `p_1` on top of the stack, -//! and we have three cases: -//! 2.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. -//! 2.2. `p_1 = _`. We return the rest of the stack: -//! p_2, .., p_n -//! 2.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting -//! stack. -//! - `S(_, (r_1, p_2, .., p_n))` -//! - `S(_, (r_2, p_2, .., p_n))` -//! -//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the -//! exhaustive integer matching rules, so they're written here for posterity. -//! -//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by -//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with -//! the given constructor, and popping a wildcard keeps those rows that start with a wildcard. +//! The entrypoint of this file is the [`compute_match_usefulness`] function, which computes +//! reachability for each match branch and exhaustiveness for the whole match. //! //! -//! The algorithm for computing `U` -//! ------------------------------- -//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). -//! That means we're going to check the components from left-to-right, so the algorithm -//! operates principally on the first component of the matrix and new pattern-stack `p`. -//! This algorithm is realised in the `is_useful` function. +//! # Constructors and fields //! -//! Base case. (`n = 0`, i.e., an empty tuple pattern) -//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), -//! then `U(P, p)` is false. -//! - Otherwise, `P` must be empty, so `U(P, p)` is true. +//! Note: we will often abbreviate "constructor" as "ctor". //! -//! Inductive step. (`n > 0`, i.e., whether there's at least one column -//! [which may then be expanded into further columns later]) -//! We're going to match on the top of the new pattern-stack, `p_1`. -//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. -//! Then, the usefulness of `p_1` can be reduced to whether it is useful when -//! we ignore all the patterns in the first column of `P` that involve other constructors. -//! This is where `S(c, P)` comes in: -//! `U(P, p) := U(S(c, P), S(c, p))` +//! The idea that powers everything that is done in this file is the following: a (matcheable) +//! value is made from a constructor applied to a number of subvalues. Examples of constructors are +//! `Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for a struct +//! `Foo`), and `2` (the constructor for the number `2`). This is natural when we think of +//! pattern-matching, and this is the basis for what follows. //! -//! For example, if `P` is: +//! Some of the ctors listed above might feel weird: `None` and `2` don't take any arguments. +//! That's ok: those are ctors that take a list of 0 arguments; they are the simplest case of +//! ctors. We treat `2` as a ctor because `u64` and other number types behave exactly like a huge +//! `enum`, with one variant for each number. This allows us to see any matcheable value as made up +//! from a tree of ctors, each having a set number of children. For example: `Foo { bar: None, +//! baz: Ok(0) }` is made from 4 different ctors, namely `Foo{..}`, `None`, `Ok` and `0`. //! +//! This idea can be extended to patterns: they are also made from constructors applied to fields. +//! A pattern for a given type is allowed to use all the ctors for values of that type (which we +//! call "value constructors"), but there are also pattern-only ctors. The most important one is +//! the wildcard (`_`), and the others are integer ranges (`0..=10`), variable-length slices (`[x, +//! ..]`), and or-patterns (`Ok(0) | Err(_)`). Examples of valid patterns are `42`, `Some(_)`, `Foo +//! { bar: Some(0) | None, baz: _ }`. Note that a binder in a pattern (e.g. `Some(x)`) matches the +//! same values as a wildcard (e.g. `Some(_)`), so we treat both as wildcards. +//! +//! From this deconstruction we can compute whether a given value matches a given pattern; we +//! simply look at ctors one at a time. Given a pattern `p` and a value `v`, we want to compute +//! `matches!(v, p)`. It's mostly straightforward: we compare the head ctors and when they match +//! we compare their fields recursively. A few representative examples: +//! +//! - `matches!(v, _) := true` +//! - `matches!((v0, v1), (p0, p1)) := matches!(v0, p0) && matches!(v1, p1)` +//! - `matches!(Foo { bar: v0, baz: v1 }, Foo { bar: p0, baz: p1 }) := matches!(v0, p0) && matches!(v1, p1)` +//! - `matches!(Ok(v0), Ok(p0)) := matches!(v0, p0)` +//! - `matches!(Ok(v0), Err(p0)) := false` (incompatible variants) +//! - `matches!(v, 1..=100) := matches!(v, 1) || ... || matches!(v, 100)` +//! - `matches!([v0], [p0, .., p1]) := false` (incompatible lengths) +//! - `matches!([v0, v1, v2], [p0, .., p1]) := matches!(v0, p0) && matches!(v2, p1)` +//! - `matches!(v, p0 | p1) := matches!(v, p0) || matches!(v, p1)` +//! +//! Constructors, fields and relevant operations are defined in the [`super::deconstruct_pat`] module. +//! +//! Note: this constructors/fields distinction may not straightforwardly apply to every Rust type. +//! For example a value of type `Rc` can't be deconstructed that way, and `&str` has an +//! infinitude of constructors. There are also subtleties with visibility of fields and +//! uninhabitedness and various other things. The constructors idea can be extended to handle most +//! of these subtleties though; caveats are documented where relevant throughout the code. +//! +//! Whether constructors cover each other is computed by [`Constructor::is_covered_by`]. +//! +//! +//! # Specialization +//! +//! Recall that we wish to compute `usefulness(p_1 .. p_n, q)`: given a list of patterns `p_1 .. +//! p_n` and a pattern `q`, all of the same type, we want to find a list of values (called +//! "witnesses") that are matched by `q` and by none of the `p_i`. We obviously don't just +//! enumerate all possible values. From the discussion above we see that we can proceed +//! ctor-by-ctor: for each value ctor of the given type, we ask "is there a value that starts with +//! this constructor and matches `q` and none of the `p_i`?". As we saw above, there's a lot we can +//! say from knowing only the first constructor of our candidate value. +//! +//! Let's take the following example: //! ``` -//! [ -//! [Some(true), _], -//! [None, 0], -//! ] +//! match x { +//! Enum::Variant1(_) => {} // `p1` +//! Enum::Variant2(None, 0) => {} // `p2` +//! Enum::Variant2(Some(_), 0) => {} // `q` +//! } //! ``` //! -//! and `p` is `[Some(false), 0]`, then we don't care about row 2 since we know `p` only -//! matches values that row 2 doesn't. For row 1 however, we need to dig into the -//! arguments of `Some` to know whether some new value is covered. So we compute -//! `U([[true, _]], [false, 0])`. -//! -//! - If `p_1 == _`, then we look at the list of constructors that appear in the first -//! component of the rows of `P`: -//! + If there are some constructors that aren't present, then we might think that the -//! wildcard `_` is useful, since it covers those constructors that weren't covered -//! before. -//! That's almost correct, but only works if there were no wildcards in those first -//! components. So we need to check that `p` is useful with respect to the rows that -//! start with a wildcard, if there are any. This is where `S(_, x)` comes in: -//! `U(P, p) := U(S(_, P), S(_, p))` -//! -//! For example, if `P` is: +//! We can easily see that if our candidate value `v` starts with `Variant1` it will not match `q`. +//! If `v = Variant2(v0, v1)` however, whether or not it matches `p2` and `q` will depend on `v0` +//! and `v1`. In fact, such a `v` will be a witness of usefulness of `q` exactly when the tuple +//! `(v0, v1)` is a witness of usefulness of `q'` in the following reduced match: //! //! ``` -//! [ -//! [_, true, _], -//! [None, false, 1], -//! ] +//! match x { +//! (None, 0) => {} // `p2'` +//! (Some(_), 0) => {} // `q'` +//! } //! ``` //! -//! and `p` is `[_, false, _]`, the `Some` constructor doesn't appear in `P`. So if we -//! only had row 2, we'd know that `p` is useful. However row 1 starts with a -//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`. -//! -//! + Otherwise, all possible constructors (for the relevant type) are present. In this -//! case we must check whether the wildcard pattern covers any unmatched value. For -//! that, we can think of the `_` pattern as a big OR-pattern that covers all -//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for -//! example. The wildcard pattern is useful in this case if it is useful when -//! specialized to one of the possible constructors. So we compute: -//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` -//! -//! For example, if `P` is: +//! This motivates a new step in computing usefulness, that we call _specialization_. +//! Specialization consist of filtering a list of patterns for those that match a constructor, and +//! then looking into the constructor's fields. This enables usefulness to be computed recursively. //! +//! Instead of acting on a single pattern in each row, we will consider a list of patterns for each +//! row, and we call such a list a _pattern-stack_. The idea is that we will specialize the +//! leftmost pattern, which amounts to popping the constructor and pushing its fields, which feels +//! like a stack. We note a pattern-stack simply with `[p_1 ... p_n]`. +//! Here's a sequence of specializations of a list of pattern-stacks, to illustrate what's +//! happening: //! ``` -//! [ -//! [Some(true), _], -//! [None, false], -//! ] +//! [Enum::Variant1(_)] +//! [Enum::Variant2(None, 0)] +//! [Enum::Variant2(Some(_), 0)] +//! //==>> specialize with `Variant2` +//! [None, 0] +//! [Some(_), 0] +//! //==>> specialize with `Some` +//! [_, 0] +//! //==>> specialize with `true` (say the type was `bool`) +//! [0] +//! //==>> specialize with `0` +//! [] //! ``` //! -//! and `p` is `[_, false]`, both `None` and `Some` constructors appear in the first -//! components of `P`. We will therefore try popping both constructors in turn: we -//! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]], -//! [false])` for the `None` constructor. The first case returns true, so we know that -//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched -//! before. +//! The function `specialize(c, p)` takes a value constructor `c` and a pattern `p`, and returns 0 +//! or more pattern-stacks. If `c` does not match the head constructor of `p`, it returns nothing; +//! otherwise if returns the fields of the constructor. This only returns more than one +//! pattern-stack if `p` has a pattern-only constructor. //! -//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: -//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) -//! || U(P, (r_2, p_2, .., p_n))` +//! - Specializing for the wrong constructor returns nothing //! -//! Modifications to the algorithm -//! ------------------------------ -//! The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for -//! example uninhabited types and variable-length slice patterns. These are drawn attention to -//! throughout the code below. I'll make a quick note here about how exhaustive integer matching is -//! accounted for, though. +//! `specialize(None, Some(p0)) := []` //! -//! Exhaustive integer matching -//! --------------------------- -//! An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ... -//! So to support exhaustive integer matching, we can make use of the logic in the paper for -//! OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because -//! they are likely gigantic. So we instead treat ranges as constructors of the integers. This means -//! that we have a constructor *of* constructors (the integers themselves). We then need to work -//! through all the inductive step rules above, deriving how the ranges would be treated as -//! OR-patterns, and making sure that they're treated in the same way even when they're ranges. -//! There are really only four special cases here: -//! - When we match on a constructor that's actually a range, we have to treat it as if we would -//! an OR-pattern. -//! + It turns out that we can simply extend the case for single-value patterns in -//! `specialize` to either be *equal* to a value constructor, or *contained within* a range -//! constructor. -//! + When the pattern itself is a range, you just want to tell whether any of the values in -//! the pattern range coincide with values in the constructor range, which is precisely -//! intersection. -//! Since when encountering a range pattern for a value constructor, we also use inclusion, it -//! means that whenever the constructor is a value/range and the pattern is also a value/range, -//! we can simply use intersection to test usefulness. -//! - When we're testing for usefulness of a pattern and the pattern's first component is a -//! wildcard. -//! + If all the constructors appear in the matrix, we have a slight complication. By default, -//! the behaviour (i.e., a disjunction over specialised matrices for each constructor) is -//! invalid, because we want a disjunction over every *integer* in each range, not just a -//! disjunction over every range. This is a bit more tricky to deal with: essentially we need -//! to form equivalence classes of subranges of the constructor range for which the behaviour -//! of the matrix `P` and new pattern `p` are the same. This is described in more -//! detail in `Constructor::split`. -//! + If some constructors are missing from the matrix, it turns out we don't need to do -//! anything special (because we know none of the integers are actually wildcards: i.e., we -//! can't span wildcards using ranges). +//! - Specializing for the correct constructor returns a single row with the fields +//! +//! `specialize(Variant1, Variant1(p0, p1, p2)) := [[p0, p1, p2]]` +//! +//! `specialize(Foo{..}, Foo { bar: p0, baz: p1 }) := [[p0, p1]]` +//! +//! - For or-patterns, we specialize each branch and concatenate the results +//! +//! `specialize(c, p0 | p1) := specialize(c, p0) ++ specialize(c, p1)` +//! +//! - We treat the other pattern constructors as if they were a large or-pattern of all the +//! possibilities: +//! +//! `specialize(c, _) := specialize(c, Variant1(_) | Variant2(_, _) | ...)` +//! +//! `specialize(c, 1..=100) := specialize(c, 1 | ... | 100)` +//! +//! `specialize(c, [p0, .., p1]) := specialize(c, [p0, p1] | [p0, _, p1] | [p0, _, _, p1] | ...)` +//! +//! - If `c` is a pattern-only constructor, `specialize` is defined on a case-by-case basis. See +//! the discussion about constructor splitting in [`super::deconstruct_pat`]. +//! +//! +//! We then extend this function to work with pattern-stacks as input, by acting on the first +//! column and keeping the other columns untouched. +//! +//! Specialization for the whole matrix is done in [`Matrix::specialize_constructor`]. Note that +//! or-patterns in the first column are expanded before being stored in the matrix. Specialization +//! for a single patstack is done from a combination of [`Constructor::is_covered_by`] and +//! [`PatStack::pop_head_constructor`]. The internals of how it's done mostly live in the +//! [`Fields`] struct. +//! +//! +//! # Computing usefulness +//! +//! We now have all we need to compute usefulness. The inputs to usefulness are a list of +//! pattern-stacks `p_1 ... p_n` (one per row), and a new pattern_stack `q`. The paper and this +//! file calls the list of patstacks a _matrix_. They must all have the same number of columns and +//! the patterns in a given column must all have the same type. `usefulness` returns a (possibly +//! empty) list of witnesses of usefulness. These witnesses will also be pattern-stacks. +//! +//! - base case: `n_columns == 0`. +//! Since a pattern-stack functions like a tuple of patterns, an empty one functions like the +//! unit type. Thus `q` is useful iff there are no rows above it, i.e. if `n == 0`. +//! +//! - inductive case: `n_columns > 0`. +//! We need a way to list the constructors we want to try. We will be more clever in the next +//! section but for now assume we list all value constructors for the type of the first column. +//! +//! - for each such ctor `c`: +//! +//! - for each `q'` returned by `specialize(c, q)`: +//! +//! - we compute `usefulness(specialize(c, p_1) ... specialize(c, p_n), q')` +//! +//! - for each witness found, we revert specialization by pushing the constructor `c` on top. +//! +//! - We return the concatenation of all the witnesses found, if any. +//! +//! Example: +//! ``` +//! [Some(true)] // p_1 +//! [None] // p_2 +//! [Some(_)] // q +//! //==>> try `None`: `specialize(None, q)` returns nothing +//! //==>> try `Some`: `specialize(Some, q)` returns a single row +//! [true] // p_1' +//! [_] // q' +//! //==>> try `true`: `specialize(true, q')` returns a single row +//! [] // p_1'' +//! [] // q'' +//! //==>> base case; `n != 0` so `q''` is not useful. +//! //==>> go back up a step +//! [true] // p_1' +//! [_] // q' +//! //==>> try `false`: `specialize(false, q')` returns a single row +//! [] // q'' +//! //==>> base case; `n == 0` so `q''` is useful. We return the single witness `[]` +//! witnesses: +//! [] +//! //==>> undo the specialization with `false` +//! witnesses: +//! [false] +//! //==>> undo the specialization with `Some` +//! witnesses: +//! [Some(false)] +//! //==>> we have tried all the constructors. The output is the single witness `[Some(false)]`. +//! ``` +//! +//! This computation is done in [`is_useful`]. In practice we don't care about the list of +//! witnesses when computing reachability; we only need to know whether any exist. We do keep the +//! witnesses when computing exhaustiveness to report them to the user. +//! +//! +//! # Making usefulness tractable: constructor splitting +//! +//! We're missing one last detail: which constructors do we list? Naively listing all value +//! constructors cannot work for types like `u64` or `&str`, so we need to be more clever. The +//! first obvious insight is that we only want to list constructors that are covered by the head +//! constructor of `q`. If it's a value constructor, we only try that one. If it's a pattern-only +//! constructor, we use the final clever idea for this algorithm: _constructor splitting_, where we +//! group together constructors that behave the same. +//! +//! The details are not necessary to understand this file, so we explain them in +//! [`super::deconstruct_pat`]. Splitting is done by the [`Constructor::split`] function. use self::Usefulness::*; use self::WitnessPreference::*; -use super::deconstruct_pat::{Constructor, Fields, MissingConstructors}; +use super::deconstruct_pat::{Constructor, Fields, SplitWildcard}; use super::{Pat, PatKind}; use super::{PatternFoldable, PatternFolder}; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::OnceCell; use rustc_arena::TypedArena; @@ -359,8 +335,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { #[derive(Copy, Clone)] pub(super) struct PatCtxt<'a, 'p, 'tcx> { pub(super) cx: &'a MatchCheckCtxt<'p, 'tcx>, - /// Current state of the matrix. - pub(super) matrix: &'a Matrix<'p, 'tcx>, /// Type of the current column under investigation. pub(super) ty: Ty<'tcx>, /// Span of the current pattern under investigation. @@ -474,7 +448,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. let mut new_fields = - ctor_wild_subpatterns.replace_with_pattern_arguments(self.head()).filtered_patterns(); + ctor_wild_subpatterns.replace_with_pattern_arguments(self.head()).into_patterns(); new_fields.extend_from_slice(&self.pats[1..]); PatStack::from_vec(new_fields) } @@ -539,7 +513,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { pub(super) fn head_ctors<'a>( &'a self, cx: &'a MatchCheckCtxt<'p, 'tcx>, - ) -> impl Iterator> + Captures<'p> { + ) -> impl Iterator> + Captures<'p> + Clone { self.patterns.iter().map(move |r| r.head_ctor(cx)) } @@ -626,11 +600,82 @@ impl<'p, 'tcx> FromIterator> for Matrix<'p, 'tcx> { } } +/// Represents a set of `Span`s closed under the containment relation. That is, if a `Span` is +/// contained in the set then all `Span`s contained in it are also implicitly contained in the set. +/// In particular this means that when intersecting two sets, taking the intersection of some span +/// and one of its subspans returns the subspan, whereas a simple `HashSet` would have returned an +/// empty intersection. +/// It is assumed that two spans don't overlap without one being contained in the other; in other +/// words, that the inclusion structure forms a tree and not a DAG. +/// Intersection is not very efficient. It compares everything pairwise. If needed it could be made +/// faster by sorting the `Span`s and merging cleverly. +#[derive(Debug, Clone, Default)] +pub(crate) struct SpanSet { + /// The minimal set of `Span`s required to represent the whole set. If A and B are `Span`s in + /// the `SpanSet`, and A is a descendant of B, then only B will be in `root_spans`. + /// Invariant: the spans are disjoint. + root_spans: Vec, +} + +impl SpanSet { + /// Creates an empty set. + fn new() -> Self { + Self::default() + } + + /// Tests whether the set is empty. + pub(crate) fn is_empty(&self) -> bool { + self.root_spans.is_empty() + } + + /// Iterate over the disjoint list of spans at the roots of this set. + pub(crate) fn iter<'a>(&'a self) -> impl Iterator + Captures<'a> { + self.root_spans.iter().copied() + } + + /// Tests whether the set contains a given Span. + fn contains(&self, span: Span) -> bool { + self.iter().any(|root_span| root_span.contains(span)) + } + + /// Add a span to the set if we know the span has no intersection in this set. + fn push_nonintersecting(&mut self, new_span: Span) { + self.root_spans.push(new_span); + } + + fn intersection_mut(&mut self, other: &Self) { + if self.is_empty() || other.is_empty() { + *self = Self::new(); + return; + } + // Those that were in `self` but not contained in `other` + let mut leftover = SpanSet::new(); + // We keep the elements in `self` that are also in `other`. + self.root_spans.retain(|span| { + let retain = other.contains(*span); + if !retain { + leftover.root_spans.push(*span); + } + retain + }); + // We keep the elements in `other` that are also in the original `self`. You might think + // this is not needed because `self` already contains the intersection. But those aren't + // just sets of things. If `self = [a]`, `other = [b]` and `a` contains `b`, then `b` + // belongs in the intersection but we didn't catch it in the filtering above. We look at + // `leftover` instead of the full original `self` to avoid duplicates. + for span in other.iter() { + if leftover.contains(span) { + self.root_spans.push(span); + } + } + } +} + #[derive(Clone, Debug)] crate enum Usefulness<'tcx> { - /// Carries, for each column in the matrix, a set of sub-branches that have been found to be - /// unreachable. Used only in the presence of or-patterns, otherwise it stays empty. - Useful(Vec>), + /// Pontentially carries a set of sub-branches that have been found to be unreachable. Used + /// only in the presence of or-patterns, otherwise it stays empty. + Useful(SpanSet), /// Carries a list of witnesses of non-exhaustiveness. UsefulWithWitness(Vec>), NotUseful, @@ -640,25 +685,119 @@ impl<'tcx> Usefulness<'tcx> { fn new_useful(preference: WitnessPreference) -> Self { match preference { ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), - LeaveOutWitness => Useful(vec![]), + LeaveOutWitness => Useful(Default::default()), } } - fn is_useful(&self) -> bool { - !matches!(*self, NotUseful) + /// When trying several branches and each returns a `Usefulness`, we need to combine the + /// results together. + fn merge(usefulnesses: impl Iterator) -> Self { + // If we have detected some unreachable sub-branches, we only want to keep them when they + // were unreachable in _all_ branches. Eg. in the following, the last `true` is unreachable + // in the second branch of the first or-pattern, but not otherwise. Therefore we don't want + // to lint that it is unreachable. + // ``` + // match (true, true) { + // (true, true) => {} + // (false | true, false | true) => {} + // } + // ``` + // Here however we _do_ want to lint that the last `false` is unreachable. So we don't want + // to intersect the spans that come directly from the or-pattern, since each branch of the + // or-pattern brings a new disjoint pattern. + // ``` + // match None { + // Some(false) => {} + // None | Some(true | false) => {} + // } + // ``` + + // Is `None` when no branch was useful. Will often be `Some(Spanset::new())` because the + // sets are only non-empty in the presence of or-patterns. + let mut unreachables: Option = None; + // Witnesses of usefulness, if any. + let mut witnesses = Vec::new(); + + for u in usefulnesses { + match u { + Useful(spans) if spans.is_empty() => { + // Once we reach the empty set, more intersections won't change the result. + return Useful(SpanSet::new()); + } + Useful(spans) => { + if let Some(unreachables) = &mut unreachables { + if !unreachables.is_empty() { + unreachables.intersection_mut(&spans); + } + if unreachables.is_empty() { + return Useful(SpanSet::new()); + } + } else { + unreachables = Some(spans); + } + } + NotUseful => {} + UsefulWithWitness(wits) => { + witnesses.extend(wits); + } + } + } + + if !witnesses.is_empty() { + UsefulWithWitness(witnesses) + } else if let Some(unreachables) = unreachables { + Useful(unreachables) + } else { + NotUseful + } } + /// After calculating the usefulness for a branch of an or-pattern, call this to make this + /// usefulness mergeable with those from the other branches. + fn unsplit_or_pat(self, this_span: Span, or_pat_spans: &[Span]) -> Self { + match self { + Useful(mut spans) => { + // We register the spans of the other branches of this or-pattern as being + // unreachable from this one. This ensures that intersecting together the sets of + // spans returns what we want. + // Until we optimize `SpanSet` however, intersecting this entails a number of + // comparisons quadratic in the number of branches. + for &span in or_pat_spans { + if span != this_span { + spans.push_nonintersecting(span); + } + } + Useful(spans) + } + x => x, + } + } + + /// After calculating usefulness after a specialization, call this to recontruct a usefulness + /// that makes sense for the matrix pre-specialization. This new usefulness can then be merged + /// with the results of specializing with the other constructors. fn apply_constructor<'p>( self, pcx: PatCtxt<'_, 'p, 'tcx>, + matrix: &Matrix<'p, 'tcx>, // used to compute missing ctors ctor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, ) -> Self { match self { UsefulWithWitness(witnesses) => { - let new_witnesses = if ctor.is_wildcard() { - let missing_ctors = MissingConstructors::new(pcx); - let new_patterns = missing_ctors.report_patterns(pcx); + let new_witnesses = if matches!(ctor, Constructor::Missing) { + let mut split_wildcard = SplitWildcard::new(pcx); + split_wildcard.split(pcx, matrix.head_ctors(pcx.cx)); + // Construct for each missing constructor a "wild" version of this + // constructor, that matches everything that can be built with + // it. For example, if `ctor` is a `Constructor::Variant` for + // `Option::Some`, we get the pattern `Some(_)`. + let new_patterns: Vec<_> = split_wildcard + .iter_missing(pcx) + .map(|missing_ctor| { + Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor) + }) + .collect(); witnesses .into_iter() .flat_map(|witness| { @@ -677,23 +816,6 @@ impl<'tcx> Usefulness<'tcx> { }; UsefulWithWitness(new_witnesses) } - Useful(mut unreachables) => { - if !unreachables.is_empty() { - // When we apply a constructor, there are `arity` columns of the matrix that - // corresponded to its arguments. All the unreachables found in these columns - // will, after `apply`, come from the first column. So we take the union of all - // the corresponding sets and put them in the first column. - // Note that `arity` may be 0, in which case we just push a new empty set. - let len = unreachables.len(); - let arity = ctor_wild_subpatterns.len(); - let mut unioned = FxHashSet::default(); - for set in unreachables.drain((len - arity)..) { - unioned.extend(set) - } - unreachables.push(unioned); - } - Useful(unreachables) - } x => x, } } @@ -730,10 +852,10 @@ enum WitnessPreference { /// We'll perform the following steps: /// 1. Start with an empty witness /// `Witness(vec![])` -/// 2. Push a witness `Some(_)` against the `None` -/// `Witness(vec![Some(_)])` -/// 3. Push a witness `true` against the `false` -/// `Witness(vec![Some(_), true])` +/// 2. Push a witness `true` against the `false` +/// `Witness(vec![true])` +/// 3. Push a witness `Some(_)` against the `None` +/// `Witness(vec![true, Some(_)])` /// 4. Apply the `Pair` constructor to the witnesses /// `Witness(vec![Pair(Some(_), true)])` /// @@ -829,112 +951,59 @@ fn is_useful<'p, 'tcx>( assert!(rows.iter().all(|r| r.len() == v.len())); + // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). + let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty); + let pcx = PatCtxt { cx, ty, span: v.head().span, is_top_level }; + + debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head()); + // If the first pattern is an or-pattern, expand it. - if let Some(vs) = v.expand_or_pat() { + let ret = if let Some(vs) = v.expand_or_pat() { + let subspans: Vec<_> = vs.iter().map(|v| v.head().span).collect(); // We expand the or pattern, trying each of its branches in turn and keeping careful track // of possible unreachable sub-branches. - // - // If two branches have detected some unreachable sub-branches, we need to be careful. If - // they were detected in columns that are not the current one, we want to keep only the - // sub-branches that were unreachable in _all_ branches. Eg. in the following, the last - // `true` is unreachable in the second branch of the first or-pattern, but not otherwise. - // Therefore we don't want to lint that it is unreachable. - // - // ``` - // match (true, true) { - // (true, true) => {} - // (false | true, false | true) => {} - // } - // ``` - // If however the sub-branches come from the current column, they come from the inside of - // the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want - // to lint that the last `false` is unreachable. - // ``` - // match None { - // Some(false) => {} - // None | Some(true | false) => {} - // } - // ``` - let mut matrix = matrix.clone(); - // We keep track of sub-branches separately depending on whether they come from this column - // or from others. - let mut unreachables_this_column: FxHashSet = FxHashSet::default(); - let mut unreachables_other_columns: Vec> = Vec::default(); - // Whether at least one branch is reachable. - let mut any_is_useful = false; - - for v in vs { - let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); - match res { - Useful(unreachables) => { - if let Some((this_column, other_columns)) = unreachables.split_last() { - // We keep the union of unreachables found in the first column. - unreachables_this_column.extend(this_column); - // We keep the intersection of unreachables found in other columns. - if unreachables_other_columns.is_empty() { - unreachables_other_columns = other_columns.to_vec(); - } else { - unreachables_other_columns = unreachables_other_columns - .into_iter() - .zip(other_columns) - .map(|(x, y)| x.intersection(&y).copied().collect()) - .collect(); - } - } - any_is_useful = true; - } - NotUseful => { - unreachables_this_column.insert(v.head().span); - } - UsefulWithWitness(_) => bug!( - "encountered or-pat in the expansion of `_` during exhaustiveness checking" - ), - } - + let usefulnesses = vs.into_iter().map(|v| { + let v_span = v.head().span; + let usefulness = + is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); // If pattern has a guard don't add it to the matrix. if !is_under_guard { // We push the already-seen patterns into the matrix in order to detect redundant // branches like `Some(_) | Some(0)`. matrix.push(v); } + usefulness.unsplit_or_pat(v_span, &subspans) + }); + Usefulness::merge(usefulnesses) + } else { + let v_ctor = v.head_ctor(cx); + if let Constructor::IntRange(ctor_range) = &v_ctor { + // Lint on likely incorrect range patterns (#63987) + ctor_range.lint_overlapping_range_endpoints( + pcx, + matrix.head_ctors_and_spans(cx), + matrix.column_count().unwrap_or(0), + hir_id, + ) } - - return if any_is_useful { - let mut unreachables = if unreachables_other_columns.is_empty() { - let n_columns = v.len(); - (0..n_columns - 1).map(|_| FxHashSet::default()).collect() - } else { - unreachables_other_columns - }; - unreachables.push(unreachables_this_column); - Useful(unreachables) - } else { - NotUseful - }; - } - - // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). - let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty); - let pcx = PatCtxt { cx, matrix, ty, span: v.head().span, is_top_level }; - - debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head()); - - let ret = v - .head_ctor(cx) - .split(pcx, Some(hir_id)) - .into_iter() - .map(|ctor| { + // We split the head constructor of `v`. + let split_ctors = v_ctor.split(pcx, matrix.head_ctors(cx)); + // For each constructor, we compute whether there's a value that starts with it that would + // witness the usefulness of `v`. + let start_matrix = &matrix; + let usefulnesses = split_ctors.into_iter().map(|ctor| { // We cache the result of `Fields::wildcards` because it is used a lot. let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor); - let matrix = pcx.matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns); + let spec_matrix = + start_matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns); let v = v.pop_head_constructor(&ctor_wild_subpatterns); let usefulness = - is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); - usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns) - }) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful); + is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false); + usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns) + }); + Usefulness::merge(usefulnesses) + }; debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret); ret } @@ -942,7 +1011,7 @@ fn is_useful<'p, 'tcx>( /// The arm of a match expression. #[derive(Clone, Copy)] crate struct MatchArm<'p, 'tcx> { - /// The pattern must have been lowered through `MatchVisitor::lower_pattern`. + /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`. crate pat: &'p super::Pat<'tcx>, crate hir_id: HirId, crate has_guard: bool, @@ -960,7 +1029,8 @@ crate struct UsefulnessReport<'p, 'tcx> { /// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which /// of its arms are reachable. /// -/// Note: the input patterns must have been lowered through `MatchVisitor::lower_pattern`. +/// Note: the input patterns must have been lowered through +/// `check_match::MatchVisitor::lower_pattern`. crate fn compute_match_usefulness<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 44999c9b63ab..9abffbacfc3b 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -7,22 +7,18 @@ use rustc_ast as ast; use rustc_ast::attr::HasAttrs; -use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, LazyTokenStream, TokenStream, TokenTree}; +use rustc_ast::token::{self, Nonterminal}; +use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens, LazyTokenStream, TokenStream}; use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; use rustc_session::parse::ParseSess; -use rustc_span::{symbol::kw, FileName, SourceFile, Span, DUMMY_SP}; +use rustc_span::{FileName, SourceFile, Span}; -use smallvec::SmallVec; -use std::cell::RefCell; -use std::mem; use std::path::Path; use std::str; -use tracing::{debug, info}; +use tracing::debug; pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); @@ -237,7 +233,12 @@ pub fn parse_in<'a, T>( // NOTE(Centril): The following probably shouldn't be here but it acknowledges the // fact that architecturally, we are using parsing (read on below to understand why). -pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> TokenStream { +pub fn nt_to_tokenstream( + nt: &Nonterminal, + sess: &ParseSess, + span: Span, + synthesize_tokens: CanSynthesizeMissingTokens, +) -> TokenStream { // A `Nonterminal` is often a parsed AST item. At this point we now // need to convert the parsed AST to an actual token stream, e.g. // un-parse it basically. @@ -255,9 +256,11 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke |tokens: Option<&LazyTokenStream>| tokens.as_ref().map(|t| t.create_token_stream()); let tokens = match *nt { - Nonterminal::NtItem(ref item) => prepend_attrs(&item.attrs, item.tokens.as_ref()), + Nonterminal::NtItem(ref item) => { + prepend_attrs(sess, &item.attrs, nt, span, item.tokens.as_ref()) + } Nonterminal::NtBlock(ref block) => convert_tokens(block.tokens.as_ref()), - Nonterminal::NtStmt(ref stmt) => prepend_attrs(stmt.attrs(), stmt.tokens()), + Nonterminal::NtStmt(ref stmt) => prepend_attrs(sess, stmt.attrs(), nt, span, stmt.tokens()), Nonterminal::NtPat(ref pat) => convert_tokens(pat.tokens.as_ref()), Nonterminal::NtTy(ref ty) => convert_tokens(ty.tokens.as_ref()), Nonterminal::NtIdent(ident, is_raw) => { @@ -274,376 +277,45 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke if expr.tokens.is_none() { debug!("missing tokens for expr {:?}", expr); } - prepend_attrs(&expr.attrs, expr.tokens.as_ref()) + prepend_attrs(sess, &expr.attrs, nt, span, expr.tokens.as_ref()) } }; - // Caches the stringification of 'good' `TokenStreams` which passed - // `tokenstream_probably_equal_for_proc_macro`. This allows us to avoid - // repeatedly stringifying and comparing the same `TokenStream` for deeply - // nested nonterminals. - // - // We cache by the strinification instead of the `TokenStream` to avoid - // needing to implement `Hash` for `TokenStream`. Note that it's possible to - // have two distinct `TokenStream`s that stringify to the same result - // (e.g. if they differ only in hygiene information). However, any - // information lost during the stringification process is also intentionally - // ignored by `tokenstream_probably_equal_for_proc_macro`, so it's fine - // that a single cache entry may 'map' to multiple distinct `TokenStream`s. - // - // This is a temporary hack to prevent compilation blowup on certain inputs. - // The entire pretty-print/retokenize process will be removed soon. - thread_local! { - static GOOD_TOKEN_CACHE: RefCell> = Default::default(); - } - - // FIXME(#43081): Avoid this pretty-print + reparse hack - // Pretty-print the AST struct without inserting any parenthesis - // beyond those explicitly written by the user (e.g. `ExpnKind::Paren`). - // The resulting stream may have incorrect precedence, but it's only - // ever used for a comparison against the capture tokenstream. - let source = pprust::nonterminal_to_string_no_extra_parens(nt); - let filename = FileName::macro_expansion_source_code(&source); - let reparsed_tokens = parse_stream_from_source_str(filename, source.clone(), sess, Some(span)); - - // During early phases of the compiler the AST could get modified - // directly (e.g., attributes added or removed) and the internal cache - // of tokens my not be invalidated or updated. Consequently if the - // "lossless" token stream disagrees with our actual stringification - // (which has historically been much more battle-tested) then we go - // with the lossy stream anyway (losing span information). - // - // Note that the comparison isn't `==` here to avoid comparing spans, - // but it *also* is a "probable" equality which is a pretty weird - // definition. We mostly want to catch actual changes to the AST - // like a `#[cfg]` being processed or some weird `macro_rules!` - // expansion. - // - // What we *don't* want to catch is the fact that a user-defined - // literal like `0xf` is stringified as `15`, causing the cached token - // stream to not be literal `==` token-wise (ignoring spans) to the - // token stream we got from stringification. - // - // Instead the "probably equal" check here is "does each token - // recursively have the same discriminant?" We basically don't look at - // the token values here and assume that such fine grained token stream - // modifications, including adding/removing typically non-semantic - // tokens such as extra braces and commas, don't happen. if let Some(tokens) = tokens { - if GOOD_TOKEN_CACHE.with(|cache| cache.borrow().contains(&source)) { - return tokens; - } - - // Compare with a non-relaxed delim match to start. - if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess, false) { - GOOD_TOKEN_CACHE.with(|cache| cache.borrow_mut().insert(source.clone())); - return tokens; - } - - // The check failed. This time, we pretty-print the AST struct with parenthesis - // inserted to preserve precedence. This may cause `None`-delimiters in the captured - // token stream to match up with inserted parenthesis in the reparsed stream. - let source_with_parens = pprust::nonterminal_to_string(nt); - let filename_with_parens = FileName::macro_expansion_source_code(&source_with_parens); - - if GOOD_TOKEN_CACHE.with(|cache| cache.borrow().contains(&source_with_parens)) { - return tokens; - } - - let reparsed_tokens_with_parens = parse_stream_from_source_str( - filename_with_parens, - source_with_parens, - sess, - Some(span), - ); - - // Compare with a relaxed delim match - we want inserted parenthesis in the - // reparsed stream to match `None`-delimiters in the original stream. - if tokenstream_probably_equal_for_proc_macro( - &tokens, - &reparsed_tokens_with_parens, - sess, - true, - ) { - GOOD_TOKEN_CACHE.with(|cache| cache.borrow_mut().insert(source.clone())); - return tokens; - } - - info!( - "cached tokens found, but they're not \"probably equal\", \ - going with stringified version" - ); - info!("cached tokens: {}", pprust::tts_to_string(&tokens)); - info!("reparsed tokens: {}", pprust::tts_to_string(&reparsed_tokens_with_parens)); - - info!("cached tokens debug: {:?}", tokens); - info!("reparsed tokens debug: {:?}", reparsed_tokens_with_parens); - } - reparsed_tokens -} - -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -// -// This is otherwise the same as `eq_unspanned`, only recursing with a -// different method. -pub fn tokenstream_probably_equal_for_proc_macro( - tokens: &TokenStream, - reparsed_tokens: &TokenStream, - sess: &ParseSess, - relaxed_delim_match: bool, -) -> bool { - // When checking for `probably_eq`, we ignore certain tokens that aren't - // preserved in the AST. Because they are not preserved, the pretty - // printer arbitrarily adds or removes them when printing as token - // streams, making a comparison between a token stream generated from an - // AST and a token stream which was parsed into an AST more reliable. - fn semantic_tree(tree: &TokenTree) -> bool { - if let TokenTree::Token(token) = tree { - if let - // The pretty printer tends to add trailing commas to - // everything, and in particular, after struct fields. - | token::Comma - // The pretty printer collapses many semicolons into one. - | token::Semi - // We don't preserve leading `|` tokens in patterns, so - // we ignore them entirely - | token::BinOp(token::BinOpToken::Or) - // We don't preserve trailing '+' tokens in trait bounds, - // so we ignore them entirely - | token::BinOp(token::BinOpToken::Plus) - // The pretty printer can turn `$crate` into `::crate_name` - | token::ModSep = token.kind { - return false; - } - } - true - } - - // When comparing two `TokenStream`s, we ignore the `IsJoint` information. - // - // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will - // use `Token.glue` on adjacent tokens with the proper `IsJoint`. - // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`) - // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent - // when determining if two `TokenStream`s are 'probably equal'. - // - // Therefore, we use `break_two_token_op` to convert all tokens - // to the 'unglued' form (if it exists). This ensures that two - // `TokenStream`s which differ only in how their tokens are glued - // will be considered 'probably equal', which allows us to keep spans. - // - // This is important when the original `TokenStream` contained - // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces - // will be omitted when we pretty-print, which can cause the original - // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`, - // leading to some tokens being 'glued' together in one stream but not - // the other. See #68489 for more details. - fn break_tokens(tree: TokenTree) -> impl Iterator { - // In almost all cases, we should have either zero or one levels - // of 'unglueing'. However, in some unusual cases, we may need - // to iterate breaking tokens mutliple times. For example: - // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]' - let mut token_trees: SmallVec<[_; 2]>; - if let TokenTree::Token(token) = tree { - let mut out = SmallVec::<[_; 2]>::new(); - out.push(token); - // Iterate to fixpoint: - // * We start off with 'out' containing our initial token, and `temp` empty - // * If we are able to break any tokens in `out`, then `out` will have - // at least one more element than 'temp', so we will try to break tokens - // again. - // * If we cannot break any tokens in 'out', we are done - loop { - let mut temp = SmallVec::<[_; 2]>::new(); - let mut changed = false; - - for token in out.into_iter() { - if let Some((first, second)) = token.kind.break_two_token_op() { - temp.push(Token::new(first, DUMMY_SP)); - temp.push(Token::new(second, DUMMY_SP)); - changed = true; - } else { - temp.push(token); - } - } - out = temp; - if !changed { - break; - } - } - token_trees = out.into_iter().map(TokenTree::Token).collect(); - } else { - token_trees = SmallVec::new(); - token_trees.push(tree); - } - token_trees.into_iter() - } - - fn expand_token(tree: TokenTree, sess: &ParseSess) -> impl Iterator { - // When checking tokenstreams for 'probable equality', we are comparing - // a captured (from parsing) `TokenStream` to a reparsed tokenstream. - // The reparsed Tokenstream will never have `None`-delimited groups, - // since they are only ever inserted as a result of macro expansion. - // Therefore, inserting a `None`-delimtied group here (when we - // convert a nested `Nonterminal` to a tokenstream) would cause - // a mismatch with the reparsed tokenstream. - // - // Note that we currently do not handle the case where the - // reparsed stream has a `Parenthesis`-delimited group - // inserted. This will cause a spurious mismatch: - // issue #75734 tracks resolving this. - - let expanded: SmallVec<[_; 1]> = - if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree { - nt_to_tokenstream(nt, sess, *span) - .into_trees() - .flat_map(|t| expand_token(t, sess)) - .collect() - } else { - // Filter before and after breaking tokens, - // since we may want to ignore both glued and unglued tokens. - std::iter::once(tree) - .filter(semantic_tree) - .flat_map(break_tokens) - .filter(semantic_tree) - .collect() - }; - expanded.into_iter() - } - - // Break tokens after we expand any nonterminals, so that we break tokens - // that are produced as a result of nonterminal expansion. - let tokens = tokens.trees().flat_map(|t| expand_token(t, sess)); - let reparsed_tokens = reparsed_tokens.trees().flat_map(|t| expand_token(t, sess)); - - tokens.eq_by(reparsed_tokens, |t, rt| { - tokentree_probably_equal_for_proc_macro(&t, &rt, sess, relaxed_delim_match) - }) -} - -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -// -// This is otherwise the same as `eq_unspanned`, only recursing with a -// different method. -pub fn tokentree_probably_equal_for_proc_macro( - token: &TokenTree, - reparsed_token: &TokenTree, - sess: &ParseSess, - relaxed_delim_match: bool, -) -> bool { - match (token, reparsed_token) { - (TokenTree::Token(token), TokenTree::Token(reparsed_token)) => { - token_probably_equal_for_proc_macro(token, reparsed_token) - } - ( - TokenTree::Delimited(_, delim, tokens), - TokenTree::Delimited(_, reparsed_delim, reparsed_tokens), - ) if delim == reparsed_delim => tokenstream_probably_equal_for_proc_macro( - tokens, - reparsed_tokens, - sess, - relaxed_delim_match, - ), - (TokenTree::Delimited(_, DelimToken::NoDelim, tokens), reparsed_token) => { - if relaxed_delim_match { - if let TokenTree::Delimited(_, DelimToken::Paren, reparsed_tokens) = reparsed_token - { - if tokenstream_probably_equal_for_proc_macro( - tokens, - reparsed_tokens, - sess, - relaxed_delim_match, - ) { - return true; - } - } - } - tokens.len() == 1 - && tokentree_probably_equal_for_proc_macro( - &tokens.trees().next().unwrap(), - reparsed_token, - sess, - relaxed_delim_match, - ) - } - _ => false, + return tokens; + } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) { + return fake_token_stream(sess, nt, span); + } else { + let pretty = rustc_ast_pretty::pprust::nonterminal_to_string_no_extra_parens(&nt); + panic!("Missing tokens at {:?} for nt {:?}", span, pretty); } } -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool { - if mem::discriminant(&first.kind) != mem::discriminant(&other.kind) { - return false; - } - use rustc_ast::token::TokenKind::*; - match (&first.kind, &other.kind) { - (&Eq, &Eq) - | (&Lt, &Lt) - | (&Le, &Le) - | (&EqEq, &EqEq) - | (&Ne, &Ne) - | (&Ge, &Ge) - | (&Gt, &Gt) - | (&AndAnd, &AndAnd) - | (&OrOr, &OrOr) - | (&Not, &Not) - | (&Tilde, &Tilde) - | (&At, &At) - | (&Dot, &Dot) - | (&DotDot, &DotDot) - | (&DotDotDot, &DotDotDot) - | (&DotDotEq, &DotDotEq) - | (&Comma, &Comma) - | (&Semi, &Semi) - | (&Colon, &Colon) - | (&ModSep, &ModSep) - | (&RArrow, &RArrow) - | (&LArrow, &LArrow) - | (&FatArrow, &FatArrow) - | (&Pound, &Pound) - | (&Dollar, &Dollar) - | (&Question, &Question) - | (&Eof, &Eof) => true, - - (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b, - - (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b, - - (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3, - - (&Literal(a), &Literal(b)) => a == b, - - (&Lifetime(a), &Lifetime(b)) => a == b, - (&Ident(a, b), &Ident(c, d)) => { - b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate) - } - - (&Interpolated(..), &Interpolated(..)) => panic!("Unexpanded Interpolated!"), - - _ => panic!("forgot to add a token?"), - } +pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal, span: Span) -> TokenStream { + let source = pprust::nonterminal_to_string(nt); + let filename = FileName::macro_expansion_source_code(&source); + parse_stream_from_source_str(filename, source, sess, Some(span)) } fn prepend_attrs( + sess: &ParseSess, attrs: &[ast::Attribute], + nt: &Nonterminal, + span: Span, tokens: Option<&tokenstream::LazyTokenStream>, ) -> Option { - let tokens = tokens?.create_token_stream(); if attrs.is_empty() { - return Some(tokens); + return Some(tokens?.create_token_stream()); } let mut builder = tokenstream::TokenStreamBuilder::new(); for attr in attrs { // FIXME: Correctly handle tokens for inner attributes. // For now, we fall back to reparsing the original AST node if attr.style == ast::AttrStyle::Inner { - return None; + return Some(fake_token_stream(sess, nt, span)); } builder.push(attr.tokens()); } - builder.push(tokens); + builder.push(tokens?.create_token_stream()); Some(builder.build()) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 350a372a684c..98c7b9a63a55 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1912,4 +1912,22 @@ impl<'a> Parser<'a> { *self = snapshot; Err(err) } + + /// Get the diagnostics for the cases where `move async` is found. + /// + /// `move_async_span` starts at the 'm' of the move keyword and ends with the 'c' of the async keyword + pub(super) fn incorrect_move_async_order_found( + &self, + move_async_span: Span, + ) -> DiagnosticBuilder<'a> { + let mut err = + self.struct_span_err(move_async_span, "the order of `move` and `async` is incorrect"); + err.span_suggestion_verbose( + move_async_span, + "try switching the order", + "async move".to_owned(), + Applicability::MaybeIncorrect, + ); + err + } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 93be478fc8c2..b147f42fada2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,5 +1,5 @@ -use super::pat::{GateOr, PARAM_EXPECTED}; -use super::ty::{AllowPlus, RecoverQPath}; +use super::pat::{GateOr, RecoverComma, PARAM_EXPECTED}; +use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; use super::{SemiColonMode, SeqSep, TokenExpectType}; use crate::maybe_recover_from_interpolated_ty_qpath; @@ -1603,7 +1603,7 @@ impl<'a> Parser<'a> { self.sess.gated_spans.gate(sym::async_closure, span); } - let capture_clause = self.parse_capture_clause(); + let capture_clause = self.parse_capture_clause()?; let decl = self.parse_fn_block_decl()?; let decl_hi = self.prev_token.span; let body = match decl.output { @@ -1626,8 +1626,18 @@ impl<'a> Parser<'a> { } /// Parses an optional `move` prefix to a closure-like construct. - fn parse_capture_clause(&mut self) -> CaptureBy { - if self.eat_keyword(kw::Move) { CaptureBy::Value } else { CaptureBy::Ref } + fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> { + if self.eat_keyword(kw::Move) { + // Check for `move async` and recover + if self.check_keyword(kw::Async) { + let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); + Err(self.incorrect_move_async_order_found(move_async_span)) + } else { + Ok(CaptureBy::Value) + } + } else { + Ok(CaptureBy::Ref) + } } /// Parses the `|arg, arg|` header of a closure. @@ -1647,7 +1657,8 @@ impl<'a> Parser<'a> { self.expect_or()?; args }; - let output = self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes)?; + let output = + self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes)?; Ok(P(FnDecl { inputs, output })) } @@ -1728,7 +1739,7 @@ impl<'a> Parser<'a> { /// The `let` token has already been eaten. fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_top_pat(GateOr::No)?; + let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?; self.expect(&token::Eq)?; let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) @@ -1791,7 +1802,7 @@ impl<'a> Parser<'a> { _ => None, }; - let pat = self.parse_top_pat(GateOr::Yes)?; + let pat = self.parse_top_pat(GateOr::Yes, RecoverComma::Yes)?; if !self.eat_keyword(kw::In) { self.error_missing_in_for_loop(); } @@ -1901,7 +1912,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - let pat = self.parse_top_pat(GateOr::No)?; + let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?; let guard = if self.eat_keyword(kw::If) { let if_span = self.prev_token.span; let cond = self.parse_expr()?; @@ -2018,7 +2029,7 @@ impl<'a> Parser<'a> { fn parse_async_block(&mut self, mut attrs: AttrVec) -> PResult<'a, P> { let lo = self.token.span; self.expect_keyword(kw::Async)?; - let capture_clause = self.parse_capture_clause(); + let capture_clause = self.parse_capture_clause()?; let (iattrs, body) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); let kind = ExprKind::Async(capture_clause, DUMMY_NODE_ID, body); diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index dd99a7587dd5..860e63020bb0 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -5,7 +5,7 @@ use rustc_ast::{ self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause, }; use rustc_errors::PResult; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::kw; impl<'a> Parser<'a> { /// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. @@ -56,8 +56,6 @@ impl<'a> Parser<'a> { self.expect(&token::Colon)?; let ty = self.parse_ty()?; - self.sess.gated_spans.gate(sym::min_const_generics, const_span.to(self.prev_token.span)); - Ok(GenericParam { ident, id: ast::DUMMY_NODE_ID, @@ -240,7 +238,7 @@ impl<'a> Parser<'a> { // Parse type with mandatory colon and (possibly empty) bounds, // or with mandatory equality sign and the second type. - let ty = self.parse_ty()?; + let ty = self.parse_ty_for_where_clause()?; if self.eat(&token::Colon) { let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; Ok(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 5954b370e6d9..c6669f046829 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,5 +1,5 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error}; -use super::ty::{AllowPlus, RecoverQPath}; +use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{FollowedByType, Parser, PathStyle}; use crate::maybe_whole; @@ -247,9 +247,14 @@ impl<'a> Parser<'a> { (ident, ItemKind::Static(ty, m, expr)) } else if let Const::Yes(const_span) = self.parse_constness() { // CONST ITEM - self.recover_const_mut(const_span); - let (ident, ty, expr) = self.parse_item_global(None)?; - (ident, ItemKind::Const(def(), ty, expr)) + if self.token.is_keyword(kw::Impl) { + // recover from `const impl`, suggest `impl const` + self.recover_const_impl(const_span, attrs, def())? + } else { + self.recover_const_mut(const_span); + let (ident, ty, expr) = self.parse_item_global(None)?; + (ident, ItemKind::Const(def(), ty, expr)) + } } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM self.parse_item_trait(attrs, lo)? @@ -489,7 +494,7 @@ impl<'a> Parser<'a> { let polarity = self.parse_polarity(); // Parse both types and traits as a type, then reinterpret if necessary. - let err_path = |span| ast::Path::from_ident(Ident::new(kw::Invalid, span)); + let err_path = |span| ast::Path::from_ident(Ident::new(kw::Empty, span)); let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) { let span = self.prev_token.span.between(self.token.span); @@ -988,6 +993,36 @@ impl<'a> Parser<'a> { } } + /// Recover on `const impl` with `const` already eaten. + fn recover_const_impl( + &mut self, + const_span: Span, + attrs: &mut Vec, + defaultness: Defaultness, + ) -> PResult<'a, ItemInfo> { + let impl_span = self.token.span; + let mut err = self.expected_ident_found(); + let mut impl_info = self.parse_item_impl(attrs, defaultness)?; + match impl_info.1 { + // only try to recover if this is implementing a trait for a type + ItemKind::Impl { of_trait: Some(ref trai), ref mut constness, .. } => { + *constness = Const::Yes(const_span); + + let before_trait = trai.path.span.shrink_to_lo(); + let const_up_to_impl = const_span.with_hi(impl_span.lo()); + err.multipart_suggestion( + "you might have meant to write a const trait impl", + vec![(const_up_to_impl, "".to_owned()), (before_trait, "const ".to_owned())], + Applicability::MaybeIncorrect, + ) + .emit(); + } + ItemKind::Impl { .. } => return Err(err), + _ => unreachable!(), + } + Ok(impl_info) + } + /// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with /// `["const" | ("static" "mut"?)]` already parsed and stored in `m`. /// @@ -1514,7 +1549,7 @@ impl<'a> Parser<'a> { let header = self.parse_fn_front_matter()?; // `const ... fn` let ident = self.parse_ident()?; // `foo` let mut generics = self.parse_generics()?; // `<'a, T, ...>` - let decl = self.parse_fn_decl(req_name, AllowPlus::Yes)?; // `(p: u8, ...)` + let decl = self.parse_fn_decl(req_name, AllowPlus::Yes, RecoverReturnSign::Yes)?; // `(p: u8, ...)` generics.where_clause = self.parse_where_clause()?; // `where T: Ord` let mut sig_hi = self.prev_token.span; @@ -1645,10 +1680,11 @@ impl<'a> Parser<'a> { &mut self, req_name: ReqName, ret_allow_plus: AllowPlus, + recover_return_sign: RecoverReturnSign, ) -> PResult<'a, P> { Ok(P(FnDecl { inputs: self.parse_fn_params(req_name)?, - output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes)?, + output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes, recover_return_sign)?, })) } @@ -1663,7 +1699,7 @@ impl<'a> Parser<'a> { // Skip every token until next possible arg or end. p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]); // Create a placeholder argument for proper arg count (issue #34264). - Ok(dummy_arg(Ident::new(kw::Invalid, lo.to(p.prev_token.span)))) + Ok(dummy_arg(Ident::new(kw::Empty, lo.to(p.prev_token.span)))) }); // ...now that we've parsed the first argument, `self` is no longer allowed. first_param = false; @@ -1723,7 +1759,7 @@ impl<'a> Parser<'a> { } match ty { Ok(ty) => { - let ident = Ident::new(kw::Invalid, self.prev_token.span); + let ident = Ident::new(kw::Empty, self.prev_token.span); let bm = BindingMode::ByValue(Mutability::Not); let pat = self.mk_pat_ident(ty.span, bm, ident); (pat, ty) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index df4695b18e71..073e62c41d31 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -17,7 +17,7 @@ pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, DelimToken, Token, TokenKind}; use rustc_ast::tokenstream::{self, DelimSpan, LazyTokenStream, Spacing}; -use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree, TreeAndSpacing}; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe}; use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit}; @@ -132,6 +132,28 @@ struct TokenCursor { // Counts the number of calls to `next` or `next_desugared`, // depending on whether `desugar_doc_comments` is set. num_next_calls: usize, + // During parsing, we may sometimes need to 'unglue' a + // glued token into two component tokens + // (e.g. '>>' into '>' and '>), so that the parser + // can consume them one at a time. This process + // bypasses the normal capturing mechanism + // (e.g. `num_next_calls` will not be incremented), + // since the 'unglued' tokens due not exist in + // the original `TokenStream`. + // + // If we end up consuming both unglued tokens, + // then this is not an issue - we'll end up + // capturing the single 'glued' token. + // + // However, in certain circumstances, we may + // want to capture just the first 'unglued' token. + // For example, capturing the `Vec` + // in `Option>` requires us to unglue + // the trailing `>>` token. The `append_unglued_token` + // field is used to track this token - it gets + // appended to the captured stream when + // we evaluate a `LazyTokenStream` + append_unglued_token: Option, } #[derive(Clone)] @@ -336,6 +358,7 @@ impl<'a> Parser<'a> { stack: Vec::new(), num_next_calls: 0, desugar_doc_comments, + append_unglued_token: None, }, desugar_doc_comments, unmatched_angle_bracket_count: 0, @@ -359,6 +382,10 @@ impl<'a> Parser<'a> { self.token_cursor.next() }; self.token_cursor.num_next_calls += 1; + // We've retrieved an token from the underlying + // cursor, so we no longer need to worry about + // an unglued token. See `break_and_eat` for more details + self.token_cursor.append_unglued_token = None; if next.span.is_dummy() { // Tweak the location for better diagnostics, but keep syntactic context intact. next.span = fallback_span.with_ctxt(next.span.ctxt()); @@ -555,6 +582,14 @@ impl<'a> Parser<'a> { let first_span = self.sess.source_map().start_point(self.token.span); let second_span = self.token.span.with_lo(first_span.hi()); self.token = Token::new(first, first_span); + // Keep track of this token - if we end token capturing now, + // we'll want to append this token to the captured stream. + // + // If we consume any additional tokens, then this token + // is not needed (we'll capture the entire 'glued' token), + // and `next_tok` will set this field to `None` + self.token_cursor.append_unglued_token = + Some((TokenTree::Token(self.token.clone()), Spacing::Alone)); // Use the spacing of the glued token as the spacing // of the unglued second token. self.bump_with((Token::new(second, second_span), self.token_spacing)); @@ -685,13 +720,9 @@ impl<'a> Parser<'a> { Ok(t) => { // Parsed successfully, therefore most probably the code only // misses a separator. - let mut exp_span = self.sess.source_map().next_point(sp); - if self.sess.source_map().is_multiline(exp_span) { - exp_span = sp; - } expect_err .span_suggestion_short( - exp_span, + sp, &format!("missing `{}`", token_str), token_str, Applicability::MaybeIncorrect, @@ -1230,6 +1261,7 @@ impl<'a> Parser<'a> { num_calls: usize, desugar_doc_comments: bool, trailing_semi: bool, + append_unglued_token: Option, } impl CreateTokenStream for LazyTokenStreamImpl { fn create_token_stream(&self) -> TokenStream { @@ -1253,12 +1285,18 @@ impl<'a> Parser<'a> { })) .take(num_calls); - make_token_stream(tokens) + make_token_stream(tokens, self.append_unglued_token.clone()) } fn add_trailing_semi(&self) -> Box { if self.trailing_semi { panic!("Called `add_trailing_semi` twice!"); } + if self.append_unglued_token.is_some() { + panic!( + "Cannot call `add_trailing_semi` when we have an unglued token {:?}", + self.append_unglued_token + ); + } let mut new = self.clone(); new.trailing_semi = true; Box::new(new) @@ -1271,6 +1309,7 @@ impl<'a> Parser<'a> { cursor_snapshot, desugar_doc_comments: self.desugar_doc_comments, trailing_semi: false, + append_unglued_token: self.token_cursor.append_unglued_token.clone(), }; Ok((ret, Some(LazyTokenStream::new(lazy_impl)))) } @@ -1325,7 +1364,10 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec, sess: &Pa /// Converts a flattened iterator of tokens (including open and close delimiter tokens) /// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair /// of open and close delims. -fn make_token_stream(tokens: impl Iterator) -> TokenStream { +fn make_token_stream( + tokens: impl Iterator, + append_unglued_token: Option, +) -> TokenStream { #[derive(Debug)] struct FrameData { open: Span, @@ -1348,14 +1390,17 @@ fn make_token_stream(tokens: impl Iterator) -> TokenStr .inner .push((delimited, Spacing::Alone)); } - token => stack - .last_mut() - .expect("Bottom token frame is missing!") - .inner - .push((TokenTree::Token(token), spacing)), + token => { + stack + .last_mut() + .expect("Bottom token frame is missing!") + .inner + .push((TokenTree::Token(token), spacing)); + } } } - let final_buf = stack.pop().expect("Missing final buf!"); + let mut final_buf = stack.pop().expect("Missing final buf!"); + final_buf.inner.extend(append_unglued_token); assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); TokenStream::new(final_buf.inner) } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index c007f96a7980..eb5d7075f008 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -4,6 +4,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; +use crate::parser::pat::{GateOr, RecoverComma}; use crate::parser::{FollowedByType, Parser, PathStyle}; impl<'a> Parser<'a> { @@ -27,6 +28,8 @@ impl<'a> Parser<'a> { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) + // This exception is here for backwards compatibility. + && !token.is_keyword(kw::Const) } NonterminalKind::Ty => token.can_begin_type(), NonterminalKind::Ident => get_macro_ident(token).is_some(), @@ -55,7 +58,7 @@ impl<'a> Parser<'a> { }, _ => false, }, - NonterminalKind::Pat => match token.kind { + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => match token.kind { token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) token::OpenDelim(token::Paren) | // tuple pattern token::OpenDelim(token::Bracket) | // slice pattern @@ -68,6 +71,8 @@ impl<'a> Parser<'a> { token::ModSep | // path token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) + // leading vert `|` or-pattern + token::BinOp(token::Or) => matches!(kind, NonterminalKind::Pat2021 {..}), token::Interpolated(ref nt) => may_be_ident(nt), _ => false, }, @@ -84,6 +89,7 @@ impl<'a> Parser<'a> { } } + /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> { // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) // needs to have them force-captured here. @@ -127,8 +133,14 @@ impl<'a> Parser<'a> { } } } - NonterminalKind::Pat => { - let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?; + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { + let (mut pat, tokens) = self.collect_tokens(|this| match kind { + NonterminalKind::Pat2018 { .. } => this.parse_pat(None), + NonterminalKind::Pat2021 { .. } => { + this.parse_top_pat(GateOr::Yes, RecoverComma::No) + } + _ => unreachable!(), + })?; // We have have eaten an NtPat, which could already have tokens if pat.tokens.is_none() { pat.tokens = tokens; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index b62c73738008..456e32680fe5 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -26,7 +26,7 @@ pub(super) enum GateOr { /// Whether or not to recover a `,` when parsing or-patterns. #[derive(PartialEq, Copy, Clone)] -enum RecoverComma { +pub(super) enum RecoverComma { Yes, No, } @@ -43,13 +43,17 @@ impl<'a> Parser<'a> { /// Entry point to the main pattern parser. /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - pub(super) fn parse_top_pat(&mut self, gate_or: GateOr) -> PResult<'a, P> { + pub(super) fn parse_top_pat( + &mut self, + gate_or: GateOr, + rc: RecoverComma, + ) -> PResult<'a, P> { // Allow a '|' before the pats (RFCs 1925, 2530, and 2535). let gated_leading_vert = self.eat_or_separator(None) && gate_or == GateOr::Yes; let leading_vert_span = self.prev_token.span; // Parse the possibly-or-pattern. - let pat = self.parse_pat_with_or(None, gate_or, RecoverComma::Yes)?; + let pat = self.parse_pat_with_or(None, gate_or, rc)?; // If we parsed a leading `|` which should be gated, // and no other gated or-pattern has been parsed thus far, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 17e5bcf76050..60a47ca12b86 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -1,4 +1,4 @@ -use super::ty::{AllowPlus, RecoverQPath}; +use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, TokenType}; use crate::maybe_whole; use rustc_ast::ptr::P; @@ -231,7 +231,8 @@ impl<'a> Parser<'a> { // `(T, U) -> R` let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; let span = ident.span.to(self.prev_token.span); - let output = self.parse_ret_ty(AllowPlus::No, RecoverQPath::No)?; + let output = + self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?; ParenthesizedArgs { inputs, output, span }.into() }; @@ -500,10 +501,9 @@ impl<'a> Parser<'a> { pub(super) fn expr_is_valid_const_arg(&self, expr: &P) -> bool { match &expr.kind { ast::ExprKind::Block(_, _) | ast::ExprKind::Lit(_) => true, - ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind { - ast::ExprKind::Lit(_) => true, - _ => false, - }, + ast::ExprKind::Unary(ast::UnOp::Neg, expr) => { + matches!(expr.kind, ast::ExprKind::Lit(_)) + } // We can only resolve single-segment paths at the moment, because multi-segment paths // require type-checking: see `visit_generic_arg` in `src/librustc_resolve/late.rs`. ast::ExprKind::Path(None, path) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index e974556f43a4..2942747991a1 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,7 +1,7 @@ use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; use super::diagnostics::{AttemptLocalParseRecovery, Error}; use super::expr::LhsExpr; -use super::pat::GateOr; +use super::pat::{GateOr, RecoverComma}; use super::path::PathStyle; use super::{BlockMode, Parser, Restrictions, SemiColonMode}; use crate::maybe_whole; @@ -185,7 +185,7 @@ impl<'a> Parser<'a> { /// Parses a local variable declaration. fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_top_pat(GateOr::Yes)?; + let pat = self.parse_top_pat(GateOr::Yes, RecoverComma::Yes)?; let (err, ty) = if self.eat(&token::Colon) { // Save the state of the parser before parsing type normally, in case there is a `:` diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 7a6ebca4e154..9553f5d09e83 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -43,6 +43,37 @@ pub(super) enum RecoverQPath { No, } +/// Signals whether parsing a type should recover `->`. +/// +/// More specifically, when parsing a function like: +/// ```rust +/// fn foo() => u8 { 0 } +/// fn bar(): u8 { 0 } +/// ``` +/// The compiler will try to recover interpreting `foo() => u8` as `foo() -> u8` when calling +/// `parse_ty` with anything except `RecoverReturnSign::No`, and it will try to recover `bar(): u8` +/// as `bar() -> u8` when passing `RecoverReturnSign::Yes` to `parse_ty` +#[derive(Copy, Clone, PartialEq)] +pub(super) enum RecoverReturnSign { + Yes, + OnlyFatArrow, + No, +} + +impl RecoverReturnSign { + /// [RecoverReturnSign::Yes] allows for recovering `fn foo() => u8` and `fn foo(): u8`, + /// [RecoverReturnSign::OnlyFatArrow] allows for recovering only `fn foo() => u8` (recovering + /// colons can cause problems when parsing where clauses), and + /// [RecoverReturnSign::No] doesn't allow for any recovery of the return type arrow + fn can_recover(self, token: &TokenKind) -> bool { + match self { + Self::Yes => matches!(token, token::FatArrow | token::Colon), + Self::OnlyFatArrow => matches!(token, token::FatArrow), + Self::No => false, + } + } +} + // Is `...` (`CVarArgs`) legal at this level of type parsing? #[derive(PartialEq)] enum AllowCVariadic { @@ -62,14 +93,24 @@ fn can_continue_type_after_non_fn_ident(t: &Token) -> bool { impl<'a> Parser<'a> { /// Parses a type. pub fn parse_ty(&mut self) -> PResult<'a, P> { - self.parse_ty_common(AllowPlus::Yes, RecoverQPath::Yes, AllowCVariadic::No) + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + ) } /// Parse a type suitable for a function or function pointer parameter. /// The difference from `parse_ty` is that this version allows `...` /// (`CVarArgs`) at the top level of the type. pub(super) fn parse_ty_for_param(&mut self) -> PResult<'a, P> { - self.parse_ty_common(AllowPlus::Yes, RecoverQPath::Yes, AllowCVariadic::Yes) + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::Yes, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + ) } /// Parses a type in restricted contexts where `+` is not permitted. @@ -79,7 +120,22 @@ impl<'a> Parser<'a> { /// Example 2: `value1 as TYPE + value2` /// `+` is prohibited to avoid interactions with expression grammar. pub(super) fn parse_ty_no_plus(&mut self) -> PResult<'a, P> { - self.parse_ty_common(AllowPlus::No, RecoverQPath::Yes, AllowCVariadic::No) + self.parse_ty_common( + AllowPlus::No, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + ) + } + + /// Parse a type without recovering `:` as `->` to avoid breaking code such as `where fn() : for<'a>` + pub(super) fn parse_ty_for_where_clause(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::Yes, + RecoverQPath::Yes, + RecoverReturnSign::OnlyFatArrow, + ) } /// Parses an optional return type `[ -> TY ]` in a function declaration. @@ -87,10 +143,35 @@ impl<'a> Parser<'a> { &mut self, allow_plus: AllowPlus, recover_qpath: RecoverQPath, + recover_return_sign: RecoverReturnSign, ) -> PResult<'a, FnRetTy> { Ok(if self.eat(&token::RArrow) { // FIXME(Centril): Can we unconditionally `allow_plus`? - let ty = self.parse_ty_common(allow_plus, recover_qpath, AllowCVariadic::No)?; + let ty = self.parse_ty_common( + allow_plus, + AllowCVariadic::No, + recover_qpath, + recover_return_sign, + )?; + FnRetTy::Ty(ty) + } else if recover_return_sign.can_recover(&self.token.kind) { + // Don't `eat` to prevent `=>` from being added as an expected token which isn't + // actually expected and could only confuse users + self.bump(); + self.struct_span_err(self.prev_token.span, "return types are denoted using `->`") + .span_suggestion_short( + self.prev_token.span, + "use `->` instead", + "->".to_string(), + Applicability::MachineApplicable, + ) + .emit(); + let ty = self.parse_ty_common( + allow_plus, + AllowCVariadic::No, + recover_qpath, + recover_return_sign, + )?; FnRetTy::Ty(ty) } else { FnRetTy::Default(self.token.span.shrink_to_lo()) @@ -100,8 +181,9 @@ impl<'a> Parser<'a> { fn parse_ty_common( &mut self, allow_plus: AllowPlus, - recover_qpath: RecoverQPath, allow_c_variadic: AllowCVariadic, + recover_qpath: RecoverQPath, + recover_return_sign: RecoverReturnSign, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -129,14 +211,14 @@ impl<'a> Parser<'a> { TyKind::Infer } else if self.check_fn_front_matter() { // Function pointer type - self.parse_ty_bare_fn(lo, Vec::new())? + self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? } else if self.check_keyword(kw::For) { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; if self.check_fn_front_matter() { - self.parse_ty_bare_fn(lo, lifetime_defs)? + self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); @@ -338,9 +420,14 @@ impl<'a> Parser<'a> { /// Function Style ABI Parameter types /// ``` /// We actually parse `FnHeader FnDecl`, but we error on `const` and `async` qualifiers. - fn parse_ty_bare_fn(&mut self, lo: Span, params: Vec) -> PResult<'a, TyKind> { + fn parse_ty_bare_fn( + &mut self, + lo: Span, + params: Vec, + recover_return_sign: RecoverReturnSign, + ) -> PResult<'a, TyKind> { let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter()?; - let decl = self.parse_fn_decl(|_| false, AllowPlus::No)?; + let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); if let ast::Const::Yes(span) = constness { self.error_fn_ptr_bad_qualifier(whole_span, span, "const"); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 73fb28e5c9aa..aeaa862f5fd6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -882,6 +882,18 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_item(self, item) } + fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) { + let target = Target::from_generic_param(generic_param); + self.check_attributes( + generic_param.hir_id, + generic_param.attrs, + &generic_param.span, + target, + None, + ); + intravisit::walk_generic_param(self, generic_param) + } + fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) { let target = Target::from_trait_item(trait_item); self.check_attributes(trait_item.hir_id, &trait_item.attrs, &trait_item.span, target, None); diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index e37c6418eb81..2d6bbff460d7 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -45,6 +45,8 @@ impl NonConstExpr { return None; } + Self::Match(IfLetGuardDesugar) => bug!("if-let guard outside a `match` expression"), + // All other expressions are allowed. Self::Loop(Loop | While | WhileLet) | Self::Match( diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 00152878d6d9..c4fb0cf5b28d 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -23,18 +23,18 @@ use rustc_span::symbol::{sym, Symbol}; // function, then we should explore its block to check for codes that // may need to be marked as live. fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { - match tcx.hir().find(hir_id) { + matches!( + tcx.hir().find(hir_id), Some( Node::Item(..) - | Node::ImplItem(..) - | Node::ForeignItem(..) - | Node::TraitItem(..) - | Node::Variant(..) - | Node::AnonConst(..) - | Node::Pat(..), - ) => true, - _ => false, - } + | Node::ImplItem(..) + | Node::ForeignItem(..) + | Node::TraitItem(..) + | Node::Variant(..) + | Node::AnonConst(..) + | Node::Pat(..), + ) + ) } struct MarkSymbolVisitor<'tcx> { @@ -500,16 +500,16 @@ struct DeadVisitor<'tcx> { impl DeadVisitor<'tcx> { fn should_warn_about_item(&mut self, item: &hir::Item<'_>) -> bool { - let should_warn = match item.kind { + let should_warn = matches!( + item.kind, hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) => true, - _ => false, - }; + | hir::ItemKind::Const(..) + | hir::ItemKind::Fn(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..) + ); should_warn && !self.symbol_is_live(item.hir_id) } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index a161ad16b8c2..fcea1b29ec36 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -360,14 +360,14 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { self.add_from_pat(&arm.pat); + if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard { + self.add_from_pat(pat); + } intravisit::walk_arm(self, arm); } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - let is_shorthand = match param.pat.kind { - rustc_hir::PatKind::Struct(..) => true, - _ => false, - }; + let is_shorthand = matches!(param.pat.kind, rustc_hir::PatKind::Struct(..)); param.pat.each_binding(|_bm, hir_id, _x, ident| { let var = if is_shorthand { Local(LocalInfo { id: hir_id, name: ident.name, is_shorthand: true }) @@ -866,10 +866,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { for arm in arms { let body_succ = self.propagate_through_expr(&arm.body, succ); - let guard_succ = self.propagate_through_opt_expr( - arm.guard.as_ref().map(|hir::Guard::If(e)| *e), - body_succ, - ); + let guard_succ = arm.guard.as_ref().map_or(body_succ, |g| match g { + hir::Guard::If(e) => self.propagate_through_expr(e, body_succ), + hir::Guard::IfLet(pat, e) => { + let let_bind = self.define_bindings_in_pat(pat, body_succ); + self.propagate_through_expr(e, let_bind) + } + }); let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ); self.merge_from_succ(ln, arm_succ); } @@ -1379,7 +1382,7 @@ impl<'tcx> Liveness<'_, 'tcx> { fn should_warn(&self, var: Variable) -> Option { let name = self.ir.variable_name(var); - if name == kw::Invalid { + if name == kw::Empty { return None; } let name: &str = &name.as_str(); diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index f6bbbd80bf1e..3c2462aab26b 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -182,28 +182,32 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { for (dep_v, stab_v) in dep_since.as_str().split('.').zip(stab_since.as_str().split('.')) { - if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::(), stab_v.parse()) { - match dep_v.cmp(&stab_v) { - Ordering::Less => { - self.tcx.sess.span_err( - item_sp, - "An API can't be stabilized \ - after it is deprecated", - ); + match stab_v.parse::() { + Err(_) => { + self.tcx.sess.span_err(item_sp, "Invalid stability version found"); + break; + } + Ok(stab_vp) => match dep_v.parse::() { + Ok(dep_vp) => match dep_vp.cmp(&stab_vp) { + Ordering::Less => { + self.tcx.sess.span_err( + item_sp, + "An API can't be stabilized after it is deprecated", + ); + break; + } + Ordering::Equal => continue, + Ordering::Greater => break, + }, + Err(_) => { + if dep_v != "TBD" { + self.tcx + .sess + .span_err(item_sp, "Invalid deprecation version found"); + } break; } - Ordering::Equal => continue, - Ordering::Greater => break, - } - } else { - // Act like it isn't less because the question is now nonsensical, - // and this makes us not do anything else interesting. - self.tcx.sess.span_err( - item_sp, - "Invalid stability or deprecation \ - version found", - ); - break; + }, } } } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 4414bf57c6b7..1bcfdf0faf66 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -184,8 +184,8 @@ where ty::Dynamic(predicates, ..) => { // All traits in the list are considered the "primary" part of the type // and are visited by shallow visitors. - for predicate in predicates.skip_binder() { - let trait_ref = match predicate { + for predicate in predicates { + let trait_ref = match predicate.skip_binder() { ty::ExistentialPredicate::Trait(trait_ref) => trait_ref, ty::ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx), ty::ExistentialPredicate::AutoTrait(def_id) => { @@ -842,11 +842,9 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { let macro_module_def_id = ty::DefIdTree::parent(self.tcx, self.tcx.hir().local_def_id(md.hir_id).to_def_id()) .unwrap(); - // FIXME(#71104) Should really be using just `as_local_hir_id` but - // some `DefId` do not seem to have a corresponding HirId. let hir_id = macro_module_def_id .as_local() - .and_then(|def_id| self.tcx.hir().opt_local_def_id_to_hir_id(def_id)); + .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)); let mut module_id = match hir_id { Some(module_id) if self.tcx.hir().is_hir_id_module(module_id) => module_id, // `module_id` doesn't correspond to a `mod`, return early (#63164, #65252). @@ -959,7 +957,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { in_update_syntax: bool, ) { // definition of the field - let ident = Ident::new(kw::Invalid, use_ctxt); + let ident = Ident::new(kw::Empty, use_ctxt); let current_hir = self.current_item.unwrap(); let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did, current_hir).1; if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) { diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 09e5dc857a75..ff52fdab19c5 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -60,9 +60,8 @@ pub struct DepNode { // * When a `DepNode::construct` is called, `arg.to_fingerprint()` // is responsible for calling `OnDiskCache::store_foreign_def_id_hash` // if needed - // * When a `DepNode` is loaded from the `PreviousDepGraph`, - // then `PreviousDepGraph::index_to_node` is responsible for calling - // `tcx.register_reused_dep_path_hash` + // * When we serialize the on-disk cache, `OnDiskCache::serialize` is + // responsible for calling `DepGraph::register_reused_dep_nodes`. // // FIXME: Enforce this by preventing manual construction of `DefNode` // (e.g. add a `_priv: ()` field) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 8bde552e2d49..605d7ae4af67 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -3,7 +3,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::QueryInvocationId; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; +use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, LockGuard, Lrc, Ordering}; use rustc_data_structures::unlikely; use rustc_errors::Diagnostic; use rustc_index::vec::{Idx, IndexVec}; @@ -15,6 +15,7 @@ use std::env; use std::hash::Hash; use std::marker::PhantomData; use std::mem; +use std::ops::Range; use std::sync::atomic::Ordering::Relaxed; use super::debug::EdgeFilter; @@ -68,7 +69,7 @@ struct DepGraphData { /// The new encoding of the dependency graph, optimized for red/green /// tracking. The `current` field is the dependency graph of only the /// current compilation session: We don't merge the previous dep-graph into - /// current one anymore. + /// current one anymore, but we do reference shared data to save space. current: CurrentDepGraph, /// The dep-graph from the previous compilation session. It contains all @@ -134,17 +135,61 @@ impl DepGraph { } pub fn query(&self) -> DepGraphQuery { - let data = self.data.as_ref().unwrap().current.data.lock(); - let nodes: Vec<_> = data.iter().map(|n| n.node).collect(); - let mut edges = Vec::new(); - for (from, edge_targets) in data.iter().map(|d| (d.node, &d.edges)) { - for &edge_target in edge_targets.iter() { - let to = data[edge_target].node; - edges.push((from, to)); + let data = self.data.as_ref().unwrap(); + let previous = &data.previous; + + // Note locking order: `prev_index_to_index`, then `data`. + let prev_index_to_index = data.current.prev_index_to_index.lock(); + let data = data.current.data.lock(); + let node_count = data.hybrid_indices.len(); + let edge_count = self.edge_count(&data); + + let mut nodes = Vec::with_capacity(node_count); + let mut edge_list_indices = Vec::with_capacity(node_count); + let mut edge_list_data = Vec::with_capacity(edge_count); + + // See `serialize` for notes on the approach used here. + + edge_list_data.extend(data.unshared_edges.iter().map(|i| i.index())); + + for &hybrid_index in data.hybrid_indices.iter() { + match hybrid_index.into() { + HybridIndex::New(new_index) => { + nodes.push(data.new.nodes[new_index]); + let edges = &data.new.edges[new_index]; + edge_list_indices.push((edges.start.index(), edges.end.index())); + } + HybridIndex::Red(red_index) => { + nodes.push(previous.index_to_node(data.red.node_indices[red_index])); + let edges = &data.red.edges[red_index]; + edge_list_indices.push((edges.start.index(), edges.end.index())); + } + HybridIndex::LightGreen(lg_index) => { + nodes.push(previous.index_to_node(data.light_green.node_indices[lg_index])); + let edges = &data.light_green.edges[lg_index]; + edge_list_indices.push((edges.start.index(), edges.end.index())); + } + HybridIndex::DarkGreen(prev_index) => { + nodes.push(previous.index_to_node(prev_index)); + + let edges_iter = previous + .edge_targets_from(prev_index) + .iter() + .map(|&dst| prev_index_to_index[dst].unwrap().index()); + + let start = edge_list_data.len(); + edge_list_data.extend(edges_iter); + let end = edge_list_data.len(); + edge_list_indices.push((start, end)); + } } } - DepGraphQuery::new(&nodes[..], &edges[..]) + debug_assert_eq!(nodes.len(), node_count); + debug_assert_eq!(edge_list_indices.len(), node_count); + debug_assert_eq!(edge_list_data.len(), edge_count); + + DepGraphQuery::new(&nodes[..], &edge_list_indices[..], &edge_list_data[..]) } pub fn assert_ignored(&self) { @@ -201,7 +246,6 @@ impl DepGraph { key, cx, arg, - false, task, |_key| { Some(TaskDeps { @@ -212,7 +256,6 @@ impl DepGraph { phantom_data: PhantomData, }) }, - |data, key, fingerprint, task| data.complete_task(key, task.unwrap(), fingerprint), hash_result, ) } @@ -222,66 +265,69 @@ impl DepGraph { key: DepNode, cx: Ctxt, arg: A, - no_tcx: bool, task: fn(Ctxt, A) -> R, create_task: fn(DepNode) -> Option>, - finish_task_and_alloc_depnode: fn( - &CurrentDepGraph, - DepNode, - Fingerprint, - Option>, - ) -> DepNodeIndex, hash_result: impl FnOnce(&mut Ctxt::StableHashingContext, &R) -> Option, ) -> (R, DepNodeIndex) { if let Some(ref data) = self.data { let task_deps = create_task(key).map(Lock::new); + let result = K::with_deps(task_deps.as_ref(), || task(cx, arg)); + let edges = task_deps.map_or_else(|| smallvec![], |lock| lock.into_inner().reads); - // In incremental mode, hash the result of the task. We don't - // do anything with the hash yet, but we are computing it - // anyway so that - // - we make sure that the infrastructure works and - // - we can get an idea of the runtime cost. let mut hcx = cx.create_stable_hashing_context(); - - let result = if no_tcx { - task(cx, arg) - } else { - K::with_deps(task_deps.as_ref(), || task(cx, arg)) - }; - let current_fingerprint = hash_result(&mut hcx, &result); - let dep_node_index = finish_task_and_alloc_depnode( - &data.current, - key, - current_fingerprint.unwrap_or(Fingerprint::ZERO), - task_deps.map(|lock| lock.into_inner()), - ); - let print_status = cfg!(debug_assertions) && cx.debug_dep_tasks(); - // Determine the color of the new DepNode. - if let Some(prev_index) = data.previous.node_to_index_opt(&key) { - let prev_fingerprint = data.previous.fingerprint_by_index(prev_index); - - let color = if let Some(current_fingerprint) = current_fingerprint { - if current_fingerprint == prev_fingerprint { + // Intern the new `DepNode`. + let dep_node_index = if let Some(prev_index) = data.previous.node_to_index_opt(&key) { + // Determine the color and index of the new `DepNode`. + let (color, dep_node_index) = if let Some(current_fingerprint) = current_fingerprint + { + if current_fingerprint == data.previous.fingerprint_by_index(prev_index) { if print_status { eprintln!("[task::green] {:?}", key); } - DepNodeColor::Green(dep_node_index) + + // This is a light green node: it existed in the previous compilation, + // its query was re-executed, and it has the same result as before. + let dep_node_index = + data.current.intern_light_green_node(&data.previous, prev_index, edges); + + (DepNodeColor::Green(dep_node_index), dep_node_index) } else { if print_status { eprintln!("[task::red] {:?}", key); } - DepNodeColor::Red + + // This is a red node: it existed in the previous compilation, its query + // was re-executed, but it has a different result from before. + let dep_node_index = data.current.intern_red_node( + &data.previous, + prev_index, + edges, + current_fingerprint, + ); + + (DepNodeColor::Red, dep_node_index) } } else { if print_status { eprintln!("[task::unknown] {:?}", key); } - // Mark the node as Red if we can't hash the result - DepNodeColor::Red + + // This is a red node, effectively: it existed in the previous compilation + // session, its query was re-executed, but it doesn't compute a result hash + // (i.e. it represents a `no_hash` query), so we have no way of determining + // whether or not the result was the same as before. + let dep_node_index = data.current.intern_red_node( + &data.previous, + prev_index, + edges, + Fingerprint::ZERO, + ); + + (DepNodeColor::Red, dep_node_index) }; debug_assert!( @@ -292,12 +338,27 @@ impl DepGraph { ); data.colors.insert(prev_index, color); - } else if print_status { - eprintln!("[task::new] {:?}", key); - } + dep_node_index + } else { + if print_status { + eprintln!("[task::new] {:?}", key); + } + + // This is a new node: it didn't exist in the previous compilation session. + data.current.intern_new_node( + &data.previous, + key, + edges, + current_fingerprint.unwrap_or(Fingerprint::ZERO), + ) + }; (result, dep_node_index) } else { + // Incremental compilation is turned off. We just execute the task + // without tracking. We still provide a dep-node index that uniquely + // identifies the task so that we have a cheap way of referring to + // the query for self-profiling. (task(cx, arg), self.next_virtual_depnode_index()) } } @@ -308,13 +369,36 @@ impl DepGraph { where OP: FnOnce() -> R, { + debug_assert!(!dep_kind.is_eval_always()); + if let Some(ref data) = self.data { let task_deps = Lock::new(TaskDeps::default()); - let result = K::with_deps(Some(&task_deps), op); let task_deps = task_deps.into_inner(); - let dep_node_index = data.current.complete_anon_task(dep_kind, task_deps); + // The dep node indices are hashed here instead of hashing the dep nodes of the + // dependencies. These indices may refer to different nodes per session, but this isn't + // a problem here because we that ensure the final dep node hash is per session only by + // combining it with the per session random number `anon_id_seed`. This hash only need + // to map the dependencies to a single value on a per session basis. + let mut hasher = StableHasher::new(); + task_deps.reads.hash(&mut hasher); + + let target_dep_node = DepNode { + kind: dep_kind, + // Fingerprint::combine() is faster than sending Fingerprint + // through the StableHasher (at least as long as StableHasher + // is so slow). + hash: data.current.anon_id_seed.combine(hasher.finish()).into(), + }; + + let dep_node_index = data.current.intern_new_node( + &data.previous, + target_dep_node, + task_deps.reads, + Fingerprint::ZERO, + ); + (result, dep_node_index) } else { (op(), self.next_virtual_depnode_index()) @@ -331,69 +415,106 @@ impl DepGraph { task: fn(Ctxt, A) -> R, hash_result: impl FnOnce(&mut Ctxt::StableHashingContext, &R) -> Option, ) -> (R, DepNodeIndex) { - self.with_task_impl( - key, - cx, - arg, - false, - task, - |_| None, - |data, key, fingerprint, _| data.alloc_node(key, smallvec![], fingerprint), - hash_result, - ) - } - - #[inline] - pub fn read(&self, v: DepNode) { - if let Some(ref data) = self.data { - let map = data.current.node_to_node_index.get_shard_by_value(&v).lock(); - if let Some(dep_node_index) = map.get(&v).copied() { - std::mem::drop(map); - data.read_index(dep_node_index); - } else { - panic!("DepKind {:?} should be pre-allocated but isn't.", v.kind) - } - } + self.with_task_impl(key, cx, arg, task, |_| None, hash_result) } #[inline] pub fn read_index(&self, dep_node_index: DepNodeIndex) { if let Some(ref data) = self.data { - data.read_index(dep_node_index); + K::read_deps(|task_deps| { + if let Some(task_deps) = task_deps { + let mut task_deps = task_deps.lock(); + let task_deps = &mut *task_deps; + if cfg!(debug_assertions) { + data.current.total_read_count.fetch_add(1, Relaxed); + } + + // As long as we only have a low number of reads we can avoid doing a hash + // insert and potentially allocating/reallocating the hashmap + let new_read = if task_deps.reads.len() < TASK_DEPS_READS_CAP { + task_deps.reads.iter().all(|other| *other != dep_node_index) + } else { + task_deps.read_set.insert(dep_node_index) + }; + if new_read { + task_deps.reads.push(dep_node_index); + if task_deps.reads.len() == TASK_DEPS_READS_CAP { + // Fill `read_set` with what we have so far so we can use the hashset + // next time + task_deps.read_set.extend(task_deps.reads.iter().copied()); + } + + #[cfg(debug_assertions)] + { + if let Some(target) = task_deps.node { + if let Some(ref forbidden_edge) = data.current.forbidden_edge { + let src = self.dep_node_of(dep_node_index); + if forbidden_edge.test(&src, &target) { + panic!("forbidden edge {:?} -> {:?} created", src, target) + } + } + } + } + } else if cfg!(debug_assertions) { + data.current.total_duplicate_read_count.fetch_add(1, Relaxed); + } + } + }) } } #[inline] pub fn dep_node_index_of(&self, dep_node: &DepNode) -> DepNodeIndex { - self.data - .as_ref() - .unwrap() - .current - .node_to_node_index - .get_shard_by_value(dep_node) - .lock() - .get(dep_node) - .cloned() - .unwrap() + self.dep_node_index_of_opt(dep_node).unwrap() + } + + #[inline] + pub fn dep_node_index_of_opt(&self, dep_node: &DepNode) -> Option { + let data = self.data.as_ref().unwrap(); + let current = &data.current; + + if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) { + current.prev_index_to_index.lock()[prev_index] + } else { + current.new_node_to_index.get_shard_by_value(dep_node).lock().get(dep_node).copied() + } } #[inline] pub fn dep_node_exists(&self, dep_node: &DepNode) -> bool { - if let Some(ref data) = self.data { - data.current - .node_to_node_index - .get_shard_by_value(&dep_node) - .lock() - .contains_key(dep_node) - } else { - false + self.data.is_some() && self.dep_node_index_of_opt(dep_node).is_some() + } + + #[inline] + pub fn dep_node_of(&self, dep_node_index: DepNodeIndex) -> DepNode { + let data = self.data.as_ref().unwrap(); + let previous = &data.previous; + let data = data.current.data.lock(); + + match data.hybrid_indices[dep_node_index].into() { + HybridIndex::New(new_index) => data.new.nodes[new_index], + HybridIndex::Red(red_index) => previous.index_to_node(data.red.node_indices[red_index]), + HybridIndex::LightGreen(light_green_index) => { + previous.index_to_node(data.light_green.node_indices[light_green_index]) + } + HybridIndex::DarkGreen(prev_index) => previous.index_to_node(prev_index), } } #[inline] pub fn fingerprint_of(&self, dep_node_index: DepNodeIndex) -> Fingerprint { - let data = self.data.as_ref().expect("dep graph enabled").current.data.lock(); - data[dep_node_index].fingerprint + let data = self.data.as_ref().unwrap(); + let previous = &data.previous; + let data = data.current.data.lock(); + + match data.hybrid_indices[dep_node_index].into() { + HybridIndex::New(new_index) => data.new.fingerprints[new_index], + HybridIndex::Red(red_index) => data.red.fingerprints[red_index], + HybridIndex::LightGreen(light_green_index) => { + previous.fingerprint_by_index(data.light_green.node_indices[light_green_index]) + } + HybridIndex::DarkGreen(prev_index) => previous.fingerprint_by_index(prev_index), + } } pub fn prev_fingerprint_of(&self, dep_node: &DepNode) -> Option { @@ -443,30 +564,95 @@ impl DepGraph { } } - pub fn serialize(&self) -> SerializedDepGraph { - let data = self.data.as_ref().unwrap().current.data.lock(); + fn edge_count(&self, node_data: &LockGuard<'_, DepNodeData>) -> usize { + let data = self.data.as_ref().unwrap(); + let previous = &data.previous; - let fingerprints: IndexVec = - data.iter().map(|d| d.fingerprint).collect(); - let nodes: IndexVec = data.iter().map(|d| d.node).collect(); + let mut edge_count = node_data.unshared_edges.len(); - let total_edge_count: usize = data.iter().map(|d| d.edges.len()).sum(); - - let mut edge_list_indices = IndexVec::with_capacity(nodes.len()); - let mut edge_list_data = Vec::with_capacity(total_edge_count); - - for (current_dep_node_index, edges) in data.iter_enumerated().map(|(i, d)| (i, &d.edges)) { - let start = edge_list_data.len() as u32; - // This should really just be a memcpy :/ - edge_list_data.extend(edges.iter().map(|i| SerializedDepNodeIndex::new(i.index()))); - let end = edge_list_data.len() as u32; - - debug_assert_eq!(current_dep_node_index.index(), edge_list_indices.len()); - edge_list_indices.push((start, end)); + for &hybrid_index in node_data.hybrid_indices.iter() { + if let HybridIndex::DarkGreen(prev_index) = hybrid_index.into() { + edge_count += previous.edge_targets_from(prev_index).len() + } } + edge_count + } + + pub fn serialize(&self) -> SerializedDepGraph { + type SDNI = SerializedDepNodeIndex; + + let data = self.data.as_ref().unwrap(); + let previous = &data.previous; + + // Note locking order: `prev_index_to_index`, then `data`. + let prev_index_to_index = data.current.prev_index_to_index.lock(); + let data = data.current.data.lock(); + let node_count = data.hybrid_indices.len(); + let edge_count = self.edge_count(&data); + + let mut nodes = IndexVec::with_capacity(node_count); + let mut fingerprints = IndexVec::with_capacity(node_count); + let mut edge_list_indices = IndexVec::with_capacity(node_count); + let mut edge_list_data = Vec::with_capacity(edge_count); + + // `rustc_middle::ty::query::OnDiskCache` expects nodes to be in + // `DepNodeIndex` order. The edges in `edge_list_data`, on the other + // hand, don't need to be in a particular order, as long as each node + // can reference its edges as a contiguous range within it. This is why + // we're able to copy `unshared_edges` directly into `edge_list_data`. + // It meets the above requirements, and each non-dark-green node already + // knows the range of edges to reference within it, which they'll push + // onto `edge_list_indices`. Dark green nodes, however, don't have their + // edges in `unshared_edges`, so need to add them to `edge_list_data`. + + edge_list_data.extend(data.unshared_edges.iter().map(|i| SDNI::new(i.index()))); + + for &hybrid_index in data.hybrid_indices.iter() { + match hybrid_index.into() { + HybridIndex::New(i) => { + let new = &data.new; + nodes.push(new.nodes[i]); + fingerprints.push(new.fingerprints[i]); + let edges = &new.edges[i]; + edge_list_indices.push((edges.start.as_u32(), edges.end.as_u32())); + } + HybridIndex::Red(i) => { + let red = &data.red; + nodes.push(previous.index_to_node(red.node_indices[i])); + fingerprints.push(red.fingerprints[i]); + let edges = &red.edges[i]; + edge_list_indices.push((edges.start.as_u32(), edges.end.as_u32())); + } + HybridIndex::LightGreen(i) => { + let lg = &data.light_green; + nodes.push(previous.index_to_node(lg.node_indices[i])); + fingerprints.push(previous.fingerprint_by_index(lg.node_indices[i])); + let edges = &lg.edges[i]; + edge_list_indices.push((edges.start.as_u32(), edges.end.as_u32())); + } + HybridIndex::DarkGreen(prev_index) => { + nodes.push(previous.index_to_node(prev_index)); + fingerprints.push(previous.fingerprint_by_index(prev_index)); + + let edges_iter = previous + .edge_targets_from(prev_index) + .iter() + .map(|&dst| prev_index_to_index[dst].as_ref().unwrap()); + + let start = edge_list_data.len() as u32; + edge_list_data.extend(edges_iter.map(|i| SDNI::new(i.index()))); + let end = edge_list_data.len() as u32; + edge_list_indices.push((start, end)); + } + } + } + + debug_assert_eq!(nodes.len(), node_count); + debug_assert_eq!(fingerprints.len(), node_count); + debug_assert_eq!(edge_list_indices.len(), node_count); + debug_assert_eq!(edge_list_data.len(), edge_count); debug_assert!(edge_list_data.len() <= u32::MAX as usize); - debug_assert_eq!(edge_list_data.len(), total_edge_count); SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data } } @@ -540,31 +726,22 @@ impl DepGraph { #[cfg(not(parallel_compiler))] { - debug_assert!( - !data - .current - .node_to_node_index - .get_shard_by_value(dep_node) - .lock() - .contains_key(dep_node) - ); + debug_assert!(!self.dep_node_exists(dep_node)); debug_assert!(data.colors.get(prev_dep_node_index).is_none()); } // We never try to mark eval_always nodes as green debug_assert!(!dep_node.kind.is_eval_always()); - data.previous.debug_assert_eq(prev_dep_node_index, *dep_node); + debug_assert_eq!(data.previous.index_to_node(prev_dep_node_index), *dep_node); let prev_deps = data.previous.edge_targets_from(prev_dep_node_index); - let mut current_deps = SmallVec::new(); - for &dep_dep_node_index in prev_deps { let dep_dep_node_color = data.colors.get(dep_dep_node_index); match dep_dep_node_color { - Some(DepNodeColor::Green(node_index)) => { + Some(DepNodeColor::Green(_)) => { // This dependency has been marked as green before, we are // still fine and can continue with checking the other // dependencies. @@ -572,9 +749,8 @@ impl DepGraph { "try_mark_previous_green({:?}) --- found dependency {:?} to \ be immediately green", dep_node, - data.previous.debug_dep_node(dep_dep_node_index), + data.previous.index_to_node(dep_dep_node_index) ); - current_deps.push(node_index); } Some(DepNodeColor::Red) => { // We found a dependency the value of which has changed @@ -585,20 +761,20 @@ impl DepGraph { "try_mark_previous_green({:?}) - END - dependency {:?} was \ immediately red", dep_node, - data.previous.debug_dep_node(dep_dep_node_index) + data.previous.index_to_node(dep_dep_node_index) ); return None; } None => { - let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index, tcx); + let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index); // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. if !dep_dep_node.kind.is_eval_always() { debug!( - "try_mark_previous_green({:?}) --- state of dependency {:?} \ + "try_mark_previous_green({:?}) --- state of dependency {:?} ({}) \ is unknown, trying to mark it green", - dep_node, dep_dep_node + dep_node, dep_dep_node, dep_dep_node.hash, ); let node_index = self.try_mark_previous_green( @@ -607,13 +783,12 @@ impl DepGraph { dep_dep_node_index, dep_dep_node, ); - if let Some(node_index) = node_index { + if node_index.is_some() { debug!( "try_mark_previous_green({:?}) --- managed to MARK \ dependency {:?} as green", dep_node, dep_dep_node ); - current_deps.push(node_index); continue; } } @@ -628,13 +803,12 @@ impl DepGraph { let dep_dep_node_color = data.colors.get(dep_dep_node_index); match dep_dep_node_color { - Some(DepNodeColor::Green(node_index)) => { + Some(DepNodeColor::Green(_)) => { debug!( "try_mark_previous_green({:?}) --- managed to \ FORCE dependency {:?} to green", dep_node, dep_dep_node ); - current_deps.push(node_index); } Some(DepNodeColor::Red) => { debug!( @@ -690,13 +864,9 @@ impl DepGraph { // There may be multiple threads trying to mark the same dep node green concurrently let dep_node_index = { - // Copy the fingerprint from the previous graph, - // so we don't have to recompute it - let fingerprint = data.previous.fingerprint_by_index(prev_dep_node_index); - // We allocating an entry for the node in the current dependency graph and // adding all the appropriate edges imported from the previous graph - data.current.intern_node(*dep_node, current_deps, fingerprint) + data.current.intern_dark_green_node(&data.previous, prev_dep_node_index) }; // ... emitting any stored diagnostic ... @@ -801,7 +971,7 @@ impl DepGraph { for prev_index in data.colors.values.indices() { match data.colors.get(prev_index) { Some(DepNodeColor::Green(_)) => { - let dep_node = data.previous.index_to_node(prev_index, tcx); + let dep_node = data.previous.index_to_node(prev_index); tcx.try_load_from_on_disk_cache(&dep_node); } None | Some(DepNodeColor::Red) => { @@ -813,6 +983,20 @@ impl DepGraph { } } + // Register reused dep nodes (i.e. nodes we've marked red or green) with the context. + pub fn register_reused_dep_nodes>(&self, tcx: Ctxt) { + let data = self.data.as_ref().unwrap(); + for prev_index in data.colors.values.indices() { + match data.colors.get(prev_index) { + Some(DepNodeColor::Red) | Some(DepNodeColor::Green(_)) => { + let dep_node = data.previous.index_to_node(prev_index); + tcx.register_reused_dep_node(&dep_node); + } + None => {} + } + } + } + fn next_virtual_depnode_index(&self) -> DepNodeIndex { let index = self.virtual_dep_node_index.fetch_add(1, Relaxed); DepNodeIndex::from_u32(index) @@ -857,31 +1041,234 @@ pub struct WorkProduct { pub saved_file: Option, } -#[derive(Clone)] -struct DepNodeData { - node: DepNode, - edges: EdgesVec, - fingerprint: Fingerprint, +// The maximum value of the follow index types leaves the upper two bits unused +// so that we can store multiple index types in `CompressedHybridIndex`, and use +// those bits to encode which index type it contains. + +// Index type for `NewDepNodeData`. +rustc_index::newtype_index! { + struct NewDepNodeIndex { + MAX = 0x7FFF_FFFF + } } -/// `CurrentDepGraph` stores the dependency graph for the current session. -/// It will be populated as we run queries or tasks. +// Index type for `RedDepNodeData`. +rustc_index::newtype_index! { + struct RedDepNodeIndex { + MAX = 0x7FFF_FFFF + } +} + +// Index type for `LightGreenDepNodeData`. +rustc_index::newtype_index! { + struct LightGreenDepNodeIndex { + MAX = 0x7FFF_FFFF + } +} + +/// Compressed representation of `HybridIndex` enum. Bits unused by the +/// contained index types are used to encode which index type it contains. +#[derive(Copy, Clone)] +struct CompressedHybridIndex(u32); + +impl CompressedHybridIndex { + const NEW_TAG: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000; + const RED_TAG: u32 = 0b0100_0000_0000_0000_0000_0000_0000_0000; + const LIGHT_GREEN_TAG: u32 = 0b1000_0000_0000_0000_0000_0000_0000_0000; + const DARK_GREEN_TAG: u32 = 0b1100_0000_0000_0000_0000_0000_0000_0000; + + const TAG_MASK: u32 = 0b1100_0000_0000_0000_0000_0000_0000_0000; + const INDEX_MASK: u32 = !Self::TAG_MASK; +} + +impl From for CompressedHybridIndex { + #[inline] + fn from(index: NewDepNodeIndex) -> Self { + CompressedHybridIndex(Self::NEW_TAG | index.as_u32()) + } +} + +impl From for CompressedHybridIndex { + #[inline] + fn from(index: RedDepNodeIndex) -> Self { + CompressedHybridIndex(Self::RED_TAG | index.as_u32()) + } +} + +impl From for CompressedHybridIndex { + #[inline] + fn from(index: LightGreenDepNodeIndex) -> Self { + CompressedHybridIndex(Self::LIGHT_GREEN_TAG | index.as_u32()) + } +} + +impl From for CompressedHybridIndex { + #[inline] + fn from(index: SerializedDepNodeIndex) -> Self { + CompressedHybridIndex(Self::DARK_GREEN_TAG | index.as_u32()) + } +} + +/// Contains an index into one of several node data collections. Elsewhere, we +/// store `CompressedHyridIndex` instead of this to save space, but convert to +/// this type during processing to take advantage of the enum match ergonomics. +enum HybridIndex { + New(NewDepNodeIndex), + Red(RedDepNodeIndex), + LightGreen(LightGreenDepNodeIndex), + DarkGreen(SerializedDepNodeIndex), +} + +impl From for HybridIndex { + #[inline] + fn from(hybrid_index: CompressedHybridIndex) -> Self { + let index = hybrid_index.0 & CompressedHybridIndex::INDEX_MASK; + + match hybrid_index.0 & CompressedHybridIndex::TAG_MASK { + CompressedHybridIndex::NEW_TAG => HybridIndex::New(NewDepNodeIndex::from_u32(index)), + CompressedHybridIndex::RED_TAG => HybridIndex::Red(RedDepNodeIndex::from_u32(index)), + CompressedHybridIndex::LIGHT_GREEN_TAG => { + HybridIndex::LightGreen(LightGreenDepNodeIndex::from_u32(index)) + } + CompressedHybridIndex::DARK_GREEN_TAG => { + HybridIndex::DarkGreen(SerializedDepNodeIndex::from_u32(index)) + } + _ => unreachable!(), + } + } +} + +// Index type for `DepNodeData`'s edges. +rustc_index::newtype_index! { + struct EdgeIndex { .. } +} + +/// Data for nodes in the current graph, divided into different collections +/// based on their presence in the previous graph, and if present, their color. +/// We divide nodes this way because different types of nodes are able to share +/// more or less data with the previous graph. /// -/// The nodes in it are identified by an index (`DepNodeIndex`). -/// The data for each node is stored in its `DepNodeData`, found in the `data` field. +/// To enable more sharing, we distinguish between two kinds of green nodes. +/// Light green nodes are nodes in the previous graph that have been marked +/// green because we re-executed their queries and the results were the same as +/// in the previous session. Dark green nodes are nodes in the previous graph +/// that have been marked green because we were able to mark all of their +/// dependencies green. /// -/// We never remove nodes from the graph: they are only added. +/// Both light and dark green nodes can share the dep node and fingerprint with +/// the previous graph, but for light green nodes, we can't be sure that the +/// edges may be shared without comparing them against the previous edges, so we +/// store them directly (an approach in which we compare edges with the previous +/// edges to see if they can be shared was evaluated, but was not found to be +/// very profitable). /// -/// This struct uses two locks internally. The `data` and `node_to_node_index` fields are -/// locked separately. Operations that take a `DepNodeIndex` typically just access -/// the data field. +/// For dark green nodes, we can share everything with the previous graph, which +/// is why the `HybridIndex::DarkGreen` enum variant contains the index of the +/// node in the previous graph, and why we don't have a separate collection for +/// dark green node data--the collection is the `PreviousDepGraph` itself. /// -/// The only operation that must manipulate both locks is adding new nodes, in which case -/// we first acquire the `node_to_node_index` lock and then, once a new node is to be inserted, -/// acquire the lock on `data.` +/// (Note that for dark green nodes, the edges in the previous graph +/// (`SerializedDepNodeIndex`s) must be converted to edges in the current graph +/// (`DepNodeIndex`s). `CurrentDepGraph` contains `prev_index_to_index`, which +/// can perform this conversion. It should always be possible, as by definition, +/// a dark green node is one whose dependencies from the previous session have +/// all been marked green--which means `prev_index_to_index` contains them.) +/// +/// Node data is stored in parallel vectors to eliminate the padding between +/// elements that would be needed to satisfy alignment requirements of the +/// structure that would contain all of a node's data. We could group tightly +/// packing subsets of node data together and use fewer vectors, but for +/// consistency's sake, we use separate vectors for each piece of data. +struct DepNodeData { + /// Data for nodes not in previous graph. + new: NewDepNodeData, + + /// Data for nodes in previous graph that have been marked red. + red: RedDepNodeData, + + /// Data for nodes in previous graph that have been marked light green. + light_green: LightGreenDepNodeData, + + // Edges for all nodes other than dark-green ones. Edges for each node + // occupy a contiguous region of this collection, which a node can reference + // using two indices. Storing edges this way rather than using an `EdgesVec` + // for each node reduces memory consumption by a not insignificant amount + // when compiling large crates. The downside is that we have to copy into + // this collection the edges from the `EdgesVec`s that are built up during + // query execution. But this is mostly balanced out by the more efficient + // implementation of `DepGraph::serialize` enabled by this representation. + unshared_edges: IndexVec, + + /// Mapping from `DepNodeIndex` to an index into a collection above. + /// Indicates which of the above collections contains a node's data. + /// + /// This collection is wasteful in time and space during incr-full builds, + /// because for those, all nodes are new. However, the waste is relatively + /// small, and the maintenance cost of avoiding using this for incr-full + /// builds is somewhat high and prone to bugginess. It does not seem worth + /// it at the time of this writing, but we may want to revisit the idea. + hybrid_indices: IndexVec, +} + +/// Data for nodes not in previous graph. Since we cannot share any data with +/// the previous graph, so we must store all of such a node's data here. +struct NewDepNodeData { + nodes: IndexVec>, + edges: IndexVec>, + fingerprints: IndexVec, +} + +/// Data for nodes in previous graph that have been marked red. We can share the +/// dep node with the previous graph, but the edges may be different, and the +/// fingerprint is known to be different, so we store the latter two directly. +struct RedDepNodeData { + node_indices: IndexVec, + edges: IndexVec>, + fingerprints: IndexVec, +} + +/// Data for nodes in previous graph that have been marked green because we +/// re-executed their queries and the results were the same as in the previous +/// session. We can share the dep node and the fingerprint with the previous +/// graph, but the edges may be different, so we store them directly. +struct LightGreenDepNodeData { + node_indices: IndexVec, + edges: IndexVec>, +} + +/// `CurrentDepGraph` stores the dependency graph for the current session. It +/// will be populated as we run queries or tasks. We never remove nodes from the +/// graph: they are only added. +/// +/// The nodes in it are identified by a `DepNodeIndex`. Internally, this maps to +/// a `HybridIndex`, which identifies which collection in the `data` field +/// contains a node's data. Which collection is used for a node depends on +/// whether the node was present in the `PreviousDepGraph`, and if so, the color +/// of the node. Each type of node can share more or less data with the previous +/// graph. When possible, we can store just the index of the node in the +/// previous graph, rather than duplicating its data in our own collections. +/// This is important, because these graph structures are some of the largest in +/// the compiler. +/// +/// For the same reason, we also avoid storing `DepNode`s more than once as map +/// keys. The `new_node_to_index` map only contains nodes not in the previous +/// graph, and we map nodes in the previous graph to indices via a two-step +/// mapping. `PreviousDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`, +/// and the `prev_index_to_index` vector (which is more compact and faster than +/// using a map) maps from `SerializedDepNodeIndex` to `DepNodeIndex`. +/// +/// This struct uses three locks internally. The `data`, `new_node_to_index`, +/// and `prev_index_to_index` fields are locked separately. Operations that take +/// a `DepNodeIndex` typically just access the `data` field. +/// +/// We only need to manipulate at most two locks simultaneously: +/// `new_node_to_index` and `data`, or `prev_index_to_index` and `data`. When +/// manipulating both, we acquire `new_node_to_index` or `prev_index_to_index` +/// first, and `data` second. pub(super) struct CurrentDepGraph { - data: Lock>>, - node_to_node_index: Sharded, DepNodeIndex>>, + data: Lock>, + new_node_to_index: Sharded, DepNodeIndex>>, + prev_index_to_index: Lock>>, /// Used to trap when a specific edge is added to the graph. /// This is used for debug purposes and is only active with `debug_assertions`. @@ -930,18 +1317,63 @@ impl CurrentDepGraph { // Pre-allocate the dep node structures. We over-allocate a little so // that we hopefully don't have to re-allocate during this compilation - // session. The over-allocation is 2% plus a small constant to account - // for the fact that in very small crates 2% might not be enough. - let new_node_count_estimate = (prev_graph_node_count * 102) / 100 + 200; + // session. The over-allocation for new nodes is 2% plus a small + // constant to account for the fact that in very small crates 2% might + // not be enough. The allocation for red and green node data doesn't + // include a constant, as we don't want to allocate anything for these + // structures during full incremental builds, where they aren't used. + // + // These estimates are based on the distribution of node and edge counts + // seen in rustc-perf benchmarks, adjusted somewhat to account for the + // fact that these benchmarks aren't perfectly representative. + // + // FIXME Use a collection type that doesn't copy node and edge data and + // grow multiplicatively on reallocation. Without such a collection or + // solution having the same effect, there is a performance hazard here + // in both time and space, as growing these collections means copying a + // large amount of data and doubling already large buffer capacities. A + // solution for this will also mean that it's less important to get + // these estimates right. + let new_node_count_estimate = (prev_graph_node_count * 2) / 100 + 200; + let red_node_count_estimate = (prev_graph_node_count * 3) / 100; + let light_green_node_count_estimate = (prev_graph_node_count * 25) / 100; + let total_node_count_estimate = prev_graph_node_count + new_node_count_estimate; + + let average_edges_per_node_estimate = 6; + let unshared_edge_count_estimate = average_edges_per_node_estimate + * (new_node_count_estimate + red_node_count_estimate + light_green_node_count_estimate); + + // We store a large collection of these in `prev_index_to_index` during + // non-full incremental builds, and want to ensure that the element size + // doesn't inadvertently increase. + static_assert_size!(Option, 4); CurrentDepGraph { - data: Lock::new(IndexVec::with_capacity(new_node_count_estimate)), - node_to_node_index: Sharded::new(|| { + data: Lock::new(DepNodeData { + new: NewDepNodeData { + nodes: IndexVec::with_capacity(new_node_count_estimate), + edges: IndexVec::with_capacity(new_node_count_estimate), + fingerprints: IndexVec::with_capacity(new_node_count_estimate), + }, + red: RedDepNodeData { + node_indices: IndexVec::with_capacity(red_node_count_estimate), + edges: IndexVec::with_capacity(red_node_count_estimate), + fingerprints: IndexVec::with_capacity(red_node_count_estimate), + }, + light_green: LightGreenDepNodeData { + node_indices: IndexVec::with_capacity(light_green_node_count_estimate), + edges: IndexVec::with_capacity(light_green_node_count_estimate), + }, + unshared_edges: IndexVec::with_capacity(unshared_edge_count_estimate), + hybrid_indices: IndexVec::with_capacity(total_node_count_estimate), + }), + new_node_to_index: Sharded::new(|| { FxHashMap::with_capacity_and_hasher( new_node_count_estimate / sharded::SHARDS, Default::default(), ) }), + prev_index_to_index: Lock::new(IndexVec::from_elem_n(None, prev_graph_node_count)), anon_id_seed: stable_hasher.finish(), forbidden_edge, total_read_count: AtomicU64::new(0), @@ -949,114 +1381,125 @@ impl CurrentDepGraph { } } - fn complete_task( - &self, - node: DepNode, - task_deps: TaskDeps, - fingerprint: Fingerprint, - ) -> DepNodeIndex { - self.alloc_node(node, task_deps.reads, fingerprint) - } - - fn complete_anon_task(&self, kind: K, task_deps: TaskDeps) -> DepNodeIndex { - debug_assert!(!kind.is_eval_always()); - - let mut hasher = StableHasher::new(); - - // The dep node indices are hashed here instead of hashing the dep nodes of the - // dependencies. These indices may refer to different nodes per session, but this isn't - // a problem here because we that ensure the final dep node hash is per session only by - // combining it with the per session random number `anon_id_seed`. This hash only need - // to map the dependencies to a single value on a per session basis. - task_deps.reads.hash(&mut hasher); - - let target_dep_node = DepNode { - kind, - - // Fingerprint::combine() is faster than sending Fingerprint - // through the StableHasher (at least as long as StableHasher - // is so slow). - hash: self.anon_id_seed.combine(hasher.finish()).into(), - }; - - self.intern_node(target_dep_node, task_deps.reads, Fingerprint::ZERO) - } - - fn alloc_node( + fn intern_new_node( &self, + prev_graph: &PreviousDepGraph, dep_node: DepNode, edges: EdgesVec, fingerprint: Fingerprint, ) -> DepNodeIndex { debug_assert!( - !self.node_to_node_index.get_shard_by_value(&dep_node).lock().contains_key(&dep_node) + prev_graph.node_to_index_opt(&dep_node).is_none(), + "node in previous graph should be interned using one \ + of `intern_red_node`, `intern_light_green_node`, etc." ); - self.intern_node(dep_node, edges, fingerprint) - } - fn intern_node( - &self, - dep_node: DepNode, - edges: EdgesVec, - fingerprint: Fingerprint, - ) -> DepNodeIndex { - match self.node_to_node_index.get_shard_by_value(&dep_node).lock().entry(dep_node) { + match self.new_node_to_index.get_shard_by_value(&dep_node).lock().entry(dep_node) { Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { - let mut data = self.data.lock(); - let dep_node_index = DepNodeIndex::new(data.len()); - data.push(DepNodeData { node: dep_node, edges, fingerprint }); + let data = &mut *self.data.lock(); + let new_index = data.new.nodes.push(dep_node); + add_edges(&mut data.unshared_edges, &mut data.new.edges, edges); + data.new.fingerprints.push(fingerprint); + let dep_node_index = data.hybrid_indices.push(new_index.into()); entry.insert(dep_node_index); dep_node_index } } } + + fn intern_red_node( + &self, + prev_graph: &PreviousDepGraph, + prev_index: SerializedDepNodeIndex, + edges: EdgesVec, + fingerprint: Fingerprint, + ) -> DepNodeIndex { + self.debug_assert_not_in_new_nodes(prev_graph, prev_index); + + let mut prev_index_to_index = self.prev_index_to_index.lock(); + + match prev_index_to_index[prev_index] { + Some(dep_node_index) => dep_node_index, + None => { + let data = &mut *self.data.lock(); + let red_index = data.red.node_indices.push(prev_index); + add_edges(&mut data.unshared_edges, &mut data.red.edges, edges); + data.red.fingerprints.push(fingerprint); + let dep_node_index = data.hybrid_indices.push(red_index.into()); + prev_index_to_index[prev_index] = Some(dep_node_index); + dep_node_index + } + } + } + + fn intern_light_green_node( + &self, + prev_graph: &PreviousDepGraph, + prev_index: SerializedDepNodeIndex, + edges: EdgesVec, + ) -> DepNodeIndex { + self.debug_assert_not_in_new_nodes(prev_graph, prev_index); + + let mut prev_index_to_index = self.prev_index_to_index.lock(); + + match prev_index_to_index[prev_index] { + Some(dep_node_index) => dep_node_index, + None => { + let data = &mut *self.data.lock(); + let light_green_index = data.light_green.node_indices.push(prev_index); + add_edges(&mut data.unshared_edges, &mut data.light_green.edges, edges); + let dep_node_index = data.hybrid_indices.push(light_green_index.into()); + prev_index_to_index[prev_index] = Some(dep_node_index); + dep_node_index + } + } + } + + fn intern_dark_green_node( + &self, + prev_graph: &PreviousDepGraph, + prev_index: SerializedDepNodeIndex, + ) -> DepNodeIndex { + self.debug_assert_not_in_new_nodes(prev_graph, prev_index); + + let mut prev_index_to_index = self.prev_index_to_index.lock(); + + match prev_index_to_index[prev_index] { + Some(dep_node_index) => dep_node_index, + None => { + let mut data = self.data.lock(); + let dep_node_index = data.hybrid_indices.push(prev_index.into()); + prev_index_to_index[prev_index] = Some(dep_node_index); + dep_node_index + } + } + } + + #[inline] + fn debug_assert_not_in_new_nodes( + &self, + prev_graph: &PreviousDepGraph, + prev_index: SerializedDepNodeIndex, + ) { + let node = &prev_graph.index_to_node(prev_index); + debug_assert!( + !self.new_node_to_index.get_shard_by_value(node).lock().contains_key(node), + "node from previous graph present in new node collection" + ); + } } -impl DepGraphData { - #[inline(never)] - fn read_index(&self, source: DepNodeIndex) { - K::read_deps(|task_deps| { - if let Some(task_deps) = task_deps { - let mut task_deps = task_deps.lock(); - let task_deps = &mut *task_deps; - if cfg!(debug_assertions) { - self.current.total_read_count.fetch_add(1, Relaxed); - } - - // As long as we only have a low number of reads we can avoid doing a hash - // insert and potentially allocating/reallocating the hashmap - let new_read = if task_deps.reads.len() < TASK_DEPS_READS_CAP { - task_deps.reads.iter().all(|other| *other != source) - } else { - task_deps.read_set.insert(source) - }; - if new_read { - task_deps.reads.push(source); - if task_deps.reads.len() == TASK_DEPS_READS_CAP { - // Fill `read_set` with what we have so far so we can use the hashset next - // time - task_deps.read_set.extend(task_deps.reads.iter().copied()); - } - - #[cfg(debug_assertions)] - { - if let Some(target) = task_deps.node { - let data = self.current.data.lock(); - if let Some(ref forbidden_edge) = self.current.forbidden_edge { - let source = data[source].node; - if forbidden_edge.test(&source, &target) { - panic!("forbidden edge {:?} -> {:?} created", source, target) - } - } - } - } - } else if cfg!(debug_assertions) { - self.current.total_duplicate_read_count.fetch_add(1, Relaxed); - } - } - }) - } +#[inline] +fn add_edges( + edges: &mut IndexVec, + edge_indices: &mut IndexVec>, + new_edges: EdgesVec, +) { + let start = edges.next_index(); + edges.extend(new_edges); + let end = edges.next_index(); + edge_indices.push(start..end); } /// The capacity of the `reads` field `SmallVec` diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 3b4b62ad6be8..da0b5aad6c81 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -15,7 +15,6 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; -use rustc_span::def_id::DefPathHash; use std::fmt; use std::hash::Hash; @@ -33,7 +32,7 @@ pub trait DepContext: Copy { /// Try to force a dep node to execute and see if it's green. fn try_force_from_dep_node(&self, dep_node: &DepNode) -> bool; - fn register_reused_dep_path_hash(&self, hash: DefPathHash); + fn register_reused_dep_node(&self, dep_node: &DepNode); /// Return whether the current session is tainted by errors. fn has_errors_or_delayed_span_bugs(&self) -> bool; diff --git a/compiler/rustc_query_system/src/dep_graph/prev.rs b/compiler/rustc_query_system/src/dep_graph/prev.rs index 9298b652da2d..29357ce9449c 100644 --- a/compiler/rustc_query_system/src/dep_graph/prev.rs +++ b/compiler/rustc_query_system/src/dep_graph/prev.rs @@ -1,9 +1,7 @@ use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use super::{DepKind, DepNode}; -use crate::dep_graph::DepContext; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; -use rustc_span::def_id::DefPathHash; #[derive(Debug, Encodable, Decodable)] pub struct PreviousDepGraph { @@ -33,44 +31,7 @@ impl PreviousDepGraph { } #[inline] - pub fn index_to_node>( - &self, - dep_node_index: SerializedDepNodeIndex, - tcx: CTX, - ) -> DepNode { - let dep_node = self.data.nodes[dep_node_index]; - // We have just loaded a deserialized `DepNode` from the previous - // compilation session into the current one. If this was a foreign `DefId`, - // then we stored additional information in the incr comp cache when we - // initially created its fingerprint (see `DepNodeParams::to_fingerprint`) - // We won't be calling `to_fingerprint` again for this `DepNode` (we no longer - // have the original value), so we need to copy over this additional information - // from the old incremental cache into the new cache that we serialize - // and the end of this compilation session. - if dep_node.kind.can_reconstruct_query_key() { - tcx.register_reused_dep_path_hash(DefPathHash(dep_node.hash.into())); - } - dep_node - } - - /// When debug assertions are enabled, asserts that the dep node at `dep_node_index` is equal to `dep_node`. - /// This method should be preferred over manually calling `index_to_node`. - /// Calls to `index_to_node` may affect global state, so gating a call - /// to `index_to_node` on debug assertions could cause behavior changes when debug assertions - /// are enabled. - #[inline] - pub fn debug_assert_eq(&self, dep_node_index: SerializedDepNodeIndex, dep_node: DepNode) { - debug_assert_eq!(self.data.nodes[dep_node_index], dep_node); - } - - /// Obtains a debug-printable version of the `DepNode`. - /// See `debug_assert_eq` for why this should be preferred over manually - /// calling `dep_node_index` - pub fn debug_dep_node(&self, dep_node_index: SerializedDepNodeIndex) -> impl std::fmt::Debug { - // We're returning the `DepNode` without calling `register_reused_dep_path_hash`, - // but `impl Debug` return type means that it can only be used for debug printing. - // So, there's no risk of calls trying to create new dep nodes that have this - // node as a dependency + pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode { self.data.nodes[dep_node_index] } diff --git a/compiler/rustc_query_system/src/dep_graph/query.rs b/compiler/rustc_query_system/src/dep_graph/query.rs index a27b716b95ae..cc25d08cb54f 100644 --- a/compiler/rustc_query_system/src/dep_graph/query.rs +++ b/compiler/rustc_query_system/src/dep_graph/query.rs @@ -9,17 +9,23 @@ pub struct DepGraphQuery { } impl DepGraphQuery { - pub fn new(nodes: &[DepNode], edges: &[(DepNode, DepNode)]) -> DepGraphQuery { - let mut graph = Graph::with_capacity(nodes.len(), edges.len()); + pub fn new( + nodes: &[DepNode], + edge_list_indices: &[(usize, usize)], + edge_list_data: &[usize], + ) -> DepGraphQuery { + let mut graph = Graph::with_capacity(nodes.len(), edge_list_data.len()); let mut indices = FxHashMap::default(); for node in nodes { indices.insert(*node, graph.add_node(*node)); } - for &(ref source, ref target) in edges { - let source = indices[source]; - let target = indices[target]; - graph.add_edge(source, target, ()); + for (source, &(start, end)) in edge_list_indices.iter().enumerate() { + for &target in &edge_list_data[start..end] { + let source = indices[&nodes[source]]; + let target = indices[&nodes[target]]; + graph.add_edge(source, target, ()); + } } DepGraphQuery { graph, indices } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 932c6d2a2f18..28e074069185 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -4,8 +4,13 @@ use super::{DepKind, DepNode}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_index::vec::IndexVec; +// The maximum value of `SerializedDepNodeIndex` leaves the upper two bits +// unused so that we can store multiple index types in `CompressedHybridIndex`, +// and use those bits to encode which index type it contains. rustc_index::newtype_index! { - pub struct SerializedDepNodeIndex { .. } + pub struct SerializedDepNodeIndex { + MAX = 0x7FFF_FFFF + } } /// Data for use when recompiling the **current crate**. diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 6d7e4ebc253b..c5f783e84a91 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -185,15 +185,15 @@ impl<'a> Resolver<'a> { crate fn get_macro(&mut self, res: Res) -> Option> { match res { - Res::Def(DefKind::Macro(..), def_id) => self.get_macro_by_def_id(def_id), + Res::Def(DefKind::Macro(..), def_id) => Some(self.get_macro_by_def_id(def_id)), Res::NonMacroAttr(attr_kind) => Some(self.non_macro_attr(attr_kind.is_used())), _ => None, } } - crate fn get_macro_by_def_id(&mut self, def_id: DefId) -> Option> { + crate fn get_macro_by_def_id(&mut self, def_id: DefId) -> Lrc { if let Some(ext) = self.macro_map.get(&def_id) { - return Some(ext.clone()); + return ext.clone(); } let ext = Lrc::new(match self.cstore().load_macro_untracked(def_id, &self.session) { @@ -202,7 +202,7 @@ impl<'a> Resolver<'a> { }); self.macro_map.insert(def_id, ext.clone()); - Some(ext) + ext } crate fn build_reduced_graph( @@ -258,7 +258,16 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { Ok(ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))) } ast::VisibilityKind::Inherited => { - Ok(ty::Visibility::Restricted(parent_scope.module.normal_ancestor_id)) + if matches!(self.parent_scope.module.kind, ModuleKind::Def(DefKind::Enum, _, _)) { + // Any inherited visibility resolved directly inside an enum + // (e.g. variants or fields) inherits from the visibility of the enum. + let parent_enum = self.parent_scope.module.def_id().unwrap().expect_local(); + Ok(self.r.visibilities[&parent_enum]) + } else { + // If it's not in an enum, its visibility is restricted to the `mod` item + // that it's defined in. + Ok(ty::Visibility::Restricted(self.parent_scope.module.normal_ancestor_id)) + } } ast::VisibilityKind::Restricted { ref path, id, .. } => { // For visibilities we are not ready to provide correct implementation of "uniform @@ -333,7 +342,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let field_names = vdata .fields() .iter() - .map(|field| respan(field.span, field.ident.map_or(kw::Invalid, |ident| ident.name))) + .map(|field| respan(field.span, field.ident.map_or(kw::Empty, |ident| ident.name))) .collect(); self.insert_field_names(def_id, field_names); } @@ -516,9 +525,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { ModuleKind::Block(..) => unreachable!(), }; // HACK(eddyb) unclear how good this is, but keeping `$crate` - // in `source` breaks `src/test/compile-fail/import-crate-var.rs`, + // in `source` breaks `src/test/ui/imports/import-crate-var.rs`, // while the current crate doesn't have a valid `crate_name`. - if crate_name != kw::Invalid { + if crate_name != kw::Empty { // `crate_name` should not be interpreted as relative. module_path.push(Segment { ident: Ident { name: kw::PathRoot, span: source.ident.span }, @@ -647,7 +656,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { /// Constructs the reduced graph for one item. fn build_reduced_graph_for_item(&mut self, item: &'b Item) { - if matches!(item.kind, ItemKind::Mod(..)) && item.ident.name == kw::Invalid { + if matches!(item.kind, ItemKind::Mod(..)) && item.ident.name == kw::Empty { // Fake crate root item from expand. return; } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 69773ba1d722..48bce8843942 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -74,7 +74,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { // information we encapsulate into, the better let def_data = match &i.kind { ItemKind::Impl { .. } => DefPathData::Impl, - ItemKind::Mod(..) if i.ident.name == kw::Invalid => { + ItemKind::Mod(..) if i.ident.name == kw::Empty => { // Fake crate root item from expand. return visit::walk_item(self, i); } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f156caf23ba9..dd1874debbd9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -29,7 +29,7 @@ use rustc_span::Span; use smallvec::{smallvec, SmallVec}; use rustc_span::source_map::{respan, Spanned}; -use std::collections::BTreeSet; +use std::collections::{hash_map::Entry, BTreeSet}; use std::mem::{replace, take}; use tracing::debug; @@ -953,8 +953,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); }; - for item in trait_items { - this.with_trait_items(trait_items, |this| { + this.with_trait_items(trait_items, |this| { + for item in trait_items { match &item.kind { AssocItemKind::Const(_, ty, default) => { this.visit_ty(ty); @@ -983,8 +983,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { panic!("unexpanded macro in resolve!") } }; - }); - } + } + }); }); }); } @@ -1060,36 +1060,29 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { continue; } - let def_kind = match param.kind { - GenericParamKind::Type { .. } => DefKind::TyParam, - GenericParamKind::Const { .. } => DefKind::ConstParam, - _ => unreachable!(), - }; - let ident = param.ident.normalize_to_macros_2_0(); debug!("with_generic_param_rib: {}", param.id); - if seen_bindings.contains_key(&ident) { - let span = seen_bindings.get(&ident).unwrap(); - let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, *span); - self.report_error(param.ident.span, err); + match seen_bindings.entry(ident) { + Entry::Occupied(entry) => { + let span = *entry.get(); + let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); + self.report_error(param.ident.span, err); + } + Entry::Vacant(entry) => { + entry.insert(param.ident.span); + } } - seen_bindings.entry(ident).or_insert(param.ident.span); // Plain insert (no renaming). - let res = Res::Def(def_kind, self.r.local_def_id(param.id).to_def_id()); - - match param.kind { - GenericParamKind::Type { .. } => { - function_type_rib.bindings.insert(ident, res); - self.r.record_partial_res(param.id, PartialRes::new(res)); - } - GenericParamKind::Const { .. } => { - function_value_rib.bindings.insert(ident, res); - self.r.record_partial_res(param.id, PartialRes::new(res)); - } + let (rib, def_kind) = match param.kind { + GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), + GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), _ => unreachable!(), - } + }; + let res = Res::Def(def_kind, self.r.local_def_id(param.id).to_def_id()); + self.r.record_partial_res(param.id, PartialRes::new(res)); + rib.bindings.insert(ident, res); } self.ribs[ValueNS].push(function_value_rib); @@ -1158,13 +1151,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// When evaluating a `trait` use its associated types' idents for suggestions in E0412. fn with_trait_items( &mut self, - trait_items: &'ast Vec>, + trait_items: &'ast [P], f: impl FnOnce(&mut Self) -> T, ) -> T { - let trait_assoc_items = replace( - &mut self.diagnostic_metadata.current_trait_assoc_items, - Some(&trait_items[..]), - ); + let trait_assoc_items = + replace(&mut self.diagnostic_metadata.current_trait_assoc_items, Some(&trait_items)); let result = f(self); self.diagnostic_metadata.current_trait_assoc_items = trait_assoc_items; result @@ -1648,7 +1639,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } // Record as bound if it's valid: - let ident_valid = ident.name != kw::Invalid; + let ident_valid = ident.name != kw::Empty; if ident_valid { bindings.last_mut().unwrap().1.insert(ident); } @@ -1778,7 +1769,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { path ); let ns = source.namespace(); - let is_expected = &|res| source.is_expected(res); let report_errors = |this: &mut Self, res: Option| { if this.should_report_errs() { @@ -1881,7 +1871,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { crate_lint, ) { Ok(Some(partial_res)) if partial_res.unresolved_segments() == 0 => { - if is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err { + if source.is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err + { partial_res } else { report_errors(self, Some(partial_res.base_res())) @@ -1898,11 +1889,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.r.trait_map.insert(id, traits); } - let mut std_path = vec![Segment::from_ident(Ident::with_dummy_span(sym::std))]; - - std_path.extend(path); - if self.r.primitive_type_table.primitive_types.contains_key(&path[0].ident.name) { + let mut std_path = Vec::with_capacity(1 + path.len()); + + std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std))); + std_path.extend(path); if let PathResult::Module(_) | PathResult::NonModule(_) = self.resolve_path(&std_path, Some(ns), false, span, CrateLint::No) { @@ -1983,7 +1974,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ) -> Result, Spanned>> { let mut fin_res = None; - for (i, ns) in [primary_ns, TypeNS, ValueNS].iter().cloned().enumerate() { + for (i, &ns) in [primary_ns, TypeNS, ValueNS].iter().enumerate() { if i == 0 || ns != primary_ns { match self.resolve_qpath(id, qself, path, ns, span, crate_lint)? { Some(partial_res) @@ -1993,7 +1984,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } partial_res => { if fin_res.is_none() { - fin_res = partial_res + fin_res = partial_res; } } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6ce299a94170..7d8f112af8a5 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -542,6 +542,26 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(base_span, fallback_label); } } + if let Some(err_code) = &err.code { + if err_code == &rustc_errors::error_code!(E0425) { + for label_rib in &self.label_ribs { + for (label_ident, _) in &label_rib.bindings { + if format!("'{}", ident) == label_ident.to_string() { + let msg = "a label with a similar name exists"; + // FIXME: consider only emitting this suggestion if a label would be valid here + // which is pretty much only the case for `break` expressions. + err.span_suggestion( + span, + &msg, + label_ident.name.to_string(), + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + (err, candidates) } @@ -1633,17 +1653,14 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { for missing in &self.missing_named_lifetime_spots { match missing { MissingLifetimeSpot::Generics(generics) => { - let (span, sugg) = if let Some(param) = - generics.params.iter().find(|p| match p.kind { - hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => false, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided, - } => false, - _ => true, - }) { + let (span, sugg) = if let Some(param) = generics.params.iter().find(|p| { + !matches!(p.kind, hir::GenericParamKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } | hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Elided, + }) + }) { (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)) } else { suggests_in_band = true; @@ -1965,8 +1982,8 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } - /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics` so - /// this function will emit an error if `min_const_generics` is enabled, the body identified by + /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`. + /// This function will emit an error if `const_generics` is not enabled, the body identified by /// `body_id` is an anonymous constant and `lifetime_ref` is non-static. crate fn maybe_emit_forbidden_non_static_lifetime_error( &self, @@ -1982,7 +1999,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore ); - if self.tcx.features().min_const_generics && is_anon_const && !is_allowed_lifetime { + if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime { feature_err( &self.tcx.sess.parse_sess, sym::const_generics, diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 69f28045bb5b..d5ba6f3b53b2 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1769,8 +1769,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let result = loop { match *scope { Scope::Body { id, s } => { - // Non-static lifetimes are prohibited in anonymous constants under - // `min_const_generics`. + // Non-static lifetimes are prohibited in anonymous constants without + // `const_generics`. self.maybe_emit_forbidden_non_static_lifetime_error(id, lifetime_ref); outermost_body = Some(id); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index e8a06265adaf..ca30d90e6ad1 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -403,6 +403,7 @@ enum PathResult<'a> { }, } +#[derive(Debug)] enum ModuleKind { /// An anonymous module; e.g., just a block. /// @@ -1181,12 +1182,12 @@ impl<'a> Resolver<'a> { ) -> Resolver<'a> { let root_local_def_id = LocalDefId { local_def_index: CRATE_DEF_INDEX }; let root_def_id = root_local_def_id.to_def_id(); - let root_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid); + let root_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Empty); let graph_root = arenas.alloc_module(ModuleData { no_implicit_prelude: session.contains_name(&krate.attrs, sym::no_implicit_prelude), ..ModuleData::new(None, root_module_kind, root_def_id, ExpnId::root(), krate.span) }); - let empty_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid); + let empty_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Empty); let empty_module = arenas.alloc_module(ModuleData { no_implicit_prelude: true, ..ModuleData::new( @@ -1796,7 +1797,7 @@ impl<'a> Resolver<'a> { ribs: &[Rib<'a>], ) -> Option> { assert!(ns == TypeNS || ns == ValueNS); - if ident.name == kw::Invalid { + if ident.name == kw::Empty { return Some(LexicalScopeBinding::Res(Res::Err)); } let (general_span, normalized_span) = if ident.name == kw::SelfUpper { @@ -1990,14 +1991,13 @@ impl<'a> Resolver<'a> { { // The macro is a proc macro derive if let Some(def_id) = module.expansion.expn_data().macro_def_id { - if let Some(ext) = self.get_macro_by_def_id(def_id) { - if !ext.is_builtin - && ext.macro_kind() == MacroKind::Derive - && parent.expansion.outer_expn_is_descendant_of(span.ctxt()) - { - *poisoned = Some(node_id); - return module.parent; - } + let ext = self.get_macro_by_def_id(def_id); + if !ext.is_builtin + && ext.macro_kind() == MacroKind::Derive + && parent.expansion.outer_expn_is_descendant_of(span.ctxt()) + { + *poisoned = Some(node_id); + return module.parent; } } } @@ -2415,7 +2415,10 @@ impl<'a> Resolver<'a> { } else if i == 0 { if ident .name - .with(|n| n.chars().next().map_or(false, |c| c.is_ascii_uppercase())) + .as_str() + .chars() + .next() + .map_or(false, |c| c.is_ascii_uppercase()) { (format!("use of undeclared type `{}`", ident), None) } else { @@ -2623,8 +2626,12 @@ impl<'a> Resolver<'a> { continue; } ConstantItemRibKind(trivial) => { + let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !trivial && self.session.features_untracked().min_const_generics { + if !(trivial + || features.const_generics + || features.lazy_normalization_consts) + { // HACK(min_const_generics): If we encounter `Self` in an anonymous constant // we can't easily tell if it's generic at this stage, so we instead remember // this and then enforce the self type to be concrete later on. @@ -2712,8 +2719,12 @@ impl<'a> Resolver<'a> { continue; } ConstantItemRibKind(trivial) => { + let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !trivial && self.session.features_untracked().min_const_generics { + if !(trivial + || features.const_generics + || features.lazy_normalization_consts) + { if record_used { self.report_error( span, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index c8dbe6851285..5ad7c83ca36a 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -160,7 +160,7 @@ impl<'a> ResolverExpand for Resolver<'a> { hygiene::update_dollar_crate_names(|ctxt| { let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt)); match self.resolve_crate_root(ident).kind { - ModuleKind::Def(.., name) if name != kw::Invalid => name, + ModuleKind::Def(.., name) if name != kw::Empty => name, _ => kw::Crate, } }); diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index eed9f2eb74d4..056c0b3d9d51 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -825,7 +825,7 @@ impl<'tcx> SaveContext<'tcx> { for attr in attrs { if let Some(val) = attr.doc_str() { // FIXME: Should save-analysis beautify doc strings itself or leave it to users? - result.push_str(&beautify_doc_string(val)); + result.push_str(&beautify_doc_string(val).as_str()); result.push('\n'); } else if self.tcx.sess.check_name(attr, sym::doc) { if let Some(meta_list) = attr.meta_item_list() { diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index fab29f29e873..ac1cdc6ad45f 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -13,7 +13,7 @@ Core encoding and decoding interfaces. #![feature(never_type)] #![feature(nll)] #![feature(associated_type_bounds)] -#![feature(min_const_generics)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![cfg_attr(test, feature(test))] #![allow(rustc::internal)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b648e14360c0..c9ddcbdb5f5c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -221,6 +221,23 @@ pub enum DebugInfo { Full, } +/// Some debuginfo requires link-time relocation and some does not. LLVM can partition the debuginfo +/// into sections depending on whether or not it requires link-time relocation. Split DWARF +/// provides a mechanism which allows the linker to skip the sections which don't require link-time +/// relocation - either by putting those sections into DWARF object files, or keeping them in the +/// object file in such a way that the linker will skip them. +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum SplitDwarfKind { + /// Disabled. + None, + /// Sections which do not require relocation are written into the object file but ignored + /// by the linker. + Single, + /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file, + /// which is skipped by the linker by virtue of being a different file. + Split, +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[derive(Encodable, Decodable)] pub enum OutputType { @@ -533,6 +550,7 @@ impl_stable_hash_via_hash!(OutputFilenames); pub const RLINK_EXT: &str = "rlink"; pub const RUST_CGU_EXT: &str = "rcgu"; +pub const DWARF_OBJECT_EXT: &str = "dwo"; impl OutputFilenames { pub fn new( @@ -566,7 +584,12 @@ impl OutputFilenames { self.temp_path_ext(extension, codegen_unit_name) } - /// Like temp_path, but also supports things where there is no corresponding + /// Like `temp_path`, but specifically for dwarf objects. + pub fn temp_path_dwo(&self, codegen_unit_name: Option<&str>) -> PathBuf { + self.temp_path_ext(DWARF_OBJECT_EXT, codegen_unit_name) + } + + /// Like `temp_path`, but also supports things where there is no corresponding /// OutputType, like noopt-bitcode or lto-bitcode. pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { let mut extension = String::new(); @@ -593,6 +616,37 @@ impl OutputFilenames { path.set_extension(extension); path } + + /// Returns the name of the Split DWARF file - this can differ depending on which Split DWARF + /// mode is being used, which is the logic that this function is intended to encapsulate. + pub fn split_dwarf_filename( + &self, + split_dwarf_kind: SplitDwarfKind, + cgu_name: Option<&str>, + ) -> Option { + self.split_dwarf_path(split_dwarf_kind, cgu_name) + .map(|path| path.strip_prefix(&self.out_directory).unwrap_or(&path).to_path_buf()) + } + + /// Returns the path for the Split DWARF file - this can differ depending on which Split DWARF + /// mode is being used, which is the logic that this function is intended to encapsulate. + pub fn split_dwarf_path( + &self, + split_dwarf_kind: SplitDwarfKind, + cgu_name: Option<&str>, + ) -> Option { + let obj_out = self.temp_path(OutputType::Object, cgu_name); + let dwo_out = self.temp_path_dwo(cgu_name); + match split_dwarf_kind { + SplitDwarfKind::None => None, + // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes + // (pointing at the path which is being determined here). Use the path to the current + // object file. + SplitDwarfKind::Single => Some(obj_out), + // Split mode emits the DWARF into a different file, use that path. + SplitDwarfKind::Split => Some(dwo_out), + } + } } pub fn host_triple() -> &'static str { @@ -692,6 +746,10 @@ impl DebuggingOptions { deduplicate_diagnostics: self.deduplicate_diagnostics, } } + + pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion { + self.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy) + } } // The type of entry function, so users can have their own entry functions @@ -1296,8 +1354,10 @@ fn parse_output_types( if !debugging_opts.parse_only { for list in matches.opt_strs("emit") { for output_type in list.split(',') { - let mut parts = output_type.splitn(2, '='); - let shorthand = parts.next().unwrap(); + let (shorthand, path) = match output_type.split_once('=') { + None => (output_type, None), + Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))), + }; let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { early_error( error_format, @@ -1308,7 +1368,6 @@ fn parse_output_types( ), ) }); - let path = parts.next().map(PathBuf::from); output_types.insert(output_type, path); } } @@ -1432,7 +1491,7 @@ fn parse_target_triple(matches: &getopts::Matches, error_format: ErrorOutputType early_error(error_format, &format!("target file {:?} does not exist", path)) }) } - Some(target) => TargetTriple::TargetTriple(target), + Some(target) => TargetTriple::from_alias(target), _ => TargetTriple::from_triple(host_triple()), } } @@ -1452,11 +1511,10 @@ fn parse_opt_level( let max_c = matches .opt_strs_pos("C") .into_iter() - .flat_map( - |(i, s)| { - if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } - }, - ) + .flat_map(|(i, s)| { + // NB: This can match a string without `=`. + if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } + }) .max(); if max_o > max_c { OptLevel::Default @@ -1491,11 +1549,10 @@ fn select_debuginfo( let max_c = matches .opt_strs_pos("C") .into_iter() - .flat_map( - |(i, s)| { - if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } - }, - ) + .flat_map(|(i, s)| { + // NB: This can match a string without `=`. + if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } + }) .max(); if max_g > max_c { DebugInfo::Full @@ -1528,23 +1585,26 @@ fn parse_libs( .map(|s| { // Parse string of the form "[KIND=]lib[:new_name]", // where KIND is one of "dylib", "framework", "static". - let mut parts = s.splitn(2, '='); - let kind = parts.next().unwrap(); - let (name, kind) = match (parts.next(), kind) { - (None, name) => (name, NativeLibKind::Unspecified), - (Some(name), "dylib") => (name, NativeLibKind::Dylib), - (Some(name), "framework") => (name, NativeLibKind::Framework), - (Some(name), "static") => (name, NativeLibKind::StaticBundle), - (Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle), - (_, s) => { - early_error( - error_format, - &format!( - "unknown library kind `{}`, expected \ - one of dylib, framework, or static", - s - ), - ); + let (name, kind) = match s.split_once('=') { + None => (s, NativeLibKind::Unspecified), + Some((kind, name)) => { + let kind = match kind { + "dylib" => NativeLibKind::Dylib, + "framework" => NativeLibKind::Framework, + "static" => NativeLibKind::StaticBundle, + "static-nobundle" => NativeLibKind::StaticNoBundle, + s => { + early_error( + error_format, + &format!( + "unknown library kind `{}`, expected \ + one of dylib, framework, or static", + s + ), + ); + } + }; + (name.to_string(), kind) } }; if kind == NativeLibKind::StaticNoBundle @@ -1556,10 +1616,11 @@ fn parse_libs( accepted on the nightly compiler", ); } - let mut name_parts = name.splitn(2, ':'); - let name = name_parts.next().unwrap(); - let new_name = name_parts.next(); - (name.to_owned(), new_name.map(|n| n.to_owned()), kind) + let (name, new_name) = match name.split_once(':') { + None => (name, None), + Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())), + }; + (name, new_name, kind) }) .collect() } @@ -1580,20 +1641,13 @@ pub fn parse_externs( let is_unstable_enabled = debugging_opts.unstable_options; let mut externs: BTreeMap = BTreeMap::new(); for arg in matches.opt_strs("extern") { - let mut parts = arg.splitn(2, '='); - let name = parts - .next() - .unwrap_or_else(|| early_error(error_format, "--extern value must not be empty")); - let path = parts.next().map(|s| s.to_string()); - - let mut name_parts = name.splitn(2, ':'); - let first_part = name_parts.next(); - let second_part = name_parts.next(); - let (options, name) = match (first_part, second_part) { - (Some(opts), Some(name)) => (Some(opts), name), - (Some(name), None) => (None, name), - (None, None) => early_error(error_format, "--extern name must not be empty"), - _ => unreachable!(), + let (name, path) = match arg.split_once('=') { + None => (arg, None), + Some((name, path)) => (name.to_string(), Some(path.to_string())), + }; + let (options, name) = match name.split_once(':') { + None => (None, name), + Some((opts, name)) => (Some(opts), name.to_string()), }; let entry = externs.entry(name.to_owned()); @@ -1682,17 +1736,12 @@ fn parse_remap_path_prefix( matches .opt_strs("remap-path-prefix") .into_iter() - .map(|remap| { - let mut parts = remap.rsplitn(2, '='); // reverse iterator - let to = parts.next(); - let from = parts.next(); - match (from, to) { - (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), - _ => early_error( - error_format, - "--remap-path-prefix must contain '=' between FROM and TO", - ), - } + .map(|remap| match remap.rsplit_once('=') { + None => early_error( + error_format, + "--remap-path-prefix must contain '=' between FROM and TO", + ), + Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), }) .collect() } @@ -1766,7 +1815,30 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over // multiple runs, including some changes to source code; so mangled names must be consistent // across compilations. - debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; + match debugging_opts.symbol_mangling_version { + None => { + debugging_opts.symbol_mangling_version = Some(SymbolManglingVersion::V0); + } + Some(SymbolManglingVersion::Legacy) => { + early_warn( + error_format, + "-Z instrument-coverage requires symbol mangling version `v0`, \ + but `-Z symbol-mangling-version=legacy` was specified", + ); + } + Some(SymbolManglingVersion::V0) => {} + } + + if debugging_opts.mir_opt_level > 1 { + early_warn( + error_format, + &format!( + "`-Z mir-opt-level={}` (any level > 1) enables function inlining, which \ + limits the effectiveness of `-Z instrument-coverage`.", + debugging_opts.mir_opt_level, + ), + ); + } } if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { @@ -2171,7 +2243,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(Edition); impl_dep_tracking_hash_via_hash!(LinkerPluginLto); impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); - impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(TrimmedDefPaths); diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 55ee4e52082e..3a757e5f0075 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -76,7 +76,7 @@ impl<'a> FileSearch<'a> { pub fn new( sysroot: &'a Path, triple: &'a str, - search_paths: &'a Vec, + search_paths: &'a [SearchPath], tlib_path: &'a SearchPath, kind: PathKind, ) -> FileSearch<'a> { diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index d002f5973916..36bf8634c6ee 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,6 +1,7 @@ #![feature(crate_visibility_modifier)] #![feature(once_cell)] #![feature(or_patterns)] +#![feature(str_split_once)] #[macro_use] extern crate bitflags; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 91ebc9a7c82e..81f79f4b0e0f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -179,9 +179,10 @@ macro_rules! options { { let mut op = $defaultfn(); for option in matches.opt_strs($prefix) { - let mut iter = option.splitn(2, '='); - let key = iter.next().unwrap(); - let value = iter.next(); + let (key, value) = match option.split_once('=') { + None => (option, None), + Some((k, v)) => (k.to_string(), Some(v)), + }; let option_to_lookup = key.replace("-", "_"); let mut found = false; for &(candidate, setter, type_desc, _) in $stat { @@ -268,6 +269,7 @@ macro_rules! options { pub const parse_switch_with_opt_path: &str = "an optional path to the profiling data output directory"; pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`"; + pub const parse_split_dwarf_kind: &str = "one of: `none`, `single` or `split`"; pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)"; pub const parse_src_file_hash: &str = "either `md5` or `sha1`"; pub const parse_relocation_model: &str = @@ -675,13 +677,26 @@ macro_rules! options { true } - fn parse_symbol_mangling_version( - slot: &mut SymbolManglingVersion, + fn parse_split_dwarf_kind( + slot: &mut SplitDwarfKind, v: Option<&str>, ) -> bool { *slot = match v { - Some("legacy") => SymbolManglingVersion::Legacy, - Some("v0") => SymbolManglingVersion::V0, + Some("none") => SplitDwarfKind::None, + Some("split") => SplitDwarfKind::Split, + Some("single") => SplitDwarfKind::Single, + _ => return false, + }; + true + } + + fn parse_symbol_mangling_version( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("legacy") => Some(SymbolManglingVersion::Legacy), + Some("v0") => Some(SymbolManglingVersion::V0), _ => return false, }; true @@ -1087,9 +1102,14 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), - symbol_mangling_version: SymbolManglingVersion = (SymbolManglingVersion::Legacy, + split_dwarf: SplitDwarfKind = (SplitDwarfKind::None, parse_split_dwarf_kind, [UNTRACKED], + "enable generation of split dwarf"), + split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED], + "provide minimal debug info in the object/executable to facilitate online \ + symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), + symbol_mangling_version: Option = (None, parse_symbol_mangling_version, [TRACKED], - "which mangling version to use for symbol names"), + "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), teach: bool = (false, parse_bool, [TRACKED], "show extended diagnostic help (default: no)"), terminal_width: Option = (None, parse_opt_uint, [UNTRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 66c3738fb5b5..b1a483424173 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -119,6 +119,7 @@ pub struct ParseSess { pub unstable_features: UnstableFeatures, pub config: CrateConfig, pub edition: Edition, + pub missing_fragment_specifiers: Lock>, /// Places where raw identifiers were used. This is used for feature-gating raw identifiers. pub raw_identifier_spans: Lock>, /// Used to determine and report recursive module inclusions. @@ -152,6 +153,7 @@ impl ParseSess { unstable_features: UnstableFeatures::from_environment(None), config: FxHashSet::default(), edition: ExpnId::root().expn_data().edition, + missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), included_mod_stack: Lock::new(vec![]), source_map, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 6ca9050339fd..93675303035d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1109,10 +1109,7 @@ impl Session { } pub fn link_dead_code(&self) -> bool { - match self.opts.cg.link_dead_code { - Some(explicitly_set) => explicitly_set, - None => false, - } + self.opts.cg.link_dead_code.unwrap_or(false) } pub fn mark_attr_known(&self, attr: &Attribute) { diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index 4d0c92f51d7c..efbb0a23a6f0 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -4,24 +4,25 @@ use std::str::FromStr; use rustc_macros::HashStable_Generic; -/// The edition of the compiler (RFC 2052) +/// The edition of the compiler. (See [RFC 2052](https://github.com/rust-lang/rfcs/blob/master/text/2052-epochs.md).) #[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, Encodable, Decodable, Eq)] #[derive(HashStable_Generic)] pub enum Edition { - // editions must be kept in order, oldest to newest + // When adding new editions, be sure to do the following: + // + // - update the `ALL_EDITIONS` const + // - update the `EDITION_NAME_LIST` const + // - add a `rust_####()` function to the session + // - update the enum in Cargo's sources as well + // + // Editions *must* be kept in order, oldest to newest. /// The 2015 edition Edition2015, /// The 2018 edition Edition2018, - // when adding new editions, be sure to update: - // - // - Update the `ALL_EDITIONS` const - // - Update the EDITION_NAME_LIST const - // - add a `rust_####()` function to the session - // - update the enum in Cargo's sources as well } -// must be in order from oldest to newest +// Must be in order from oldest to newest. pub const ALL_EDITIONS: &[Edition] = &[Edition::Edition2015, Edition::Edition2018]; pub const EDITION_NAME_LIST: &str = "2015|2018"; diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 0f82db1d05aa..fdc0d225bb82 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1065,7 +1065,7 @@ pub fn decode_syntax_context< parent: SyntaxContext::root(), opaque: SyntaxContext::root(), opaque_and_semitransparent: SyntaxContext::root(), - dollar_crate_name: kw::Invalid, + dollar_crate_name: kw::Empty, }); let mut ctxts = outer_ctxts.lock(); let new_len = raw_id as usize + 1; @@ -1092,7 +1092,7 @@ pub fn decode_syntax_context< ctxt_data, ); // Make sure nothing weird happening while `decode_data` was running - assert_eq!(dummy.dollar_crate_name, kw::Invalid); + assert_eq!(dummy.dollar_crate_name, kw::Empty); }); Ok(new_ctxt) diff --git a/compiler/rustc_span/src/lev_distance.rs b/compiler/rustc_span/src/lev_distance.rs index edc6625a6ead..cea7871923bc 100644 --- a/compiler/rustc_span/src/lev_distance.rs +++ b/compiler/rustc_span/src/lev_distance.rs @@ -1,10 +1,16 @@ +//! Levenshtein distances. +//! +//! The [Levenshtein distance] is a metric for measuring the difference between two strings. +//! +//! [Levenshtein distance]: https://en.wikipedia.org/wiki/Levenshtein_distance + use crate::symbol::Symbol; use std::cmp; #[cfg(test)] mod tests; -/// Finds the Levenshtein distance between two strings +/// Finds the Levenshtein distance between two strings. pub fn lev_distance(a: &str, b: &str) -> usize { // cases which don't require further computation if a.is_empty() { @@ -35,14 +41,14 @@ pub fn lev_distance(a: &str, b: &str) -> usize { dcol[t_last + 1] } -/// Finds the best match for a given word in the given iterator +/// Finds the best match for a given word in the given iterator. /// /// As a loose rule to avoid the obviously incorrect suggestions, it takes /// an optional limit for the maximum allowable edit distance, which defaults /// to one-third of the given word. /// -/// Besides Levenshtein, we use case insensitive comparison to improve accuracy on an edge case with -/// a lower(upper)case letters mismatch. +/// Besides Levenshtein, we use case insensitive comparison to improve accuracy +/// on an edge case with a lower(upper)case letters mismatch. #[cold] pub fn find_best_match_for_name( name_vec: &[Symbol], @@ -98,7 +104,7 @@ fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option String { let mut split_words: Vec<&str> = name.split('_').collect(); - // We are sorting primitive &strs and can use unstable sort here + // We are sorting primitive &strs and can use unstable sort here. split_words.sort_unstable(); split_words.join("_") } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f63a73acbf4b..800953071756 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1,4 +1,13 @@ -//! The source positions and related helper functions. +//! Source positions and related helper functions. +//! +//! Important concepts in this module include: +//! +//! - the *span*, represented by [`SpanData`] and related types; +//! - source code as represented by a [`SourceMap`]; and +//! - interned strings, represented by [`Symbol`]s, with some common symbols available statically in the [`sym`] module. +//! +//! Unlike most compilers, the span contains not only the position in the source code, but also various other metadata, +//! such as the edition and macro hygiene. This metadata is stored in [`SyntaxContext`] and [`ExpnData`]. //! //! ## Note //! @@ -124,7 +133,7 @@ pub enum RealFileName { impl RealFileName { /// Returns the path suitable for reading from the file system on the local host. - /// Avoid embedding this in build artifacts; see `stable_name` for that. + /// Avoid embedding this in build artifacts; see `stable_name()` for that. pub fn local_path(&self) -> &Path { match self { RealFileName::Named(p) @@ -133,7 +142,7 @@ impl RealFileName { } /// Returns the path suitable for reading from the file system on the local host. - /// Avoid embedding this in build artifacts; see `stable_name` for that. + /// Avoid embedding this in build artifacts; see `stable_name()` for that. pub fn into_local_path(self) -> PathBuf { match self { RealFileName::Named(p) @@ -143,7 +152,7 @@ impl RealFileName { /// Returns the path suitable for embedding into build artifacts. Note that /// a virtualized path will not correspond to a valid file system path; see - /// `local_path` for something that is more likely to return paths into the + /// `local_path()` for something that is more likely to return paths into the /// local host file system. pub fn stable_name(&self) -> &Path { match self { @@ -173,7 +182,7 @@ pub enum FileName { /// Custom sources for explicit parser calls from plugins and drivers. Custom(String), DocTest(PathBuf, isize), - /// Post-substitution inline assembly from LLVM + /// Post-substitution inline assembly from LLVM. InlineAsm(u64), } @@ -182,7 +191,7 @@ impl std::fmt::Display for FileName { use FileName::*; match *self { Real(RealFileName::Named(ref path)) => write!(fmt, "{}", path.display()), - // FIXME: might be nice to display both compoments of Devirtualized. + // FIXME: might be nice to display both components of Devirtualized. // But for now (to backport fix for issue #70924), best to not // perturb diagnostics so its obvious test suite still works. Real(RealFileName::Devirtualized { ref local_path, virtual_name: _ }) => { @@ -266,14 +275,17 @@ impl FileName { } } +/// Represents a span. +/// /// Spans represent a region of code, used for error reporting. Positions in spans -/// are *absolute* positions from the beginning of the source_map, not positions -/// relative to `SourceFile`s. Methods on the `SourceMap` can be used to relate spans back +/// are *absolute* positions from the beginning of the [`SourceMap`], not positions +/// relative to [`SourceFile`]s. Methods on the `SourceMap` can be used to relate spans back /// to the original source. -/// You must be careful if the span crosses more than one file - you will not be +/// +/// You must be careful if the span crosses more than one file, since you will not be /// able to use many of the functions on spans in source_map and you cannot assume -/// that the length of the `span = hi - lo`; there may be space in the `BytePos` -/// range between files. +/// that the length of the span is equal to `span.hi - span.lo`; there may be space in the +/// [`BytePos`] range between files. /// /// `SpanData` is public because `Span` uses a thread-local interner and can't be /// sent to other threads, but some pieces of performance infra run in a separate thread. @@ -384,7 +396,7 @@ impl Span { Span::new(lo, hi, SyntaxContext::root()) } - /// Returns a new span representing an empty span at the beginning of this span + /// Returns a new span representing an empty span at the beginning of this span. #[inline] pub fn shrink_to_lo(self) -> Span { let span = self.data(); @@ -398,7 +410,7 @@ impl Span { } #[inline] - /// Returns true if hi == lo + /// Returns `true` if `hi == lo`. pub fn is_empty(&self) -> bool { let span = self.data(); span.hi == span.lo @@ -512,7 +524,7 @@ impl Span { } /// Checks if a span is "internal" to a macro in which `unsafe` - /// can be used without triggering the `unsafe_code` lint + /// can be used without triggering the `unsafe_code` lint. // (that is, a macro marked with `#[allow_internal_unsafe]`). pub fn allows_unsafe(&self) -> bool { self.ctxt().outer_expn_data().allow_internal_unsafe @@ -700,6 +712,7 @@ impl Span { } } +/// A span together with some additional data. #[derive(Clone, Debug)] pub struct SpanLabel { /// The span we are going to include in the final snippet. @@ -743,7 +756,7 @@ impl Decodable for Span { /// any spans that are debug-printed during the closure's execution. /// /// Normally, the global `TyCtxt` is used to retrieve the `SourceMap` -/// (see `rustc_interface::callbacks::span_debug1). However, some parts +/// (see `rustc_interface::callbacks::span_debug1`). However, some parts /// of the compiler (e.g. `rustc_parse`) may debug-print `Span`s before /// a `TyCtxt` is available. In this case, we fall back to /// the `SourceMap` provided to this function. If that is not available, @@ -994,9 +1007,9 @@ pub enum ExternalSource { Unneeded, Foreign { kind: ExternalSourceKind, - /// This SourceFile's byte-offset within the source_map of its original crate + /// This SourceFile's byte-offset within the source_map of its original crate. original_start_pos: BytePos, - /// The end of this SourceFile within the source_map of its original crate + /// The end of this SourceFile within the source_map of its original crate. original_end_pos: BytePos, }, } @@ -1099,7 +1112,7 @@ impl SourceFileHash { } } -/// A single source in the `SourceMap`. +/// A single source in the [`SourceMap`]. #[derive(Clone)] pub struct SourceFile { /// The name of the file that the source came from. Source that doesn't @@ -1580,7 +1593,7 @@ fn remove_bom(src: &mut String, normalized_pos: &mut Vec) { /// Replaces `\r\n` with `\n` in-place in `src`. /// -/// Returns error if there's a lone `\r` in the string +/// Returns error if there's a lone `\r` in the string. fn normalize_newlines(src: &mut String, normalized_pos: &mut Vec) { if !src.as_bytes().contains(&b'\r') { return; @@ -1705,13 +1718,16 @@ macro_rules! impl_pos { } impl_pos! { - /// A byte offset. Keep this small (currently 32-bits), as AST contains - /// a lot of them. + /// A byte offset. + /// + /// Keep this small (currently 32-bits), as AST contains a lot of them. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] pub struct BytePos(pub u32); - /// A character offset. Because of multibyte UTF-8 characters, a byte offset - /// is not equivalent to a character offset. The `SourceMap` will convert `BytePos` + /// A character offset. + /// + /// Because of multibyte UTF-8 characters, a byte offset + /// is not equivalent to a character offset. The [`SourceMap`] will convert [`BytePos`] /// values to `CharPos` values as necessary. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct CharPos(pub usize); @@ -1835,8 +1851,9 @@ fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize { } /// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc_middle. +/// +/// This is a hack to allow using the [`HashStable_Generic`] derive macro +/// instead of implementing everything in rustc_middle. pub trait HashStableContext { fn hash_def_id(&mut self, _: DefId, hasher: &mut StableHasher); fn hash_crate_num(&mut self, _: CrateNum, hasher: &mut StableHasher); @@ -1856,6 +1873,7 @@ where /// offsets into the `SourceMap`). Instead, we hash the (file name, line, column) /// triple, which stays the same even if the containing `SourceFile` has moved /// within the `SourceMap`. + /// /// Also note that we are hashing byte offsets for the column, not unicode /// codepoint offsets. For the purpose of the hash that's sufficient. /// Also, hashing filenames is expensive so we avoid doing it twice when the diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index e9b4eb6e4abe..fefc0cb48ddd 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -1,9 +1,11 @@ -//! The `SourceMap` tracks all the source code used within a single crate, mapping +//! Types for tracking pieces of source code within a crate. +//! +//! The [`SourceMap`] tracks all the source code used within a single crate, mapping //! from integer byte positions to the original source code location. Each bit //! of source parsed during crate parsing (typically files, in-memory strings, //! or various bits of macro expansion) cover a continuous range of bytes in the -//! `SourceMap` and are represented by `SourceFile`s. Byte positions are stored in -//! `Span` and used pervasively in the compiler. They are absolute positions +//! `SourceMap` and are represented by [`SourceFile`]s. Byte positions are stored in +//! [`Span`] and used pervasively in the compiler. They are absolute positions //! within the `SourceMap`, which upon request can be converted to line and column //! information, source code snippets, etc. diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index b05e01d666bd..ceb9b59b13ad 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxIndexSet; /// A compressed span. /// -/// `SpanData` is 12 bytes, which is a bit too big to stick everywhere. `Span` +/// Whereas [`SpanData`] is 12 bytes, which is a bit too big to stick everywhere, `Span` /// is a form that only takes up 8 bytes, with less space for the length and /// context. The vast majority (99.9%+) of `SpanData` instances will fit within /// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are @@ -42,13 +42,11 @@ use rustc_data_structures::fx::FxIndexSet; /// - `base` is 32 bits in both `Span` and `SpanData`, which means that `base` /// values never cause interning. The number of bits needed for `base` /// depends on the crate size. 32 bits allows up to 4 GiB of code in a crate. -/// `script-servo` is the largest crate in `rustc-perf`, requiring 26 bits -/// for some spans. /// - `len` is 15 bits in `Span` (a u16, minus 1 bit for the tag) and 32 bits /// in `SpanData`, which means that large `len` values will cause interning. /// The number of bits needed for `len` does not depend on the crate size. -/// The most common number of bits for `len` are 0--7, with a peak usually at -/// 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough +/// The most common numbers of bits for `len` are from 0 to 7, with a peak usually +/// at 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough /// for 99.99%+ of cases, but larger values (sometimes 20+ bits) might occur /// dozens of times in a typical crate. /// - `ctxt` is 16 bits in `Span` and 32 bits in `SpanData`, which means that diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b9c942d61a92..382b7a4f2db7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -13,7 +13,7 @@ use std::fmt; use std::hash::{Hash, Hasher}; use std::str; -use crate::{Span, DUMMY_SP, SESSION_GLOBALS}; +use crate::{Edition, Span, DUMMY_SP, SESSION_GLOBALS}; #[cfg(test)] mod tests; @@ -25,7 +25,7 @@ symbols! { Keywords { // Special reserved identifiers used internally for elided lifetimes, // unnamed method parameters, crate root module, error recovery etc. - Invalid: "", + Empty: "", PathRoot: "{{root}}", DollarCrate: "$crate", Underscore: "_", @@ -460,6 +460,9 @@ symbols! { document_private_items, dotdot_in_tuple_patterns, dotdoteq_in_patterns, + dreg, + dreg_low16, + dreg_low8, drop, drop_in_place, drop_types_in_const, @@ -467,6 +470,7 @@ symbols! { dropck_parametricity, dylib, dyn_trait, + edition_macro_pats, eh_catch_typeinfo, eh_personality, emit_enum, @@ -544,6 +548,7 @@ symbols! { format_args_capture, format_args_nl, freeze, + freg, frem_fast, from, from_desugaring, @@ -627,6 +632,7 @@ symbols! { iter, keyword, kind, + kreg, label, label_break_value, lang, @@ -652,6 +658,7 @@ symbols! { lint_reasons, literal, llvm_asm, + local, local_inner_macros, log10f32, log10f64, @@ -802,6 +809,8 @@ symbols! { partial_ord, passes, pat, + pat2018, + pat2021, path, pattern_parentheses, phantom_data, @@ -854,6 +863,9 @@ symbols! { pub_restricted, pure, pushpop_unsafe, + qreg, + qreg_low4, + qreg_low8, quad_precision_float, question_mark, quote, @@ -875,6 +887,13 @@ symbols! { reexport_test_harness_main, reference, reflect, + reg, + reg16, + reg32, + reg64, + reg_abcd, + reg_byte, + reg_thumb, register_attr, register_tool, relaxed_adts, @@ -1060,6 +1079,8 @@ symbols! { spotlight, sqrtf32, sqrtf64, + sreg, + sreg_low16, sse4a_target_feature, stable, staged_api, @@ -1215,6 +1236,8 @@ symbols! { volatile_load, volatile_set_memory, volatile_store, + vreg, + vreg_low16, warn, wasm_import_module, wasm_target_feature, @@ -1226,6 +1249,9 @@ symbols! { wrapping_mul, wrapping_sub, write_bytes, + xmm_reg, + ymm_reg, + zmm_reg, } } @@ -1250,7 +1276,7 @@ impl Ident { #[inline] pub fn invalid() -> Ident { - Ident::with_dummy_span(kw::Invalid) + Ident::with_dummy_span(kw::Empty) } /// Maps a string to an identifier with a dummy span. @@ -1428,12 +1454,6 @@ impl Symbol { with_interner(|interner| interner.intern(string)) } - /// Access the symbol's chars. This is a slowish operation because it - /// requires locking the symbol interner. - pub fn with R, R>(self, f: F) -> R { - with_interner(|interner| f(interner.get(self))) - } - /// Convert to a `SymbolStr`. This is a slowish operation because it /// requires locking the symbol interner. pub fn as_str(self) -> SymbolStr { @@ -1446,6 +1466,10 @@ impl Symbol { self.0.as_u32() } + pub fn is_empty(self) -> bool { + self == kw::Empty + } + /// This method is supposed to be used in error messages, so it's expected to be /// identical to printing the original identifier token written in source code /// (`token_to_string`, `Ident::to_string`), except that symbols don't keep the rawness flag @@ -1457,19 +1481,19 @@ impl Symbol { impl fmt::Debug for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with(|str| fmt::Debug::fmt(&str, f)) + fmt::Debug::fmt(&self.as_str(), f) } } impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with(|str| fmt::Display::fmt(&str, f)) + fmt::Display::fmt(&self.as_str(), f) } } impl Encodable for Symbol { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - self.with(|string| s.emit_str(string)) + s.emit_str(&self.as_str()) } } @@ -1550,8 +1574,7 @@ impl Interner { /// Given that `kw` is imported, use them like `kw::keyword_name`. /// For example `kw::Loop` or `kw::Break`. pub mod kw { - use super::Symbol; - keywords!(); + pub use super::kw_generated::*; } // This module has a very short name because it's used a lot. @@ -1559,22 +1582,23 @@ pub mod kw { /// /// Given that `sym` is imported, use them like `sym::symbol_name`. /// For example `sym::rustfmt` or `sym::u8`. -#[allow(rustc::default_hash_types)] pub mod sym { use super::Symbol; use std::convert::TryInto; - define_symbols!(); + pub use super::sym_generated::*; // Used from a macro in `librustc_feature/accepted.rs` pub use super::kw::MacroRules as macro_rules; - // Get the symbol for an integer. The first few non-negative integers each - // have a static symbol and therefore are fast. + /// Get the symbol for an integer. + /// + /// The first few non-negative integers each have a static symbol and therefore + /// are fast. pub fn integer + Copy + ToString>(n: N) -> Symbol { if let Result::Ok(idx) = n.try_into() { - if let Option::Some(&sym_) = digits_array.get(idx) { - return sym_; + if idx < 10 { + return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32); } } Symbol::intern(&n.to_string()) @@ -1582,12 +1606,32 @@ pub mod sym { } impl Symbol { - fn is_used_keyword_2018(self) -> bool { - self >= kw::Async && self <= kw::Dyn + fn is_special(self) -> bool { + self <= kw::Underscore } - fn is_unused_keyword_2018(self) -> bool { - self == kw::Try + fn is_used_keyword_always(self) -> bool { + self >= kw::As && self <= kw::While + } + + fn is_used_keyword_conditional(self, edition: impl FnOnce() -> Edition) -> bool { + (self >= kw::Async && self <= kw::Dyn) && edition() >= Edition::Edition2018 + } + + fn is_unused_keyword_always(self) -> bool { + self >= kw::Abstract && self <= kw::Yield + } + + fn is_unused_keyword_conditional(self, edition: impl FnOnce() -> Edition) -> bool { + self == kw::Try && edition() >= Edition::Edition2018 + } + + pub fn is_reserved(self, edition: impl Copy + FnOnce() -> Edition) -> bool { + self.is_special() + || self.is_used_keyword_always() + || self.is_unused_keyword_always() + || self.is_used_keyword_conditional(edition) + || self.is_unused_keyword_conditional(edition) } /// A keyword or reserved identifier that can be used as a path segment. @@ -1605,9 +1649,9 @@ impl Symbol { self == kw::True || self == kw::False } - /// This symbol can be a raw identifier. + /// Returns `true` if this symbol can be a raw identifier. pub fn can_be_raw(self) -> bool { - self != kw::Invalid && self != kw::Underscore && !self.is_path_segment_keyword() + self != kw::Empty && self != kw::Underscore && !self.is_path_segment_keyword() } } @@ -1615,26 +1659,27 @@ impl Ident { // Returns `true` for reserved identifiers used internally for elided lifetimes, // unnamed method parameters, crate root module, error recovery etc. pub fn is_special(self) -> bool { - self.name <= kw::Underscore + self.name.is_special() } /// Returns `true` if the token is a keyword used in the language. pub fn is_used_keyword(self) -> bool { // Note: `span.edition()` is relatively expensive, don't call it unless necessary. - self.name >= kw::As && self.name <= kw::While - || self.name.is_used_keyword_2018() && self.span.rust_2018() + self.name.is_used_keyword_always() + || self.name.is_used_keyword_conditional(|| self.span.edition()) } /// Returns `true` if the token is a keyword reserved for possible future use. pub fn is_unused_keyword(self) -> bool { // Note: `span.edition()` is relatively expensive, don't call it unless necessary. - self.name >= kw::Abstract && self.name <= kw::Yield - || self.name.is_unused_keyword_2018() && self.span.rust_2018() + self.name.is_unused_keyword_always() + || self.name.is_unused_keyword_conditional(|| self.span.edition()) } /// Returns `true` if the token is either a special identifier or a keyword. pub fn is_reserved(self) -> bool { - self.is_special() || self.is_used_keyword() || self.is_unused_keyword() + // Note: `span.edition()` is relatively expensive, don't call it unless necessary. + self.name.is_reserved(|| self.span.edition()) } /// A keyword or reserved identifier that can be used as a path segment. @@ -1654,7 +1699,7 @@ fn with_interner T>(f: F) -> T { SESSION_GLOBALS.with(|session_globals| f(&mut *session_globals.symbol_interner.lock())) } -/// An alternative to `Symbol`, useful when the chars within the symbol need to +/// An alternative to [`Symbol`], useful when the chars within the symbol need to /// be accessed. It deliberately has limited functionality and should only be /// used for temporary values. /// diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index eba8e1a0613f..6356a7e78325 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -222,7 +222,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { fn print_dyn_existential( mut self, - predicates: &'tcx ty::List>, + predicates: &'tcx ty::List>>, ) -> Result { let mut first = true; for p in predicates { diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 10245d21b63a..7f8cded0ac0e 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -245,7 +245,7 @@ fn compute_symbol_name( // 2. we favor `instantiating_crate` where possible (i.e. when `Some`) let mangling_version_crate = instantiating_crate.unwrap_or(def_id.krate); let mangling_version = if mangling_version_crate == LOCAL_CRATE { - tcx.sess.opts.debugging_opts.symbol_mangling_version + tcx.sess.opts.debugging_opts.get_symbol_mangling_version() } else { tcx.symbol_mangling_version(mangling_version_crate) }; diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index c28c2fecfbb4..7b6e6ad0696a 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -319,7 +319,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { // Late-bound lifetimes use indices starting at 1, // see `BinderLevel` for more details. - ty::ReLateBound(debruijn, ty::BrAnon(i)) => { + ty::ReLateBound(debruijn, ty::BoundRegion { kind: ty::BrAnon(i) }) => { let binder = &self.binders[self.binders.len() - 1 - debruijn.index()]; let depth = binder.lifetime_depths.start + i; @@ -465,9 +465,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { ty::Dynamic(predicates, r) => { self.push("D"); - self = self.in_binder(&predicates, |cx, predicates| { - cx.print_dyn_existential(predicates) - })?; + self = self.print_dyn_existential(predicates)?; self = r.print(self)?; } @@ -486,26 +484,29 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { fn print_dyn_existential( mut self, - predicates: &'tcx ty::List>, + predicates: &'tcx ty::List>>, ) -> Result { for predicate in predicates { - match predicate { - ty::ExistentialPredicate::Trait(trait_ref) => { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = self.tcx.mk_ty_infer(ty::FreshTy(0)); - let trait_ref = trait_ref.with_self_ty(self.tcx, dummy_self); - self = self.print_def_path(trait_ref.def_id, trait_ref.substs)?; + self = self.in_binder(&predicate, |mut cx, predicate| { + match predicate { + ty::ExistentialPredicate::Trait(trait_ref) => { + // Use a type that can't appear in defaults of type parameters. + let dummy_self = cx.tcx.mk_ty_infer(ty::FreshTy(0)); + let trait_ref = trait_ref.with_self_ty(cx.tcx, dummy_self); + cx = cx.print_def_path(trait_ref.def_id, trait_ref.substs)?; + } + ty::ExistentialPredicate::Projection(projection) => { + let name = cx.tcx.associated_item(projection.item_def_id).ident; + cx.push("p"); + cx.push_ident(&name.as_str()); + cx = projection.ty.print(cx)?; + } + ty::ExistentialPredicate::AutoTrait(def_id) => { + cx = cx.print_def_path(*def_id, &[])?; + } } - ty::ExistentialPredicate::Projection(projection) => { - let name = self.tcx.associated_item(projection.item_def_id).ident; - self.push("p"); - self.push_ident(&name.as_str()); - self = projection.ty.print(self)?; - } - ty::ExistentialPredicate::AutoTrait(def_id) => { - self = self.print_def_path(def_id, &[])?; - } - } + Ok(cx) + })?; } self.push("E"); Ok(self) diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index aff1ff92d31b..3c65c84b0de4 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -20,16 +20,16 @@ macro_rules! def_reg_class { } impl $arch_regclass { - pub fn name(self) -> &'static str { + pub fn name(self) -> rustc_span::Symbol { match self { - $(Self::$class => stringify!($class),)* + $(Self::$class => rustc_span::symbol::sym::$class,)* } } - pub fn parse(_arch: super::InlineAsmArch, name: &str) -> Result { + pub fn parse(_arch: super::InlineAsmArch, name: rustc_span::Symbol) -> Result { match name { $( - stringify!($class) => Ok(Self::$class), + rustc_span::sym::$class => Ok(Self::$class), )* _ => Err("unknown register class"), } @@ -327,7 +327,7 @@ pub enum InlineAsmRegClass { } impl InlineAsmRegClass { - pub fn name(self) -> &'static str { + pub fn name(self) -> Symbol { match self { Self::X86(r) => r.name(), Self::Arm(r) => r.name(), @@ -422,29 +422,22 @@ impl InlineAsmRegClass { } pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { - // FIXME: use direct symbol comparison for register class names - name.with(|name| { - Ok(match arch { - InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - Self::X86(X86InlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::AArch64 => { - Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { - Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::Hexagon => { - Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::Mips | InlineAsmArch::Mips64 => { - Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::Wasm32 => Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?), - }) + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { + Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Wasm32 => Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?), }) } @@ -484,7 +477,7 @@ impl fmt::Display for InlineAsmRegOrRegClass { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Reg(r) => write!(f, "\"{}\"", r.name()), - Self::RegClass(r) => f.write_str(r.name()), + Self::RegClass(r) => write!(f, "{}", r.name()), } } } diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index fb747dfcbd33..1ad57582ebaf 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -15,6 +15,7 @@ #![feature(never_type)] #![feature(associated_type_bounds)] #![feature(exhaustive_patterns)] +#![feature(str_split_once)] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index e271a6dec40d..884223952164 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -54,10 +54,7 @@ fn macos_deployment_target() -> (u32, u32) { let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok(); let version = deployment_target .as_ref() - .and_then(|s| { - let mut i = s.splitn(2, '.'); - i.next().and_then(|a| i.next().map(|b| (a, b))) - }) + .and_then(|s| s.split_once('.')) .and_then(|(a, b)| a.parse::().and_then(|a| b.parse::().map(|b| (a, b))).ok()); version.unwrap_or((10, 7)) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 8d749493d0a4..8d72df6850fc 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1800,6 +1800,24 @@ impl TargetTriple { Ok(TargetTriple::TargetPath(canonicalized_path)) } + /// Creates a target triple from its alias + pub fn from_alias(triple: String) -> Self { + macro_rules! target_aliases { + ( $(($alias:literal, $target:literal ),)+ ) => { + match triple.as_str() { + $( $alias => TargetTriple::from_triple($target), )+ + _ => TargetTriple::TargetTriple(triple), + } + } + } + + target_aliases! { + // `x86_64-pc-solaris` is an alias for `x86_64_sun_solaris` for backwards compatibility reasons. + // (See .) + ("x86_64-pc-solaris", "x86_64-sun-solaris"), + } + } + /// Returns a string triple for this target. /// /// If this target is a path, the file name (without extension) is returned. diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 41184ce21168..da66fbc8587d 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -162,7 +162,7 @@ impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> { /// 'b` (and hence, transitively, that `T: 'a`). This method would /// add those assumptions into the outlives-environment. /// - /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` + /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs` fn add_implied_bounds( &mut self, infcx: &InferCtxt<'a, 'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 6ab16886ed23..fc6a9a7f2097 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -35,10 +35,7 @@ pub enum AutoTraitResult { #[allow(dead_code)] impl AutoTraitResult { fn is_auto(&self) -> bool { - match *self { - AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl => true, - _ => false, - } + matches!(self, AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl) } } @@ -601,10 +598,7 @@ impl AutoTraitFinder<'tcx> { } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { - match *p.ty().skip_binder().kind() { - ty::Projection(proj) if proj == p.skip_binder().projection_ty => true, - _ => false, - } + matches!(*p.ty().skip_binder().kind(), ty::Projection(proj) if proj == p.skip_binder().projection_ty) } fn evaluate_nested_obligations( diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 9324d55ac1b2..99b96f609647 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -193,10 +193,8 @@ fn overlap_within_probe( let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes(); debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); - let involves_placeholder = match selcx.infcx().region_constraints_added_in_snapshot(snapshot) { - Some(true) => true, - _ => false, - }; + let involves_placeholder = + matches!(selcx.infcx().region_constraints_added_in_snapshot(snapshot), Some(true)); Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 9feba7bfc492..1d82e732907b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -861,10 +861,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { let arg_length = arguments.len(); - let distinct = match &other[..] { - &[ArgKind::Tuple(..)] => true, - _ => false, - }; + let distinct = matches!(other, &[ArgKind::Tuple(..)]); match (arg_length, arguments.get(0)) { (1, Some(&ArgKind::Tuple(_, ref fields))) => { format!("a single {}-tuple as argument", fields.len()) @@ -1201,12 +1198,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { normalized_ty, data.ty ); - let is_normalized_ty_expected = match &obligation.cause.code { - ObligationCauseCode::ItemObligation(_) + let is_normalized_ty_expected = !matches!(obligation.cause.code, ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ObjectCastObligation(_) => false, - _ => true, - }; + | ObligationCauseCode::ObjectCastObligation(_)); if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( is_normalized_ty_expected, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 1b5375938af6..69f66f6e6b1a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -219,8 +219,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } if let ty::Dynamic(traits, _) = self_ty.kind() { - for t in traits.skip_binder() { - if let ty::ExistentialPredicate::Trait(trait_ref) = t { + for t in traits.iter() { + if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 095483aa5a25..79fea83a6674 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -254,27 +254,21 @@ fn suggest_restriction( let pred = trait_ref.without_const().to_predicate(tcx).to_string(); let pred = pred.replace(&impl_trait_str, &type_param_name); let mut sugg = vec![ + // Find the last of the generic parameters contained within the span of + // the generics match generics .params .iter() - .filter(|p| match p.kind { - hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => false, - _ => true, - }) - .last() + .map(|p| p.bounds_span().unwrap_or(p.span)) + .filter(|&span| generics.span.contains(span) && span.desugaring_kind().is_none()) + .max_by_key(|span| span.hi()) { // `fn foo(t: impl Trait)` // ^ suggest `` here None => (generics.span, format!("<{}>", type_param)), // `fn foo(t: impl Trait)` // ^^^ suggest `` here - Some(param) => ( - param.bounds_span().unwrap_or(param.span).shrink_to_hi(), - format!(", {}", type_param), - ), + Some(span) => (span.shrink_to_hi(), format!(", {}", type_param)), }, // `fn foo(t: impl Trait)` // ^ suggest `where ::A: Bound` @@ -1151,9 +1145,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) -> DiagnosticBuilder<'tcx> { crate fn build_fn_sig_string<'tcx>( tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, ) -> String { - let inputs = trait_ref.substs.type_at(1); + let inputs = trait_ref.skip_binder().substs.type_at(1); let sig = if let ty::Tuple(inputs) = inputs.kind() { tcx.mk_fn_sig( inputs.iter().map(|k| k.expect_ty()), @@ -1171,7 +1165,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { abi::Abi::Rust, ) }; - ty::Binder::bind(sig).to_string() + trait_ref.rebind(sig).to_string() } let argument_is_closure = expected_ref.skip_binder().substs.type_at(0).is_closure(); @@ -1183,17 +1177,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if argument_is_closure { "closure" } else { "function" } ); - let found_str = format!( - "expected signature of `{}`", - build_fn_sig_string(self.tcx, found.skip_binder()) - ); + let found_str = format!("expected signature of `{}`", build_fn_sig_string(self.tcx, found)); err.span_label(span, found_str); let found_span = found_span.unwrap_or(span); - let expected_str = format!( - "found signature of `{}`", - build_fn_sig_string(self.tcx, expected_ref.skip_binder()) - ); + let expected_str = + format!("found signature of `{}`", build_fn_sig_string(self.tcx, expected_ref)); err.span_label(found_span, expected_str); err @@ -1422,7 +1411,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // generator frame. Bound regions are preserved by // `erase_regions` and so we must also call // `erase_late_bound_regions`. - let ty_erased = self.tcx.erase_late_bound_regions(ty::Binder::bind(ty)); + let ty_erased = self.tcx.erase_late_bound_regions(ty); let ty_erased = self.tcx.erase_regions(ty_erased); let eq = ty::TyS::same_type(ty_erased, target_ty_erased); debug!( @@ -1440,7 +1429,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| { let upvar_ty = typeck_results.node_type(*upvar_id); let upvar_ty = self.resolve_vars_if_possible(upvar_ty); - if ty_matches(&upvar_ty) { + if ty_matches(ty::Binder::dummy(upvar_ty)) { Some(GeneratorInteriorOrUpvar::Upvar(upvar.span)) } else { None @@ -1448,31 +1437,33 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }); }; - typeck_results - .generator_interior_types - .iter() - .find(|ty::GeneratorInteriorTypeCause { ty, .. }| ty_matches(ty)) - .map(|cause| { - // Check to see if any awaited expressions have the target type. - let from_awaited_ty = visitor - .awaits - .into_iter() - .map(|id| hir.expect_expr(id)) - .find(|await_expr| { - let ty = typeck_results.expr_ty_adjusted(&await_expr); - debug!( - "maybe_note_obligation_cause_for_async_await: await_expr={:?}", - await_expr - ); - ty_matches(ty) - }) - .map(|expr| expr.span); - let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = - cause; + // The generator interior types share the same binders + if let Some(cause) = + typeck_results.generator_interior_types.as_ref().skip_binder().iter().find( + |ty::GeneratorInteriorTypeCause { ty, .. }| { + ty_matches(typeck_results.generator_interior_types.rebind(ty)) + }, + ) + { + // Check to see if any awaited expressions have the target type. + let from_awaited_ty = visitor + .awaits + .into_iter() + .map(|id| hir.expect_expr(id)) + .find(|await_expr| { + let ty = typeck_results.expr_ty_adjusted(&await_expr); + debug!( + "maybe_note_obligation_cause_for_async_await: await_expr={:?}", + await_expr + ); + ty_matches(ty::Binder::dummy(ty)) + }) + .map(|expr| expr.span); + let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = cause; - interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span)); - interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty)); - }); + interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span)); + interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty)); + }; debug!( "maybe_note_obligation_cause_for_async_await: interior_or_upvar={:?} \ diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 2fb9b3cd5d30..9c894e99a389 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -323,9 +323,8 @@ pub fn normalize_param_env_or_error<'tcx>( // This works fairly well because trait matching does not actually care about param-env // TypeOutlives predicates - these are normally used by regionck. let outlives_predicates: Vec<_> = predicates - .drain_filter(|predicate| match predicate.skip_binders() { - ty::PredicateAtom::TypeOutlives(..) => true, - _ => false, + .drain_filter(|predicate| { + matches!(predicate.skip_binders(), ty::PredicateAtom::TypeOutlives(..)) }) .collect(); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index d912a00d6b70..8b6e30f34fd4 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -418,11 +418,11 @@ fn virtual_call_violation_for_method<'tcx>( } for (i, &input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { - if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) { + if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) { return Some(MethodViolationCode::ReferencesSelfInput(i)); } } - if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output().skip_binder()) { + if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { return Some(MethodViolationCode::ReferencesSelfOutput); } @@ -551,8 +551,9 @@ fn object_ty_for_trait<'tcx>( let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); - let trait_predicate = - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + let trait_predicate = ty::Binder::dummy(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), + )); let mut associated_types = traits::supertraits(tcx, ty::Binder::dummy(trait_ref)) .flat_map(|super_trait_ref| { @@ -569,24 +570,19 @@ fn object_ty_for_trait<'tcx>( let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| { // We *can* get bound lifetimes here in cases like // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`. - // - // binder moved to (*)... - let super_trait_ref = super_trait_ref.skip_binder(); - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - ty: tcx.mk_projection(item.def_id, super_trait_ref.substs), - item_def_id: item.def_id, - substs: super_trait_ref.substs, + super_trait_ref.map_bound(|super_trait_ref| { + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + ty: tcx.mk_projection(item.def_id, super_trait_ref.substs), + item_def_id: item.def_id, + substs: super_trait_ref.substs, + }) }) }); - let existential_predicates = - tcx.mk_existential_predicates(iter::once(trait_predicate).chain(projection_predicates)); + let existential_predicates = tcx + .mk_poly_existential_predicates(iter::once(trait_predicate).chain(projection_predicates)); - let object_ty = tcx.mk_dynamic( - // (*) ... binder re-introduced here - ty::Binder::bind(existential_predicates), - lifetime, - ); + let object_ty = tcx.mk_dynamic(existential_predicates, lifetime); debug!("object_ty_for_trait: object_ty=`{}`", object_ty); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index df472e6ed7e9..fa0526445c19 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -496,12 +496,6 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( return Ok(None); } Err(ProjectionCacheEntry::InProgress) => { - // If while normalized A::B, we are asked to normalize - // A::B, just return A::B itself. This is a conservative - // answer, in the sense that A::B *is* clearly equivalent - // to A::B, though there may be a better value we can - // find. - // Under lazy normalization, this can arise when // bootstrapping. That is, imagine an environment with a // where-clause like `A::B == u32`. Now, if we are asked @@ -512,6 +506,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( debug!("found cache entry: in-progress"); + // Cache that normalizing this projection resulted in a cycle. This + // should ensure that, unless this happens within a snapshot that's + // rolled back, fulfillment or evaluation will notice the cycle. + + infcx.inner.borrow_mut().projection_cache().recur(cache_key); + return Err(InProgress); + } + Err(ProjectionCacheEntry::Recur) => { return Err(InProgress); } Err(ProjectionCacheEntry::NormalizedTy(ty)) => { @@ -734,7 +736,14 @@ fn project_type<'cx, 'tcx>( if !selcx.tcx().sess.recursion_limit().value_within_limit(obligation.recursion_depth) { debug!("project: overflow!"); - return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow)); + match selcx.query_mode() { + super::TraitQueryMode::Standard => { + selcx.infcx().report_overflow_error(&obligation, true); + } + super::TraitQueryMode::Canonical => { + return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow)); + } + } } let obligation_trait_ref = &obligation.predicate.trait_ref(selcx.tcx()); @@ -951,7 +960,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // If we are resolving `>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: - let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + let poly_trait_ref = ty::Binder::dummy(*obligation_trait_ref); let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate()); let _ = selcx.infcx().commit_if_ok(|_| { let impl_source = match selcx.select(&trait_obligation) { @@ -1247,7 +1256,9 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>( ty: self_ty.discriminant_ty(tcx), }; - confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate), false) + // We get here from `poly_project_and_unify_type` which replaces bound vars + // with placeholders, so dummy is okay here. + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index ca3369b8f1e9..f09ce8d64ed5 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -247,7 +247,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - self.assemble_candidates_for_trait_alias(obligation, &mut candidates)?; + self.assemble_candidates_for_trait_alias(obligation, &mut candidates); // Other bounds. Consider both in-scope bounds from fn decl // and applicable impls. There is a certain set of precedence rules here. @@ -259,11 +259,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // User-defined copy impls are permitted, but only for // structs and enums. - self.assemble_candidates_from_impls(obligation, &mut candidates)?; + self.assemble_candidates_from_impls(obligation, &mut candidates); // For other types, we'll use the builtin rules. let copy_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?; + self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); } else if lang_items.discriminant_kind_trait() == Some(def_id) { // `DiscriminantKind` is automatically implemented for every type. candidates.vec.push(DiscriminantKindCandidate); @@ -271,7 +271,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Sized is never implementable by end-users, it is // always automatically computed. let sized_conditions = self.sized_conditions(obligation); - self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; + self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); } else if lang_items.unsize_trait() == Some(def_id) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); } else { @@ -280,13 +280,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` // types have builtin support for `Clone`. let clone_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; + self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); } - self.assemble_generator_candidates(obligation, &mut candidates)?; - self.assemble_closure_candidates(obligation, &mut candidates)?; - self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; - self.assemble_candidates_from_impls(obligation, &mut candidates)?; + self.assemble_generator_candidates(obligation, &mut candidates); + self.assemble_closure_candidates(obligation, &mut candidates); + self.assemble_fn_pointer_candidates(obligation, &mut candidates); + self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_from_object_ty(obligation, &mut candidates); } @@ -295,7 +295,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Auto implementations have lower priority, so we only // consider triggering a default if there is no other impl that can apply. if candidates.vec.is_empty() { - self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?; + self.assemble_candidates_from_auto_impls(obligation, &mut candidates); } debug!("candidate list size: {}", candidates.vec.len()); Ok(candidates) @@ -367,9 +367,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) { - return Ok(()); + return; } // Okay to skip binder because the substs on generator types never @@ -388,8 +388,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } _ => {} } - - Ok(()) } /// Checks for the artificial impl that the compiler will create for an obligation like `X : @@ -402,11 +400,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { let kind = match self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) { Some(k) => k, None => { - return Ok(()); + return; } }; @@ -435,8 +433,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } _ => {} } - - Ok(()) } /// Implements one of the `Fn()` family for a fn pointer. @@ -444,10 +440,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { // We provide impl of all fn traits for fn pointers. if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() { - return Ok(()); + return; } // Okay to skip binder because what we are inspecting doesn't involve bound regions. @@ -485,8 +481,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } _ => {} } - - Ok(()) } /// Searches for impls that might apply to `obligation`. @@ -494,7 +488,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { debug!(?obligation, "assemble_candidates_from_impls"); // Essentially any user-written impl will match with an error type, @@ -504,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Since compilation is already guaranteed to fail, this is just // to try to show the 'nicest' possible errors to the user. if obligation.references_error() { - return Ok(()); + return; } self.tcx().for_each_relevant_impl( @@ -518,15 +512,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }); }, ); - - Ok(()) } fn assemble_candidates_from_auto_impls( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { // Okay to skip binder here because the tests we do below do not involve bound regions. let self_ty = obligation.self_ty().skip_binder(); debug!(?self_ty, "assemble_candidates_from_auto_impls"); @@ -558,7 +550,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // where-clause or, in the case of an object type, // it could be that the object type lists the // trait (e.g., `Foo+Send : Send`). See - // `compile-fail/typeck-default-trait-impl-send-param.rs` + // `ui/typeck/typeck-default-trait-impl-send-param.rs` // for an example of a test case that exercises // this path. } @@ -585,8 +577,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => candidates.vec.push(AutoImplCandidate(def_id)), } } - - Ok(()) } /// Searches for impls that might apply to `obligation`. @@ -753,7 +743,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { // Okay to skip binder here because the tests we do below do not involve bound regions. let self_ty = obligation.self_ty().skip_binder(); debug!(?self_ty, "assemble_candidates_for_trait_alias"); @@ -763,8 +753,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.tcx().is_trait_alias(def_id) { candidates.vec.push(TraitAliasCandidate(def_id)); } - - Ok(()) } /// Assembles the trait which are built-in to the language itself: @@ -773,7 +761,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, conditions: BuiltinImplConditions<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { match conditions { BuiltinImplConditions::Where(nested) => { debug!(?nested, "builtin_bound"); @@ -787,7 +775,5 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.ambiguous = true; } } - - Ok(()) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index a42c80213464..030c29171a15 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -259,10 +259,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> ImplSourceAutoImplData> { debug!(?obligation, ?trait_def_id, "confirm_auto_impl_candidate"); - let types = obligation.predicate.map_bound(|inner| { - let self_ty = self.infcx.shallow_resolve(inner.self_ty()); - self.constituent_types_for_ty(self_ty) - }); + let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty()); + let types = self.constituent_types_for_ty(self_ty); self.vtable_auto_impl(obligation, trait_def_id, types) } @@ -336,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn vtable_impl( &mut self, impl_def_id: DefId, - mut substs: Normalized<'tcx, SubstsRef<'tcx>>, + substs: Normalized<'tcx, SubstsRef<'tcx>>, cause: ObligationCause<'tcx>, recursion_depth: usize, param_env: ty::ParamEnv<'tcx>, @@ -358,9 +356,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // relying on projections in the impl-trait-ref. // // e.g., `impl> Foo<::T> for V` - substs.obligations.append(&mut impl_obligations); + impl_obligations.extend(substs.obligations); - ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: substs.obligations } + ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: impl_obligations } } fn confirm_object_candidate( @@ -375,24 +373,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty()); let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref); let data = match *self_ty.kind() { - ty::Dynamic(data, ..) => { - self.infcx - .replace_bound_vars_with_fresh_vars( - obligation.cause.span, - HigherRankedType, - data, - ) - .0 - } + ty::Dynamic(data, ..) => data, _ => span_bug!(obligation.cause.span, "object candidate with non-object"), }; - let object_trait_ref = data - .principal() - .unwrap_or_else(|| { - span_bug!(obligation.cause.span, "object candidate with no principal") - }) - .with_self_ty(self.tcx(), self_ty); + let object_trait_ref = data.principal().unwrap_or_else(|| { + span_bug!(obligation.cause.span, "object candidate with no principal") + }); + let object_trait_ref = self + .infcx + .replace_bound_vars_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + object_trait_ref, + ) + .0; + let object_trait_ref = object_trait_ref.with_self_ty(self.tcx(), self_ty); let mut nested = vec![]; @@ -447,7 +443,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); nested.push(Obligation::new( obligation.cause.clone(), - obligation.param_env.clone(), + obligation.param_env, normalized_super_trait, )); } @@ -485,7 +481,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); nested.push(Obligation::new( obligation.cause.clone(), - obligation.param_env.clone(), + obligation.param_env, normalized_bound, )); } @@ -711,15 +707,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { // See `assemble_candidates_for_unsizing` for more info. - let existential_predicates = data_a.map_bound(|data_a| { - let iter = data_a - .principal() - .map(ty::ExistentialPredicate::Trait) - .into_iter() - .chain(data_a.projection_bounds().map(ty::ExistentialPredicate::Projection)) - .chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait)); - tcx.mk_existential_predicates(iter) - }); + let iter = data_a + .principal() + .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) + .into_iter() + .chain( + data_a + .projection_bounds() + .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), + ) + .chain( + data_b + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let existential_predicates = tcx.mk_poly_existential_predicates(iter); let source_trait = tcx.mk_dynamic(existential_predicates, r_b); // Require that the traits involved in this upcast are **equal**; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 74b6652981a8..a8f81445b039 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -291,6 +291,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.tcx } + pub(super) fn query_mode(&self) -> TraitQueryMode { + self.query_mode + } + /////////////////////////////////////////////////////////////////////////// // Selection // @@ -1276,7 +1280,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // FIXME(generic_associated_types): Compare the whole projections let data_poly_trait_ref = projection_ty.map_bound(|proj| proj.trait_ref(self.tcx())); - let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + let obligation_poly_trait_ref = ty::Binder::dummy(*obligation_trait_ref); self.infcx .at(&obligation.cause, obligation.param_env) .sup(obligation_poly_trait_ref, data_poly_trait_ref) @@ -1648,8 +1652,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Bar where struct Bar { x: T, y: u32 } -> [i32, u32] /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] /// ``` - fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec> { - match *t.kind() { + fn constituent_types_for_ty(&self, t: ty::Binder>) -> ty::Binder>> { + match *t.skip_binder().kind() { ty::Uint(_) | ty::Int(_) | ty::Bool @@ -1660,7 +1664,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never - | ty::Char => Vec::new(), + | ty::Char => ty::Binder::dummy(Vec::new()), ty::Placeholder(..) | ty::Dynamic(..) @@ -1673,44 +1677,44 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { - vec![element_ty] + t.rebind(vec![element_ty]) } - ty::Array(element_ty, _) | ty::Slice(element_ty) => vec![element_ty], + ty::Array(element_ty, _) | ty::Slice(element_ty) => t.rebind(vec![element_ty]), ty::Tuple(ref tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - tys.iter().map(|k| k.expect_ty()).collect() + t.rebind(tys.iter().map(|k| k.expect_ty()).collect()) } ty::Closure(_, ref substs) => { let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty()); - vec![ty] + t.rebind(vec![ty]) } ty::Generator(_, ref substs, _) => { let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty()); let witness = substs.as_generator().witness(); - vec![ty].into_iter().chain(iter::once(witness)).collect() + t.rebind(vec![ty].into_iter().chain(iter::once(witness)).collect()) } ty::GeneratorWitness(types) => { - // This is sound because no regions in the witness can refer to - // the binder outside the witness. So we'll effectivly reuse - // the implicit binder around the witness. - types.skip_binder().to_vec() + debug_assert!(!types.has_escaping_bound_vars()); + types.map_bound(|types| types.to_vec()) } // For `PhantomData`, we pass `T`. - ty::Adt(def, substs) if def.is_phantom_data() => substs.types().collect(), + ty::Adt(def, substs) if def.is_phantom_data() => t.rebind(substs.types().collect()), - ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect(), + ty::Adt(def, substs) => { + t.rebind(def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect()) + } ty::Opaque(def_id, substs) => { // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. - vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)] + t.rebind(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)]) } } } @@ -1738,10 +1742,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 3. Re-bind the regions back to `for<'a> &'a i32 : Copy` types + .as_ref() .skip_binder() // binder moved -\ .iter() .flat_map(|ty| { - let ty: ty::Binder> = ty::Binder::bind(ty); // <----/ + let ty: ty::Binder> = types.rebind(ty); // <----/ self.infcx.commit_unconditionally(|_| { let placeholder_ty = self.infcx.replace_bound_vars_with_placeholders(ty); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index ab4a81c7d152..8888ea2c8490 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -6,7 +6,7 @@ use smallvec::SmallVec; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; pub use rustc_infer::traits::util::*; @@ -333,11 +333,12 @@ pub fn closure_trait_ref_and_return_type( TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()), }; + debug_assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef { def_id: fn_trait_def_id, substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]), }; - ty::Binder::bind((trait_ref, sig.skip_binder().output())) + sig.map_bound(|sig| (trait_ref, sig.output())) } pub fn generator_trait_ref_and_outputs( @@ -346,11 +347,12 @@ pub fn generator_trait_ref_and_outputs( self_ty: Ty<'tcx>, sig: ty::PolyGenSig<'tcx>, ) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { + debug_assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef { def_id: fn_trait_def_id, substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]), }; - ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty)) + sig.map_bound(|sig| (trait_ref, sig.yield_ty, sig.return_ty)) } pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 5bcb16d21e09..3f58fd72f409 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -706,7 +706,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { fn from_object_ty( &mut self, ty: Ty<'tcx>, - data: ty::Binder<&'tcx ty::List>>, + data: &'tcx ty::List>>, region: ty::Region<'tcx>, ) { // Imagine a type like this: @@ -769,7 +769,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// `infer::required_region_bounds`, see that for more information. pub fn object_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, - existential_predicates: ty::Binder<&'tcx ty::List>>, + existential_predicates: &'tcx ty::List>>, ) -> Vec> { // Since we don't actually *know* the self type for an object, // this "open(err)" serves as a kind of dummy standin -- basically diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index b1b9ef343d5a..1893d74335ab 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -648,7 +648,7 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t /// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked /// var bound at index `0`. For types, we use a `BoundVar` index equal to -/// the type parameter index. For regions, we use the `BoundRegion::BrNamed` +/// the type parameter index. For regions, we use the `BoundRegionKind::BrNamed` /// variant (which has a `DefId`). fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind { @@ -662,12 +662,10 @@ fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { )) .into(), - ty::GenericParamDefKind::Lifetime => tcx - .mk_region(ty::RegionKind::ReLateBound( - ty::INNERMOST, - ty::BoundRegion::BrAnon(substs.len() as u32), - )) - .into(), + ty::GenericParamDefKind::Lifetime => { + let br = ty::BoundRegion { kind: ty::BrAnon(substs.len() as u32) }; + tcx.mk_region(ty::RegionKind::ReLateBound(ty::INNERMOST, br)).into() + } ty::GenericParamDefKind::Const => tcx .mk_const(ty::Const { diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 9afb980f84d2..8aa68e533a2e 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -35,9 +35,7 @@ use rustc_ast::ast; use rustc_middle::traits::{ChalkEnvironmentAndGoal, ChalkRustInterner as RustInterner}; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; -use rustc_middle::ty::{ - self, Binder, BoundRegion, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor, -}; +use rustc_middle::ty::{self, Binder, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::def_id::DefId; use chalk_ir::{FnSig, ForeignDefId}; @@ -444,15 +442,15 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'t ReEarlyBound(_) => { panic!("Should have already been substituted."); } - ReLateBound(db, br) => match br { - ty::BoundRegion::BrAnon(var) => { + ReLateBound(db, br) => match br.kind { + ty::BoundRegionKind::BrAnon(var) => { chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(db.as_u32()), - *var as usize, + var as usize, )) .intern(interner) } - ty::BoundRegion::BrNamed(_def_id, _name) => unimplemented!(), + ty::BoundRegionKind::BrNamed(_def_id, _name) => unimplemented!(), ty::BrEnv => unimplemented!(), }, ReFree(_) => unimplemented!(), @@ -477,13 +475,13 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime ty::RegionKind::ReLateBound( ty::DebruijnIndex::from_u32(var.debruijn.depth()), - ty::BoundRegion::BrAnon(var.index as u32), + ty::BoundRegion { kind: ty::BrAnon(var.index as u32) }, ), chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(), chalk_ir::LifetimeData::Placeholder(p) => { ty::RegionKind::RePlaceholder(ty::Placeholder { universe: ty::UniverseIndex::from_usize(p.ui.counter), - name: ty::BoundRegion::BrAnon(p.idx as u32), + name: ty::BoundRegionKind::BrAnon(p.idx as u32), }) } chalk_ir::LifetimeData::Static => ty::RegionKind::ReStatic, @@ -615,7 +613,7 @@ impl<'tcx> LowerInto<'tcx, Option LowerInto<'tcx, chalk_ir::Binders>>> - for Binder<&'tcx ty::List>> + for &'tcx ty::List>> { fn lower_into( self, @@ -627,48 +625,53 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders>]> // This means that any variables that are escaping `self` need to be // shifted in by one so that they are still escaping. - let shifted_predicates = ty::fold::shift_vars(interner.tcx, self, 1); + let predicates = ty::fold::shift_vars(interner.tcx, self, 1); - let (predicates, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, shifted_predicates); let self_ty = interner.tcx.mk_ty(ty::Bound( // This is going to be wrapped in a binder ty::DebruijnIndex::from_usize(1), ty::BoundTy { var: ty::BoundVar::from_usize(0), kind: ty::BoundTyKind::Anon }, )); - let where_clauses = predicates.into_iter().map(|predicate| match predicate { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => { - chalk_ir::Binders::new( + let where_clauses = predicates.into_iter().map(|predicate| { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + match predicate { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => { + chalk_ir::Binders::new( + binders.clone(), + chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { + trait_id: chalk_ir::TraitId(def_id), + substitution: interner + .tcx + .mk_substs_trait(self_ty, substs) + .lower_into(interner), + }), + ) + } + ty::ExistentialPredicate::Projection(predicate) => chalk_ir::Binders::new( + binders.clone(), + chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { + alias: chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id: chalk_ir::AssocTypeId(predicate.item_def_id), + substitution: interner + .tcx + .mk_substs_trait(self_ty, predicate.substs) + .lower_into(interner), + }), + ty: predicate.ty.lower_into(interner), + }), + ), + ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new( binders.clone(), chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { trait_id: chalk_ir::TraitId(def_id), substitution: interner .tcx - .mk_substs_trait(self_ty, substs) + .mk_substs_trait(self_ty, &[]) .lower_into(interner), }), - ) + ), } - ty::ExistentialPredicate::Projection(predicate) => chalk_ir::Binders::new( - binders.clone(), - chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { - alias: chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { - associated_ty_id: chalk_ir::AssocTypeId(predicate.item_def_id), - substitution: interner - .tcx - .mk_substs_trait(self_ty, predicate.substs) - .lower_into(interner), - }), - ty: predicate.ty.lower_into(interner), - }), - ), - ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new( - binders.clone(), - chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { - trait_id: chalk_ir::TraitId(def_id), - substitution: interner.tcx.mk_substs_trait(self_ty, &[]).lower_into(interner), - }), - ), }); // Binder for the bound variable representing the concrete underlying type. @@ -800,7 +803,7 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound } /// To collect bound vars, we have to do two passes. In the first pass, we -/// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then +/// collect all `BoundRegionKind`s and `ty::Bound`s. In the second pass, we then /// replace `BrNamed` into `BrAnon`. The two separate passes are important, /// since we can only replace `BrNamed` with `BrAnon`s with indices *after* all /// "real" `BrAnon`s. @@ -888,14 +891,14 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow { match r { - ty::ReLateBound(index, br) if *index == self.binder_index => match br { - ty::BoundRegion::BrNamed(def_id, _name) => { - if self.named_parameters.iter().find(|d| *d == def_id).is_none() { - self.named_parameters.push(*def_id); + ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind { + ty::BoundRegionKind::BrNamed(def_id, _name) => { + if self.named_parameters.iter().find(|d| **d == def_id).is_none() { + self.named_parameters.push(def_id); } } - ty::BoundRegion::BrAnon(var) => match self.parameters.entry(*var) { + ty::BoundRegionKind::BrAnon(var) => match self.parameters.entry(var) { Entry::Vacant(entry) => { entry.insert(chalk_ir::VariableKind::Lifetime); } @@ -921,7 +924,7 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { } } -/// This is used to replace `BoundRegion::BrNamed` with `BoundRegion::BrAnon`. +/// This is used to replace `BoundRegionKind::BrNamed` with `BoundRegionKind::BrAnon`. /// Note: we assume that we will always have room for more bound vars. (i.e. we /// won't ever hit the `u32` limit in `BrAnon`s). struct NamedBoundVarSubstitutor<'a, 'tcx> { @@ -950,20 +953,16 @@ impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { match r { - ty::ReLateBound(index, br) if *index == self.binder_index => match br { - ty::BoundRegion::BrNamed(def_id, _name) => { - match self.named_parameters.get(def_id) { - Some(idx) => { - return self.tcx.mk_region(RegionKind::ReLateBound( - *index, - BoundRegion::BrAnon(*idx), - )); - } - None => panic!("Missing `BrNamed`."), + ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind { + ty::BrNamed(def_id, _name) => match self.named_parameters.get(&def_id) { + Some(idx) => { + let new_br = ty::BoundRegion { kind: ty::BrAnon(*idx) }; + return self.tcx.mk_region(RegionKind::ReLateBound(*index, new_br)); } - } + None => panic!("Missing `BrNamed`."), + }, ty::BrEnv => unimplemented!(), - ty::BoundRegion::BrAnon(_) => {} + ty::BrAnon(_) => {} }, _ => (), }; @@ -1039,17 +1038,15 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { // FIXME(chalk) - jackh726 - this currently isn't hit in any tests. // This covers any region variables in a goal, right? ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) { - Some(idx) => self.tcx.mk_region(RegionKind::ReLateBound( - self.binder_index, - BoundRegion::BrAnon(*idx), - )), + Some(idx) => { + let br = ty::BoundRegion { kind: ty::BrAnon(*idx) }; + self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br)) + } None => { let idx = self.named_regions.len() as u32; + let br = ty::BoundRegion { kind: ty::BrAnon(idx) }; self.named_regions.insert(_re.def_id, idx); - self.tcx.mk_region(RegionKind::ReLateBound( - self.binder_index, - BoundRegion::BrAnon(idx), - )) + self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br)) } }, @@ -1091,7 +1088,7 @@ impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow { match r { ty::RePlaceholder(p) if p.universe == self.universe_index => { - if let ty::BoundRegion::BrAnon(anon) = p.name { + if let ty::BoundRegionKind::BrAnon(anon) = p.name { self.next_anon_region_placeholder = self.next_anon_region_placeholder.max(anon); } } diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index b117e28875e7..bd2f87f70a2f 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -44,7 +44,7 @@ crate fn evaluate_goal<'tcx>( let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { universe: ty::UniverseIndex::ROOT, - name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder + 1), + name: ty::BoundRegionKind::BrAnon(placeholders_collector.next_anon_region_placeholder + 1), })); let mut params_substitutor = @@ -98,7 +98,7 @@ crate fn evaluate_goal<'tcx>( let mut solver = chalk_engine::solve::SLGSolver::new(32, None); let db = ChalkRustIrDatabase { interner, reempty_placeholder }; let solution = solver.solve(&db, &lowered_goal); - debug!(?obligation, ?solution, "evaluatate goal"); + debug!(?obligation, ?solution, "evaluate goal"); // Ideally, the code to convert *back* to rustc types would live close to // the code to convert *from* rustc types. Right now though, we don't diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml new file mode 100644 index 000000000000..d50451b7794c --- /dev/null +++ b/compiler/rustc_type_ir/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rustc_type_ir" +version = "0.0.0" +authors = ["The Rust Project Developers"] +edition = "2018" + +[lib] +doctest = false + +[dependencies] +bitflags = "1.2.1" +rustc_index = { path = "../rustc_index" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs new file mode 100644 index 000000000000..37abb4496ac3 --- /dev/null +++ b/compiler/rustc_type_ir/src/lib.rs @@ -0,0 +1,204 @@ +#![feature(never_type)] +#![feature(const_panic)] +#![feature(control_flow_enum)] + +#[macro_use] +extern crate bitflags; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + +bitflags! { + /// Flags that we track on types. These flags are propagated upwards + /// through the type during type construction, so that we can quickly check + /// whether the type has various kinds of types in it without recursing + /// over the type itself. + pub struct TypeFlags: u32 { + // Does this have parameters? Used to determine whether substitution is + // required. + /// Does this have `Param`? + const HAS_TY_PARAM = 1 << 0; + /// Does this have `ReEarlyBound`? + const HAS_RE_PARAM = 1 << 1; + /// Does this have `ConstKind::Param`? + const HAS_CT_PARAM = 1 << 2; + + const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_RE_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits; + + /// Does this have `Infer`? + const HAS_TY_INFER = 1 << 3; + /// Does this have `ReVar`? + const HAS_RE_INFER = 1 << 4; + /// Does this have `ConstKind::Infer`? + const HAS_CT_INFER = 1 << 5; + + /// Does this have inference variables? Used to determine whether + /// inference is required. + const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_RE_INFER.bits + | TypeFlags::HAS_CT_INFER.bits; + + /// Does this have `Placeholder`? + const HAS_TY_PLACEHOLDER = 1 << 6; + /// Does this have `RePlaceholder`? + const HAS_RE_PLACEHOLDER = 1 << 7; + /// Does this have `ConstKind::Placeholder`? + const HAS_CT_PLACEHOLDER = 1 << 8; + + /// `true` if there are "names" of regions and so forth + /// that are local to a particular fn/inferctxt + const HAS_FREE_LOCAL_REGIONS = 1 << 9; + + /// `true` if there are "names" of types and regions and so forth + /// that are local to a particular fn + const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits + | TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_CT_INFER.bits + | TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits + | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; + + /// Does this have `Projection`? + const HAS_TY_PROJECTION = 1 << 10; + /// Does this have `Opaque`? + const HAS_TY_OPAQUE = 1 << 11; + /// Does this have `ConstKind::Unevaluated`? + const HAS_CT_PROJECTION = 1 << 12; + + /// Could this type be normalized further? + const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits + | TypeFlags::HAS_TY_OPAQUE.bits + | TypeFlags::HAS_CT_PROJECTION.bits; + + /// Is an error type/const reachable? + const HAS_ERROR = 1 << 13; + + /// Does this have any region that "appears free" in the type? + /// Basically anything but `ReLateBound` and `ReErased`. + const HAS_FREE_REGIONS = 1 << 14; + + /// Does this have any `ReLateBound` regions? Used to check + /// if a global bound is safe to evaluate. + const HAS_RE_LATE_BOUND = 1 << 15; + + /// Does this have any `ReErased` regions? + const HAS_RE_ERASED = 1 << 16; + + /// Does this value have parameters/placeholders/inference variables which could be + /// replaced later, in a way that would change the results of `impl` specialization? + const STILL_FURTHER_SPECIALIZABLE = 1 << 17; + } +} + +rustc_index::newtype_index! { + /// A [De Bruijn index][dbi] is a standard means of representing + /// regions (and perhaps later types) in a higher-ranked setting. In + /// particular, imagine a type like this: + /// + /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) + /// ^ ^ | | | + /// | | | | | + /// | +------------+ 0 | | + /// | | | + /// +----------------------------------+ 1 | + /// | | + /// +----------------------------------------------+ 0 + /// + /// In this type, there are two binders (the outer fn and the inner + /// fn). We need to be able to determine, for any given region, which + /// fn type it is bound by, the inner or the outer one. There are + /// various ways you can do this, but a De Bruijn index is one of the + /// more convenient and has some nice properties. The basic idea is to + /// count the number of binders, inside out. Some examples should help + /// clarify what I mean. + /// + /// Let's start with the reference type `&'b isize` that is the first + /// argument to the inner function. This region `'b` is assigned a De + /// Bruijn index of 0, meaning "the innermost binder" (in this case, a + /// fn). The region `'a` that appears in the second argument type (`&'a + /// isize`) would then be assigned a De Bruijn index of 1, meaning "the + /// second-innermost binder". (These indices are written on the arrows + /// in the diagram). + /// + /// What is interesting is that De Bruijn index attached to a particular + /// variable will vary depending on where it appears. For example, + /// the final type `&'a char` also refers to the region `'a` declared on + /// the outermost fn. But this time, this reference is not nested within + /// any other binders (i.e., it is not an argument to the inner fn, but + /// rather the outer one). Therefore, in this case, it is assigned a + /// De Bruijn index of 0, because the innermost binder in that location + /// is the outer fn. + /// + /// [dbi]: https://en.wikipedia.org/wiki/De_Bruijn_index + pub struct DebruijnIndex { + DEBUG_FORMAT = "DebruijnIndex({})", + const INNERMOST = 0, + } +} + +impl DebruijnIndex { + /// Returns the resulting index when this value is moved into + /// `amount` number of new binders. So, e.g., if you had + /// + /// for<'a> fn(&'a x) + /// + /// and you wanted to change it to + /// + /// for<'a> fn(for<'b> fn(&'a x)) + /// + /// you would need to shift the index for `'a` into a new binder. + #[must_use] + pub fn shifted_in(self, amount: u32) -> DebruijnIndex { + DebruijnIndex::from_u32(self.as_u32() + amount) + } + + /// Update this index in place by shifting it "in" through + /// `amount` number of binders. + pub fn shift_in(&mut self, amount: u32) { + *self = self.shifted_in(amount); + } + + /// Returns the resulting index when this value is moved out from + /// `amount` number of new binders. + #[must_use] + pub fn shifted_out(self, amount: u32) -> DebruijnIndex { + DebruijnIndex::from_u32(self.as_u32() - amount) + } + + /// Update in place by shifting out from `amount` binders. + pub fn shift_out(&mut self, amount: u32) { + *self = self.shifted_out(amount); + } + + /// Adjusts any De Bruijn indices so as to make `to_binder` the + /// innermost binder. That is, if we have something bound at `to_binder`, + /// it will now be bound at INNERMOST. This is an appropriate thing to do + /// when moving a region out from inside binders: + /// + /// ``` + /// for<'a> fn(for<'b> for<'c> fn(&'a u32), _) + /// // Binder: D3 D2 D1 ^^ + /// ``` + /// + /// Here, the region `'a` would have the De Bruijn index D3, + /// because it is the bound 3 binders out. However, if we wanted + /// to refer to that region `'a` in the second argument (the `_`), + /// those two binders would not be in scope. In that case, we + /// might invoke `shift_out_to_binder(D3)`. This would adjust the + /// De Bruijn index of `'a` to D1 (the innermost binder). + /// + /// If we invoke `shift_out_to_binder` and the region is in fact + /// bound by one of the binders we are shifting out of, that is an + /// error (and should fail an assertion failure). + pub fn shifted_out_to_binder(self, to_binder: DebruijnIndex) -> Self { + self.shifted_out(to_binder.as_u32() - INNERMOST.as_u32()) + } +} + +impl HashStable for DebruijnIndex { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.as_u32().hash_stable(ctx, hasher); + } +} diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index b7e77f389f85..0feac036f002 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -526,18 +526,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generics: &ty::Generics, ) -> bool { let explicit = !seg.infer_args; - let impl_trait = - generics.params.iter().any(|param| match param.kind { - ty::GenericParamDefKind::Type { - synthetic: - Some( - hir::SyntheticTyParamKind::ImplTrait - | hir::SyntheticTyParamKind::FromAttr, - ), - .. - } => true, - _ => false, - }); + let impl_trait = generics.params.iter().any(|param| { + matches!(param.kind, ty::GenericParamDefKind::Type { + synthetic: + Some( + hir::SyntheticTyParamKind::ImplTrait + | hir::SyntheticTyParamKind::FromAttr, + ), + .. + }) + }); if explicit && impl_trait { let spans = seg diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 7888cb1b9f59..38d33e558664 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -196,11 +196,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Some(rl::Region::LateBound(debruijn, id, _)) => { let name = lifetime_name(id.expect_local()); - tcx.mk_region(ty::ReLateBound(debruijn, ty::BrNamed(id, name))) + let br = ty::BoundRegion { kind: ty::BrNamed(id, name) }; + tcx.mk_region(ty::ReLateBound(debruijn, br)) } Some(rl::Region::LateBoundAnon(debruijn, index)) => { - tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index))) + let br = ty::BoundRegion { kind: ty::BrAnon(index) }; + tcx.mk_region(ty::ReLateBound(debruijn, br)) } Some(rl::Region::EarlyBound(index, id, _)) => { @@ -837,9 +839,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .instantiate_lang_item_trait_ref( lang_item, span, hir_id, args, param_ty, bounds, ), - hir::GenericBound::Outlives(ref l) => { - bounds.region_bounds.push((self.ast_region_to_region(l, None), l.span)) - } + hir::GenericBound::Outlives(ref l) => bounds + .region_bounds + .push((ty::Binder::bind(self.ast_region_to_region(l, None)), l.span)), } } } @@ -1254,22 +1256,22 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }) }); - // Calling `skip_binder` is okay because the predicates are re-bound. - let regular_trait_predicates = existential_trait_refs - .map(|trait_ref| ty::ExistentialPredicate::Trait(trait_ref.skip_binder())); - let auto_trait_predicates = auto_traits - .into_iter() - .map(|trait_ref| ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())); + let regular_trait_predicates = existential_trait_refs.map(|trait_ref| { + trait_ref.map_bound(|trait_ref| ty::ExistentialPredicate::Trait(trait_ref)) + }); + let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| { + ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())) + }); let mut v = regular_trait_predicates .chain(auto_trait_predicates) .chain( existential_projections - .map(|x| ty::ExistentialPredicate::Projection(x.skip_binder())), + .map(|x| x.map_bound(|x| ty::ExistentialPredicate::Projection(x))), ) .collect::>(); - v.sort_by(|a, b| a.stable_cmp(tcx, b)); + v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); v.dedup(); - let existential_predicates = ty::Binder::bind(tcx.mk_existential_predicates(v.into_iter())); + let existential_predicates = tcx.mk_poly_existential_predicates(v.into_iter()); // Use explicitly-specified region bound. let region_bound = if !lifetime.is_elided() { @@ -2295,8 +2297,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn validate_late_bound_regions( &self, - constrained_regions: FxHashSet, - referenced_regions: FxHashSet, + constrained_regions: FxHashSet, + referenced_regions: FxHashSet, generate_err: impl Fn(&str) -> rustc_errors::DiagnosticBuilder<'tcx>, ) { for br in referenced_regions.difference(&constrained_regions) { @@ -2331,7 +2333,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn compute_object_lifetime_bound( &self, span: Span, - existential_predicates: ty::Binder<&'tcx ty::List>>, + existential_predicates: &'tcx ty::List>>, ) -> Option> // if None, use the default { let tcx = self.tcx(); diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_typeck/src/bounds.rs index 497754f20e4c..7ba90ad88193 100644 --- a/compiler/rustc_typeck/src/bounds.rs +++ b/compiler/rustc_typeck/src/bounds.rs @@ -26,7 +26,7 @@ pub struct Bounds<'tcx> { /// A list of region bounds on the (implicit) self type. So if you /// had `T: 'a + 'b` this might would be a list `['a, 'b]` (but /// the `T` is not explicitly included). - pub region_bounds: Vec<(ty::Region<'tcx>, Span)>, + pub region_bounds: Vec<(ty::Binder>, Span)>, /// A list of trait bounds. So if you had `T: Debug` this would be /// `T: Debug`. Note that the self-type is explicit here. @@ -68,8 +68,12 @@ impl<'tcx> Bounds<'tcx> { sized_predicate .into_iter() .chain(self.region_bounds.iter().map(|&(region_bound, span)| { - let outlives = ty::OutlivesPredicate(param_ty, region_bound); - (ty::Binder::bind(outlives).to_predicate(tcx), span) + ( + region_bound + .map_bound(|region_bound| ty::OutlivesPredicate(param_ty, region_bound)) + .to_predicate(tcx), + span, + ) })) .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 3a5eeb5381be..6467e0440793 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -31,7 +31,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => (false, false, false), }; - // Type check the descriminant and get its type. + // Type check the discriminant and get its type. let scrutinee_ty = if force_scrutinee_bool { // Here we want to ensure: // @@ -43,7 +43,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(60707): Consider removing hack with principled solution. self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {}) } else { - self.demand_scrutinee_type(arms, scrut) + self.demand_scrutinee_type(scrut, arms_contain_ref_bindings(arms), arms.is_empty()) }; // If there are no arms, that is a diverging match; a special case. @@ -98,7 +98,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.diverges.set(Diverges::Maybe); match g { hir::Guard::If(e) => { - self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {}) + self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {}); + } + hir::Guard::IfLet(pat, e) => { + let scrutinee_ty = self.demand_scrutinee_type( + e, + pat.contains_explicit_ref_binding(), + false, + ); + self.check_pat_top(&pat, scrutinee_ty, None, true); } }; } @@ -450,8 +458,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn demand_scrutinee_type( &self, - arms: &'tcx [hir::Arm<'tcx>], scrut: &'tcx hir::Expr<'tcx>, + contains_ref_bindings: Option, + no_arms: bool, ) -> Ty<'tcx> { // Not entirely obvious: if matches may create ref bindings, we want to // use the *precise* type of the scrutinee, *not* some supertype, as @@ -505,17 +514,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (once introduced) is populated by the time we get here. // // See #44848. - let contains_ref_bindings = arms - .iter() - .filter_map(|a| a.pat.contains_explicit_ref_binding()) - .max_by_key(|m| match *m { - hir::Mutability::Mut => 1, - hir::Mutability::Not => 0, - }); - if let Some(m) = contains_ref_bindings { self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m)) - } else if arms.is_empty() { + } else if no_arms { self.check_expr(scrut) } else { // ...but otherwise we want to use any supertype of the @@ -546,3 +547,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + +fn arms_contain_ref_bindings(arms: &'tcx [hir::Arm<'tcx>]) -> Option { + arms.iter().filter_map(|a| a.pat.contains_explicit_ref_binding()).max_by_key(|m| match *m { + hir::Mutability::Mut => 1, + hir::Mutability::Not => 0, + }) +} diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index ebfb401fcf3a..22e287320d84 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -389,7 +389,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // In that case, we check each argument against "error" in order to // set up all the node type bindings. ( - ty::Binder::bind(self.tcx.mk_fn_sig( + ty::Binder::dummy(self.tcx.mk_fn_sig( self.err_args(arg_exprs.len()).into_iter(), self.tcx.ty_error(), false, diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index ec7369fd3e8e..9c60e8933d4d 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -543,10 +543,9 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( if let Some(ty) = prohibit_opaque.break_value() { let is_async = match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin { - hir::OpaqueTyOrigin::AsyncFn => true, - _ => false, - }, + ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + matches!(origin, hir::OpaqueTyOrigin::AsyncFn) + } _ => unreachable!(), }; @@ -1321,10 +1320,7 @@ pub fn check_enum<'tcx>( } if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant { - let is_unit = |var: &hir::Variant<'_>| match var.data { - hir::VariantData::Unit(..) => true, - _ => false, - }; + let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..)); let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); let has_non_units = vs.iter().any(|var| !is_unit(var)); diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 8082a230216e..7470c1a76a94 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -24,7 +24,7 @@ use std::iter; struct ExpectedSig<'tcx> { /// Span that gave us this expectation, if we know that. cause_span: Option, - sig: ty::FnSig<'tcx>, + sig: ty::PolyFnSig<'tcx>, } struct ClosureSignatures<'tcx> { @@ -174,7 +174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Infer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid), ty::FnPtr(sig) => { - let expected_sig = ExpectedSig { cause_span: None, sig: sig.skip_binder() }; + let expected_sig = ExpectedSig { cause_span: None, sig }; (Some(expected_sig), Some(ty::ClosureKind::Fn)) } _ => (None, None), @@ -274,13 +274,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty); debug!("deduce_sig_from_projection: ret_param_ty={:?}", ret_param_ty); - let sig = self.tcx.mk_fn_sig( + let sig = projection.rebind(self.tcx.mk_fn_sig( input_tys.iter(), &ret_param_ty, false, hir::Unsafety::Normal, Abi::Rust, - ); + )); debug!("deduce_sig_from_projection: sig={:?}", sig); Some(ExpectedSig { cause_span, sig }) @@ -374,9 +374,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Watch out for some surprises and just ignore the // expectation if things don't see to match up with what we // expect. - if expected_sig.sig.c_variadic != decl.c_variadic { + if expected_sig.sig.c_variadic() != decl.c_variadic { return self.sig_of_closure_no_expectation(expr_def_id, decl, body); - } else if expected_sig.sig.inputs_and_output.len() != decl.inputs.len() + 1 { + } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 { return self.sig_of_closure_with_mismatched_number_of_arguments( expr_def_id, decl, @@ -388,14 +388,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Create a `PolyFnSig`. Note the oddity that late bound // regions appearing free in `expected_sig` are now bound up // in this binder we are creating. - assert!(!expected_sig.sig.has_vars_bound_above(ty::INNERMOST)); - let bound_sig = ty::Binder::bind(self.tcx.mk_fn_sig( - expected_sig.sig.inputs().iter().cloned(), - expected_sig.sig.output(), - decl.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - )); + assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST)); + let bound_sig = expected_sig.sig.map_bound(|sig| { + self.tcx.mk_fn_sig( + sig.inputs().iter().cloned(), + sig.output(), + sig.c_variadic, + hir::Unsafety::Normal, + Abi::RustCall, + ) + }); // `deduce_expectations_from_expected_type` introduces // late-bound lifetimes defined elsewhere, which we now @@ -428,6 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr_map_node = hir.get_if_local(expr_def_id).unwrap(); let expected_args: Vec<_> = expected_sig .sig + .skip_binder() .inputs() .iter() .map(|ty| ArgKind::from_expected_ty(ty, None)) @@ -500,7 +503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (supplied_ty, _) = self.infcx.replace_bound_vars_with_fresh_vars( hir_ty.span, LateBoundRegionConversionTime::FnCall, - ty::Binder::bind(supplied_ty), + supplied_sig.inputs().rebind(supplied_ty), ); // recreated from (*) above // Check that E' = S'. @@ -619,12 +622,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // where R is the return type we are expecting. This type `T` // will be our output. let output_ty = self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { - if let ty::PredicateAtom::Projection(proj_predicate) = - obligation.predicate.skip_binders() - { + let bound_predicate = obligation.predicate.bound_atom(); + if let ty::PredicateAtom::Projection(proj_predicate) = bound_predicate.skip_binder() { self.deduce_future_output_from_projection( obligation.cause.span, - ty::Binder::bind(proj_predicate), + bound_predicate.rebind(proj_predicate), ) } else { None @@ -704,7 +706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { astconv.ast_ty_to_ty(&output); } - let result = ty::Binder::bind(self.tcx.mk_fn_sig( + let result = ty::Binder::dummy(self.tcx.mk_fn_sig( supplied_arguments, self.tcx.ty_error(), decl.c_variadic, diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 20090d376060..bb324d0d8bc1 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -494,12 +494,11 @@ fn compare_self_type<'tcx>( ty::ImplContainer(_) => impl_trait_ref.self_ty(), ty::TraitContainer(_) => tcx.types.self_param, }; - let self_arg_ty = tcx.fn_sig(method.def_id).input(0).skip_binder(); + let self_arg_ty = tcx.fn_sig(method.def_id).input(0); let param_env = ty::ParamEnv::reveal_all(); tcx.infer_ctxt().enter(|infcx| { - let self_arg_ty = - tcx.liberate_late_bound_regions(method.def_id, ty::Binder::bind(self_arg_ty)); + let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty); let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok(); match ExplicitSelf::determine(self_arg_ty, can_eq_self) { ExplicitSelf::ByValue => "self".to_owned(), diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index aa4d57f7e1d9..2728e03171a7 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -360,10 +360,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn replace_prefix(&self, s: &str, old: &str, new: &str) -> Option { - s.strip_prefix(old).map(|stripped| new.to_string() + stripped) - } - /// This function is used to determine potential "simple" improvements or users' errors and /// provide them useful help. For example: /// @@ -394,6 +390,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } + let replace_prefix = |s: &str, old: &str, new: &str| { + s.strip_prefix(old).map(|stripped| new.to_string() + stripped) + }; + let is_struct_pat_shorthand_field = self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp); @@ -409,7 +409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { - if let Some(src) = self.replace_prefix(&src, "b\"", "\"") { + if let Some(src) = replace_prefix(&src, "b\"", "\"") { return Some(( sp, "consider removing the leading `b`", @@ -423,7 +423,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { - if let Some(src) = self.replace_prefix(&src, "\"", "b\"") { + if let Some(src) = replace_prefix(&src, "\"", "b\"") { return Some(( sp, "consider adding a leading `b`", @@ -583,23 +583,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Mutability::Mut => { let new_prefix = "&mut ".to_owned() + derefs; match mutbl_a { - hir::Mutability::Mut => self - .replace_prefix(&src, "&mut ", &new_prefix) - .map(|s| (s, Applicability::MachineApplicable)), - hir::Mutability::Not => self - .replace_prefix(&src, "&", &new_prefix) - .map(|s| (s, Applicability::Unspecified)), + hir::Mutability::Mut => { + replace_prefix(&src, "&mut ", &new_prefix) + .map(|s| (s, Applicability::MachineApplicable)) + } + hir::Mutability::Not => { + replace_prefix(&src, "&", &new_prefix) + .map(|s| (s, Applicability::Unspecified)) + } } } hir::Mutability::Not => { let new_prefix = "&".to_owned() + derefs; match mutbl_a { - hir::Mutability::Mut => self - .replace_prefix(&src, "&mut ", &new_prefix) - .map(|s| (s, Applicability::MachineApplicable)), - hir::Mutability::Not => self - .replace_prefix(&src, "&", &new_prefix) - .map(|s| (s, Applicability::MachineApplicable)), + hir::Mutability::Mut => { + replace_prefix(&src, "&mut ", &new_prefix) + .map(|s| (s, Applicability::MachineApplicable)) + } + hir::Mutability::Not => { + replace_prefix(&src, "&", &new_prefix) + .map(|s| (s, Applicability::MachineApplicable)) + } } } } { diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index ad675f1e3833..be19919c0ea1 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -267,15 +267,13 @@ crate fn check_drop_obligations<'a, 'tcx>( ty: Ty<'tcx>, span: Span, body_id: hir::HirId, -) -> Result<(), ErrorReported> { +) { debug!("check_drop_obligations typ: {:?}", ty); let cause = &ObligationCause::misc(span, body_id); let infer_ok = rcx.infcx.at(cause, rcx.fcx.param_env).dropck_outlives(ty); debug!("dropck_outlives = {:#?}", infer_ok); rcx.fcx.register_infer_ok_obligations(infer_ok); - - Ok(()) } // This is an implementation of the TypeRelation trait with the diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index b8a2a4779d98..93eb2cfc72a8 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -883,7 +883,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) } Err(error) => { - if segment.ident.name != kw::Invalid { + if segment.ident.name != kw::Empty { self.report_extended_method_error(segment, span, args, rcvr_t, error); } Err(()) @@ -1248,7 +1248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if no_accessible_remaining_fields { self.report_no_accessible_fields(adt_ty, span); } else { - self.report_missing_field(adt_ty, span, remaining_fields); + self.report_missing_fields(adt_ty, span, remaining_fields); } } @@ -1279,7 +1279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// error: aborting due to previous error /// ``` - fn report_missing_field( + fn report_missing_fields( &self, adt_ty: Ty<'tcx>, span: Span, @@ -1547,7 +1547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return field_ty; } - if field.name == kw::Invalid { + if field.name == kw::Empty { } else if self.method_exists(field, expr_t, expr.hir_id, true) { self.ban_take_value_of_method(expr, expr_t, field); } else if !expr_t.is_primitive_ty() { diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index e1a2f593b8d9..2a8b77da44fc 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -764,12 +764,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .pending_obligations() .into_iter() .filter_map(move |obligation| { - match obligation.predicate.skip_binders() { + let bound_predicate = obligation.predicate.bound_atom(); + match bound_predicate.skip_binder() { ty::PredicateAtom::Projection(data) => { - Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation)) + Some((bound_predicate.rebind(data).to_poly_trait_ref(self.tcx), obligation)) } ty::PredicateAtom::Trait(data, _) => { - Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation)) + Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation)) } ty::PredicateAtom::Subtype(..) => None, ty::PredicateAtom::RegionOutlives(..) => None, @@ -913,7 +914,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), _ => Err(ErrorReported), }; - if item_name.name != kw::Invalid { + if item_name.name != kw::Empty { if let Some(mut e) = self.report_method_error( span, ty, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 333bda00dbe8..3e60924d6fcf 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -325,10 +325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); } - let is_closure = match arg.kind { - ExprKind::Closure(..) => true, - _ => false, - }; + let is_closure = matches!(arg.kind, ExprKind::Closure(..)); if is_closure != check_closures { continue; diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index 602b79802b33..91708465b3f6 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -186,7 +186,8 @@ pub fn resolve_interior<'a, 'tcx>( // which means that none of the regions inside relate to any other, even if // typeck had previously found constraints that would cause them to be related. let folded = fcx.tcx.fold_regions(erased, &mut false, |_, current_depth| { - let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter))); + let br = ty::BoundRegion { kind: ty::BrAnon(counter) }; + let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); counter += 1; r }); @@ -204,7 +205,8 @@ pub fn resolve_interior<'a, 'tcx>( let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind(type_list)); // Store the generator types and spans into the typeck results for this generator. - visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types = type_causes; + visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types = + ty::Binder::bind(type_causes); debug!( "types in generator after region replacement {:?}, span = {:?}", @@ -246,6 +248,10 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { Guard::If(ref e) => { self.visit_expr(e); } + Guard::IfLet(ref pat, ref e) => { + self.visit_pat(pat); + self.visit_expr(e); + } } let mut scope_var_ids = diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index e2712a303399..673dec6c7f9a 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -12,7 +12,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_target::spec::abi::Abi; @@ -23,10 +23,7 @@ fn equate_intrinsic_type<'tcx>( it: &hir::ForeignItem<'_>, def_id: DefId, n_tps: usize, - abi: Abi, - safety: hir::Unsafety, - inputs: Vec>, - output: Ty<'tcx>, + sig: ty::PolyFnSig<'tcx>, ) { match it.kind { hir::ForeignItemKind::Fn(..) => {} @@ -53,13 +50,7 @@ fn equate_intrinsic_type<'tcx>( return; } - let fty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - inputs.into_iter(), - output, - false, - safety, - abi, - ))); + let fty = tcx.mk_fn_ptr(sig); let cause = ObligationCause::new(it.span, it.hir_id, ObligationCauseCode::IntrinsicType); require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(def_id)), fty); } @@ -101,6 +92,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::rustc_peek | sym::maxnumf64 | sym::type_name + | sym::forget | sym::variant_count => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, } @@ -116,13 +108,12 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let mk_va_list_ty = |mutbl| { tcx.lang_items().va_list().map(|did| { - let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))); - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let region = tcx + .mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrAnon(0) })); + let env_region = + tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrEnv })); let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]); - ( - tcx.mk_ref(tcx.mk_region(env_region), ty::TypeAndMut { ty: va_list_ty, mutbl }), - va_list_ty, - ) + (tcx.mk_ref(env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty) }) }; @@ -320,12 +311,12 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + let br = ty::BoundRegion { kind: ty::BrAnon(0) }; ( 1, - vec![tcx.mk_imm_ref( - tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))), - param(0), - )], + vec![ + tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)), param(0)), + ], tcx.mk_projection(discriminant_def_id, tcx.mk_substs([param(0).into()].iter())), ) } @@ -380,7 +371,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { }; (n_tps, inputs, output, unsafety) }; - equate_intrinsic_type(tcx, it, def_id, n_tps, Abi::RustIntrinsic, unsafety, inputs, output) + let sig = tcx.mk_fn_sig(inputs.into_iter(), output, false, unsafety, Abi::RustIntrinsic); + let sig = ty::Binder::bind(sig); + equate_intrinsic_type(tcx, it, def_id, n_tps, sig) } /// Type-check `extern "platform-intrinsic" { ... }` functions. @@ -466,14 +459,13 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) } }; - equate_intrinsic_type( - tcx, - it, - def_id, - n_tps, - Abi::PlatformIntrinsic, - hir::Unsafety::Unsafe, - inputs, + let sig = tcx.mk_fn_sig( + inputs.into_iter(), output, - ) + false, + hir::Unsafety::Unsafe, + Abi::PlatformIntrinsic, + ); + let sig = ty::Binder::dummy(sig); + equate_intrinsic_type(tcx, it, def_id, n_tps, sig) } diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 891dd8b2f022..d4631c465a3a 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -423,9 +423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { probe_cx.assemble_inherent_candidates(); match scope { ProbeScope::TraitsInScope => { - probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)? + probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id) } - ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits()?, + ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(), }; op(probe_cx) }) @@ -866,35 +866,29 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - fn assemble_extension_candidates_for_traits_in_scope( - &mut self, - expr_hir_id: hir::HirId, - ) -> Result<(), MethodError<'tcx>> { + fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_hir_id: hir::HirId) { let mut duplicates = FxHashSet::default(); let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id); if let Some(applicable_traits) = opt_applicable_traits { for trait_candidate in applicable_traits.iter() { let trait_did = trait_candidate.def_id; if duplicates.insert(trait_did) { - let result = self.assemble_extension_candidates_for_trait( + self.assemble_extension_candidates_for_trait( &trait_candidate.import_ids, trait_did, ); - result?; } } } - Ok(()) } - fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(), MethodError<'tcx>> { + fn assemble_extension_candidates_for_all_traits(&mut self) { let mut duplicates = FxHashSet::default(); for trait_info in suggest::all_traits(self.tcx) { if duplicates.insert(trait_info.def_id) { - self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id)?; + self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id); } } - Ok(()) } pub fn matches_return_type( @@ -932,7 +926,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &mut self, import_ids: &SmallVec<[LocalDefId; 1]>, trait_def_id: DefId, - ) -> Result<(), MethodError<'tcx>> { + ) { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); let trait_substs = self.fresh_item_substs(trait_def_id); let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs); @@ -980,7 +974,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ); } } - Ok(()) } fn candidate_method_names(&self) -> Vec { @@ -1027,7 +1020,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let span = self.span; let tcx = self.tcx; - self.assemble_extension_candidates_for_all_traits()?; + self.assemble_extension_candidates_for_all_traits(); let out_of_scope_traits = match self.pick_core() { Some(Ok(p)) => vec![p.item.container.id()], diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 7ed2933c08bb..3bf41981ef6b 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::hir::map as hir_map; +use rustc_middle::ty::fast_reject::simplify_type; use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::{ self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, @@ -619,8 +620,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(def, _) => bound_spans.push((def_span(def.did), msg)), // Point at the trait object that couldn't satisfy the bound. ty::Dynamic(preds, _) => { - for pred in preds.skip_binder() { - match pred { + for pred in preds.iter() { + match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { bound_spans.push((def_span(tr.def_id), msg.clone())) } @@ -673,9 +674,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .filter_map(|(pred, parent_pred)| { format_pred(*pred).map(|(p, self_ty)| match parent_pred { - None => format!("`{}`", p), + None => format!("`{}`", &p), Some(parent_pred) => match format_pred(*parent_pred) { - None => format!("`{}`", p), + None => format!("`{}`", &p), Some((parent_p, _)) => { collect_type_param_suggestions(self_ty, parent_pred, &p); format!("`{}`\nwhich is required by `{}`", p, parent_p) @@ -1074,19 +1075,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { "items from traits can only be used if the trait is implemented and in scope" }); + let candidates_len = candidates.len(); let message = |action| { format!( "the following {traits_define} an item `{name}`, perhaps you need to {action} \ {one_of_them}:", traits_define = - if candidates.len() == 1 { "trait defines" } else { "traits define" }, + if candidates_len == 1 { "trait defines" } else { "traits define" }, action = action, - one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, + one_of_them = if candidates_len == 1 { "it" } else { "one of them" }, name = item_name, ) }; // Obtain the span for `param` and use it for a structured suggestion. - let mut suggested = false; if let (Some(ref param), Some(ref table)) = (param_type, self.in_progress_typeck_results) { @@ -1147,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - suggested = true; + return; } Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., bounds, _), @@ -1167,45 +1168,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }), Applicability::MaybeIncorrect, ); - suggested = true; + return; } _ => {} } } } - if !suggested { - let action = if let Some(param) = param_type { - format!("restrict type parameter `{}` with", param) - } else { - // FIXME: it might only need to be imported into scope, not implemented. - "implement".to_string() - }; - let mut use_note = true; - if let [trait_info] = &candidates[..] { - if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) { - err.span_note( - self.tcx.sess.source_map().guess_head_span(span), - &format!( - "`{}` defines an item `{}`, perhaps you need to {} it", - self.tcx.def_path_str(trait_info.def_id), - item_name, - action - ), - ); - use_note = false + let (potential_candidates, explicitly_negative) = if param_type.is_some() { + // FIXME: Even though negative bounds are not implemented, we could maybe handle + // cases where a positive bound implies a negative impl. + (candidates, Vec::new()) + } else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, true) { + let mut potential_candidates = Vec::new(); + let mut explicitly_negative = Vec::new(); + for candidate in candidates { + // Check if there's a negative impl of `candidate` for `rcvr_ty` + if self + .tcx + .all_impls(candidate.def_id) + .filter(|imp_did| { + self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative + }) + .any(|imp_did| { + let imp = self.tcx.impl_trait_ref(imp_did).unwrap(); + let imp_simp = simplify_type(self.tcx, imp.self_ty(), true); + imp_simp.map(|s| s == simp_rcvr_ty).unwrap_or(false) + }) + { + explicitly_negative.push(candidate); + } else { + potential_candidates.push(candidate); } } - if use_note { + (potential_candidates, explicitly_negative) + } else { + // We don't know enough about `recv_ty` to make proper suggestions. + (candidates, Vec::new()) + }; + + let action = if let Some(param) = param_type { + format!("restrict type parameter `{}` with", param) + } else { + // FIXME: it might only need to be imported into scope, not implemented. + "implement".to_string() + }; + match &potential_candidates[..] { + [] => {} + [trait_info] if trait_info.def_id.is_local() => { + let span = self.tcx.hir().span_if_local(trait_info.def_id).unwrap(); + err.span_note( + self.tcx.sess.source_map().guess_head_span(span), + &format!( + "`{}` defines an item `{}`, perhaps you need to {} it", + self.tcx.def_path_str(trait_info.def_id), + item_name, + action + ), + ); + } + trait_infos => { let mut msg = message(action); - for (i, trait_info) in candidates.iter().enumerate() { + for (i, trait_info) in trait_infos.iter().enumerate() { msg.push_str(&format!( "\ncandidate #{}: `{}`", i + 1, self.tcx.def_path_str(trait_info.def_id), )); } - err.note(&msg[..]); + err.note(&msg); + } + } + match &explicitly_negative[..] { + [] => {} + [trait_info] => { + let msg = format!( + "the trait `{}` defines an item `{}`, but is explicitely unimplemented", + self.tcx.def_path_str(trait_info.def_id), + item_name + ); + err.note(&msg); + } + trait_infos => { + let mut msg = format!( + "the following traits define an item `{}`, but are explicitely unimplemented:", + item_name + ); + for trait_info in trait_infos { + msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id))); + } + err.note(&msg); } } } diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index d27a68ccf1bd..8177b363a5a5 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -270,7 +270,7 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option { /// If this `DefId` is a "primary tables entry", returns /// `Some((body_id, header, decl))` with information about -/// it's body-id, fn-header and fn-decl (if any). Otherwise, +/// its body-id, fn-header and fn-decl (if any). Otherwise, /// returns `None`. /// /// If this function returns `Some`, then `typeck_results(def_id)` will diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 854bc70108f6..9ab056c0d74b 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -503,8 +503,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !self.tcx.has_typeck_results(def_id) { return false; } - // We're emitting a suggestion, so we can just ignore regions - let fn_sig = self.tcx.fn_sig(def_id).skip_binder(); + // FIXME: Instead of exiting early when encountering bound vars in + // the function signature, consider keeping the binder here and + // propagating it downwards. + let fn_sig = if let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() { + fn_sig + } else { + return false; + }; let other_ty = if let FnDef(def_id, _) = *other_ty.kind() { if !self.tcx.has_typeck_results(def_id) { diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index b8b98cef7637..88e8dd3cb129 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -325,7 +325,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { pat.each_binding(|_, hir_id, span, _| { let typ = self.resolve_node_type(hir_id); let body_id = self.body_id; - let _ = dropck::check_drop_obligations(self, typ, span, body_id); + dropck::check_drop_obligations(self, typ, span, body_id); }) } } @@ -354,10 +354,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { hir_id: hir::HirId, ) { assert!( - match fk { - intravisit::FnKind::Closure(..) => true, - _ => false, - }, + matches!(fk, intravisit::FnKind::Closure(..)), "visit_fn invoked for something other than a closure" ); @@ -491,7 +488,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { if place_with_id.place.projections.is_empty() { let typ = self.resolve_type(place_with_id.place.ty()); let body_id = self.body_id; - let _ = dropck::check_drop_obligations(self, typ, span, body_id); + dropck::check_drop_obligations(self, typ, span, body_id); } } } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 019fa78fb1e0..e8cbefd44ee6 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -202,7 +202,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // inference algorithm will reject it). // Equate the type variables for the upvars with the actual types. - let final_upvar_tys = self.final_upvar_tys(closure_hir_id); + let final_upvar_tys = self.final_upvar_tys(closure_def_id); debug!( "analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}", closure_hir_id, substs, final_upvar_tys @@ -222,36 +222,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Returns a list of `Ty`s for each upvar. - fn final_upvar_tys(&self, closure_id: hir::HirId) -> Vec> { + fn final_upvar_tys(&self, closure_id: DefId) -> Vec> { // Presently an unboxed closure type cannot "escape" out of a // function, so we will only encounter ones that originated in the // local crate or were inlined into it along with some function. // This may change if abstract return types of some sort are // implemented. let tcx = self.tcx; - let closure_def_id = tcx.hir().local_def_id(closure_id); self.typeck_results .borrow() - .closure_captures - .get(&closure_def_id.to_def_id()) - .iter() - .flat_map(|upvars| { - upvars.iter().map(|(&var_hir_id, _)| { - let upvar_ty = self.node_ty(var_hir_id); - let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); - let capture = self.typeck_results.borrow().upvar_capture(upvar_id); + .closure_min_captures_flattened(closure_id) + .map(|captured_place| { + let upvar_ty = captured_place.place.ty(); + let capture = captured_place.info.capture_kind; - debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture); + debug!( + "place={:?} upvar_ty={:?} capture={:?}", + captured_place.place, upvar_ty, capture + ); - match capture { - ty::UpvarCapture::ByValue(_) => upvar_ty, - ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref( - borrow.region, - ty::TypeAndMut { ty: upvar_ty, mutbl: borrow.kind.to_mutbl_lossy() }, - ), - } - }) + match capture { + ty::UpvarCapture::ByValue(_) => upvar_ty, + ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref( + borrow.region, + ty::TypeAndMut { ty: upvar_ty, mutbl: borrow.kind.to_mutbl_lossy() }, + ), + } }) .collect() } @@ -297,17 +294,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_captures.insert(*var_hir_id, upvar_id); - let new_capture_kind = if let Some(capture_kind) = - upvar_capture_map.get(&upvar_id) - { - // upvar_capture_map only stores the UpvarCapture (CaptureKind), - // so we create a fake capture info with no expression. - let fake_capture_info = - ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; - determine_capture_info(fake_capture_info, capture_info).capture_kind - } else { - capture_info.capture_kind - }; + let new_capture_kind = + if let Some(capture_kind) = upvar_capture_map.get(&upvar_id) { + // upvar_capture_map only stores the UpvarCapture (CaptureKind), + // so we create a fake capture info with no expression. + let fake_capture_info = + ty::CaptureInfo { expr_id: None, capture_kind: *capture_kind }; + determine_capture_info(fake_capture_info, capture_info).capture_kind + } else { + capture_info.capture_kind + }; upvar_capture_map.insert(upvar_id, new_capture_kind); } } @@ -394,7 +390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { - let min_cap_list = vec![ty::CapturedPlace { place: place, info: capture_info }]; + let min_cap_list = vec![ty::CapturedPlace { place, info: capture_info }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; } diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index c09f8cce5b44..2ae9ded3fa0c 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -51,7 +51,7 @@ impl<'tcx> CheckWfFcxBuilder<'tcx> { let fcx = FnCtxt::new(&inh, param_env, id); if !inh.tcx.features().trivial_bounds { // As predicates are cached rather than obligations, this - // needsto be called first so that they are checked with an + // needs to be called first so that they are checked with an // empty `param_env`. check_false_global_bounds(&fcx, span, id); } @@ -293,7 +293,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { let err_ty_str; let mut is_ptr = true; - let err = if tcx.features().min_const_generics { + let err = if tcx.features().const_generics { + match ty.peel_refs().kind() { + ty::FnPtr(_) => Some("function pointers"), + ty::RawPtr(_) => Some("raw pointers"), + _ => None, + } + } else { match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None, ty::FnPtr(_) => Some("function pointers"), @@ -304,12 +310,6 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { Some(err_ty_str.as_str()) } } - } else { - match ty.peel_refs().kind() { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - } }; if let Some(unsupported_type) = err { if is_ptr { diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 68b85a4da34f..7c9cfe69fc94 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -55,6 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => (), } wbcx.visit_body(body); + wbcx.visit_min_capture_map(); wbcx.visit_upvar_capture_map(); wbcx.visit_closures(); wbcx.visit_liberated_fn_sigs(); @@ -331,6 +332,37 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { } impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { + fn visit_min_capture_map(&mut self) { + let mut min_captures_wb = ty::MinCaptureInformationMap::with_capacity_and_hasher( + self.fcx.typeck_results.borrow().closure_min_captures.len(), + Default::default(), + ); + for (closure_def_id, root_min_captures) in + self.fcx.typeck_results.borrow().closure_min_captures.iter() + { + let mut root_var_map_wb = ty::RootVariableMinCaptureList::with_capacity_and_hasher( + root_min_captures.len(), + Default::default(), + ); + for (var_hir_id, min_list) in root_min_captures.iter() { + let min_list_wb = min_list + .iter() + .map(|captured_place| { + let locatable = captured_place.info.expr_id.unwrap_or( + self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local()), + ); + + self.resolve(captured_place.clone(), &locatable) + }) + .collect(); + root_var_map_wb.insert(*var_hir_id, min_list_wb); + } + min_captures_wb.insert(*closure_def_id, root_var_map_wb); + } + + self.typeck_results.closure_min_captures = min_captures_wb; + } + fn visit_upvar_capture_map(&mut self) { for (upvar_id, upvar_capture) in self.fcx.typeck_results.borrow().upvar_capture_map.iter() { let new_upvar_capture = match *upvar_capture { diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index dd90724e93fe..50d886743281 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -1,10 +1,13 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::Symbol; use rustc_trait_selection::traits::{self, SkipLeakCheck}; use smallvec::SmallVec; +use std::collections::hash_map::Entry; pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) { assert_eq!(crate_num, LOCAL_CRATE); @@ -33,12 +36,9 @@ impl InherentOverlapChecker<'tcx> { } for item1 in impl_items1.in_definition_order() { - let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).any(|item2| { - // Symbols and namespace match, compare hygienically. - item1.kind.namespace() == item2.kind.namespace() - && item1.ident.normalize_to_macros_2_0() - == item2.ident.normalize_to_macros_2_0() - }); + let collision = impl_items2 + .filter_by_name_unhygienic(item1.ident.name) + .any(|item2| self.compare_hygienically(item1, item2)); if collision { return true; @@ -48,6 +48,12 @@ impl InherentOverlapChecker<'tcx> { false } + fn compare_hygienically(&self, item1: &ty::AssocItem, item2: &ty::AssocItem) -> bool { + // Symbols and namespace match, compare hygienically. + item1.kind.namespace() == item2.kind.namespace() + && item1.ident.normalize_to_macros_2_0() == item2.ident.normalize_to_macros_2_0() + } + fn check_for_common_items_in_impls( &self, impl1: DefId, @@ -58,12 +64,9 @@ impl InherentOverlapChecker<'tcx> { let impl_items2 = self.tcx.associated_items(impl2); for item1 in impl_items1.in_definition_order() { - let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).find(|item2| { - // Symbols and namespace match, compare hygienically. - item1.kind.namespace() == item2.kind.namespace() - && item1.ident.normalize_to_macros_2_0() - == item2.ident.normalize_to_macros_2_0() - }); + let collision = impl_items2 + .filter_by_name_unhygienic(item1.ident.name) + .find(|item2| self.compare_hygienically(item1, item2)); if let Some(item2) = collision { let name = item1.ident.normalize_to_macros_2_0(); @@ -134,10 +137,150 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> { .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id))) .collect::>(); - for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { - for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { - if self.impls_have_common_items(impl_items1, impl_items2) { - self.check_for_overlapping_inherent_impls(impl1_def_id, impl2_def_id); + // Perform a O(n^2) algorithm for small n, + // otherwise switch to an allocating algorithm with + // faster asymptotic runtime. + const ALLOCATING_ALGO_THRESHOLD: usize = 500; + if impls.len() < ALLOCATING_ALGO_THRESHOLD { + for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { + for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { + if self.impls_have_common_items(impl_items1, impl_items2) { + self.check_for_overlapping_inherent_impls( + impl1_def_id, + impl2_def_id, + ); + } + } + } + } else { + // Build a set of connected regions of impl blocks. + // Two impl blocks are regarded as connected if they share + // an item with the same unhygienic identifier. + // After we have assembled the connected regions, + // run the O(n^2) algorithm on each connected region. + // This is advantageous to running the algorithm over the + // entire graph when there are many connected regions. + + struct ConnectedRegion { + idents: SmallVec<[Symbol; 8]>, + impl_blocks: FxHashSet, + } + // Highest connected region id + let mut highest_region_id = 0; + let mut connected_region_ids = FxHashMap::default(); + let mut connected_regions = FxHashMap::default(); + + for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() { + if impl_items.len() == 0 { + continue; + } + // First obtain a list of existing connected region ids + let mut idents_to_add = SmallVec::<[Symbol; 8]>::new(); + let ids = impl_items + .in_definition_order() + .filter_map(|item| { + let entry = connected_region_ids.entry(item.ident.name); + if let Entry::Occupied(e) = &entry { + Some(*e.get()) + } else { + idents_to_add.push(item.ident.name); + None + } + }) + .collect::>(); + match ids.len() { + 0 | 1 => { + let id_to_set = if ids.len() == 0 { + // Create a new connected region + let region = ConnectedRegion { + idents: idents_to_add, + impl_blocks: std::iter::once(i).collect(), + }; + connected_regions.insert(highest_region_id, region); + (highest_region_id, highest_region_id += 1).0 + } else { + // Take the only id inside the list + let id_to_set = *ids.iter().next().unwrap(); + let region = connected_regions.get_mut(&id_to_set).unwrap(); + region.impl_blocks.insert(i); + region.idents.extend_from_slice(&idents_to_add); + id_to_set + }; + let (_id, region) = connected_regions.iter().next().unwrap(); + // Update the connected region ids + for ident in region.idents.iter() { + connected_region_ids.insert(*ident, id_to_set); + } + } + _ => { + // We have multiple connected regions to merge. + // In the worst case this might add impl blocks + // one by one and can thus be O(n^2) in the size + // of the resulting final connected region, but + // this is no issue as the final step to check + // for overlaps runs in O(n^2) as well. + + // Take the smallest id from the list + let id_to_set = *ids.iter().min().unwrap(); + + // Sort the id list so that the algorithm is deterministic + let mut ids = ids.into_iter().collect::>(); + ids.sort(); + + let mut region = connected_regions.remove(&id_to_set).unwrap(); + region.idents.extend_from_slice(&idents_to_add); + region.impl_blocks.insert(i); + + for &id in ids.iter() { + if id == id_to_set { + continue; + } + let r = connected_regions.remove(&id).unwrap(); + // Update the connected region ids + for ident in r.idents.iter() { + connected_region_ids.insert(*ident, id_to_set); + } + region.idents.extend_from_slice(&r.idents); + region.impl_blocks.extend(r.impl_blocks); + } + connected_regions.insert(id_to_set, region); + } + } + } + + debug!( + "churning through {} components (sum={}, avg={}, var={}, max={})", + connected_regions.len(), + impls.len(), + impls.len() / connected_regions.len(), + { + let avg = impls.len() / connected_regions.len(); + let s = connected_regions + .iter() + .map(|r| r.1.impl_blocks.len() as isize - avg as isize) + .map(|v| v.abs() as usize) + .sum::(); + s / connected_regions.len() + }, + connected_regions.iter().map(|r| r.1.impl_blocks.len()).max().unwrap() + ); + // List of connected regions is built. Now, run the overlap check + // for each pair of impl blocks in the same connected region. + for (_id, region) in connected_regions.into_iter() { + let mut impl_blocks = + region.impl_blocks.into_iter().collect::>(); + impl_blocks.sort(); + for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { + let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; + for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { + let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; + if self.impls_have_common_items(impl_items1, impl_items2) { + self.check_for_overlapping_inherent_impls( + impl1_def_id, + impl2_def_id, + ); + } + } } } } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 0ff10abb60a3..3e40f5ba28a7 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -156,10 +156,10 @@ crate fn placeholder_type_error( if let Some(span) = span { sugg.push((span, format!("<{}>", type_name))); } - } else if let Some(arg) = generics.iter().find(|arg| match arg.name { - hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true, - _ => false, - }) { + } else if let Some(arg) = generics + .iter() + .find(|arg| matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. }))) + { // Account for `_` already present in cases like `struct S<_>(_);` and suggest // `struct S(T);` instead of `struct S<_, T>(T);`. sugg.push((arg.span, (*type_name).to_string())); @@ -359,8 +359,8 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { self.tcx().sess, span, E0212, - "cannot extract an associated type from a higher-ranked trait bound \ - in this context" + "cannot use the associated type of a trait \ + with uninferred generic parameters" ); match self.node() { @@ -461,7 +461,7 @@ fn get_new_lifetime_name<'tcx>( .collect_referenced_late_bound_regions(&poly_trait_ref) .into_iter() .filter_map(|lt| { - if let ty::BoundRegion::BrNamed(_, name) = lt { + if let ty::BoundRegionKind::BrNamed(_, name) = lt { Some(name.as_str().to_string()) } else { None @@ -1260,7 +1260,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // used with const generics, e.g. `Foo<{N+1}>`, can work at all. // // Note that we do not supply the parent generics when using - // `feature(min_const_generics)`. + // `min_const_generics`. Some(parent_def_id.to_def_id()) } else { let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); @@ -1369,7 +1369,11 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { generics.parent_count + generics.params.len() }); - let mut params: Vec<_> = opt_self.into_iter().collect(); + let mut params: Vec<_> = Vec::with_capacity(ast_generics.params.len() + has_self as usize); + + if let Some(opt_self) = opt_self { + params.push(opt_self); + } let early_lifetimes = early_bound_lifetimes_from_generics(tcx, ast_generics); params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef { @@ -1540,12 +1544,27 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { let mut diag = bad_placeholder_type(tcx, visitor.0); let ret_ty = fn_sig.output(); if ret_ty != tcx.ty_error() { - diag.span_suggestion( - ty.span, - "replace with the correct return type", - ret_ty.to_string(), - Applicability::MaybeIncorrect, - ); + if !ret_ty.is_closure() { + let ret_ty_str = match ret_ty.kind() { + // Suggest a function pointer return type instead of a unique function definition + // (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid + // syntax) + ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(), + _ => ret_ty.to_string(), + }; + diag.span_suggestion( + ty.span, + "replace with the correct return type", + ret_ty_str, + Applicability::MaybeIncorrect, + ); + } else { + // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds + // to prevent the user from getting a papercut while trying to use the unique closure + // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`). + diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); + diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html"); + } } diag.emit(); ty::Binder::bind(fn_sig) @@ -1748,8 +1767,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty(); // We use an `IndexSet` to preserves order of insertion. - // Preserving the order of insertion is important here so as not to break - // compile-fail UI tests. + // Preserving the order of insertion is important here so as not to break UI tests. let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default(); let ast_generics = match node { @@ -1919,10 +1937,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } else { let span = bound_pred.bounded_ty.span; let re_root_empty = tcx.lifetimes.re_root_empty; - let predicate = ty::OutlivesPredicate(ty, re_root_empty); + let predicate = ty::Binder::bind(ty::PredicateAtom::TypeOutlives( + ty::OutlivesPredicate(ty, re_root_empty), + )); predicates.insert(( - ty::PredicateAtom::TypeOutlives(predicate) - .potentially_quantified(tcx, ty::PredicateKind::ForAll), + predicate.potentially_quantified(tcx, ty::PredicateKind::ForAll), span, )); } @@ -1965,8 +1984,10 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP &hir::GenericBound::Outlives(ref lifetime) => { let region = AstConv::ast_region_to_region(&icx, lifetime, None); predicates.insert(( - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, region)) - .potentially_quantified(tcx, ty::PredicateKind::ForAll), + ty::Binder::bind(ty::PredicateAtom::TypeOutlives( + ty::OutlivesPredicate(ty, region), + )) + .potentially_quantified(tcx, ty::PredicateKind::ForAll), lifetime.span, )); } @@ -1983,9 +2004,10 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } _ => bug!(), }; - let pred = ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r1, r2)); + let pred = ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r1, r2)) + .to_predicate(icx.tcx); - (pred.potentially_quantified(icx.tcx, ty::PredicateKind::ForAll), span) + (pred, span) })) } @@ -2141,13 +2163,8 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat // * It must be an associated type for this trait (*not* a // supertrait). if let ty::Projection(projection) = ty.kind() { - if projection.substs == trait_identity_substs + projection.substs == trait_identity_substs && tcx.associated_item(projection.item_def_id).container.id() == def_id - { - true - } else { - false - } } else { false } @@ -2239,7 +2256,7 @@ fn predicates_from_bound<'tcx>( hir::GenericBound::Outlives(ref lifetime) => { let region = astconv.ast_region_to_region(lifetime, None); let pred = ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(param_ty, region)) - .potentially_quantified(astconv.tcx(), ty::PredicateKind::ForAll); + .to_predicate(astconv.tcx()); vec![(pred, lifetime.span)] } } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 88ba5788b05d..3c97b55005c4 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -6,7 +6,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit; use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; +use rustc_hir::{HirId, Node}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::util::IntTypeExt; @@ -22,7 +22,6 @@ use super::{bad_placeholder_type, is_suggestable_infer_ty}; /// This should be called using the query `tcx.opt_const_param_of`. pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { use hir::*; - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); if let Node::AnonConst(_) = tcx.hir().get(hir_id) { @@ -62,9 +61,9 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< } Node::Ty(&Ty { kind: TyKind::Path(_), .. }) - | Node::Expr(&Expr { kind: ExprKind::Struct(..), .. }) - | Node::Expr(&Expr { kind: ExprKind::Path(_), .. }) - | Node::TraitRef(..) => { + | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. }) + | Node::TraitRef(..) + | Node::Pat(_) => { let path = match parent_node { Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) | Node::TraitRef(&TraitRef { path, .. }) => &*path, @@ -79,6 +78,20 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< let _tables = tcx.typeck(body_owner); &*path } + Node::Pat(pat) => { + if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) { + path + } else { + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + &format!( + "unable to find const parent for {} in pat {:?}", + hir_id, pat + ), + ); + return None; + } + } _ => { tcx.sess.delay_span_bug( tcx.def_span(def_id), @@ -91,7 +104,6 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< // We've encountered an `AnonConst` in some path, so we need to // figure out which generic parameter it corresponds to and return // the relevant type. - let (arg_index, segment) = path .segments .iter() @@ -144,6 +156,34 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< } } +fn get_path_containing_arg_in_pat<'hir>( + pat: &'hir hir::Pat<'hir>, + arg_id: HirId, +) -> Option<&'hir hir::Path<'hir>> { + use hir::*; + + let is_arg_in_path = |p: &hir::Path<'_>| { + p.segments + .iter() + .filter_map(|seg| seg.args) + .flat_map(|args| args.args) + .any(|arg| arg.id() == arg_id) + }; + let mut arg_path = None; + pat.walk(|pat| match pat.kind { + PatKind::Struct(QPath::Resolved(_, path), _, _) + | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) + | PatKind::Path(QPath::Resolved(_, path)) + if is_arg_in_path(path) => + { + arg_path = Some(path); + false + } + _ => true, + }); + arg_path +} + pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { let def_id = def_id.expect_local(); use rustc_hir::*; diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 95848ac2c076..3ce244e11bf4 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -15,7 +15,6 @@ use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; use rustc_middle::hir::place::ProjectionKind; use rustc_middle::ty::{self, adjustment, TyCtxt}; -use rustc_span::Span; use rustc_target::abi::VariantIdx; use crate::mem_categorization as mc; @@ -571,38 +570,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { })); } - /// Walk closure captures but using `closure_caputes` instead - /// of `closure_min_captures`. - /// - /// This is needed because clippy uses `ExprUseVisitor` after TypeckResults - /// are written back. We don't currently writeback min_captures to - /// TypeckResults. - fn walk_captures_closure_captures(&mut self, closure_expr: &hir::Expr<'_>) { - // FIXME(arora-aman): Remove this function once rust-lang/project-rfc-2229#18 - // is completed. - debug!("walk_captures_closure_captures({:?}), ", closure_expr); - - let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id(); - let cl_span = self.tcx().hir().span(closure_expr.hir_id); - - let captures = &self.mc.typeck_results.closure_captures[&closure_def_id]; - - for (&var_id, &upvar_id) in captures { - let upvar_capture = self.mc.typeck_results.upvar_capture(upvar_id); - let captured_place = - return_if_err!(self.cat_captured_var(closure_expr.hir_id, cl_span, var_id)); - match upvar_capture { - ty::UpvarCapture::ByValue(_) => { - let mode = copy_or_move(&self.mc, &captured_place); - self.delegate.consume(&captured_place, captured_place.hir_id, mode); - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow(&captured_place, captured_place.hir_id, upvar_borrow.kind); - } - } - } - } - /// Handle the case where the current body contains a closure. /// /// When the current body being handled is a closure, then we must make sure that @@ -628,10 +595,10 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let upvars = self.tcx().upvars_mentioned(self.body_owner); // For purposes of this function, generator and closures are equivalent. - let body_owner_is_closure = match self.tcx().type_of(self.body_owner.to_def_id()).kind() { - ty::Closure(..) | ty::Generator(..) => true, - _ => false, - }; + let body_owner_is_closure = matches!( + self.tcx().type_of(self.body_owner.to_def_id()).kind(), + ty::Closure(..) | ty::Generator(..) + ); if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id) { @@ -646,16 +613,18 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let place = &captured_place.place; let capture_info = captured_place.info; - let upvar_id = if body_owner_is_closure { + let place_base = if body_owner_is_closure { // Mark the place to be captured by the enclosing closure - ty::UpvarId::new(*var_hir_id, self.body_owner) + PlaceBase::Upvar(ty::UpvarId::new(*var_hir_id, self.body_owner)) } else { - ty::UpvarId::new(*var_hir_id, closure_def_id.expect_local()) + // If the body owner isn't a closure then the variable must + // be a local variable + PlaceBase::Local(*var_hir_id) }; let place_with_id = PlaceWithHirId::new( capture_info.expr_id.unwrap_or(closure_expr.hir_id), place.base_ty, - PlaceBase::Upvar(upvar_id), + place_base, place.projections.clone(), ); @@ -674,23 +643,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } } - } else if self.mc.typeck_results.closure_captures.contains_key(&closure_def_id) { - // Handle the case where clippy calls ExprUseVisitor after - self.walk_captures_closure_captures(closure_expr) } } - - fn cat_captured_var( - &mut self, - closure_hir_id: hir::HirId, - closure_span: Span, - var_id: hir::HirId, - ) -> mc::McResult> { - // Create the place for the variable being borrowed, from the - // perspective of the creator (parent) of the closure. - let var_ty = self.mc.node_ty(var_id)?; - self.mc.cat_res(closure_hir_id, closure_span, var_ty, Res::Local(var_id)) - } } fn copy_or_move<'a, 'tcx>( diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 929c88455f04..dde4a62ffbf3 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -226,19 +226,21 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) { let expected_return_type = if tcx.lang_items().termination().is_some() { // we take the return type of the given main function, the real check is done // in `check_fn` - actual.output().skip_binder() + actual.output() } else { // standard () main return type - tcx.mk_unit() + ty::Binder::dummy(tcx.mk_unit()) }; - let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - iter::empty(), - expected_return_type, - false, - hir::Unsafety::Normal, - Abi::Rust, - ))); + let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| { + tcx.mk_fn_sig( + iter::empty(), + expected_return_type, + false, + hir::Unsafety::Normal, + Abi::Rust, + ) + })); require_same_types( tcx, diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 9992094117df..a601123c8d05 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -459,7 +459,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { kind: ProjectionKind, ) -> PlaceWithHirId<'tcx> { let mut projections = base_place.place.projections; - projections.push(Projection { kind: kind, ty: ty }); + projections.push(Projection { kind, ty }); let ret = PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs index 94926f480e23..b1f79331d5f6 100644 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ b/compiler/rustc_typeck/src/outlives/mod.rs @@ -3,7 +3,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, CratePredicatesMap, TyCtxt}; +use rustc_middle::ty::{self, CratePredicatesMap, ToPredicate, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -90,14 +90,14 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CratePredica match kind1.unpack() { GenericArgKind::Type(ty1) => Some(( ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty1, region2)) - .potentially_quantified(tcx, ty::PredicateKind::ForAll), + .to_predicate(tcx), span, )), GenericArgKind::Lifetime(region1) => Some(( ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate( region1, region2, )) - .potentially_quantified(tcx, ty::PredicateKind::ForAll), + .to_predicate(tcx), span, )), GenericArgKind::Const(_) => { diff --git a/config.toml.example b/config.toml.example index 5b045d4e32d8..9e08ce9b27e0 100644 --- a/config.toml.example +++ b/config.toml.example @@ -426,6 +426,14 @@ changelog-seen = 2 # FIXME(#61117): Some tests fail when this option is enabled. #debuginfo-level-tests = 0 +# Whether to run `dsymutil` on Apple platforms to gather debug info into .dSYM +# bundles. `dsymutil` adds time to builds for no clear benefit, and also makes +# it more difficult for debuggers to find debug info. The compiler currently +# defaults to running `dsymutil` to preserve its historical default, but when +# compiling the compiler itself, we skip it by default since we know it's safe +# to do so in that case. +#run-dsymutil = false + # Whether or not `panic!`s generate backtraces (RUST_BACKTRACE) #backtrace = true @@ -661,3 +669,7 @@ changelog-seen = 2 # Whether to allow failures when building tools #missing-tools = false + +# List of compression formats to use when generating dist tarballs. The list of +# formats is provided to rust-installer, which must support all of them. +#compression-formats = ["gz", "xz"] diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs index 97ebc12175f7..76051d9e1dff 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -915,6 +915,7 @@ impl BinaryHeap { /// /// assert_eq!(heap.len(), 2); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { self.data.len() diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index bd99c4ed2f14..1d6488dd2dfe 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -30,7 +30,7 @@ impl Root { /// Pushes all key-value pairs to the end of the tree, incrementing a /// `length` variable along the way. The latter makes it easier for the /// caller to avoid a leak when the iterator panicks. - fn bulk_push(&mut self, iter: I, length: &mut usize) + pub fn bulk_push(&mut self, iter: I, length: &mut usize) where I: Iterator, { @@ -81,15 +81,18 @@ impl Root { // the appended elements even if advancing the iterator panicks. *length += 1; } - self.fix_right_edge(); + self.fix_right_border_of_plentiful(); } - fn fix_right_edge(&mut self) { - // Handle underfull nodes, start from the top. + /// Stock up any underfull nodes on the right border of the tree. + /// The other nodes, those that are not the root nor a rightmost edge, + /// must have MIN_LEN elements to spare. + fn fix_right_border_of_plentiful(&mut self) { let mut cur_node = self.borrow_mut(); while let Internal(internal) = cur_node.force() { // Check if right-most child is underfull. let mut last_kv = internal.last_kv().consider_for_balancing(); + debug_assert!(last_kv.left_child_len() >= MIN_LEN * 2); let right_child_len = last_kv.right_child_len(); if right_child_len < MIN_LEN { // We need to steal. diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index bd2ad257402f..944e0e65cf7c 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -248,7 +248,7 @@ where let (map, dormant_map) = DormantMutRef::new(self); let root_node = Self::ensure_is_owned(&mut map.root).borrow_mut(); match search::search_tree::, K, (), K>(root_node, &key) { - Found(handle) => Some(mem::replace(handle.into_key_mut(), key)), + Found(mut kv) => Some(mem::replace(kv.key_mut(), key)), GoDown(handle) => { VacantEntry { key, handle, dormant_map, _marker: PhantomData }.insert(()); None @@ -2132,6 +2132,7 @@ impl BTreeMap { /// a.insert(1, "a"); /// assert_eq!(a.len(), 1); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] pub const fn len(&self) -> usize { diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index c92888cb8973..6cc8813bc523 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -116,15 +116,16 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { } } - #[unstable(feature = "or_insert_with_key", issue = "71024")] - /// Ensures a value is in the entry by inserting, if empty, the result of the default function, - /// which takes the key as its argument, and returns a mutable reference to the value in the - /// entry. + /// Ensures a value is in the entry by inserting, if empty, the result of the default function. + /// This method allows for generating key-derived values for insertion by providing the default + /// function a reference to the key that was moved during the `.entry(key)` method call. + /// + /// The reference to the moved key is provided so that cloning or copying the key is + /// unnecessary, unlike with `.or_insert_with(|| ... )`. /// /// # Examples /// /// ``` - /// #![feature(or_insert_with_key)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); @@ -134,6 +135,7 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// assert_eq!(map["poneyland"], 9); /// ``` #[inline] + #[stable(feature = "or_insert_with_key", since = "1.50.0")] pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 97df8ea07d23..f92aed8ce15b 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -111,11 +111,23 @@ impl BTreeMap { } } } + + // Transform the tree to minimize wasted space, obtaining fewer nodes that + // are mostly filled up to their capacity. The same compact tree could have + // been obtained by inserting keys in a shrewd order. + fn compact(&mut self) + where + K: Ord, + { + let iter = mem::take(self).into_iter(); + let root = BTreeMap::ensure_is_owned(&mut self.root); + root.bulk_push(iter, &mut self.length); + } } impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { fn assert_min_len(self, min_len: usize) { - assert!(self.len() >= min_len, "{} < {}", self.len(), min_len); + assert!(self.len() >= min_len, "node len {} < {}", self.len(), min_len); if let node::ForceResult::Internal(node) = self.force() { for idx in 0..=node.len() { let edge = unsafe { Handle::new_edge(node, idx) }; @@ -1679,17 +1691,29 @@ fn test_first_last_entry() { } #[test] -fn test_insert_into_full_left() { - let mut map: BTreeMap<_, _> = (0..NODE_CAPACITY).map(|i| (i * 2, ())).collect(); - assert!(map.insert(NODE_CAPACITY, ()).is_none()); - map.check(); +fn test_insert_into_full_height_0() { + let size = NODE_CAPACITY; + for pos in 0..=size { + let mut map: BTreeMap<_, _> = (0..size).map(|i| (i * 2 + 1, ())).collect(); + assert!(map.insert(pos * 2, ()).is_none()); + map.check(); + } } #[test] -fn test_insert_into_full_right() { - let mut map: BTreeMap<_, _> = (0..NODE_CAPACITY).map(|i| (i * 2, ())).collect(); - assert!(map.insert(NODE_CAPACITY + 2, ()).is_none()); - map.check(); +fn test_insert_into_full_height_1() { + let size = NODE_CAPACITY + 1 + NODE_CAPACITY; + for pos in 0..=size { + let mut map: BTreeMap<_, _> = (0..size).map(|i| (i * 2 + 1, ())).collect(); + map.compact(); + let root_node = map.root.as_ref().unwrap().reborrow(); + assert_eq!(root_node.len(), 1); + assert_eq!(root_node.first_leaf_edge().into_node().len(), NODE_CAPACITY); + assert_eq!(root_node.last_leaf_edge().into_node().len(), NODE_CAPACITY); + + assert!(map.insert(pos * 2, ()).is_none()); + map.check(); + } } macro_rules! create_append_test { @@ -1797,7 +1821,6 @@ fn test_append_ord_chaos() { } fn rand_data(len: usize) -> Vec<(u32, u32)> { - assert!(len * 2 <= 70029); // from that point on numbers repeat let mut rng = DeterministicRng::new(); Vec::from_iter((0..len).map(|_| (rng.next(), rng.next()))) } @@ -1862,6 +1885,25 @@ fn test_split_off_tiny_right_height_2() { assert_eq!(*right.last_key_value().unwrap().0, last); } +#[test] +fn test_split_off_halfway() { + let mut rng = DeterministicRng::new(); + for &len in &[NODE_CAPACITY, 25, 50, 75, 100] { + let mut data = Vec::from_iter((0..len).map(|_| (rng.next(), ()))); + // Insertion in non-ascending order creates some variation in node length. + let mut map = BTreeMap::from_iter(data.iter().copied()); + data.sort(); + let small_keys = data.iter().take(len / 2).map(|kv| kv.0); + let large_keys = data.iter().skip(len / 2).map(|kv| kv.0); + let split_key = large_keys.clone().next().unwrap(); + let right = map.split_off(&split_key); + map.check(); + right.check(); + assert!(map.keys().copied().eq(small_keys)); + assert!(right.keys().copied().eq(large_keys)); + } +} + #[test] fn test_split_off_large_random_sorted() { // Miri is too slow diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index ebcbb0e467c4..cdb39104047f 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -38,6 +38,7 @@ pub unsafe fn unwrap_unchecked(val: Option) -> T { #[cfg(test)] /// XorShiftRng struct DeterministicRng { + count: usize, x: u32, y: u32, z: u32, @@ -47,11 +48,13 @@ struct DeterministicRng { #[cfg(test)] impl DeterministicRng { fn new() -> Self { - DeterministicRng { x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } + DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } - /// Guarantees that the first 70029 results are unique. + /// Guarantees that each returned number is unique. fn next(&mut self) -> u32 { + self.count += 1; + assert!(self.count <= 70029); let x = self.x; let t = x ^ (x << 11); self.x = self.y; diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 31809fde57b7..b3641a7a0c69 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -35,6 +35,7 @@ use core::cmp::Ordering; use core::marker::PhantomData; use core::mem::{self, MaybeUninit}; use core::ptr::{self, NonNull}; +use core::slice::SliceIndex; use crate::alloc::{Allocator, Global, Layout}; use crate::boxed::Box; @@ -150,7 +151,7 @@ impl NodeRef { /// Mutably borrows the owned node. Unlike `reborrow_mut`, this is safe, /// because the return value cannot be used to destroy the node itself, /// and there cannot be other references to the tree (except during the - /// process of `into_iter` or `drop`, but that is a horrific already). + /// process of `into_iter` or `drop`, but that is horrific already). pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -192,7 +193,7 @@ impl NodeRef { let internal_node = NodeRef { height: self.height, node: top, _marker: PhantomData }; *self = internal_node.first_edge().descend(); - self.borrow_mut().clear_parent_link(); + self.clear_parent_link(); unsafe { Global.deallocate(top.cast(), Layout::new::>()); @@ -239,17 +240,16 @@ impl NodeRef { /// - We cannot get implicit coercion from say `Mut<'a>` to `Immut<'a>`. /// Therefore, we have to explicitly call `reborrow` on a more powerfull /// `NodeRef` in order to reach a method like `key_at`. -/// - All methods on `NodeRef` that return some kind of reference, except -/// `reborrow` and `reborrow_mut`, take `self` by value and not by reference. -/// This avoids silently returning a second reference somewhere in the tree. -/// That is irrelevant when `BorrowType` is `Immut<'a>`, but the rule does -/// no harm because we make those `NodeRef` implicitly `Copy`. -/// The rule also avoids implicitly returning the lifetime of `&self`, -/// instead of the lifetime carried by `BorrowType`. -/// An exception to this rule are the insert functions. -/// - Given the above, we need a `reborrow_mut` to explicitly copy a `Mut<'a>` -/// `NodeRef` whenever we want to invoke a method returning an extra reference -/// somewhere in the tree. +/// +/// All methods on `NodeRef` that return some kind of reference, either: +/// - Take `self` by value, and return the lifetime carried by `BorrowType`. +/// Sometimes, to invoke such a method, we need to call `reborrow_mut`. +/// - Take `self` by reference, and (implicitly) return that reference's +/// lifetime, instead of the lifetime carried by `BorrowType`. That way, +/// the borrow checker guarantees that the `NodeRef` remains borrowed as long +/// as the returned reference is used. +/// The methods supporting insert bend this rule by returning a raw pointer, +/// i.e., a reference without any lifetime. pub struct NodeRef { /// The number of levels that the node and the level of leaves are apart, a /// constant of the node that cannot be entirely described by `Type`, and that @@ -295,19 +295,10 @@ impl NodeRef { } } -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Exposes the data of an internal node in an immutable tree. - fn as_internal(this: &Self) -> &'a InternalNode { - let ptr = Self::as_internal_ptr(this); - // SAFETY: there can be no mutable references into this tree borrowed as `Immut`. - unsafe { &*ptr } - } -} - impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Offers exclusive access to the data of an internal node. - fn as_internal_mut(this: &mut Self) -> &'a mut InternalNode { - let ptr = Self::as_internal_ptr(this); + /// Borrows exclusive access to the data of an internal node. + fn as_internal_mut(&mut self) -> &mut InternalNode { + let ptr = Self::as_internal_ptr(self); unsafe { &mut *ptr } } } @@ -355,7 +346,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// The node has more than `idx` initialized elements. pub unsafe fn key_at(self, idx: usize) -> &'a K { debug_assert!(idx < self.len()); - unsafe { Self::as_leaf(&self).keys.get_unchecked(idx).assume_init_ref() } + unsafe { self.into_leaf().keys.get_unchecked(idx).assume_init_ref() } } /// Exposes one of the values stored in the node. @@ -364,18 +355,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// The node has more than `idx` initialized elements. unsafe fn val_at(self, idx: usize) -> &'a V { debug_assert!(idx < self.len()); - unsafe { Self::as_leaf(&self).vals.get_unchecked(idx).assume_init_ref() } - } -} - -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Exposes the contents of one of the edges in the node. - /// - /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn edge_at(self, idx: usize) -> &'a BoxedNode { - debug_assert!(idx <= self.len()); - unsafe { Self::as_internal(&self).edges.get_unchecked(idx).assume_init_ref() } + unsafe { self.into_leaf().vals.get_unchecked(idx).assume_init_ref() } } } @@ -431,8 +411,8 @@ impl NodeRef { impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Exposes the leaf portion of any leaf or internal node in an immutable tree. - fn as_leaf(this: &Self) -> &'a LeafNode { - let ptr = Self::as_leaf_ptr(this); + fn into_leaf(self) -> &'a LeafNode { + let ptr = Self::as_leaf_ptr(&self); // SAFETY: there can be no mutable references into this tree borrowed as `Immut`. unsafe { &*ptr } } @@ -489,98 +469,64 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } + /// Borrows exclusive access to the leaf portion of any leaf or internal node. + fn as_leaf_mut(&mut self) -> &mut LeafNode { + let ptr = Self::as_leaf_ptr(self); + // SAFETY: we have exclusive access to the entire node. + unsafe { &mut *ptr } + } + /// Offers exclusive access to the leaf portion of any leaf or internal node. - fn as_leaf_mut(this: &mut Self) -> &'a mut LeafNode { - let ptr = Self::as_leaf_ptr(this); + fn into_leaf_mut(mut self) -> &'a mut LeafNode { + let ptr = Self::as_leaf_ptr(&mut self); // SAFETY: we have exclusive access to the entire node. unsafe { &mut *ptr } } } impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Offers exclusive access to a part of the key storage area. + /// Borrows exclusive access to an element of the key storage area. /// /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn into_key_area_mut_at(mut self, idx: usize) -> &'a mut MaybeUninit { - debug_assert!(idx < self.len()); - unsafe { Self::as_leaf_mut(&mut self).keys.get_unchecked_mut(idx) } - } - - /// Offers exclusive access to a part of the value storage area. - /// - /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn into_val_area_mut_at(mut self, idx: usize) -> &'a mut MaybeUninit { - debug_assert!(idx < self.len()); - unsafe { Self::as_leaf_mut(&mut self).vals.get_unchecked_mut(idx) } - } -} - -impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { - /// Offers exclusive access to a part of the storage area for edge contents. - /// - /// # Safety - /// The node has at least `idx` initialized elements. - unsafe fn into_edge_area_mut_at(mut self, idx: usize) -> &'a mut MaybeUninit> { - debug_assert!(idx <= self.len()); - unsafe { Self::as_internal_mut(&mut self).edges.get_unchecked_mut(idx) } - } -} - -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Exposes the entire key storage area in the node, - /// regardless of the node's current length, - /// having exclusive access to the entire node. - unsafe fn key_area(self) -> &'a [MaybeUninit] { - Self::as_leaf(&self).keys.as_slice() - } - - /// Exposes the entire value storage area in the node, - /// regardless of the node's current length, - /// having exclusive access to the entire node. - unsafe fn val_area(self) -> &'a [MaybeUninit] { - Self::as_leaf(&self).vals.as_slice() - } -} - -impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { - /// Exposes the entire storage area for edge contents in the node, - /// regardless of the node's current length, - /// having exclusive access to the entire node. - unsafe fn edge_area(self) -> &'a [MaybeUninit>] { - Self::as_internal(&self).edges.as_slice() - } -} - -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Offers exclusive access to a sized slice of key storage area in the node. - unsafe fn into_key_area_slice(mut self) -> &'a mut [MaybeUninit] { - let len = self.len(); + /// `index` is in bounds of 0..CAPACITY + unsafe fn key_area_mut(&mut self, index: I) -> &mut Output + where + I: SliceIndex<[MaybeUninit], Output = Output>, + { // SAFETY: the caller will not be able to call further methods on self // until the key slice reference is dropped, as we have unique access // for the lifetime of the borrow. - unsafe { Self::as_leaf_mut(&mut self).keys.get_unchecked_mut(..len) } + unsafe { self.as_leaf_mut().keys.as_mut_slice().get_unchecked_mut(index) } } - /// Offers exclusive access to a sized slice of value storage area in the node. - unsafe fn into_val_area_slice(mut self) -> &'a mut [MaybeUninit] { - let len = self.len(); + /// Borrows exclusive access to an element or slice of the node's value storage area. + /// + /// # Safety + /// `index` is in bounds of 0..CAPACITY + unsafe fn val_area_mut(&mut self, index: I) -> &mut Output + where + I: SliceIndex<[MaybeUninit], Output = Output>, + { // SAFETY: the caller will not be able to call further methods on self // until the value slice reference is dropped, as we have unique access // for the lifetime of the borrow. - unsafe { Self::as_leaf_mut(&mut self).vals.get_unchecked_mut(..len) } + unsafe { self.as_leaf_mut().vals.as_mut_slice().get_unchecked_mut(index) } } } impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { - /// Offers exclusive access to a sized slice of storage area for edge contents in the node. - unsafe fn into_edge_area_slice(mut self) -> &'a mut [MaybeUninit>] { - let len = self.len(); + /// Borrows exclusive access to an element or slice of the node's storage area for edge contents. + /// + /// # Safety + /// `index` is in bounds of 0..CAPACITY + 1 + unsafe fn edge_area_mut(&mut self, index: I) -> &mut Output + where + I: SliceIndex<[MaybeUninit>], Output = Output>, + { // SAFETY: the caller will not be able to call further methods on self // until the edge slice reference is dropped, as we have unique access // for the lifetime of the borrow. - unsafe { Self::as_internal_mut(&mut self).edges.get_unchecked_mut(..len + 1) } + unsafe { self.as_internal_mut().edges.as_mut_slice().get_unchecked_mut(index) } } } @@ -604,25 +550,27 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { } impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Exposes exclusive access to the length of the node. - pub fn into_len_mut(mut self) -> &'a mut u16 { - &mut (*Self::as_leaf_mut(&mut self)).len + /// Borrows exclusive access to the length of the node. + pub fn len_mut(&mut self) -> &mut u16 { + &mut self.as_leaf_mut().len } } impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { - /// Set or clear the node's link to its parent edge, + /// Sets the node's link to its parent edge, /// without invalidating other references to the node. fn set_parent_link(&mut self, parent: NonNull>, parent_idx: usize) { let leaf = Self::as_leaf_ptr(self); unsafe { (*leaf).parent = Some(parent) }; unsafe { (*leaf).parent_idx.write(parent_idx as u16) }; } +} - /// Clear the node's link to its parent edge. - /// This only makes sense when there are no other references to the node. +impl NodeRef { + /// Clears the root's link to its parent edge. fn clear_parent_link(&mut self) { - let leaf = Self::as_leaf_mut(self); + let mut root_node = self.borrow_mut(); + let leaf = root_node.as_leaf_mut(); leaf.parent = None; } } @@ -630,24 +578,24 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// Adds a key-value pair to the end of the node. pub fn push(&mut self, key: K, val: V) { - let len = unsafe { self.reborrow_mut().into_len_mut() }; + let len = self.len_mut(); let idx = usize::from(*len); assert!(idx < CAPACITY); *len += 1; unsafe { - self.reborrow_mut().into_key_area_mut_at(idx).write(key); - self.reborrow_mut().into_val_area_mut_at(idx).write(val); + self.key_area_mut(idx).write(key); + self.val_area_mut(idx).write(val); } } /// Adds a key-value pair to the beginning of the node. fn push_front(&mut self, key: K, val: V) { - assert!(self.len() < CAPACITY); - + let new_len = self.len() + 1; + assert!(new_len <= CAPACITY); unsafe { - *self.reborrow_mut().into_len_mut() += 1; - slice_insert(self.reborrow_mut().into_key_area_slice(), 0, key); - slice_insert(self.reborrow_mut().into_val_area_slice(), 0, val); + slice_insert(self.key_area_mut(..new_len), 0, key); + slice_insert(self.val_area_mut(..new_len), 0, val); + *self.len_mut() = new_len as u16; } } } @@ -674,14 +622,14 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { pub fn push(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); - let len = unsafe { self.reborrow_mut().into_len_mut() }; + let len = self.len_mut(); let idx = usize::from(*len); assert!(idx < CAPACITY); *len += 1; unsafe { - self.reborrow_mut().into_key_area_mut_at(idx).write(key); - self.reborrow_mut().into_val_area_mut_at(idx).write(val); - self.reborrow_mut().into_edge_area_mut_at(idx + 1).write(edge.node); + self.key_area_mut(idx).write(key); + self.val_area_mut(idx).write(val); + self.edge_area_mut(idx + 1).write(edge.node); Handle::new_edge(self.reborrow_mut(), idx + 1).correct_parent_link(); } } @@ -689,14 +637,15 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { /// Adds a key-value pair, and an edge to go to the left of that pair, /// to the beginning of the node. fn push_front(&mut self, key: K, val: V, edge: Root) { + let new_len = self.len() + 1; assert!(edge.height == self.height - 1); - assert!(self.len() < CAPACITY); + assert!(new_len <= CAPACITY); unsafe { - *self.reborrow_mut().into_len_mut() += 1; - slice_insert(self.reborrow_mut().into_key_area_slice(), 0, key); - slice_insert(self.reborrow_mut().into_val_area_slice(), 0, val); - slice_insert(self.reborrow_mut().into_edge_area_slice(), 0, edge.node); + slice_insert(self.key_area_mut(..new_len), 0, key); + slice_insert(self.val_area_mut(..new_len), 0, val); + slice_insert(self.edge_area_mut(..new_len + 1), 0, edge.node); + *self.len_mut() = new_len as u16; } self.correct_all_childrens_parent_links(); @@ -713,21 +662,21 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { let idx = self.len() - 1; unsafe { - let key = ptr::read(self.reborrow().key_at(idx)); - let val = ptr::read(self.reborrow().val_at(idx)); + let key = self.key_area_mut(idx).assume_init_read(); + let val = self.val_area_mut(idx).assume_init_read(); let edge = match self.reborrow_mut().force() { ForceResult::Leaf(_) => None, - ForceResult::Internal(internal) => { - let node = ptr::read(internal.reborrow().edge_at(idx + 1)); + ForceResult::Internal(mut internal) => { + let node = internal.edge_area_mut(idx + 1).assume_init_read(); let mut edge = Root { node, height: internal.height - 1, _marker: PhantomData }; - // In practice, clearing the parent is a waste of time, because we will + // Currently, clearing the parent link is superfluous, because we will // insert the node elsewhere and set its parent link again. - edge.borrow_mut().clear_parent_link(); + edge.clear_parent_link(); Some(edge) } }; - *self.reborrow_mut().into_len_mut() -= 1; + *self.len_mut() -= 1; (key, val, edge) } } @@ -741,16 +690,16 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { let old_len = self.len(); unsafe { - let key = slice_remove(self.reborrow_mut().into_key_area_slice(), 0); - let val = slice_remove(self.reborrow_mut().into_val_area_slice(), 0); + let key = slice_remove(self.key_area_mut(..old_len), 0); + let val = slice_remove(self.val_area_mut(..old_len), 0); let edge = match self.reborrow_mut().force() { ForceResult::Leaf(_) => None, ForceResult::Internal(mut internal) => { - let node = slice_remove(internal.reborrow_mut().into_edge_area_slice(), 0); + let node = slice_remove(internal.edge_area_mut(..old_len + 1), 0); let mut edge = Root { node, height: internal.height - 1, _marker: PhantomData }; - // In practice, clearing the parent is a waste of time, because we will + // Currently, clearing the parent link is superfluous, because we will // insert the node elsewhere and set its parent link again. - edge.borrow_mut().clear_parent_link(); + edge.clear_parent_link(); internal.correct_childrens_parent_links(0..old_len); @@ -758,14 +707,14 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { } }; - *self.reborrow_mut().into_len_mut() -= 1; + *self.len_mut() -= 1; (key, val, edge) } } fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { - let leaf = Self::as_leaf_mut(&mut self); + let leaf = self.as_leaf_mut(); let keys = MaybeUninit::slice_as_mut_ptr(&mut leaf.keys); let vals = MaybeUninit::slice_as_mut_ptr(&mut leaf.vals); (keys, vals) @@ -967,13 +916,14 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// The returned pointer points to the inserted value. fn insert_fit(&mut self, key: K, val: V) -> *mut V { debug_assert!(self.node.len() < CAPACITY); + let new_len = self.node.len() + 1; unsafe { - *self.node.reborrow_mut().into_len_mut() += 1; - slice_insert(self.node.reborrow_mut().into_key_area_slice(), self.idx, key); - slice_insert(self.node.reborrow_mut().into_val_area_slice(), self.idx, val); + slice_insert(self.node.key_area_mut(..new_len), self.idx, key); + slice_insert(self.node.val_area_mut(..new_len), self.idx, val); + *self.node.len_mut() = new_len as u16; - self.node.reborrow_mut().into_val_area_mut_at(self.idx).assume_init_mut() + self.node.val_area_mut(self.idx).assume_init_mut() } } } @@ -1025,14 +975,15 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, fn insert_fit(&mut self, key: K, val: V, edge: Root) { debug_assert!(self.node.len() < CAPACITY); debug_assert!(edge.height == self.node.height - 1); + let new_len = self.node.len() + 1; unsafe { - *self.node.reborrow_mut().into_len_mut() += 1; - slice_insert(self.node.reborrow_mut().into_key_area_slice(), self.idx, key); - slice_insert(self.node.reborrow_mut().into_val_area_slice(), self.idx, val); - slice_insert(self.node.reborrow_mut().into_edge_area_slice(), self.idx + 1, edge.node); + slice_insert(self.node.key_area_mut(..new_len), self.idx, key); + slice_insert(self.node.val_area_mut(..new_len), self.idx, val); + slice_insert(self.node.edge_area_mut(..new_len + 1), self.idx + 1, edge.node); + *self.node.len_mut() = new_len as u16; - self.node.correct_childrens_parent_links((self.idx + 1)..=self.node.len()); + self.node.correct_childrens_parent_links(self.idx + 1..new_len + 1); } } @@ -1133,12 +1084,13 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeTyp } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_key_mut(self) -> &'a mut K { - unsafe { self.node.into_key_area_mut_at(self.idx).assume_init_mut() } + pub fn key_mut(&mut self) -> &mut K { + unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } pub fn into_val_mut(self) -> &'a mut V { - unsafe { self.node.into_val_area_mut_at(self.idx).assume_init_mut() } + let leaf = self.node.into_leaf_mut(); + unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() } } } @@ -1153,43 +1105,43 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. unsafe { - let leaf = NodeRef::as_leaf_mut(&mut self.node.reborrow_mut()); + let leaf = self.node.as_leaf_mut(); let key = leaf.keys.get_unchecked_mut(self.idx).assume_init_mut(); let val = leaf.vals.get_unchecked_mut(self.idx).assume_init_mut(); (key, val) } } + + /// Replace the key and value that the KV handle refers to. + pub fn replace_kv(&mut self, k: K, v: V) -> (K, V) { + let (key, val) = self.kv_mut(); + (mem::replace(key, k), mem::replace(val, v)) + } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - /// Helps implementations of `split` for a particular `NodeType`, - /// by calculating the length of the new node. - fn split_new_node_len(&self) -> usize { - debug_assert!(self.idx < self.node.len()); - self.node.len() - self.idx - 1 - } - /// Helps implementations of `split` for a particular `NodeType`, /// by taking care of leaf data. fn split_leaf_data(&mut self, new_node: &mut LeafNode) -> (K, V) { - let new_len = self.split_new_node_len(); + debug_assert!(self.idx < self.node.len()); + let new_len = self.node.len() - self.idx - 1; new_node.len = new_len as u16; unsafe { - let k = ptr::read(self.node.reborrow().key_at(self.idx)); - let v = ptr::read(self.node.reborrow().val_at(self.idx)); + let k = self.node.key_area_mut(self.idx).assume_init_read(); + let v = self.node.val_area_mut(self.idx).assume_init_read(); ptr::copy_nonoverlapping( - self.node.reborrow().key_area().as_ptr().add(self.idx + 1), + self.node.key_area_mut(self.idx + 1..).as_ptr(), new_node.keys.as_mut_ptr(), new_len, ); ptr::copy_nonoverlapping( - self.node.reborrow().val_area().as_ptr().add(self.idx + 1), + self.node.val_area_mut(self.idx + 1..).as_ptr(), new_node.vals.as_mut_ptr(), new_len, ); - *self.node.reborrow_mut().into_len_mut() = self.idx as u16; + *self.node.len_mut() = self.idx as u16; (k, v) } } @@ -1219,10 +1171,11 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark pub fn remove( mut self, ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { + let old_len = self.node.len(); unsafe { - let k = slice_remove(self.node.reborrow_mut().into_key_area_slice(), self.idx); - let v = slice_remove(self.node.reborrow_mut().into_val_area_slice(), self.idx); - *self.node.reborrow_mut().into_len_mut() -= 1; + let k = slice_remove(self.node.key_area_mut(..old_len), self.idx); + let v = slice_remove(self.node.val_area_mut(..old_len), self.idx); + *self.node.len_mut() = (old_len - 1) as u16; ((k, v), self.left_edge()) } } @@ -1239,14 +1192,13 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, pub fn split(mut self) -> SplitResult<'a, K, V, marker::Internal> { unsafe { let mut new_node = Box::new(InternalNode::new()); - let new_len = self.split_new_node_len(); - // Move edges out before reducing length: + let kv = self.split_leaf_data(&mut new_node.data); + let new_len = usize::from(new_node.data.len); ptr::copy_nonoverlapping( - self.node.reborrow().edge_area().as_ptr().add(self.idx + 1), + self.node.edge_area_mut(self.idx + 1..).as_ptr(), new_node.edges.as_mut_ptr(), new_len + 1, ); - let kv = self.split_leaf_data(&mut new_node.data); let height = self.node.height; let mut right = NodeRef::from_new_internal(new_node, height); @@ -1282,10 +1234,12 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// Chooses a balancing context involving the node as a child, thus between /// the KV immediately to the left or to the right in the parent node. /// Returns an `Err` if there is no parent. + /// Panics if the parent is empty. /// - /// This method optimizes for a node that has fewer elements than its left - /// and right siblings, if they exist, by preferring the left parent KV. - /// Merging with the left sibling is faster, since we only need to move + /// Prefers the left side, to be optimal if the given node is somehow + /// underfull, meaning here only that it has fewer elements than its left + /// sibling and than its right sibling, if they exist. In that case, + /// merging with the left sibling is faster, since we only need to move /// the node's N elements, instead of shifting them to the right and moving /// more than N elements in front. Stealing from the left sibling is also /// typically faster, since we only need to shift the node's N elements to @@ -1293,19 +1247,19 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// the left. pub fn choose_parent_kv(self) -> Result>, Self> { match unsafe { ptr::read(&self) }.ascend() { - Ok(parent) => match parent.left_kv() { + Ok(parent_edge) => match parent_edge.left_kv() { Ok(left_parent_kv) => Ok(LeftOrRight::Left(BalancingContext { parent: unsafe { ptr::read(&left_parent_kv) }, left_child: left_parent_kv.left_edge().descend(), right_child: self, })), - Err(parent) => match parent.right_kv() { + Err(parent_edge) => match parent_edge.right_kv() { Ok(right_parent_kv) => Ok(LeftOrRight::Right(BalancingContext { parent: unsafe { ptr::read(&right_parent_kv) }, left_child: self, right_child: right_parent_kv.right_edge().descend(), })), - Err(_) => unreachable!("empty non-root node"), + Err(_) => unreachable!("empty internal node"), }, }, Err(root) => Err(root), @@ -1346,66 +1300,59 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// /// Panics unless we `.can_merge()`. pub fn merge( - mut self, + self, track_edge_idx: Option>, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { + let Handle { node: mut parent_node, idx: parent_idx, _marker } = self.parent; + let old_parent_len = parent_node.len(); let mut left_node = self.left_child; - let left_len = left_node.len(); - let right_node = self.right_child; + let old_left_len = left_node.len(); + let mut right_node = self.right_child; let right_len = right_node.len(); + let new_left_len = old_left_len + 1 + right_len; - assert!(left_len + right_len < CAPACITY); + assert!(new_left_len <= CAPACITY); assert!(match track_edge_idx { None => true, - Some(LeftOrRight::Left(idx)) => idx <= left_len, + Some(LeftOrRight::Left(idx)) => idx <= old_left_len, Some(LeftOrRight::Right(idx)) => idx <= right_len, }); unsafe { - *left_node.reborrow_mut().into_len_mut() += right_len as u16 + 1; + *left_node.len_mut() = new_left_len as u16; - let parent_key = slice_remove( - self.parent.node.reborrow_mut().into_key_area_slice(), - self.parent.idx, - ); - left_node.reborrow_mut().into_key_area_mut_at(left_len).write(parent_key); + let parent_key = slice_remove(parent_node.key_area_mut(..old_parent_len), parent_idx); + left_node.key_area_mut(old_left_len).write(parent_key); ptr::copy_nonoverlapping( - right_node.reborrow().key_area().as_ptr(), - left_node.reborrow_mut().into_key_area_slice().as_mut_ptr().add(left_len + 1), + right_node.key_area_mut(..).as_ptr(), + left_node.key_area_mut(old_left_len + 1..).as_mut_ptr(), right_len, ); - let parent_val = slice_remove( - self.parent.node.reborrow_mut().into_val_area_slice(), - self.parent.idx, - ); - left_node.reborrow_mut().into_val_area_mut_at(left_len).write(parent_val); + let parent_val = slice_remove(parent_node.val_area_mut(..old_parent_len), parent_idx); + left_node.val_area_mut(old_left_len).write(parent_val); ptr::copy_nonoverlapping( - right_node.reborrow().val_area().as_ptr(), - left_node.reborrow_mut().into_val_area_slice().as_mut_ptr().add(left_len + 1), + right_node.val_area_mut(..).as_ptr(), + left_node.val_area_mut(old_left_len + 1..).as_mut_ptr(), right_len, ); - slice_remove( - &mut self.parent.node.reborrow_mut().into_edge_area_slice(), - self.parent.idx + 1, - ); - let parent_old_len = self.parent.node.len(); - self.parent.node.correct_childrens_parent_links(self.parent.idx + 1..parent_old_len); - *self.parent.node.reborrow_mut().into_len_mut() -= 1; + slice_remove(&mut parent_node.edge_area_mut(..old_parent_len + 1), parent_idx + 1); + parent_node.correct_childrens_parent_links(parent_idx + 1..old_parent_len); + *parent_node.len_mut() -= 1; - if self.parent.node.height > 1 { + if parent_node.height > 1 { // SAFETY: the height of the nodes being merged is one below the height // of the node of this edge, thus above zero, so they are internal. let mut left_node = left_node.reborrow_mut().cast_to_internal_unchecked(); - let right_node = right_node.cast_to_internal_unchecked(); + let mut right_node = right_node.cast_to_internal_unchecked(); ptr::copy_nonoverlapping( - right_node.reborrow().edge_area().as_ptr(), - left_node.reborrow_mut().into_edge_area_slice().as_mut_ptr().add(left_len + 1), + right_node.edge_area_mut(..).as_ptr(), + left_node.edge_area_mut(old_left_len + 1..).as_mut_ptr(), right_len + 1, ); - left_node.correct_childrens_parent_links(left_len + 1..=left_len + 1 + right_len); + left_node.correct_childrens_parent_links(old_left_len + 1..new_left_len + 1); Global.deallocate(right_node.node.cast(), Layout::new::>()); } else { @@ -1415,7 +1362,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { let new_idx = match track_edge_idx { None => 0, Some(LeftOrRight::Left(idx)) => idx, - Some(LeftOrRight::Right(idx)) => left_len + 1 + idx, + Some(LeftOrRight::Right(idx)) => old_left_len + 1 + idx, }; Handle::new_edge(left_node, new_idx) } @@ -1432,8 +1379,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { unsafe { let (k, v, edge) = self.left_child.pop(); - let k = mem::replace(self.parent.kv_mut().0, k); - let v = mem::replace(self.parent.kv_mut().1, v); + let (k, v) = self.parent.replace_kv(k, v); match self.right_child.reborrow_mut().force() { ForceResult::Leaf(mut leaf) => leaf.push_front(k, v), @@ -1455,8 +1401,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { unsafe { let (k, v, edge) = self.right_child.pop_front(); - let k = mem::replace(self.parent.kv_mut().0, k); - let v = mem::replace(self.parent.kv_mut().1, v); + let (k, v) = self.parent.replace_kv(k, v); match self.left_child.reborrow_mut().force() { ForceResult::Leaf(mut leaf) => leaf.push(k, v), @@ -1469,6 +1414,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// This does stealing similar to `steal_left` but steals multiple elements at once. pub fn bulk_steal_left(&mut self, count: usize) { + assert!(count > 0); unsafe { let left_node = &mut self.left_child; let old_left_len = left_node.len(); @@ -1480,6 +1426,9 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { assert!(old_left_len >= count); let new_left_len = old_left_len - count; + let new_right_len = old_right_len + count; + *left_node.len_mut() = new_left_len as u16; + *right_node.len_mut() = new_right_len as u16; // Move leaf data. { @@ -1504,16 +1453,12 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { move_kv(left_kv, new_left_len, parent_kv, 0, 1); } - *left_node.reborrow_mut().into_len_mut() -= count as u16; - *right_node.reborrow_mut().into_len_mut() += count as u16; - match (left_node.reborrow_mut().force(), right_node.reborrow_mut().force()) { (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { // Make room for stolen edges. - let left = left.reborrow(); - let right_edges = right.reborrow_mut().into_edge_area_slice().as_mut_ptr(); + let right_edges = right.edge_area_mut(..).as_mut_ptr(); ptr::copy(right_edges, right_edges.add(count), old_right_len + 1); - right.correct_childrens_parent_links(count..count + old_right_len + 1); + right.correct_childrens_parent_links(count..new_right_len + 1); // Steal edges. move_edges(left, new_left_len + 1, right, 0, count); @@ -1526,6 +1471,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// The symmetric clone of `bulk_steal_left`. pub fn bulk_steal_right(&mut self, count: usize) { + assert!(count > 0); unsafe { let left_node = &mut self.left_child; let old_left_len = left_node.len(); @@ -1536,7 +1482,10 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { assert!(old_left_len + count <= CAPACITY); assert!(old_right_len >= count); + let new_left_len = old_left_len + count; let new_right_len = old_right_len - count; + *left_node.len_mut() = new_left_len as u16; + *right_node.len_mut() = new_right_len as u16; // Move leaf data. { @@ -1561,16 +1510,13 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { ptr::copy(right_kv.1.add(count), right_kv.1, new_right_len); } - *left_node.reborrow_mut().into_len_mut() += count as u16; - *right_node.reborrow_mut().into_len_mut() -= count as u16; - match (left_node.reborrow_mut().force(), right_node.reborrow_mut().force()) { (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { // Steal edges. - move_edges(right.reborrow(), 0, left, old_left_len + 1, count); + move_edges(right.reborrow_mut(), 0, left, old_left_len + 1, count); // Fill gap where stolen edges used to be. - let right_edges = right.reborrow_mut().into_edge_area_slice().as_mut_ptr(); + let right_edges = right.edge_area_mut(..).as_mut_ptr(); ptr::copy(right_edges.add(count), right_edges, new_right_len + 1); right.correct_childrens_parent_links(0..=new_right_len); } @@ -1596,16 +1542,16 @@ unsafe fn move_kv( // Source and destination must have the same height. unsafe fn move_edges<'a, K: 'a, V: 'a>( - source: NodeRef, K, V, marker::Internal>, + mut source: NodeRef, K, V, marker::Internal>, source_offset: usize, mut dest: NodeRef, K, V, marker::Internal>, dest_offset: usize, count: usize, ) { unsafe { - let source_ptr = source.edge_area().as_ptr(); - let dest_ptr = dest.reborrow_mut().into_edge_area_slice().as_mut_ptr(); - ptr::copy_nonoverlapping(source_ptr.add(source_offset), dest_ptr.add(dest_offset), count); + let source_ptr = source.edge_area_mut(..).as_ptr(); + let dest_ptr = dest.edge_area_mut(dest_offset..).as_mut_ptr(); + ptr::copy_nonoverlapping(source_ptr.add(source_offset), dest_ptr, count); dest.correct_childrens_parent_links(dest_offset..dest_offset + count); } } @@ -1700,12 +1646,11 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma move_kv(left_kv, new_left_len, right_kv, 0, new_right_len); - *left_node.reborrow_mut().into_len_mut() = new_left_len as u16; - *right_node.reborrow_mut().into_len_mut() = new_right_len as u16; + *left_node.len_mut() = new_left_len as u16; + *right_node.len_mut() = new_right_len as u16; match (left_node.force(), right_node.force()) { (ForceResult::Internal(left), ForceResult::Internal(right)) => { - let left = left.reborrow(); move_edges(left, new_left_len + 1, right, 1, new_right_len); } (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 6886962106b0..7fe8ff743c04 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -30,11 +30,15 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> let depth = self.height(); let indent = " ".repeat(depth); result += &format!("\n{}", indent); - for idx in 0..leaf.len() { - if idx > 0 { - result += ", "; + if leaf.len() == 0 { + result += "(empty node)"; + } else { + for idx in 0..leaf.len() { + if idx > 0 { + result += ", "; + } + result += &format!("{:?}", unsafe { leaf.key_at(idx) }); } - result += &format!("{:?}", unsafe { leaf.key_at(idx) }); } } navigate::Position::Internal(_) => {} diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index 5ae1c1fcab80..7aeb39cbc320 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -1,7 +1,6 @@ use super::map::MIN_LEN; use super::node::{marker, ForceResult::*, Handle, LeftOrRight::*, NodeRef}; use super::unwrap_unchecked; -use core::mem; impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInternal>, marker::KV> { /// Removes a key-value pair from the tree, and returns that pair, as well as @@ -55,12 +54,12 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark pos = unsafe { new_pos.cast_to_leaf_unchecked() }; // Only if we merged, the parent (if any) has shrunk, but skipping - // the following step does not pay off in benchmarks. + // the following step otherwise does not pay off in benchmarks. // // SAFETY: We won't destroy or rearrange the leaf where `pos` is at // by handling its parent recursively; at worst we will destroy or // rearrange the parent through the grandparent, thus change the - // leaf's parent pointer. + // link to the parent inside the leaf. if let Ok(parent) = unsafe { pos.reborrow_mut() }.into_node().ascend() { parent.into_node().handle_shrunk_node_recursively(handle_emptied_internal_root); } @@ -84,16 +83,15 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, // The internal node may have been stolen from or merged. Go back right // to find where the original KV ended up. let mut internal = unsafe { unwrap_unchecked(left_hole.next_kv().ok()) }; - let old_key = mem::replace(internal.kv_mut().0, left_kv.0); - let old_val = mem::replace(internal.kv_mut().1, left_kv.1); + let old_kv = internal.replace_kv(left_kv.0, left_kv.1); let pos = internal.next_leaf_edge(); - ((old_key, old_val), pos) + (old_kv, pos) } } impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { - /// Stocks up a possibly underfull internal node, recursively. - /// Climbs up until it reaches an ancestor that has elements to spare or the root. + /// Stocks up a possibly underfull internal node and its ancestors, + /// until it reaches an ancestor that has elements to spare or is the root. fn handle_shrunk_node_recursively(mut self, handle_emptied_internal_root: F) { loop { self = match self.len() { @@ -124,7 +122,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { ) -> Option, K, V, marker::Internal>> { match self.forget_type().choose_parent_kv() { Ok(Left(left_parent_kv)) => { - debug_assert!(left_parent_kv.right_child_len() == MIN_LEN - 1); + debug_assert_eq!(left_parent_kv.right_child_len(), MIN_LEN - 1); if left_parent_kv.can_merge() { let pos = left_parent_kv.merge(None); let parent_edge = unsafe { unwrap_unchecked(pos.into_node().ascend().ok()) }; @@ -136,7 +134,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { } } Ok(Right(right_parent_kv)) => { - debug_assert!(right_parent_kv.left_child_len() == MIN_LEN - 1); + debug_assert_eq!(right_parent_kv.left_child_len(), MIN_LEN - 1); if right_parent_kv.can_merge() { let pos = right_parent_kv.merge(None); let parent_edge = unsafe { unwrap_unchecked(pos.into_node().ascend().ok()) }; diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index d8ce47ed77d1..c72e305a1f94 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -679,7 +679,7 @@ impl BTreeSet { /// ``` #[unstable(feature = "map_first_last", issue = "62924")] pub fn pop_first(&mut self) -> Option { - self.map.first_entry().map(|entry| entry.remove_entry().0) + self.map.pop_first().map(|kv| kv.0) } /// Removes the last value from the set and returns it, if any. @@ -701,7 +701,7 @@ impl BTreeSet { /// ``` #[unstable(feature = "map_first_last", issue = "62924")] pub fn pop_last(&mut self) -> Option { - self.map.last_entry().map(|entry| entry.remove_entry().0) + self.map.pop_last().map(|kv| kv.0) } /// Adds a value to the set. @@ -975,6 +975,7 @@ impl BTreeSet { /// v.insert(1); /// assert_eq!(v.len(), 1); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] pub const fn len(&self) -> usize { diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 4d05bc4ebfa1..fd19c0078a74 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -696,8 +696,10 @@ fn test_first_last() { assert_eq!(a.pop_last(), None); } +// Unlike the function with the same name in map/tests, returns no values. +// Which also means it returns different predetermined pseudo-random keys, +// and the test cases using this function explore slightly different trees. fn rand_data(len: usize) -> Vec { - assert!(len <= 70029); // from that point on numbers repeat let mut rng = DeterministicRng::new(); Vec::from_iter((0..len).map(|_| rng.next())) } diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index 6108c139bb3a..4561c8eaf47f 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -53,6 +53,9 @@ impl Root { } } + /// Stock up or merge away any underfull nodes on the right border of the + /// tree. The other nodes, those that are not the root nor a rightmost edge, + /// must already have at least MIN_LEN elements. fn fix_right_border(&mut self) { self.fix_top(); @@ -72,6 +75,7 @@ impl Root { } cur_node = last_kv.into_right_child(); } + debug_assert!(cur_node.len() > MIN_LEN); } } @@ -98,6 +102,7 @@ impl Root { } cur_node = first_kv.into_left_child(); } + debug_assert!(cur_node.len() > MIN_LEN); } } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 412c65681e68..397e774f1a03 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -593,6 +593,7 @@ impl LinkedList { /// dl.push_back(3); /// assert_eq!(dl.len(), 3); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { @@ -1099,68 +1100,6 @@ impl ExactSizeIterator for IterMut<'_, T> {} #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IterMut<'_, T> {} -impl IterMut<'_, T> { - /// Inserts the given element just after the element most recently returned by `.next()`. - /// The inserted element does not appear in the iteration. - /// - /// This method will be removed soon. - #[inline] - #[unstable( - feature = "linked_list_extras", - reason = "this is probably better handled by a cursor type -- we'll see", - issue = "27794" - )] - #[rustc_deprecated( - reason = "Deprecated in favor of CursorMut methods. This method will be removed soon.", - since = "1.47.0" - )] - pub fn insert_next(&mut self, element: T) { - match self.head { - // `push_back` is okay with aliasing `element` references - None => self.list.push_back(element), - Some(head) => unsafe { - let prev = match head.as_ref().prev { - // `push_front` is okay with aliasing nodes - None => return self.list.push_front(element), - Some(prev) => prev, - }; - - let node = Some( - Box::leak(box Node { next: Some(head), prev: Some(prev), element }).into(), - ); - - // Not creating references to entire nodes to not invalidate the - // reference to `element` we handed to the user. - (*prev.as_ptr()).next = node; - (*head.as_ptr()).prev = node; - - self.list.len += 1; - }, - } - } - - /// Provides a reference to the next element, without changing the iterator. - /// - /// This method will be removed soon. - #[inline] - #[unstable( - feature = "linked_list_extras", - reason = "this is probably better handled by a cursor type -- we'll see", - issue = "27794" - )] - #[rustc_deprecated( - reason = "Deprecated in favor of CursorMut methods. This method will be removed soon.", - since = "1.47.0" - )] - pub fn peek_next(&mut self) -> Option<&mut T> { - if self.len == 0 { - None - } else { - unsafe { self.head.as_mut().map(|node| &mut node.as_mut().element) } - } - } -} - /// A cursor over a `LinkedList`. /// /// A `Cursor` is like an iterator, except that it can freely seek back-and-forth. diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 85c809e0d188..f8fad6de1a3c 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1038,6 +1038,7 @@ impl VecDeque { /// v.push_back(1); /// assert_eq!(v.len(), 1); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { count(self.tail, self.head, self.cap()) @@ -1080,8 +1081,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(deque_range)] - /// /// use std::collections::VecDeque; /// /// let v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); @@ -1093,7 +1092,7 @@ impl VecDeque { /// assert_eq!(all.len(), 3); /// ``` #[inline] - #[unstable(feature = "deque_range", issue = "74217")] + #[stable(feature = "deque_range", since = "1.51.0")] pub fn range(&self, range: R) -> Iter<'_, T> where R: RangeBounds, @@ -1117,8 +1116,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(deque_range)] - /// /// use std::collections::VecDeque; /// /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); @@ -1134,7 +1131,7 @@ impl VecDeque { /// assert_eq!(v, vec![2, 4, 12]); /// ``` #[inline] - #[unstable(feature = "deque_range", issue = "74217")] + #[stable(feature = "deque_range", since = "1.51.0")] pub fn range_mut(&mut self, range: R) -> IterMut<'_, T> where R: RangeBounds, @@ -1469,6 +1466,8 @@ impl VecDeque { #[inline] fn is_contiguous(&self) -> bool { + // FIXME: Should we consider `head == 0` to mean + // that `self` is contiguous? self.tail <= self.head } @@ -2198,7 +2197,7 @@ impl VecDeque { if self.is_contiguous() { let tail = self.tail; let head = self.head; - return unsafe { &mut self.buffer_as_mut_slice()[tail..head] }; + return unsafe { RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0 }; } let buf = self.buf.ptr(); @@ -2224,7 +2223,13 @@ impl VecDeque { self.tail = 0; self.head = len; } - } else if free >= self.head { + } else if free > self.head { + // FIXME: We currently do not consider ....ABCDEFGH + // to be contiguous because `head` would be `0` in this + // case. While we probably want to change this it + // isn't trivial as a few places expect `is_contiguous` + // to mean that we can just slice using `buf[tail..head]`. + // there is enough free space to copy the head in one go, // this means that we first shift the tail forwards, and then // copy the head to the correct position. @@ -2238,7 +2243,7 @@ impl VecDeque { // ...ABCDEFGH. self.tail = self.head; - self.head = self.tail + len; + self.head = self.wrap_add(self.tail, len); } } else { // free is smaller than both head and tail, @@ -2278,7 +2283,7 @@ impl VecDeque { let tail = self.tail; let head = self.head; - unsafe { &mut self.buffer_as_mut_slice()[tail..head] } + unsafe { RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0 } } /// Rotates the double-ended queue `mid` places to the left. @@ -2785,8 +2790,12 @@ impl From> for VecDeque { let len = other.len(); // We need to extend the buf if it's not a power of two, too small - // or doesn't have at least one free space - if !buf.capacity().is_power_of_two() + // or doesn't have at least one free space. + // We check if `T` is a ZST in the first condition, + // because `usize::MAX` (the capacity returned by `capacity()` for ZST) + // is not a power of two and thus it'll always try + // to reserve more memory which will panic for ZST (rust-lang/rust#78532) + if (!buf.capacity().is_power_of_two() && mem::size_of::() != 0) || (buf.capacity() < (MINIMUM_CAPACITY + 1)) || (buf.capacity() == len) { @@ -2839,7 +2848,7 @@ impl From> for Vec { let len = other.len(); let cap = other.cap(); - if other.head != 0 { + if other.tail != 0 { ptr::copy(buf.add(other.tail), buf, len); } Vec::from_raw_parts(buf, len, cap) diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index d74f91c752c0..21f52af056b2 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -210,6 +210,20 @@ fn make_contiguous_small_free() { ); } +#[test] +fn make_contiguous_head_to_end() { + let mut dq = VecDeque::with_capacity(3); + dq.push_front('B'); + dq.push_front('A'); + dq.push_back('C'); + dq.make_contiguous(); + let expected_tail = 0; + let expected_head = 3; + assert_eq!(expected_tail, dq.tail); + assert_eq!(expected_head, dq.head); + assert_eq!((&['A', 'B', 'C'] as &[_], &[] as &[_]), dq.as_slices()); +} + #[test] fn test_remove() { // This test checks that every single combination of tail position, length, and diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 3ac34c9ae28a..e6db66ac5715 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -112,8 +112,7 @@ #![feature(never_type)] #![feature(nll)] #![feature(nonnull_slice_from_raw_parts)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(or_patterns)] #![feature(pattern)] #![feature(ptr_internals)] @@ -140,6 +139,7 @@ #![feature(try_trait)] #![feature(type_alias_impl_trait)] #![feature(associated_type_bounds)] +#![feature(slice_group_by)] // Allow testing this library #[cfg(test)] diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index a992d768d631..7d4eff6185db 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -29,6 +29,10 @@ /// to the same boxed integer value, not five references pointing to independently /// boxed integers. /// +/// Also, note that `vec![expr; 0]` is allowed, and produces an empty vector. +/// This will still evaluate `expr`, however, and immediately drop the resulting value, so +/// be mindful of side effects. +/// /// [`Vec`]: crate::vec::Vec #[cfg(not(test))] #[macro_export] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index edee439ad8df..36b7efc33a87 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -465,6 +465,7 @@ impl RawVec { // above `RawVec::grow_amortized` for details. (The `A` parameter isn't // significant, because the number of different `A` types seen in practice is // much smaller than the number of `T` types.) +#[inline(never)] fn finish_grow( new_layout: Result, current_memory: Option<(NonNull, Layout)>, diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index a96be57143d3..57df28a15c85 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1749,7 +1749,7 @@ struct WeakInner<'a> { strong: &'a Cell, } -impl Weak { +impl Weak { /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// /// The pointer is valid only if there are some strong references. The pointer may be dangling, @@ -1882,7 +1882,9 @@ impl Weak { // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. Weak { ptr: unsafe { NonNull::new_unchecked(ptr) } } } +} +impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Rc`], delaying /// dropping of the inner value if successful. /// @@ -2040,7 +2042,7 @@ impl Drop for Weak { // the strong pointers have disappeared. if inner.weak() == 0 { unsafe { - Global.deallocate(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); + Global.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); } } } diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index bb5c3f4f9043..2d183a8c88c6 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -208,30 +208,6 @@ fn into_from_weak_raw() { } } -#[test] -fn test_into_from_weak_raw_unsized() { - use std::fmt::Display; - use std::string::ToString; - - let arc: Rc = Rc::from("foo"); - let weak: Weak = Rc::downgrade(&arc); - - let ptr = Weak::into_raw(weak.clone()); - let weak2 = unsafe { Weak::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }, "foo"); - assert!(weak.ptr_eq(&weak2)); - - let arc: Rc = Rc::new(123); - let weak: Weak = Rc::downgrade(&arc); - - let ptr = Weak::into_raw(weak.clone()); - let weak2 = unsafe { Weak::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }.to_string(), "123"); - assert!(weak.ptr_eq(&weak2)); -} - #[test] fn get_mut() { let mut x = Rc::new(3); diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 064700fc72c9..cb015b949305 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -110,6 +110,8 @@ pub use core::slice::{Chunks, Windows}; pub use core::slice::{ChunksExact, ChunksExactMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{ChunksMut, Split, SplitMut}; +#[unstable(feature = "slice_group_by", issue = "80552")] +pub use core::slice::{GroupBy, GroupByMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{Iter, IterMut}; #[stable(feature = "rchunks", since = "1.31.0")] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index ce216e5336eb..919889285931 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1388,6 +1388,7 @@ impl String { /// assert_eq!(fancy_f.len(), 4); /// assert_eq!(fancy_f.chars().count(), 3); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { @@ -1413,7 +1414,7 @@ impl String { self.len() == 0 } - /// Splits the string into two at the given index. + /// Splits the string into two at the given byte index. /// /// Returns a newly allocated `String`. `self` contains bytes `[0, at)`, and /// the returned `String` contains bytes `[at, len)`. `at` must be on the diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 9d478a302e96..85c0a9f08572 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -14,7 +14,7 @@ use core::hint; use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unpin, Unsize}; -use core::mem::{self, align_of_val, size_of_val}; +use core::mem::{self, align_of_val_raw, size_of_val}; use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; use core::pin::Pin; use core::ptr::{self, NonNull}; @@ -1535,7 +1535,7 @@ struct WeakInner<'a> { strong: &'a atomic::AtomicUsize, } -impl Weak { +impl Weak { /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// /// The pointer is valid only if there are some strong references. The pointer may be dangling, @@ -1668,7 +1668,9 @@ impl Weak { // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. unsafe { Weak { ptr: NonNull::new_unchecked(ptr) } } } +} +impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying /// dropping of the inner value if successful. /// @@ -1925,7 +1927,7 @@ impl Drop for Weak { if inner.weak.fetch_sub(1, Release) == 1 { acquire!(inner.weak); - unsafe { Global.deallocate(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } + unsafe { Global.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())) } } } } @@ -2364,7 +2366,7 @@ unsafe fn data_offset(ptr: *const T) -> isize { // Because it is `?Sized`, it will always be the last field in memory. // Note: This is a detail of the current implementation of the compiler, // and is not a guaranteed language detail. Do not rely on it outside of std. - unsafe { data_offset_align(align_of_val(&*ptr)) } + unsafe { data_offset_align(align_of_val_raw(ptr)) } } #[inline] diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs index 77f328d48f94..e8e1e66da5ed 100644 --- a/library/alloc/src/sync/tests.rs +++ b/library/alloc/src/sync/tests.rs @@ -158,30 +158,6 @@ fn into_from_weak_raw() { } } -#[test] -fn test_into_from_weak_raw_unsized() { - use std::fmt::Display; - use std::string::ToString; - - let arc: Arc = Arc::from("foo"); - let weak: Weak = Arc::downgrade(&arc); - - let ptr = Weak::into_raw(weak.clone()); - let weak2 = unsafe { Weak::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }, "foo"); - assert!(weak.ptr_eq(&weak2)); - - let arc: Arc = Arc::new(123); - let weak: Weak = Arc::downgrade(&arc); - - let ptr = Weak::into_raw(weak.clone()); - let weak2 = unsafe { Weak::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }.to_string(), "123"); - assert!(weak.ptr_eq(&weak2)); -} - #[test] fn test_cowarc_clone_make_mut() { let mut cow0 = Arc::new(75); diff --git a/library/alloc/src/vec/cow.rs b/library/alloc/src/vec/cow.rs new file mode 100644 index 000000000000..73d15d306478 --- /dev/null +++ b/library/alloc/src/vec/cow.rs @@ -0,0 +1,35 @@ +use crate::borrow::Cow; +use core::iter::FromIterator; + +use super::Vec; + +#[stable(feature = "cow_from_vec", since = "1.8.0")] +impl<'a, T: Clone> From<&'a [T]> for Cow<'a, [T]> { + fn from(s: &'a [T]) -> Cow<'a, [T]> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_vec", since = "1.8.0")] +impl<'a, T: Clone> From> for Cow<'a, [T]> { + fn from(v: Vec) -> Cow<'a, [T]> { + Cow::Owned(v) + } +} + +#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] +impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { + fn from(v: &'a Vec) -> Cow<'a, [T]> { + Cow::Borrowed(v.as_slice()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> FromIterator for Cow<'a, [T]> +where + T: Clone, +{ + fn from_iter>(it: I) -> Cow<'a, [T]> { + Cow::Owned(FromIterator::from_iter(it)) + } +} diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs new file mode 100644 index 000000000000..fb32d144f872 --- /dev/null +++ b/library/alloc/src/vec/drain.rs @@ -0,0 +1,155 @@ +use crate::alloc::{Allocator, Global}; +use core::fmt; +use core::iter::{FusedIterator, TrustedLen}; +use core::mem::{self}; +use core::ptr::{self, NonNull}; +use core::slice::{self}; + +use super::Vec; + +/// A draining iterator for `Vec`. +/// +/// This `struct` is created by [`Vec::drain`]. +/// See its documentation for more. +/// +/// # Example +/// +/// ``` +/// let mut v = vec![0, 1, 2]; +/// let iter: std::vec::Drain<_> = v.drain(..); +/// ``` +#[stable(feature = "drain", since = "1.6.0")] +pub struct Drain< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, +> { + /// Index of tail to preserve + pub(super) tail_start: usize, + /// Length of tail + pub(super) tail_len: usize, + /// Current remaining range to remove + pub(super) iter: slice::Iter<'a, T>, + pub(super) vec: NonNull>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Drain<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() + } +} + +impl<'a, T, A: Allocator> Drain<'a, T, A> { + /// Returns the remaining items of this iterator as a slice. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec!['a', 'b', 'c']; + /// let mut drain = vec.drain(..); + /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); + /// let _ = drain.next().unwrap(); + /// assert_eq!(drain.as_slice(), &['b', 'c']); + /// ``` + #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] + pub fn as_slice(&self) -> &[T] { + self.iter.as_slice() + } + + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn allocator(&self) -> &A { + unsafe { self.vec.as_ref().allocator() } + } +} + +#[stable(feature = "vec_drain_as_slice", since = "1.46.0")] +impl<'a, T, A: Allocator> AsRef<[T]> for Drain<'a, T, A> { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl Sync for Drain<'_, T, A> {} +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl Send for Drain<'_, T, A> {} + +#[stable(feature = "drain", since = "1.6.0")] +impl Iterator for Drain<'_, T, A> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|elt| unsafe { ptr::read(elt as *const _) }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl DoubleEndedIterator for Drain<'_, T, A> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|elt| unsafe { ptr::read(elt as *const _) }) + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl Drop for Drain<'_, T, A> { + fn drop(&mut self) { + /// Continues dropping the remaining elements in the `Drain`, then moves back the + /// un-`Drain`ed elements to restore the original `Vec`. + struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>); + + impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { + fn drop(&mut self) { + // Continue the same loop we have below. If the loop already finished, this does + // nothing. + self.0.for_each(drop); + + if self.0.tail_len > 0 { + unsafe { + let source_vec = self.0.vec.as_mut(); + // memmove back untouched tail, update to new length + let start = source_vec.len(); + let tail = self.0.tail_start; + if tail != start { + let src = source_vec.as_ptr().add(tail); + let dst = source_vec.as_mut_ptr().add(start); + ptr::copy(src, dst, self.0.tail_len); + } + source_vec.set_len(start + self.0.tail_len); + } + } + } + } + + // exhaust self first + while let Some(item) = self.next() { + let guard = DropGuard(self); + drop(item); + mem::forget(guard); + } + + // Drop a `DropGuard` to move back the non-drained tail of `self`. + DropGuard(self); + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl ExactSizeIterator for Drain<'_, T, A> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Drain<'_, T, A> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Drain<'_, T, A> {} diff --git a/library/alloc/src/vec/drain_filter.rs b/library/alloc/src/vec/drain_filter.rs new file mode 100644 index 000000000000..3c37c92ae44b --- /dev/null +++ b/library/alloc/src/vec/drain_filter.rs @@ -0,0 +1,143 @@ +use crate::alloc::{Allocator, Global}; +use core::ptr::{self}; +use core::slice::{self}; + +use super::Vec; + +/// An iterator which uses a closure to determine if an element should be removed. +/// +/// This struct is created by [`Vec::drain_filter`]. +/// See its documentation for more. +/// +/// # Example +/// +/// ``` +/// #![feature(drain_filter)] +/// +/// let mut v = vec![0, 1, 2]; +/// let iter: std::vec::DrainFilter<_, _> = v.drain_filter(|x| *x % 2 == 0); +/// ``` +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +#[derive(Debug)] +pub struct DrainFilter< + 'a, + T, + F, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> where + F: FnMut(&mut T) -> bool, +{ + pub(super) vec: &'a mut Vec, + /// The index of the item that will be inspected by the next call to `next`. + pub(super) idx: usize, + /// The number of items that have been drained (removed) thus far. + pub(super) del: usize, + /// The original length of `vec` prior to draining. + pub(super) old_len: usize, + /// The filter test predicate. + pub(super) pred: F, + /// A flag that indicates a panic has occurred in the filter test predicate. + /// This is used as a hint in the drop implementation to prevent consumption + /// of the remainder of the `DrainFilter`. Any unprocessed items will be + /// backshifted in the `vec`, but no further items will be dropped or + /// tested by the filter predicate. + pub(super) panic_flag: bool, +} + +impl DrainFilter<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn allocator(&self) -> &A { + self.vec.allocator() + } +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl Iterator for DrainFilter<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + unsafe { + while self.idx < self.old_len { + let i = self.idx; + let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); + self.panic_flag = true; + let drained = (self.pred)(&mut v[i]); + self.panic_flag = false; + // Update the index *after* the predicate is called. If the index + // is updated prior and the predicate panics, the element at this + // index would be leaked. + self.idx += 1; + if drained { + self.del += 1; + return Some(ptr::read(&v[i])); + } else if self.del > 0 { + let del = self.del; + let src: *const T = &v[i]; + let dst: *mut T = &mut v[i - del]; + ptr::copy_nonoverlapping(src, dst, 1); + } + } + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl Drop for DrainFilter<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + fn drop(&mut self) { + struct BackshiftOnDrop<'a, 'b, T, F, A: Allocator> + where + F: FnMut(&mut T) -> bool, + { + drain: &'b mut DrainFilter<'a, T, F, A>, + } + + impl<'a, 'b, T, F, A: Allocator> Drop for BackshiftOnDrop<'a, 'b, T, F, A> + where + F: FnMut(&mut T) -> bool, + { + fn drop(&mut self) { + unsafe { + if self.drain.idx < self.drain.old_len && self.drain.del > 0 { + // This is a pretty messed up state, and there isn't really an + // obviously right thing to do. We don't want to keep trying + // to execute `pred`, so we just backshift all the unprocessed + // elements and tell the vec that they still exist. The backshift + // is required to prevent a double-drop of the last successfully + // drained item prior to a panic in the predicate. + let ptr = self.drain.vec.as_mut_ptr(); + let src = ptr.add(self.drain.idx); + let dst = src.sub(self.drain.del); + let tail_len = self.drain.old_len - self.drain.idx; + src.copy_to(dst, tail_len); + } + self.drain.vec.set_len(self.drain.old_len - self.drain.del); + } + } + } + + let backshift = BackshiftOnDrop { drain: self }; + + // Attempt to consume any remaining elements if the filter predicate + // has not yet panicked. We'll backshift any remaining elements + // whether we've already panicked or if the consumption here panics. + if !backshift.drain.panic_flag { + backshift.drain.for_each(drop); + } + } +} diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs new file mode 100644 index 000000000000..354d25c2389f --- /dev/null +++ b/library/alloc/src/vec/in_place_drop.rs @@ -0,0 +1,24 @@ +use core::ptr::{self}; +use core::slice::{self}; + +// A helper struct for in-place iteration that drops the destination slice of iteration, +// i.e. the head. The source slice (the tail) is dropped by IntoIter. +pub(super) struct InPlaceDrop { + pub(super) inner: *mut T, + pub(super) dst: *mut T, +} + +impl InPlaceDrop { + fn len(&self) -> usize { + unsafe { self.dst.offset_from(self.inner) as usize } + } +} + +impl Drop for InPlaceDrop { + #[inline] + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len())); + } + } +} diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs new file mode 100644 index 000000000000..f131d06bb18f --- /dev/null +++ b/library/alloc/src/vec/into_iter.rs @@ -0,0 +1,283 @@ +use crate::alloc::{Allocator, Global}; +use crate::raw_vec::RawVec; +use core::fmt; +use core::intrinsics::arith_offset; +use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess}; +use core::marker::PhantomData; +use core::mem::{self}; +use core::ptr::{self, NonNull}; +use core::slice::{self}; + +/// An iterator that moves out of a vector. +/// +/// This `struct` is created by the `into_iter` method on [`Vec`](super::Vec) +/// (provided by the [`IntoIterator`] trait). +/// +/// # Example +/// +/// ``` +/// let v = vec![0, 1, 2]; +/// let iter: std::vec::IntoIter<_> = v.into_iter(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + pub(super) buf: NonNull, + pub(super) phantom: PhantomData, + pub(super) cap: usize, + pub(super) alloc: A, + pub(super) ptr: *const T, + pub(super) end: *const T, +} + +#[stable(feature = "vec_intoiter_debug", since = "1.13.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.as_slice()).finish() + } +} + +impl IntoIter { + /// Returns the remaining items of this iterator as a slice. + /// + /// # Examples + /// + /// ``` + /// let vec = vec!['a', 'b', 'c']; + /// let mut into_iter = vec.into_iter(); + /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + /// let _ = into_iter.next().unwrap(); + /// assert_eq!(into_iter.as_slice(), &['b', 'c']); + /// ``` + #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] + pub fn as_slice(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.ptr, self.len()) } + } + + /// Returns the remaining items of this iterator as a mutable slice. + /// + /// # Examples + /// + /// ``` + /// let vec = vec!['a', 'b', 'c']; + /// let mut into_iter = vec.into_iter(); + /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + /// into_iter.as_mut_slice()[2] = 'z'; + /// assert_eq!(into_iter.next().unwrap(), 'a'); + /// assert_eq!(into_iter.next().unwrap(), 'b'); + /// assert_eq!(into_iter.next().unwrap(), 'z'); + /// ``` + #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] + pub fn as_mut_slice(&mut self) -> &mut [T] { + unsafe { &mut *self.as_raw_mut_slice() } + } + + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn allocator(&self) -> &A { + &self.alloc + } + + fn as_raw_mut_slice(&mut self) -> *mut [T] { + ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) + } + + pub(super) fn drop_remaining(&mut self) { + unsafe { + ptr::drop_in_place(self.as_mut_slice()); + } + self.ptr = self.end; + } + + /// Relinquishes the backing allocation, equivalent to + /// `ptr::write(&mut self, Vec::new().into_iter())` + pub(super) fn forget_allocation(&mut self) { + self.cap = 0; + self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) }; + self.ptr = self.buf.as_ptr(); + self.end = self.buf.as_ptr(); + } +} + +#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")] +impl AsRef<[T]> for IntoIter { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IntoIter {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IntoIter {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + if self.ptr as *const _ == self.end { + None + } else if mem::size_of::() == 0 { + // purposefully don't use 'ptr.offset' because for + // vectors with 0-size elements this would return the + // same pointer. + self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T }; + + // Make up a value of this ZST. + Some(unsafe { mem::zeroed() }) + } else { + let old = self.ptr; + self.ptr = unsafe { self.ptr.offset(1) }; + + Some(unsafe { ptr::read(old) }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let exact = if mem::size_of::() == 0 { + (self.end as usize).wrapping_sub(self.ptr as usize) + } else { + unsafe { self.end.offset_from(self.ptr) as usize } + }; + (exact, Some(exact)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must guarantee that `i` is in bounds of the + // `Vec`, so `i` cannot overflow an `isize`, and the `self.ptr.add(i)` + // is guaranteed to pointer to an element of the `Vec` and + // thus guaranteed to be valid to dereference. + // + // Also note the implementation of `Self: TrustedRandomAccess` requires + // that `T: Copy` so reading elements from the buffer doesn't invalidate + // them for `Drop`. + unsafe { + if mem::size_of::() == 0 { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + if self.end == self.ptr { + None + } else if mem::size_of::() == 0 { + // See above for why 'ptr.offset' isn't used + self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T }; + + // Make up a value of this ZST. + Some(unsafe { mem::zeroed() }) + } else { + self.end = unsafe { self.end.offset(-1) }; + + Some(unsafe { ptr::read(self.end) }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn is_empty(&self) -> bool { + self.ptr == self.end + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + +#[doc(hidden)] +#[unstable(issue = "none", feature = "std_internals")] +// T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr +// and thus we can't implement drop-handling +unsafe impl TrustedRandomAccess for IntoIter +where + T: Copy, +{ + fn may_have_side_effect() -> bool { + false + } +} + +#[stable(feature = "vec_into_iter_clone", since = "1.8.0")] +impl Clone for IntoIter { + #[cfg(not(test))] + fn clone(&self) -> Self { + self.as_slice().to_vec_in(self.alloc.clone()).into_iter() + } + #[cfg(test)] + fn clone(&self) -> Self { + crate::slice::to_vec(self.as_slice(), self.alloc.clone()).into_iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { + fn drop(&mut self) { + struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter); + + impl Drop for DropGuard<'_, T, A> { + fn drop(&mut self) { + unsafe { + // `IntoIter::alloc` is not used anymore after this + let alloc = ptr::read(&self.0.alloc); + // RawVec handles deallocation + let _ = RawVec::from_raw_parts_in(self.0.buf.as_ptr(), self.0.cap, alloc); + } + } + } + + let guard = DropGuard(self); + // destroy the remaining elements + unsafe { + ptr::drop_in_place(guard.0.as_raw_mut_slice()); + } + // now `guard` will be dropped and do the rest + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for IntoIter {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for IntoIter { + type Source = Self; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut Self::Source { + self + } +} + +// internal helper trait for in-place iteration specialization. +#[rustc_specialization_trait] +pub(crate) trait AsIntoIter { + type Item; + fn as_into_iter(&mut self) -> &mut IntoIter; +} + +impl AsIntoIter for IntoIter { + type Item = T; + + fn as_into_iter(&mut self) -> &mut IntoIter { + self + } +} diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs new file mode 100644 index 000000000000..b5739970b6ea --- /dev/null +++ b/library/alloc/src/vec/is_zero.rs @@ -0,0 +1,71 @@ +use crate::boxed::Box; + +#[rustc_specialization_trait] +pub(super) unsafe trait IsZero { + /// Whether this value is zero + fn is_zero(&self) -> bool; +} + +macro_rules! impl_is_zero { + ($t:ty, $is_zero:expr) => { + unsafe impl IsZero for $t { + #[inline] + fn is_zero(&self) -> bool { + $is_zero(*self) + } + } + }; +} + +impl_is_zero!(i16, |x| x == 0); +impl_is_zero!(i32, |x| x == 0); +impl_is_zero!(i64, |x| x == 0); +impl_is_zero!(i128, |x| x == 0); +impl_is_zero!(isize, |x| x == 0); + +impl_is_zero!(u16, |x| x == 0); +impl_is_zero!(u32, |x| x == 0); +impl_is_zero!(u64, |x| x == 0); +impl_is_zero!(u128, |x| x == 0); +impl_is_zero!(usize, |x| x == 0); + +impl_is_zero!(bool, |x| x == false); +impl_is_zero!(char, |x| x == '\0'); + +impl_is_zero!(f32, |x: f32| x.to_bits() == 0); +impl_is_zero!(f64, |x: f64| x.to_bits() == 0); + +unsafe impl IsZero for *const T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} + +unsafe impl IsZero for *mut T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} + +// `Option<&T>` and `Option>` are guaranteed to represent `None` as null. +// For fat pointers, the bytes that would be the pointer metadata in the `Some` +// variant are padding in the `None` variant, so ignoring them and +// zero-initializing instead is ok. +// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of +// `SpecFromElem`. + +unsafe impl IsZero for Option<&T> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} + +unsafe impl IsZero for Option> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec/mod.rs similarity index 65% rename from library/alloc/src/vec.rs rename to library/alloc/src/vec/mod.rs index 9fffb47aa597..2a83eb33fe3e 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec/mod.rs @@ -1,4 +1,3 @@ -// ignore-tidy-filelength //! A contiguous growable array type with heap-allocated contents, written //! `Vec`. //! @@ -59,9 +58,7 @@ use core::convert::TryFrom; use core::fmt; use core::hash::{Hash, Hasher}; use core::intrinsics::{arith_offset, assume}; -use core::iter::{ - FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess, -}; +use core::iter::FromIterator; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::{self, Index, IndexMut, Range, RangeBounds}; @@ -74,6 +71,61 @@ use crate::boxed::Box; use crate::collections::TryReserveError; use crate::raw_vec::RawVec; +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +pub use self::drain_filter::DrainFilter; + +mod drain_filter; + +#[stable(feature = "vec_splice", since = "1.21.0")] +pub use self::splice::Splice; + +mod splice; + +#[stable(feature = "drain", since = "1.6.0")] +pub use self::drain::Drain; + +mod drain; + +mod cow; + +pub(crate) use self::into_iter::AsIntoIter; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::into_iter::IntoIter; + +mod into_iter; + +use self::is_zero::IsZero; + +mod is_zero; + +mod source_iter_marker; + +mod partial_eq; + +use self::spec_from_elem::SpecFromElem; + +mod spec_from_elem; + +use self::set_len_on_drop::SetLenOnDrop; + +mod set_len_on_drop; + +use self::in_place_drop::InPlaceDrop; + +mod in_place_drop; + +use self::spec_from_iter_nested::SpecFromIterNested; + +mod spec_from_iter_nested; + +use self::spec_from_iter::SpecFromIter; + +mod spec_from_iter; + +use self::spec_extend::SpecExtend; + +mod spec_extend; + /// A contiguous growable array type, written `Vec` but pronounced 'vector'. /// /// # Examples @@ -1559,6 +1611,7 @@ impl Vec { /// let a = vec![1, 2, 3]; /// assert_eq!(a.len(), 3); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { @@ -1875,35 +1928,6 @@ impl Vec { } } -// Set the length of the vec when the `SetLenOnDrop` value goes out of scope. -// -// The idea is: The length field in SetLenOnDrop is a local variable -// that the optimizer will see does not alias with any stores through the Vec's data -// pointer. This is a workaround for alias analysis issue #32155 -struct SetLenOnDrop<'a> { - len: &'a mut usize, - local_len: usize, -} - -impl<'a> SetLenOnDrop<'a> { - #[inline] - fn new(len: &'a mut usize) -> Self { - SetLenOnDrop { local_len: *len, len } - } - - #[inline] - fn increment_len(&mut self, increment: usize) { - self.local_len += increment; - } -} - -impl Drop for SetLenOnDrop<'_> { - #[inline] - fn drop(&mut self) { - *self.len = self.local_len; - } -} - impl Vec { /// Removes consecutive repeated elements in the vector according to the /// [`PartialEq`] trait implementation. @@ -1963,131 +1987,6 @@ pub fn from_elem_in(elem: T, n: usize, alloc: A) -> Vec< ::from_elem(elem, n, alloc) } -// Specialization trait used for Vec::from_elem -trait SpecFromElem: Sized { - fn from_elem(elem: Self, n: usize, alloc: A) -> Vec; -} - -impl SpecFromElem for T { - default fn from_elem(elem: Self, n: usize, alloc: A) -> Vec { - let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); - v - } -} - -impl SpecFromElem for i8 { - #[inline] - fn from_elem(elem: i8, n: usize, alloc: A) -> Vec { - if elem == 0 { - return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; - } - unsafe { - let mut v = Vec::with_capacity_in(n, alloc); - ptr::write_bytes(v.as_mut_ptr(), elem as u8, n); - v.set_len(n); - v - } - } -} - -impl SpecFromElem for u8 { - #[inline] - fn from_elem(elem: u8, n: usize, alloc: A) -> Vec { - if elem == 0 { - return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; - } - unsafe { - let mut v = Vec::with_capacity_in(n, alloc); - ptr::write_bytes(v.as_mut_ptr(), elem, n); - v.set_len(n); - v - } - } -} - -impl SpecFromElem for T { - #[inline] - fn from_elem(elem: T, n: usize, alloc: A) -> Vec { - if elem.is_zero() { - return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; - } - let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); - v - } -} - -#[rustc_specialization_trait] -unsafe trait IsZero { - /// Whether this value is zero - fn is_zero(&self) -> bool; -} - -macro_rules! impl_is_zero { - ($t:ty, $is_zero:expr) => { - unsafe impl IsZero for $t { - #[inline] - fn is_zero(&self) -> bool { - $is_zero(*self) - } - } - }; -} - -impl_is_zero!(i16, |x| x == 0); -impl_is_zero!(i32, |x| x == 0); -impl_is_zero!(i64, |x| x == 0); -impl_is_zero!(i128, |x| x == 0); -impl_is_zero!(isize, |x| x == 0); - -impl_is_zero!(u16, |x| x == 0); -impl_is_zero!(u32, |x| x == 0); -impl_is_zero!(u64, |x| x == 0); -impl_is_zero!(u128, |x| x == 0); -impl_is_zero!(usize, |x| x == 0); - -impl_is_zero!(bool, |x| x == false); -impl_is_zero!(char, |x| x == '\0'); - -impl_is_zero!(f32, |x: f32| x.to_bits() == 0); -impl_is_zero!(f64, |x: f64| x.to_bits() == 0); - -unsafe impl IsZero for *const T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} - -unsafe impl IsZero for *mut T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} - -// `Option<&T>` and `Option>` are guaranteed to represent `None` as null. -// For fat pointers, the bytes that would be the pointer metadata in the `Some` -// variant are padding in the `None` variant, so ignoring them and -// zero-initializing instead is ok. -// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of -// `SpecFromElem`. - -unsafe impl IsZero for Option<&T> { - #[inline] - fn is_zero(&self) -> bool { - self.is_none() - } -} - -unsafe impl IsZero for Option> { - #[inline] - fn is_zero(&self) -> bool { - self.is_none() - } -} - //////////////////////////////////////////////////////////////////////////////// // Common trait implementations for Vec //////////////////////////////////////////////////////////////////////////////// @@ -2262,348 +2161,6 @@ impl Extend for Vec { } } -/// Specialization trait used for Vec::from_iter -/// -/// ## The delegation graph: -/// -/// ```text -/// +-------------+ -/// |FromIterator | -/// +-+-----------+ -/// | -/// v -/// +-+-------------------------------+ +---------------------+ -/// |SpecFromIter +---->+SpecFromIterNested | -/// |where I: | | |where I: | -/// | Iterator (default)----------+ | | Iterator (default) | -/// | vec::IntoIter | | | TrustedLen | -/// | SourceIterMarker---fallback-+ | | | -/// | slice::Iter | | | -/// | Iterator | +---------------------+ -/// +---------------------------------+ -/// ``` -trait SpecFromIter { - fn from_iter(iter: I) -> Self; -} - -/// Another specialization trait for Vec::from_iter -/// necessary to manually prioritize overlapping specializations -/// see [`SpecFromIter`] for details. -trait SpecFromIterNested { - fn from_iter(iter: I) -> Self; -} - -impl SpecFromIterNested for Vec -where - I: Iterator, -{ - default fn from_iter(mut iterator: I) -> Self { - // Unroll the first iteration, as the vector is going to be - // expanded on this iteration in every case when the iterable is not - // empty, but the loop in extend_desugared() is not going to see the - // vector being full in the few subsequent loop iterations. - // So we get better branch prediction. - let mut vector = match iterator.next() { - None => return Vec::new(), - Some(element) => { - let (lower, _) = iterator.size_hint(); - let mut vector = Vec::with_capacity(lower.saturating_add(1)); - unsafe { - ptr::write(vector.as_mut_ptr(), element); - vector.set_len(1); - } - vector - } - }; - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs - as SpecExtend>::spec_extend(&mut vector, iterator); - vector - } -} - -impl SpecFromIterNested for Vec -where - I: TrustedLen, -{ - fn from_iter(iterator: I) -> Self { - let mut vector = Vec::new(); - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs - vector.spec_extend(iterator); - vector - } -} - -impl SpecFromIter for Vec -where - I: Iterator, -{ - default fn from_iter(iterator: I) -> Self { - SpecFromIterNested::from_iter(iterator) - } -} - -// A helper struct for in-place iteration that drops the destination slice of iteration, -// i.e. the head. The source slice (the tail) is dropped by IntoIter. -struct InPlaceDrop { - inner: *mut T, - dst: *mut T, -} - -impl InPlaceDrop { - fn len(&self) -> usize { - unsafe { self.dst.offset_from(self.inner) as usize } - } -} - -impl Drop for InPlaceDrop { - #[inline] - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len())); - } - } -} - -impl SpecFromIter> for Vec { - fn from_iter(iterator: IntoIter) -> Self { - // A common case is passing a vector into a function which immediately - // re-collects into a vector. We can short circuit this if the IntoIter - // has not been advanced at all. - // When it has been advanced We can also reuse the memory and move the data to the front. - // But we only do so when the resulting Vec wouldn't have more unused capacity - // than creating it through the generic FromIterator implementation would. That limitation - // is not strictly necessary as Vec's allocation behavior is intentionally unspecified. - // But it is a conservative choice. - let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; - if !has_advanced || iterator.len() >= iterator.cap / 2 { - unsafe { - let it = ManuallyDrop::new(iterator); - if has_advanced { - ptr::copy(it.ptr, it.buf.as_ptr(), it.len()); - } - return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap); - } - } - - let mut vec = Vec::new(); - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs - vec.spec_extend(iterator); - vec - } -} - -fn write_in_place_with_drop( - src_end: *const T, -) -> impl FnMut(InPlaceDrop, T) -> Result, !> { - move |mut sink, item| { - unsafe { - // the InPlaceIterable contract cannot be verified precisely here since - // try_fold has an exclusive reference to the source pointer - // all we can do is check if it's still in range - debug_assert!(sink.dst as *const _ <= src_end, "InPlaceIterable contract violation"); - ptr::write(sink.dst, item); - sink.dst = sink.dst.add(1); - } - Ok(sink) - } -} - -/// Specialization marker for collecting an iterator pipeline into a Vec while reusing the -/// source allocation, i.e. executing the pipeline in place. -/// -/// The SourceIter parent trait is necessary for the specializing function to access the allocation -/// which is to be reused. But it is not sufficient for the specialization to be valid. See -/// additional bounds on the impl. -#[rustc_unsafe_specialization_marker] -trait SourceIterMarker: SourceIter {} - -// The std-internal SourceIter/InPlaceIterable traits are only implemented by chains of -// Adapter>> (all owned by core/std). Additional bounds -// on the adapter implementations (beyond `impl Trait for Adapter`) only depend on other -// traits already marked as specialization traits (Copy, TrustedRandomAccess, FusedIterator). -// I.e. the marker does not depend on lifetimes of user-supplied types. Modulo the Copy hole, which -// several other specializations already depend on. -impl SourceIterMarker for T where T: SourceIter + InPlaceIterable {} - -impl SpecFromIter for Vec -where - I: Iterator + SourceIterMarker, -{ - default fn from_iter(mut iterator: I) -> Self { - // Additional requirements which cannot expressed via trait bounds. We rely on const eval - // instead: - // a) no ZSTs as there would be no allocation to reuse and pointer arithmetic would panic - // b) size match as required by Alloc contract - // c) alignments match as required by Alloc contract - if mem::size_of::() == 0 - || mem::size_of::() - != mem::size_of::<<::Source as AsIntoIter>::Item>() - || mem::align_of::() - != mem::align_of::<<::Source as AsIntoIter>::Item>() - { - // fallback to more generic implementations - return SpecFromIterNested::from_iter(iterator); - } - - let (src_buf, src_ptr, dst_buf, dst_end, cap) = unsafe { - let inner = iterator.as_inner().as_into_iter(); - ( - inner.buf.as_ptr(), - inner.ptr, - inner.buf.as_ptr() as *mut T, - inner.end as *const T, - inner.cap, - ) - }; - - // use try-fold since - // - it vectorizes better for some iterator adapters - // - unlike most internal iteration methods, it only takes a &mut self - // - it lets us thread the write pointer through its innards and get it back in the end - let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; - let sink = iterator - .try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(dst_end)) - .unwrap(); - // iteration succeeded, don't drop head - let dst = ManuallyDrop::new(sink).dst; - - let src = unsafe { iterator.as_inner().as_into_iter() }; - // check if SourceIter contract was upheld - // caveat: if they weren't we may not even make it to this point - debug_assert_eq!(src_buf, src.buf.as_ptr()); - // check InPlaceIterable contract. This is only possible if the iterator advanced the - // source pointer at all. If it uses unchecked access via TrustedRandomAccess - // then the source pointer will stay in its initial position and we can't use it as reference - if src.ptr != src_ptr { - debug_assert!( - dst as *const _ <= src.ptr, - "InPlaceIterable contract violation, write pointer advanced beyond read pointer" - ); - } - - // drop any remaining values at the tail of the source - src.drop_remaining(); - // but prevent drop of the allocation itself once IntoIter goes out of scope - src.forget_allocation(); - - let vec = unsafe { - let len = dst.offset_from(dst_buf) as usize; - Vec::from_raw_parts(dst_buf, len, cap) - }; - - vec - } -} - -impl<'a, T: 'a, I> SpecFromIter<&'a T, I> for Vec -where - I: Iterator, - T: Clone, -{ - default fn from_iter(iterator: I) -> Self { - SpecFromIter::from_iter(iterator.cloned()) - } -} - -// This utilizes `iterator.as_slice().to_vec()` since spec_extend -// must take more steps to reason about the final capacity + length -// and thus do more work. `to_vec()` directly allocates the correct amount -// and fills it exactly. -impl<'a, T: 'a + Clone> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec { - #[cfg(not(test))] - fn from_iter(iterator: slice::Iter<'a, T>) -> Self { - iterator.as_slice().to_vec() - } - - // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is - // required for this method definition, is not available. Instead use the - // `slice::to_vec` function which is only available with cfg(test) - // NB see the slice::hack module in slice.rs for more information - #[cfg(test)] - fn from_iter(iterator: slice::Iter<'a, T>) -> Self { - crate::slice::to_vec(iterator.as_slice(), Global) - } -} - -// Specialization trait used for Vec::extend -trait SpecExtend { - fn spec_extend(&mut self, iter: I); -} - -impl SpecExtend for Vec -where - I: Iterator, -{ - default fn spec_extend(&mut self, iter: I) { - self.extend_desugared(iter) - } -} - -impl SpecExtend for Vec -where - I: TrustedLen, -{ - default fn spec_extend(&mut self, iterator: I) { - // This is the case for a TrustedLen iterator. - let (low, high) = iterator.size_hint(); - if let Some(high_value) = high { - debug_assert_eq!( - low, - high_value, - "TrustedLen iterator's size hint is not exact: {:?}", - (low, high) - ); - } - if let Some(additional) = high { - self.reserve(additional); - unsafe { - let mut ptr = self.as_mut_ptr().add(self.len()); - let mut local_len = SetLenOnDrop::new(&mut self.len); - iterator.for_each(move |element| { - ptr::write(ptr, element); - ptr = ptr.offset(1); - // NB can't overflow since we would have had to alloc the address space - local_len.increment_len(1); - }); - } - } else { - self.extend_desugared(iterator) - } - } -} - -impl SpecExtend> for Vec { - fn spec_extend(&mut self, mut iterator: IntoIter) { - unsafe { - self.append_elements(iterator.as_slice() as _); - } - iterator.ptr = iterator.end; - } -} - -impl<'a, T: 'a, I, A: Allocator + 'a> SpecExtend<&'a T, I> for Vec -where - I: Iterator, - T: Clone, -{ - default fn spec_extend(&mut self, iterator: I) { - self.spec_extend(iterator.cloned()) - } -} - -impl<'a, T: 'a, A: Allocator + 'a> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec -where - T: Copy, -{ - fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { - let slice = iterator.as_slice(); - unsafe { self.append_elements(slice) }; - } -} - impl Vec { // leaf method to which various SpecFrom/SpecExtend implementations delegate when // they have no further optimizations to apply @@ -2755,45 +2312,6 @@ impl<'a, T: Copy + 'a, A: Allocator + 'a> Extend<&'a T> for Vec { } } -macro_rules! __impl_slice_eq1 { - ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?, #[$stability:meta]) => { - #[$stability] - impl PartialEq<$rhs> for $lhs - where - T: PartialEq, - $($ty: $bound)? - { - #[inline] - fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } - #[inline] - fn ne(&self, other: &$rhs) -> bool { self[..] != other[..] } - } - } -} - -__impl_slice_eq1! { [A: Allocator] Vec, Vec, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator] Vec, &[U], #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator] Vec, &mut [U], #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator] &[T], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } -__impl_slice_eq1! { [A: Allocator] &mut [T], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } -__impl_slice_eq1! { [A: Allocator] Vec, [U], #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } -__impl_slice_eq1! { [A: Allocator] [T], Vec, #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } -__impl_slice_eq1! { [A: Allocator] Cow<'_, [T]>, Vec where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] Cow<'_, [T]>, &[U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] Cow<'_, [T]>, &mut [U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator, const N: usize] Vec, [U; N], #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator, const N: usize] Vec, &[U; N], #[stable(feature = "rust1", since = "1.0.0")] } - -// NOTE: some less important impls are omitted to reduce code bloat -// FIXME(Centril): Reconsider this? -//__impl_slice_eq1! { [const N: usize] Vec, &mut [B; N], } -//__impl_slice_eq1! { [const N: usize] [A; N], Vec, } -//__impl_slice_eq1! { [const N: usize] &[A; N], Vec, } -//__impl_slice_eq1! { [const N: usize] &mut [A; N], Vec, } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, [B; N], } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &[B; N], } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &mut [B; N], } - /// Implements comparison of vectors, [lexicographically](core::cmp::Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for Vec { @@ -2993,729 +2511,3 @@ impl TryFrom> for [T; N] { Ok(array) } } - -//////////////////////////////////////////////////////////////////////////////// -// Clone-on-write -//////////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "cow_from_vec", since = "1.8.0")] -impl<'a, T: Clone> From<&'a [T]> for Cow<'a, [T]> { - fn from(s: &'a [T]) -> Cow<'a, [T]> { - Cow::Borrowed(s) - } -} - -#[stable(feature = "cow_from_vec", since = "1.8.0")] -impl<'a, T: Clone> From> for Cow<'a, [T]> { - fn from(v: Vec) -> Cow<'a, [T]> { - Cow::Owned(v) - } -} - -#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] -impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { - fn from(v: &'a Vec) -> Cow<'a, [T]> { - Cow::Borrowed(v.as_slice()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> FromIterator for Cow<'a, [T]> -where - T: Clone, -{ - fn from_iter>(it: I) -> Cow<'a, [T]> { - Cow::Owned(FromIterator::from_iter(it)) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Iterators -//////////////////////////////////////////////////////////////////////////////// - -/// An iterator that moves out of a vector. -/// -/// This `struct` is created by the `into_iter` method on [`Vec`] (provided -/// by the [`IntoIterator`] trait). -/// -/// # Example -/// -/// ``` -/// let v = vec![0, 1, 2]; -/// let iter: std::vec::IntoIter<_> = v.into_iter(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter< - T, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - buf: NonNull, - phantom: PhantomData, - cap: usize, - alloc: A, - ptr: *const T, - end: *const T, -} - -#[stable(feature = "vec_intoiter_debug", since = "1.13.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("IntoIter").field(&self.as_slice()).finish() - } -} - -impl IntoIter { - /// Returns the remaining items of this iterator as a slice. - /// - /// # Examples - /// - /// ``` - /// let vec = vec!['a', 'b', 'c']; - /// let mut into_iter = vec.into_iter(); - /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); - /// let _ = into_iter.next().unwrap(); - /// assert_eq!(into_iter.as_slice(), &['b', 'c']); - /// ``` - #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] - pub fn as_slice(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.ptr, self.len()) } - } - - /// Returns the remaining items of this iterator as a mutable slice. - /// - /// # Examples - /// - /// ``` - /// let vec = vec!['a', 'b', 'c']; - /// let mut into_iter = vec.into_iter(); - /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); - /// into_iter.as_mut_slice()[2] = 'z'; - /// assert_eq!(into_iter.next().unwrap(), 'a'); - /// assert_eq!(into_iter.next().unwrap(), 'b'); - /// assert_eq!(into_iter.next().unwrap(), 'z'); - /// ``` - #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] - pub fn as_mut_slice(&mut self) -> &mut [T] { - unsafe { &mut *self.as_raw_mut_slice() } - } - - /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn allocator(&self) -> &A { - &self.alloc - } - - fn as_raw_mut_slice(&mut self) -> *mut [T] { - ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) - } - - fn drop_remaining(&mut self) { - unsafe { - ptr::drop_in_place(self.as_mut_slice()); - } - self.ptr = self.end; - } - - /// Relinquishes the backing allocation, equivalent to - /// `ptr::write(&mut self, Vec::new().into_iter())` - fn forget_allocation(&mut self) { - self.cap = 0; - self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) }; - self.ptr = self.buf.as_ptr(); - self.end = self.buf.as_ptr(); - } -} - -#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")] -impl AsRef<[T]> for IntoIter { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for IntoIter {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for IntoIter {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - if self.ptr as *const _ == self.end { - None - } else if mem::size_of::() == 0 { - // purposefully don't use 'ptr.offset' because for - // vectors with 0-size elements this would return the - // same pointer. - self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T }; - - // Make up a value of this ZST. - Some(unsafe { mem::zeroed() }) - } else { - let old = self.ptr; - self.ptr = unsafe { self.ptr.offset(1) }; - - Some(unsafe { ptr::read(old) }) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let exact = if mem::size_of::() == 0 { - (self.end as usize).wrapping_sub(self.ptr as usize) - } else { - unsafe { self.end.offset_from(self.ptr) as usize } - }; - (exact, Some(exact)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item - where - Self: TrustedRandomAccess, - { - // SAFETY: the caller must guarantee that `i` is in bounds of the - // `Vec`, so `i` cannot overflow an `isize`, and the `self.ptr.add(i)` - // is guaranteed to pointer to an element of the `Vec` and - // thus guaranteed to be valid to dereference. - // - // Also note the implementation of `Self: TrustedRandomAccess` requires - // that `T: Copy` so reading elements from the buffer doesn't invalidate - // them for `Drop`. - unsafe { - if mem::size_of::() == 0 { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - if self.end == self.ptr { - None - } else if mem::size_of::() == 0 { - // See above for why 'ptr.offset' isn't used - self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T }; - - // Make up a value of this ZST. - Some(unsafe { mem::zeroed() }) - } else { - self.end = unsafe { self.end.offset(-1) }; - - Some(unsafe { ptr::read(self.end) }) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn is_empty(&self) -> bool { - self.ptr == self.end - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IntoIter {} - -#[doc(hidden)] -#[unstable(issue = "none", feature = "std_internals")] -// T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr -// and thus we can't implement drop-handling -unsafe impl TrustedRandomAccess for IntoIter -where - T: Copy, -{ - fn may_have_side_effect() -> bool { - false - } -} - -#[stable(feature = "vec_into_iter_clone", since = "1.8.0")] -impl Clone for IntoIter { - #[cfg(not(test))] - fn clone(&self) -> Self { - self.as_slice().to_vec_in(self.alloc.clone()).into_iter() - } - #[cfg(test)] - fn clone(&self) -> Self { - crate::slice::to_vec(self.as_slice(), self.alloc.clone()).into_iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { - fn drop(&mut self) { - struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter); - - impl Drop for DropGuard<'_, T, A> { - fn drop(&mut self) { - unsafe { - // `IntoIter::alloc` is not used anymore after this - let alloc = ptr::read(&self.0.alloc); - // RawVec handles deallocation - let _ = RawVec::from_raw_parts_in(self.0.buf.as_ptr(), self.0.cap, alloc); - } - } - } - - let guard = DropGuard(self); - // destroy the remaining elements - unsafe { - ptr::drop_in_place(guard.0.as_raw_mut_slice()); - } - // now `guard` will be dropped and do the rest - } -} - -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl InPlaceIterable for IntoIter {} - -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl SourceIter for IntoIter { - type Source = Self; - - #[inline] - unsafe fn as_inner(&mut self) -> &mut Self::Source { - self - } -} - -// internal helper trait for in-place iteration specialization. -#[rustc_specialization_trait] -pub(crate) trait AsIntoIter { - type Item; - fn as_into_iter(&mut self) -> &mut IntoIter; -} - -impl AsIntoIter for IntoIter { - type Item = T; - - fn as_into_iter(&mut self) -> &mut IntoIter { - self - } -} - -/// A draining iterator for `Vec`. -/// -/// This `struct` is created by [`Vec::drain`]. -/// See its documentation for more. -/// -/// # Example -/// -/// ``` -/// let mut v = vec![0, 1, 2]; -/// let iter: std::vec::Drain<_> = v.drain(..); -/// ``` -#[stable(feature = "drain", since = "1.6.0")] -pub struct Drain< - 'a, - T: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, -> { - /// Index of tail to preserve - tail_start: usize, - /// Length of tail - tail_len: usize, - /// Current remaining range to remove - iter: slice::Iter<'a, T>, - vec: NonNull>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Drain<'_, T, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() - } -} - -impl<'a, T, A: Allocator> Drain<'a, T, A> { - /// Returns the remaining items of this iterator as a slice. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec!['a', 'b', 'c']; - /// let mut drain = vec.drain(..); - /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); - /// let _ = drain.next().unwrap(); - /// assert_eq!(drain.as_slice(), &['b', 'c']); - /// ``` - #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] - pub fn as_slice(&self) -> &[T] { - self.iter.as_slice() - } - - /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn allocator(&self) -> &A { - unsafe { self.vec.as_ref().allocator() } - } -} - -#[stable(feature = "vec_drain_as_slice", since = "1.46.0")] -impl<'a, T, A: Allocator> AsRef<[T]> for Drain<'a, T, A> { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl Sync for Drain<'_, T, A> {} -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl Send for Drain<'_, T, A> {} - -#[stable(feature = "drain", since = "1.6.0")] -impl Iterator for Drain<'_, T, A> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(|elt| unsafe { ptr::read(elt as *const _) }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl DoubleEndedIterator for Drain<'_, T, A> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|elt| unsafe { ptr::read(elt as *const _) }) - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl Drop for Drain<'_, T, A> { - fn drop(&mut self) { - /// Continues dropping the remaining elements in the `Drain`, then moves back the - /// un-`Drain`ed elements to restore the original `Vec`. - struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>); - - impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { - fn drop(&mut self) { - // Continue the same loop we have below. If the loop already finished, this does - // nothing. - self.0.for_each(drop); - - if self.0.tail_len > 0 { - unsafe { - let source_vec = self.0.vec.as_mut(); - // memmove back untouched tail, update to new length - let start = source_vec.len(); - let tail = self.0.tail_start; - if tail != start { - let src = source_vec.as_ptr().add(tail); - let dst = source_vec.as_mut_ptr().add(start); - ptr::copy(src, dst, self.0.tail_len); - } - source_vec.set_len(start + self.0.tail_len); - } - } - } - } - - // exhaust self first - while let Some(item) = self.next() { - let guard = DropGuard(self); - drop(item); - mem::forget(guard); - } - - // Drop a `DropGuard` to move back the non-drained tail of `self`. - DropGuard(self); - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, T, A> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Drain<'_, T, A> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, T, A> {} - -/// A splicing iterator for `Vec`. -/// -/// This struct is created by [`Vec::splice()`]. -/// See its documentation for more. -/// -/// # Example -/// -/// ``` -/// let mut v = vec![0, 1, 2]; -/// let new = [7, 8]; -/// let iter: std::vec::Splice<_> = v.splice(1.., new.iter().cloned()); -/// ``` -#[derive(Debug)] -#[stable(feature = "vec_splice", since = "1.21.0")] -pub struct Splice< - 'a, - I: Iterator + 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, -> { - drain: Drain<'a, I::Item, A>, - replace_with: I, -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl Iterator for Splice<'_, I, A> { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.drain.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.drain.size_hint() - } -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl DoubleEndedIterator for Splice<'_, I, A> { - fn next_back(&mut self) -> Option { - self.drain.next_back() - } -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl ExactSizeIterator for Splice<'_, I, A> {} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl Drop for Splice<'_, I, A> { - fn drop(&mut self) { - self.drain.by_ref().for_each(drop); - - unsafe { - if self.drain.tail_len == 0 { - self.drain.vec.as_mut().extend(self.replace_with.by_ref()); - return; - } - - // First fill the range left by drain(). - if !self.drain.fill(&mut self.replace_with) { - return; - } - - // There may be more elements. Use the lower bound as an estimate. - // FIXME: Is the upper bound a better guess? Or something else? - let (lower_bound, _upper_bound) = self.replace_with.size_hint(); - if lower_bound > 0 { - self.drain.move_tail(lower_bound); - if !self.drain.fill(&mut self.replace_with) { - return; - } - } - - // Collect any remaining elements. - // This is a zero-length vector which does not allocate if `lower_bound` was exact. - let mut collected = self.replace_with.by_ref().collect::>().into_iter(); - // Now we have an exact count. - if collected.len() > 0 { - self.drain.move_tail(collected.len()); - let filled = self.drain.fill(&mut collected); - debug_assert!(filled); - debug_assert_eq!(collected.len(), 0); - } - } - // Let `Drain::drop` move the tail back if necessary and restore `vec.len`. - } -} - -/// Private helper methods for `Splice::drop` -impl Drain<'_, T, A> { - /// The range from `self.vec.len` to `self.tail_start` contains elements - /// that have been moved out. - /// Fill that range as much as possible with new elements from the `replace_with` iterator. - /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) - unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { - let vec = unsafe { self.vec.as_mut() }; - let range_start = vec.len; - let range_end = self.tail_start; - let range_slice = unsafe { - slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start) - }; - - for place in range_slice { - if let Some(new_item) = replace_with.next() { - unsafe { ptr::write(place, new_item) }; - vec.len += 1; - } else { - return false; - } - } - true - } - - /// Makes room for inserting more elements before the tail. - unsafe fn move_tail(&mut self, additional: usize) { - let vec = unsafe { self.vec.as_mut() }; - let len = self.tail_start + self.tail_len; - vec.buf.reserve(len, additional); - - let new_tail_start = self.tail_start + additional; - unsafe { - let src = vec.as_ptr().add(self.tail_start); - let dst = vec.as_mut_ptr().add(new_tail_start); - ptr::copy(src, dst, self.tail_len); - } - self.tail_start = new_tail_start; - } -} - -/// An iterator which uses a closure to determine if an element should be removed. -/// -/// This struct is created by [`Vec::drain_filter`]. -/// See its documentation for more. -/// -/// # Example -/// -/// ``` -/// #![feature(drain_filter)] -/// -/// let mut v = vec![0, 1, 2]; -/// let iter: std::vec::DrainFilter<_, _> = v.drain_filter(|x| *x % 2 == 0); -/// ``` -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -#[derive(Debug)] -pub struct DrainFilter< - 'a, - T, - F, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> where - F: FnMut(&mut T) -> bool, -{ - vec: &'a mut Vec, - /// The index of the item that will be inspected by the next call to `next`. - idx: usize, - /// The number of items that have been drained (removed) thus far. - del: usize, - /// The original length of `vec` prior to draining. - old_len: usize, - /// The filter test predicate. - pred: F, - /// A flag that indicates a panic has occurred in the filter test predicate. - /// This is used as a hint in the drop implementation to prevent consumption - /// of the remainder of the `DrainFilter`. Any unprocessed items will be - /// backshifted in the `vec`, but no further items will be dropped or - /// tested by the filter predicate. - panic_flag: bool, -} - -impl DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn allocator(&self) -> &A { - self.vec.allocator() - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Iterator for DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - type Item = T; - - fn next(&mut self) -> Option { - unsafe { - while self.idx < self.old_len { - let i = self.idx; - let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); - self.panic_flag = true; - let drained = (self.pred)(&mut v[i]); - self.panic_flag = false; - // Update the index *after* the predicate is called. If the index - // is updated prior and the predicate panics, the element at this - // index would be leaked. - self.idx += 1; - if drained { - self.del += 1; - return Some(ptr::read(&v[i])); - } else if self.del > 0 { - let del = self.del; - let src: *const T = &v[i]; - let dst: *mut T = &mut v[i - del]; - ptr::copy_nonoverlapping(src, dst, 1); - } - } - None - } - } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(self.old_len - self.idx)) - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Drop for DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - fn drop(&mut self) { - struct BackshiftOnDrop<'a, 'b, T, F, A: Allocator> - where - F: FnMut(&mut T) -> bool, - { - drain: &'b mut DrainFilter<'a, T, F, A>, - } - - impl<'a, 'b, T, F, A: Allocator> Drop for BackshiftOnDrop<'a, 'b, T, F, A> - where - F: FnMut(&mut T) -> bool, - { - fn drop(&mut self) { - unsafe { - if self.drain.idx < self.drain.old_len && self.drain.del > 0 { - // This is a pretty messed up state, and there isn't really an - // obviously right thing to do. We don't want to keep trying - // to execute `pred`, so we just backshift all the unprocessed - // elements and tell the vec that they still exist. The backshift - // is required to prevent a double-drop of the last successfully - // drained item prior to a panic in the predicate. - let ptr = self.drain.vec.as_mut_ptr(); - let src = ptr.add(self.drain.idx); - let dst = src.sub(self.drain.del); - let tail_len = self.drain.old_len - self.drain.idx; - src.copy_to(dst, tail_len); - } - self.drain.vec.set_len(self.drain.old_len - self.drain.del); - } - } - } - - let backshift = BackshiftOnDrop { drain: self }; - - // Attempt to consume any remaining elements if the filter predicate - // has not yet panicked. We'll backshift any remaining elements - // whether we've already panicked or if the consumption here panics. - if !backshift.drain.panic_flag { - backshift.drain.for_each(drop); - } - } -} diff --git a/library/alloc/src/vec/partial_eq.rs b/library/alloc/src/vec/partial_eq.rs new file mode 100644 index 000000000000..ff90b6caf460 --- /dev/null +++ b/library/alloc/src/vec/partial_eq.rs @@ -0,0 +1,43 @@ +use crate::alloc::Allocator; +use crate::borrow::Cow; + +use super::Vec; + +macro_rules! __impl_slice_eq1 { + ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?, #[$stability:meta]) => { + #[$stability] + impl PartialEq<$rhs> for $lhs + where + T: PartialEq, + $($ty: $bound)? + { + #[inline] + fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } + #[inline] + fn ne(&self, other: &$rhs) -> bool { self[..] != other[..] } + } + } +} + +__impl_slice_eq1! { [A: Allocator] Vec, Vec, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator] Vec, &[U], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator] Vec, &mut [U], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator] &[T], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [A: Allocator] &mut [T], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [A: Allocator] Vec, [U], #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } +__impl_slice_eq1! { [A: Allocator] [T], Vec, #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } +__impl_slice_eq1! { [A: Allocator] Cow<'_, [T]>, Vec where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Cow<'_, [T]>, &[U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Cow<'_, [T]>, &mut [U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator, const N: usize] Vec, [U; N], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator, const N: usize] Vec, &[U; N], #[stable(feature = "rust1", since = "1.0.0")] } + +// NOTE: some less important impls are omitted to reduce code bloat +// FIXME(Centril): Reconsider this? +//__impl_slice_eq1! { [const N: usize] Vec, &mut [B; N], } +//__impl_slice_eq1! { [const N: usize] [A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] &[A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] &mut [A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, [B; N], } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &[B; N], } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &mut [B; N], } diff --git a/library/alloc/src/vec/set_len_on_drop.rs b/library/alloc/src/vec/set_len_on_drop.rs new file mode 100644 index 000000000000..8b66bc812129 --- /dev/null +++ b/library/alloc/src/vec/set_len_on_drop.rs @@ -0,0 +1,28 @@ +// Set the length of the vec when the `SetLenOnDrop` value goes out of scope. +// +// The idea is: The length field in SetLenOnDrop is a local variable +// that the optimizer will see does not alias with any stores through the Vec's data +// pointer. This is a workaround for alias analysis issue #32155 +pub(super) struct SetLenOnDrop<'a> { + len: &'a mut usize, + local_len: usize, +} + +impl<'a> SetLenOnDrop<'a> { + #[inline] + pub(super) fn new(len: &'a mut usize) -> Self { + SetLenOnDrop { local_len: *len, len } + } + + #[inline] + pub(super) fn increment_len(&mut self, increment: usize) { + self.local_len += increment; + } +} + +impl Drop for SetLenOnDrop<'_> { + #[inline] + fn drop(&mut self) { + *self.len = self.local_len; + } +} diff --git a/library/alloc/src/vec/source_iter_marker.rs b/library/alloc/src/vec/source_iter_marker.rs new file mode 100644 index 000000000000..8c0e95559fa1 --- /dev/null +++ b/library/alloc/src/vec/source_iter_marker.rs @@ -0,0 +1,108 @@ +use core::iter::{InPlaceIterable, SourceIter}; +use core::mem::{self, ManuallyDrop}; +use core::ptr::{self}; + +use super::{AsIntoIter, InPlaceDrop, SpecFromIter, SpecFromIterNested, Vec}; + +/// Specialization marker for collecting an iterator pipeline into a Vec while reusing the +/// source allocation, i.e. executing the pipeline in place. +/// +/// The SourceIter parent trait is necessary for the specializing function to access the allocation +/// which is to be reused. But it is not sufficient for the specialization to be valid. See +/// additional bounds on the impl. +#[rustc_unsafe_specialization_marker] +pub(super) trait SourceIterMarker: SourceIter {} + +// The std-internal SourceIter/InPlaceIterable traits are only implemented by chains of +// Adapter>> (all owned by core/std). Additional bounds +// on the adapter implementations (beyond `impl Trait for Adapter`) only depend on other +// traits already marked as specialization traits (Copy, TrustedRandomAccess, FusedIterator). +// I.e. the marker does not depend on lifetimes of user-supplied types. Modulo the Copy hole, which +// several other specializations already depend on. +impl SourceIterMarker for T where T: SourceIter + InPlaceIterable {} + +impl SpecFromIter for Vec +where + I: Iterator + SourceIterMarker, +{ + default fn from_iter(mut iterator: I) -> Self { + // Additional requirements which cannot expressed via trait bounds. We rely on const eval + // instead: + // a) no ZSTs as there would be no allocation to reuse and pointer arithmetic would panic + // b) size match as required by Alloc contract + // c) alignments match as required by Alloc contract + if mem::size_of::() == 0 + || mem::size_of::() + != mem::size_of::<<::Source as AsIntoIter>::Item>() + || mem::align_of::() + != mem::align_of::<<::Source as AsIntoIter>::Item>() + { + // fallback to more generic implementations + return SpecFromIterNested::from_iter(iterator); + } + + let (src_buf, src_ptr, dst_buf, dst_end, cap) = unsafe { + let inner = iterator.as_inner().as_into_iter(); + ( + inner.buf.as_ptr(), + inner.ptr, + inner.buf.as_ptr() as *mut T, + inner.end as *const T, + inner.cap, + ) + }; + + // use try-fold since + // - it vectorizes better for some iterator adapters + // - unlike most internal iteration methods, it only takes a &mut self + // - it lets us thread the write pointer through its innards and get it back in the end + let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; + let sink = iterator + .try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(dst_end)) + .unwrap(); + // iteration succeeded, don't drop head + let dst = ManuallyDrop::new(sink).dst; + + let src = unsafe { iterator.as_inner().as_into_iter() }; + // check if SourceIter contract was upheld + // caveat: if they weren't we may not even make it to this point + debug_assert_eq!(src_buf, src.buf.as_ptr()); + // check InPlaceIterable contract. This is only possible if the iterator advanced the + // source pointer at all. If it uses unchecked access via TrustedRandomAccess + // then the source pointer will stay in its initial position and we can't use it as reference + if src.ptr != src_ptr { + debug_assert!( + dst as *const _ <= src.ptr, + "InPlaceIterable contract violation, write pointer advanced beyond read pointer" + ); + } + + // drop any remaining values at the tail of the source + src.drop_remaining(); + // but prevent drop of the allocation itself once IntoIter goes out of scope + src.forget_allocation(); + + let vec = unsafe { + let len = dst.offset_from(dst_buf) as usize; + Vec::from_raw_parts(dst_buf, len, cap) + }; + + vec + } +} + +fn write_in_place_with_drop( + src_end: *const T, +) -> impl FnMut(InPlaceDrop, T) -> Result, !> { + move |mut sink, item| { + unsafe { + // the InPlaceIterable contract cannot be verified precisely here since + // try_fold has an exclusive reference to the source pointer + // all we can do is check if it's still in range + debug_assert!(sink.dst as *const _ <= src_end, "InPlaceIterable contract violation"); + ptr::write(sink.dst, item); + sink.dst = sink.dst.add(1); + } + Ok(sink) + } +} diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs new file mode 100644 index 000000000000..b6186a7ebaf7 --- /dev/null +++ b/library/alloc/src/vec/spec_extend.rs @@ -0,0 +1,82 @@ +use crate::alloc::Allocator; +use core::iter::TrustedLen; +use core::ptr::{self}; +use core::slice::{self}; + +use super::{IntoIter, SetLenOnDrop, Vec}; + +// Specialization trait used for Vec::extend +pub(super) trait SpecExtend { + fn spec_extend(&mut self, iter: I); +} + +impl SpecExtend for Vec +where + I: Iterator, +{ + default fn spec_extend(&mut self, iter: I) { + self.extend_desugared(iter) + } +} + +impl SpecExtend for Vec +where + I: TrustedLen, +{ + default fn spec_extend(&mut self, iterator: I) { + // This is the case for a TrustedLen iterator. + let (low, high) = iterator.size_hint(); + if let Some(high_value) = high { + debug_assert_eq!( + low, + high_value, + "TrustedLen iterator's size hint is not exact: {:?}", + (low, high) + ); + } + if let Some(additional) = high { + self.reserve(additional); + unsafe { + let mut ptr = self.as_mut_ptr().add(self.len()); + let mut local_len = SetLenOnDrop::new(&mut self.len); + iterator.for_each(move |element| { + ptr::write(ptr, element); + ptr = ptr.offset(1); + // NB can't overflow since we would have had to alloc the address space + local_len.increment_len(1); + }); + } + } else { + self.extend_desugared(iterator) + } + } +} + +impl SpecExtend> for Vec { + fn spec_extend(&mut self, mut iterator: IntoIter) { + unsafe { + self.append_elements(iterator.as_slice() as _); + } + iterator.ptr = iterator.end; + } +} + +impl<'a, T: 'a, I, A: Allocator + 'a> SpecExtend<&'a T, I> for Vec +where + I: Iterator, + T: Clone, +{ + default fn spec_extend(&mut self, iterator: I) { + self.spec_extend(iterator.cloned()) + } +} + +impl<'a, T: 'a, A: Allocator + 'a> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec +where + T: Copy, +{ + fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { + let slice = iterator.as_slice(); + unsafe { self.append_elements(slice) }; + } +} diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs new file mode 100644 index 000000000000..de610174783c --- /dev/null +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -0,0 +1,60 @@ +use crate::alloc::Allocator; +use crate::raw_vec::RawVec; +use core::ptr::{self}; + +use super::{ExtendElement, IsZero, Vec}; + +// Specialization trait used for Vec::from_elem +pub(super) trait SpecFromElem: Sized { + fn from_elem(elem: Self, n: usize, alloc: A) -> Vec; +} + +impl SpecFromElem for T { + default fn from_elem(elem: Self, n: usize, alloc: A) -> Vec { + let mut v = Vec::with_capacity_in(n, alloc); + v.extend_with(n, ExtendElement(elem)); + v + } +} + +impl SpecFromElem for i8 { + #[inline] + fn from_elem(elem: i8, n: usize, alloc: A) -> Vec { + if elem == 0 { + return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; + } + unsafe { + let mut v = Vec::with_capacity_in(n, alloc); + ptr::write_bytes(v.as_mut_ptr(), elem as u8, n); + v.set_len(n); + v + } + } +} + +impl SpecFromElem for u8 { + #[inline] + fn from_elem(elem: u8, n: usize, alloc: A) -> Vec { + if elem == 0 { + return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; + } + unsafe { + let mut v = Vec::with_capacity_in(n, alloc); + ptr::write_bytes(v.as_mut_ptr(), elem, n); + v.set_len(n); + v + } + } +} + +impl SpecFromElem for T { + #[inline] + fn from_elem(elem: T, n: usize, alloc: A) -> Vec { + if elem.is_zero() { + return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; + } + let mut v = Vec::with_capacity_in(n, alloc); + v.extend_with(n, ExtendElement(elem)); + v + } +} diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs new file mode 100644 index 000000000000..bbfcc68daeff --- /dev/null +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -0,0 +1,97 @@ +use core::mem::ManuallyDrop; +use core::ptr::{self}; +use core::slice::{self}; + +use super::{IntoIter, SpecExtend, SpecFromIterNested, Vec}; + +/// Specialization trait used for Vec::from_iter +/// +/// ## The delegation graph: +/// +/// ```text +/// +-------------+ +/// |FromIterator | +/// +-+-----------+ +/// | +/// v +/// +-+-------------------------------+ +---------------------+ +/// |SpecFromIter +---->+SpecFromIterNested | +/// |where I: | | |where I: | +/// | Iterator (default)----------+ | | Iterator (default) | +/// | vec::IntoIter | | | TrustedLen | +/// | SourceIterMarker---fallback-+ | | | +/// | slice::Iter | | | +/// | Iterator | +---------------------+ +/// +---------------------------------+ +/// ``` +pub(super) trait SpecFromIter { + fn from_iter(iter: I) -> Self; +} + +impl SpecFromIter for Vec +where + I: Iterator, +{ + default fn from_iter(iterator: I) -> Self { + SpecFromIterNested::from_iter(iterator) + } +} + +impl SpecFromIter> for Vec { + fn from_iter(iterator: IntoIter) -> Self { + // A common case is passing a vector into a function which immediately + // re-collects into a vector. We can short circuit this if the IntoIter + // has not been advanced at all. + // When it has been advanced We can also reuse the memory and move the data to the front. + // But we only do so when the resulting Vec wouldn't have more unused capacity + // than creating it through the generic FromIterator implementation would. That limitation + // is not strictly necessary as Vec's allocation behavior is intentionally unspecified. + // But it is a conservative choice. + let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; + if !has_advanced || iterator.len() >= iterator.cap / 2 { + unsafe { + let it = ManuallyDrop::new(iterator); + if has_advanced { + ptr::copy(it.ptr, it.buf.as_ptr(), it.len()); + } + return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap); + } + } + + let mut vec = Vec::new(); + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + vec.spec_extend(iterator); + vec + } +} + +impl<'a, T: 'a, I> SpecFromIter<&'a T, I> for Vec +where + I: Iterator, + T: Clone, +{ + default fn from_iter(iterator: I) -> Self { + SpecFromIter::from_iter(iterator.cloned()) + } +} + +// This utilizes `iterator.as_slice().to_vec()` since spec_extend +// must take more steps to reason about the final capacity + length +// and thus do more work. `to_vec()` directly allocates the correct amount +// and fills it exactly. +impl<'a, T: 'a + Clone> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec { + #[cfg(not(test))] + fn from_iter(iterator: slice::Iter<'a, T>) -> Self { + iterator.as_slice().to_vec() + } + + // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is + // required for this method definition, is not available. Instead use the + // `slice::to_vec` function which is only available with cfg(test) + // NB see the slice::hack module in slice.rs for more information + #[cfg(test)] + fn from_iter(iterator: slice::Iter<'a, T>) -> Self { + crate::slice::to_vec(iterator.as_slice(), crate::alloc::Global) + } +} diff --git a/library/alloc/src/vec/spec_from_iter_nested.rs b/library/alloc/src/vec/spec_from_iter_nested.rs new file mode 100644 index 000000000000..6abd4ff2a3f0 --- /dev/null +++ b/library/alloc/src/vec/spec_from_iter_nested.rs @@ -0,0 +1,56 @@ +use core::iter::TrustedLen; +use core::ptr::{self}; + +use super::{SpecExtend, Vec}; + +/// Another specialization trait for Vec::from_iter +/// necessary to manually prioritize overlapping specializations +/// see [`SpecFromIter`] for details. +pub(super) trait SpecFromIterNested { + fn from_iter(iter: I) -> Self; +} + +impl SpecFromIterNested for Vec +where + I: Iterator, +{ + default fn from_iter(mut iterator: I) -> Self { + // Unroll the first iteration, as the vector is going to be + // expanded on this iteration in every case when the iterable is not + // empty, but the loop in extend_desugared() is not going to see the + // vector being full in the few subsequent loop iterations. + // So we get better branch prediction. + let mut vector = match iterator.next() { + None => return Vec::new(), + Some(element) => { + let (lower, _) = iterator.size_hint(); + let mut vector = Vec::with_capacity(lower.saturating_add(1)); + unsafe { + ptr::write(vector.as_mut_ptr(), element); + vector.set_len(1); + } + vector + } + }; + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + as SpecExtend>::spec_extend(&mut vector, iterator); + vector + } +} + +impl SpecFromIterNested for Vec +where + I: TrustedLen, +{ + fn from_iter(iterator: I) -> Self { + let mut vector = match iterator.size_hint() { + (_, Some(upper)) => Vec::with_capacity(upper), + _ => Vec::new(), + }; + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + vector.spec_extend(iterator); + vector + } +} diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs new file mode 100644 index 000000000000..0a27b5b62ecf --- /dev/null +++ b/library/alloc/src/vec/splice.rs @@ -0,0 +1,133 @@ +use crate::alloc::{Allocator, Global}; +use core::ptr::{self}; +use core::slice::{self}; + +use super::{Drain, Vec}; + +/// A splicing iterator for `Vec`. +/// +/// This struct is created by [`Vec::splice()`]. +/// See its documentation for more. +/// +/// # Example +/// +/// ``` +/// let mut v = vec![0, 1, 2]; +/// let new = [7, 8]; +/// let iter: std::vec::Splice<_> = v.splice(1.., new.iter().cloned()); +/// ``` +#[derive(Debug)] +#[stable(feature = "vec_splice", since = "1.21.0")] +pub struct Splice< + 'a, + I: Iterator + 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, +> { + pub(super) drain: Drain<'a, I::Item, A>, + pub(super) replace_with: I, +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl Iterator for Splice<'_, I, A> { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.drain.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.drain.size_hint() + } +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl DoubleEndedIterator for Splice<'_, I, A> { + fn next_back(&mut self) -> Option { + self.drain.next_back() + } +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl ExactSizeIterator for Splice<'_, I, A> {} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl Drop for Splice<'_, I, A> { + fn drop(&mut self) { + self.drain.by_ref().for_each(drop); + + unsafe { + if self.drain.tail_len == 0 { + self.drain.vec.as_mut().extend(self.replace_with.by_ref()); + return; + } + + // First fill the range left by drain(). + if !self.drain.fill(&mut self.replace_with) { + return; + } + + // There may be more elements. Use the lower bound as an estimate. + // FIXME: Is the upper bound a better guess? Or something else? + let (lower_bound, _upper_bound) = self.replace_with.size_hint(); + if lower_bound > 0 { + self.drain.move_tail(lower_bound); + if !self.drain.fill(&mut self.replace_with) { + return; + } + } + + // Collect any remaining elements. + // This is a zero-length vector which does not allocate if `lower_bound` was exact. + let mut collected = self.replace_with.by_ref().collect::>().into_iter(); + // Now we have an exact count. + if collected.len() > 0 { + self.drain.move_tail(collected.len()); + let filled = self.drain.fill(&mut collected); + debug_assert!(filled); + debug_assert_eq!(collected.len(), 0); + } + } + // Let `Drain::drop` move the tail back if necessary and restore `vec.len`. + } +} + +/// Private helper methods for `Splice::drop` +impl Drain<'_, T, A> { + /// The range from `self.vec.len` to `self.tail_start` contains elements + /// that have been moved out. + /// Fill that range as much as possible with new elements from the `replace_with` iterator. + /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) + unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { + let vec = unsafe { self.vec.as_mut() }; + let range_start = vec.len; + let range_end = self.tail_start; + let range_slice = unsafe { + slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start) + }; + + for place in range_slice { + if let Some(new_item) = replace_with.next() { + unsafe { ptr::write(place, new_item) }; + vec.len += 1; + } else { + return false; + } + } + true + } + + /// Makes room for inserting more elements before the tail. + unsafe fn move_tail(&mut self, additional: usize) { + let vec = unsafe { self.vec.as_mut() }; + let len = self.tail_start + self.tail_len; + vec.buf.reserve(len, additional); + + let new_tail_start = self.tail_start + additional; + unsafe { + let src = vec.as_ptr().add(self.tail_start); + let dst = vec.as_mut_ptr().add(new_tail_start); + ptr::copy(src, dst, self.tail_len); + } + self.tail_start = new_tail_start; + } +} diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index b7cc03f8eb99..cd4174ed4007 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -16,11 +16,11 @@ #![feature(slice_ptr_get)] #![feature(split_inclusive)] #![feature(binary_heap_retain)] -#![feature(deque_range)] #![feature(inplace_iteration)] #![feature(iter_map_while)] #![feature(int_bits_const)] #![feature(vecdeque_binary_search)] +#![feature(slice_group_by)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index a4f0fb415fb3..777c10b1bf74 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1898,3 +1898,61 @@ fn subslice_patterns() { m!(&mut v, [..] => ()); m!(&mut v, [x, .., y] => c!((x, y), (&mut N, &mut N), (&mut N(0), &mut N(4)))); } + +#[test] +fn test_group_by() { + let slice = &[1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; + + let mut iter = slice.group_by(|a, b| a == b); + assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next(), Some(&[3, 3][..])); + assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next(), Some(&[1][..])); + assert_eq!(iter.next(), Some(&[0][..])); + assert_eq!(iter.next(), None); + + let mut iter = slice.group_by(|a, b| a == b); + assert_eq!(iter.next_back(), Some(&[0][..])); + assert_eq!(iter.next_back(), Some(&[1][..])); + assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next_back(), Some(&[3, 3][..])); + assert_eq!(iter.next_back(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next_back(), None); + + let mut iter = slice.group_by(|a, b| a == b); + assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next_back(), Some(&[0][..])); + assert_eq!(iter.next(), Some(&[3, 3][..])); + assert_eq!(iter.next_back(), Some(&[1][..])); + assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next_back(), None); +} + +#[test] +fn test_group_by_mut() { + let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; + + let mut iter = slice.group_by_mut(|a, b| a == b); + assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next(), Some(&mut [3, 3][..])); + assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next(), Some(&mut [1][..])); + assert_eq!(iter.next(), Some(&mut [0][..])); + assert_eq!(iter.next(), None); + + let mut iter = slice.group_by_mut(|a, b| a == b); + assert_eq!(iter.next_back(), Some(&mut [0][..])); + assert_eq!(iter.next_back(), Some(&mut [1][..])); + assert_eq!(iter.next_back(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next_back(), Some(&mut [3, 3][..])); + assert_eq!(iter.next_back(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next_back(), None); + + let mut iter = slice.group_by_mut(|a, b| a == b); + assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next_back(), Some(&mut [0][..])); + assert_eq!(iter.next(), Some(&mut [3, 3][..])); + assert_eq!(iter.next_back(), Some(&mut [1][..])); + assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next_back(), None); +} diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 705f0d62fbb7..0919b1325bce 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1728,3 +1728,10 @@ fn test_zero_sized_push() { } } } + +#[test] +fn test_from_zero_sized_vec() { + let v = vec![(); 100]; + let queue = VecDeque::from(v); + assert_eq!(queue.len(), 100); +} diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index c198797e650f..6ec0f0b5ffc5 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -53,6 +53,27 @@ use crate::ptr; /// * `Layout` queries and calculations in general must be correct. Callers of /// this trait are allowed to rely on the contracts defined on each method, /// and implementors must ensure such contracts remain true. +/// +/// * You may not rely on allocations actually happening, even if there are explicit +/// heap allocations in the source. The optimizer may detect unused allocations that it can either +/// eliminate entirely or move to the stack and thus never invoke the allocator. The +/// optimizer may further assume that allocation is infallible, so code that used to fail due +/// to allocator failures may now suddenly work because the optimizer worked around the +/// need for an allocation. More concretely, the following code example is unsound, irrespective +/// of whether your custom allocator allows counting how many allocations have happened. +/// +/// ```rust,ignore (unsound and has placeholders) +/// drop(Box::new(42)); +/// let number_of_heap_allocs = /* call private allocator API */; +/// unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); } +/// ``` +/// +/// Note that the optimizations mentioned above are not the only +/// optimization that can be applied. You may generally not rely on heap allocations +/// happening if they can be removed without changing program behavior. +/// Whether allocations happen or not is not part of the program behavior, even if it +/// could be detected via an allocator that tracks allocations by printing or otherwise +/// having side effects. #[stable(feature = "global_alloc", since = "1.28.0")] pub unsafe trait GlobalAlloc { /// Allocate memory as described by the given `layout`. diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index a7cb1023229b..71548bec7aae 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -463,6 +463,37 @@ impl [T; N] { unsafe { crate::mem::transmute_copy::<_, [U; N]>(&dst) } } + /// 'Zips up' two arrays into a single array of pairs. + /// + /// `zip()` returns a new array where every element is a tuple where the + /// first element comes from the first array, and the second element comes + /// from the second array. In other words, it zips two arrays together, + /// into a single one. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_zip)] + /// let x = [1, 2, 3]; + /// let y = [4, 5, 6]; + /// let z = x.zip(y); + /// assert_eq!(z, [(1, 4), (2, 5), (3, 6)]); + /// ``` + #[unstable(feature = "array_zip", issue = "80094")] + pub fn zip(self, rhs: [U; N]) -> [(T, U); N] { + use crate::mem::MaybeUninit; + + let mut dst = MaybeUninit::uninit_array::(); + for (i, (lhs, rhs)) in IntoIter::new(self).zip(IntoIter::new(rhs)).enumerate() { + dst[i].write((lhs, rhs)); + } + // FIXME: Convert to crate::mem::transmute once it works with generics. + // unsafe { crate::mem::transmute::<[MaybeUninit; N], [U; N]>(dst) } + // SAFETY: At this point we've properly initialized the whole array + // and we just need to cast it to the correct type. + unsafe { crate::mem::transmute_copy::<_, [(T, U); N]>(&dst) } + } + /// Returns a slice containing the entire array. Equivalent to `&s[..]`. #[unstable(feature = "array_methods", issue = "76118")] pub fn as_slice(&self) -> &[T] { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c7a76d33a666..c5ab7a39ff0c 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1580,7 +1580,7 @@ impl fmt::Display for RefMut<'_, T> { /// `&UnsafeCell<_>` reference); there is no magic whatsoever when dealing with _exclusive_ /// accesses (_e.g._, through an `&mut UnsafeCell<_>`): neither the cell nor the wrapped value /// may be aliased for the duration of that `&mut` borrow. -/// This is showcased by the [`.get_mut()`] accessor, which is a non-`unsafe` getter that yields +/// This is showcased by the [`.get_mut()`] accessor, which is a _safe_ getter that yields /// a `&mut T`. /// /// [`.get_mut()`]: `UnsafeCell::get_mut` @@ -1618,7 +1618,6 @@ impl fmt::Display for RefMut<'_, T> { /// implies exclusive access to its `T`: /// /// ```rust -/// #![feature(unsafe_cell_get_mut)] /// #![forbid(unsafe_code)] // with exclusive accesses, /// // `UnsafeCell` is a transparent no-op wrapper, /// // so no need for `unsafe` here. @@ -1722,7 +1721,6 @@ impl UnsafeCell { /// # Examples /// /// ``` - /// #![feature(unsafe_cell_get_mut)] /// use std::cell::UnsafeCell; /// /// let mut c = UnsafeCell::new(5); @@ -1731,7 +1729,7 @@ impl UnsafeCell { /// assert_eq!(*c.get_mut(), 6); /// ``` #[inline] - #[unstable(feature = "unsafe_cell_get_mut", issue = "76943")] + #[stable(feature = "unsafe_cell_get_mut", since = "1.50.0")] pub fn get_mut(&mut self) -> &mut T { &mut self.value } diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 394db5b5917f..de05a8c82e8f 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -47,6 +47,7 @@ use super::MAX; /// /// assert_eq!(None, c); /// ``` +#[doc(alias = "chr")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn from_u32(i: u32) -> Option { diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 47ae1a64190e..0c459a820c6e 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -325,6 +325,120 @@ pub enum Ordering { } impl Ordering { + /// Returns `true` if the ordering is the `Equal` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_eq(), false); + /// assert_eq!(Ordering::Equal.is_eq(), true); + /// assert_eq!(Ordering::Greater.is_eq(), false); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_eq(self) -> bool { + matches!(self, Equal) + } + + /// Returns `true` if the ordering is not the `Equal` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_ne(), true); + /// assert_eq!(Ordering::Equal.is_ne(), false); + /// assert_eq!(Ordering::Greater.is_ne(), true); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_ne(self) -> bool { + !matches!(self, Equal) + } + + /// Returns `true` if the ordering is the `Less` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_lt(), true); + /// assert_eq!(Ordering::Equal.is_lt(), false); + /// assert_eq!(Ordering::Greater.is_lt(), false); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_lt(self) -> bool { + matches!(self, Less) + } + + /// Returns `true` if the ordering is the `Greater` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_gt(), false); + /// assert_eq!(Ordering::Equal.is_gt(), false); + /// assert_eq!(Ordering::Greater.is_gt(), true); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_gt(self) -> bool { + matches!(self, Greater) + } + + /// Returns `true` if the ordering is either the `Less` or `Equal` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_le(), true); + /// assert_eq!(Ordering::Equal.is_le(), true); + /// assert_eq!(Ordering::Greater.is_le(), false); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_le(self) -> bool { + !matches!(self, Greater) + } + + /// Returns `true` if the ordering is either the `Greater` or `Equal` variant. + /// + /// # Examples + /// + /// ``` + /// #![feature(ordering_helpers)] + /// use std::cmp::Ordering; + /// + /// assert_eq!(Ordering::Less.is_ge(), false); + /// assert_eq!(Ordering::Equal.is_ge(), true); + /// assert_eq!(Ordering::Greater.is_ge(), true); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ordering_helpers", issue = "79885")] + pub const fn is_ge(self) -> bool { + !matches!(self, Less) + } + /// Reverses the `Ordering`. /// /// * `Less` becomes `Greater`. @@ -1122,7 +1236,7 @@ mod impls { impl PartialOrd for bool { #[inline] fn partial_cmp(&self, other: &bool) -> Option { - (*self as u8).partial_cmp(&(*other as u8)) + Some(self.cmp(other)) } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 73df8e53f82a..938dc214486a 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -815,6 +815,7 @@ extern "rust-intrinsic" { /// This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. + #[rustc_const_unstable(feature = "const_assert_type", issue = "none")] pub fn assert_inhabited(); /// A guard for unsafe functions that cannot ever be executed if `T` does not permit @@ -1735,7 +1736,6 @@ extern "rust-intrinsic" { /// Allocate at compile time. Should not be called at runtime. #[rustc_const_unstable(feature = "const_heap", issue = "79597")] - #[cfg(not(bootstrap))] pub fn const_allocate(size: usize, align: usize) -> *mut u8; } @@ -1845,20 +1845,22 @@ pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) - /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append #[doc(alias = "memcpy")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "none")] #[inline] -pub unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { +pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); } - if cfg!(debug_assertions) + // FIXME: Perform these checks only at run time + /*if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst) && is_nonoverlapping(src, dst, count)) { // Not panicking to keep codegen impact smaller. abort(); - } + }*/ // SAFETY: the safety contract for `copy_nonoverlapping` must be // upheld by the caller. @@ -1927,16 +1929,19 @@ pub unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { /// ``` #[doc(alias = "memmove")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "none")] #[inline] -pub unsafe fn copy(src: *const T, dst: *mut T, count: usize) { +pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "none")] fn copy(src: *const T, dst: *mut T, count: usize); } - if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { + // FIXME: Perform these checks only at run time + /*if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { // Not panicking to keep codegen impact smaller. abort(); - } + }*/ // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { copy(src, dst, count) } diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs new file mode 100644 index 000000000000..362326725490 --- /dev/null +++ b/library/core/src/iter/adapters/intersperse.rs @@ -0,0 +1,76 @@ +use super::Peekable; + +/// An iterator adapter that places a separator between all elements. +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[derive(Debug, Clone)] +pub struct Intersperse +where + I::Item: Clone, +{ + separator: I::Item, + iter: Peekable, + needs_sep: bool, +} + +impl Intersperse +where + I::Item: Clone, +{ + pub(in crate::iter) fn new(iter: I, separator: I::Item) -> Self { + Self { iter: iter.peekable(), separator, needs_sep: false } + } +} + +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +impl Iterator for Intersperse +where + I: Iterator, + I::Item: Clone, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.needs_sep && self.iter.peek().is_some() { + self.needs_sep = false; + Some(self.separator.clone()) + } else { + self.needs_sep = true; + self.iter.next() + } + } + + fn fold(mut self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let mut accum = init; + + // Use `peek()` first to avoid calling `next()` on an empty iterator. + if !self.needs_sep || self.iter.peek().is_some() { + if let Some(x) = self.iter.next() { + accum = f(accum, x); + } + } + + let element = &self.separator; + + self.iter.fold(accum, |mut accum, x| { + accum = f(accum, element.clone()); + accum = f(accum, x); + accum + }) + } + + fn size_hint(&self) -> (usize, Option) { + let (lo, hi) = self.iter.size_hint(); + let next_is_elem = !self.needs_sep; + let lo = lo.saturating_sub(next_is_elem as usize).saturating_add(lo); + let hi = match hi { + Some(hi) => hi.saturating_sub(next_is_elem as usize).checked_add(hi), + None => None, + }; + (lo, hi) + } +} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index b8d3430f9109..7dfbf32cea7b 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -11,6 +11,7 @@ mod filter_map; mod flatten; mod fuse; mod inspect; +mod intersperse; mod map; mod map_while; mod peekable; @@ -41,6 +42,9 @@ pub use self::flatten::Flatten; #[stable(feature = "iter_copied", since = "1.36.0")] pub use self::copied::Copied; +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +pub use self::intersperse::Intersperse; + #[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] pub use self::map_while::MapWhile; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 072373c00f67..569de719d03d 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -206,6 +206,51 @@ //! 2. If you're creating a collection, implementing [`IntoIterator`] for it //! will allow your collection to be used with the `for` loop. //! +//! # Iterating by reference +//! +//! Since [`into_iter()`] takes `self` by value, using a `for` loop to iterate +//! over a collection consumes that collection. Often, you may want to iterate +//! over a collection without consuming it. Many collections offer methods that +//! provide iterators over references, conventionally called `iter()` and +//! `iter_mut()` respectively: +//! +//! ``` +//! let mut values = vec![41]; +//! for x in values.iter_mut() { +//! *x += 1; +//! } +//! for x in values.iter() { +//! assert_eq!(*x, 42); +//! } +//! assert_eq!(values.len(), 1); // `values` is still owned by this function. +//! ``` +//! +//! If a collection type `C` provides `iter()`, it usually also implements +//! `IntoIterator` for `&C`, with an implementation that just calls `iter()`. +//! Likewise, a collection `C` that provides `iter_mut()` generally implements +//! `IntoIterator` for `&mut C` by delegating to `iter_mut()`. This enables a +//! convenient shorthand: +//! +//! ``` +//! let mut values = vec![41]; +//! for x in &mut values { // same as `values.iter_mut()` +//! *x += 1; +//! } +//! for x in &values { // same as `values.iter()` +//! assert_eq!(*x, 42); +//! } +//! assert_eq!(values.len(), 1); +//! ``` +//! +//! While many collections offer `iter()`, not all offer `iter_mut()`. For +//! example, mutating the keys of a [`HashSet`] or [`HashMap`] could +//! put the collection into an inconsistent state if the key hashes change, so +//! these collections only offer `iter()`. +//! +//! [`into_iter()`]: IntoIterator::into_iter +//! [`HashSet`]: ../../std/collections/struct.HashSet.html +//! [`HashMap`]: ../../std/collections/struct.HashMap.html +//! //! # Adapters //! //! Functions which take an [`Iterator`] and return another [`Iterator`] are @@ -350,6 +395,8 @@ pub use self::adapters::Cloned; pub use self::adapters::Copied; #[stable(feature = "iterator_flatten", since = "1.29.0")] pub use self::adapters::Flatten; +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +pub use self::adapters::Intersperse; #[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] pub use self::adapters::MapWhile; #[unstable(feature = "inplace_iteration", issue = "none")] diff --git a/library/core/src/iter/traits/exact_size.rs b/library/core/src/iter/traits/exact_size.rs index eadbdf45c7c6..996d62e2b4a4 100644 --- a/library/core/src/iter/traits/exact_size.rs +++ b/library/core/src/iter/traits/exact_size.rs @@ -91,6 +91,7 @@ pub trait ExactSizeIterator: Iterator { /// /// assert_eq!(5, five.len()); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn len(&self) -> usize { diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 19484bfd0419..633175702d87 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -8,7 +8,7 @@ use crate::ops::{Add, ControlFlow, Try}; use super::super::TrustedRandomAccess; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; -use super::super::{FromIterator, Product, Sum, Zip}; +use super::super::{FromIterator, Intersperse, Product, Sum, Zip}; use super::super::{ Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, }; @@ -569,6 +569,28 @@ pub trait Iterator { Zip::new(self, other.into_iter()) } + /// Places a copy of `separator` between all elements. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_intersperse)] + /// + /// let hello = ["Hello", "World"].iter().copied().intersperse(" ").collect::(); + /// assert_eq!(hello, "Hello World"); + /// ``` + #[inline] + #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] + fn intersperse(self, separator: Self::Item) -> Intersperse + where + Self: Sized, + Self::Item: Clone, + { + Intersperse::new(self, separator) + } + /// Takes a closure and creates an iterator which calls that closure on each /// element. /// @@ -1332,7 +1354,7 @@ pub trait Iterator { /// assert_eq!(merged, "alphabetagamma"); /// ``` /// - /// Flattening once only removes one level of nesting: + /// Flattening only removes one level of nesting at a time: /// /// ``` /// let d3 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; @@ -1346,7 +1368,7 @@ pub trait Iterator { /// /// Here we see that `flatten()` does not perform a "deep" flatten. /// Instead, only one level of nesting is removed. That is, if you - /// `flatten()` a three-dimensional array the result will be + /// `flatten()` a three-dimensional array, the result will be /// two-dimensional and not one-dimensional. To get a one-dimensional /// structure, you have to `flatten()` again. /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index bb76683e0fef..099b98b824d8 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -68,10 +68,12 @@ #![feature(arbitrary_self_types)] #![feature(asm)] #![feature(cfg_target_has_atomic)] -#![cfg_attr(not(bootstrap), feature(const_heap))] +#![feature(const_heap)] #![feature(const_alloc_layout)] +#![feature(const_assert_type)] #![feature(const_discriminant)] #![feature(const_cell_into_inner)] +#![feature(const_intrinsic_copy)] #![feature(const_checked_int_methods)] #![feature(const_euclidean_int_methods)] #![feature(const_float_classify)] @@ -92,7 +94,9 @@ #![feature(const_precise_live_drops)] #![feature(const_ptr_offset)] #![feature(const_ptr_offset_from)] +#![feature(const_ptr_read)] #![feature(const_raw_ptr_comparison)] +#![feature(const_raw_ptr_deref)] #![feature(const_slice_from_raw_parts)] #![feature(const_slice_ptr_len)] #![feature(const_size_of_val)] @@ -101,12 +105,15 @@ #![feature(const_type_name)] #![feature(const_likely)] #![feature(const_unreachable_unchecked)] +#![feature(const_maybe_uninit_assume_init)] +#![feature(const_maybe_uninit_as_ptr)] #![feature(custom_inner_attributes)] #![feature(decl_macro)] #![feature(doc_cfg)] #![feature(doc_spotlight)] #![feature(duration_consts_2)] #![feature(duration_saturating_ops)] +#![feature(extended_key_value_attributes)] #![feature(extern_types)] #![feature(fundamental)] #![feature(intrinsics)] @@ -118,8 +125,7 @@ #![feature(nll)] #![feature(exhaustive_patterns)] #![feature(no_core)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(or_patterns)] #![feature(prelude_import)] #![feature(repr_simd, platform_intrinsics)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 0699c9eab187..1634aff7b4dc 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -2,7 +2,7 @@ #[macro_export] #[allow_internal_unstable(core_panic, const_caller_location)] #[stable(feature = "core", since = "1.6.0")] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "core_panic_macro")] +#[rustc_diagnostic_item = "core_panic_macro"] macro_rules! panic { () => ( $crate::panic!("explicit panic") @@ -163,7 +163,7 @@ macro_rules! assert_ne { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "debug_assert_macro")] +#[rustc_diagnostic_item = "debug_assert_macro"] macro_rules! debug_assert { ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert!($($arg)*); }) } @@ -1217,7 +1217,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] - #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "assert_macro")] + #[rustc_diagnostic_item = "assert_macro"] #[allow_internal_unstable(core_panic)] macro_rules! assert { ($cond:expr $(,)?) => {{ /* compiler built-in */ }}; diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 1924720b949f..b2a4d897eede 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -314,8 +314,9 @@ impl MaybeUninit { /// let data = read(&mut buf); /// ``` #[unstable(feature = "maybe_uninit_uninit_array", issue = "none")] + #[rustc_const_unstable(feature = "maybe_uninit_uninit_array", issue = "none")] #[inline(always)] - pub fn uninit_array() -> [Self; LEN] { + pub const fn uninit_array() -> [Self; LEN] { // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. unsafe { MaybeUninit::<[MaybeUninit; LEN]>::uninit().assume_init() } } @@ -372,8 +373,9 @@ impl MaybeUninit { /// skip running the destructor. For your convenience, this also returns a mutable /// reference to the (now safely initialized) contents of `self`. #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[rustc_const_unstable(feature = "maybe_uninit_extra", issue = "63567")] #[inline(always)] - pub fn write(&mut self, val: T) -> &mut T { + pub const fn write(&mut self, val: T) -> &mut T { *self = MaybeUninit::new(val); // SAFETY: We just initialized this value. unsafe { self.assume_init_mut() } @@ -503,9 +505,10 @@ impl MaybeUninit { /// // `x` had not been initialized yet, so this last line caused undefined behavior. ⚠️ /// ``` #[stable(feature = "maybe_uninit", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init", issue = "none")] #[inline(always)] #[rustc_diagnostic_item = "assume_init"] - pub unsafe fn assume_init(self) -> T { + pub const unsafe fn assume_init(self) -> T { // SAFETY: the caller must guarantee that `self` is initialized. // This also means that `self` must be a `value` variant. unsafe { @@ -572,8 +575,9 @@ impl MaybeUninit { /// // they both get dropped! /// ``` #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[rustc_const_unstable(feature = "maybe_uninit_extra", issue = "63567")] #[inline(always)] - pub unsafe fn assume_init_read(&self) -> T { + pub const unsafe fn assume_init_read(&self) -> T { // SAFETY: the caller must guarantee that `self` is initialized. // Reading from `self.as_ptr()` is safe since `self` should be initialized. unsafe { @@ -666,13 +670,14 @@ impl MaybeUninit { /// } /// ``` #[unstable(feature = "maybe_uninit_ref", issue = "63568")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init", issue = "none")] #[inline(always)] - pub unsafe fn assume_init_ref(&self) -> &T { + pub const unsafe fn assume_init_ref(&self) -> &T { // SAFETY: the caller must guarantee that `self` is initialized. // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); - &*self.value + &*self.as_ptr() } } @@ -788,13 +793,14 @@ impl MaybeUninit { // to uninitialized data (e.g., in `libcore/fmt/float.rs`). We should make // a final decision about the rules before stabilization. #[unstable(feature = "maybe_uninit_ref", issue = "63568")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init", issue = "none")] #[inline(always)] - pub unsafe fn assume_init_mut(&mut self) -> &mut T { + pub const unsafe fn assume_init_mut(&mut self) -> &mut T { // SAFETY: the caller must guarantee that `self` is initialized. // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); - &mut *self.value + &mut *self.as_mut_ptr() } } @@ -810,8 +816,9 @@ impl MaybeUninit { /// /// [`assume_init_ref`]: MaybeUninit::assume_init_ref #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init", issue = "none")] #[inline(always)] - pub unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { + pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { // SAFETY: casting slice to a `*const [T]` is safe since the caller guarantees that // `slice` is initialized, and`MaybeUninit` is guaranteed to have the same layout as `T`. // The pointer obtained is valid since it refers to memory owned by `slice` which is a @@ -831,8 +838,9 @@ impl MaybeUninit { /// /// [`assume_init_mut`]: MaybeUninit::assume_init_mut #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init", issue = "none")] #[inline(always)] - pub unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { + pub const unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { // SAFETY: similar to safety notes for `slice_get_ref`, but we have a // mutable reference which is also guaranteed to be valid for writes. unsafe { &mut *(slice as *mut [Self] as *mut [T]) } @@ -840,15 +848,167 @@ impl MaybeUninit { /// Gets a pointer to the first element of the array. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub fn slice_as_ptr(this: &[MaybeUninit]) -> *const T { + pub const fn slice_as_ptr(this: &[MaybeUninit]) -> *const T { this.as_ptr() as *const T } /// Gets a mutable pointer to the first element of the array. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub fn slice_as_mut_ptr(this: &mut [MaybeUninit]) -> *mut T { + pub const fn slice_as_mut_ptr(this: &mut [MaybeUninit]) -> *mut T { this.as_mut_ptr() as *mut T } + + /// Copies the elements from `src` to `this`, returning a mutable reference to the now initalized contents of `this`. + /// + /// If `T` does not implement `Copy`, use [`write_slice_cloned`] + /// + /// This is similar to [`slice::copy_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut dst = [MaybeUninit::uninit(); 32]; + /// let src = [0; 32]; + /// + /// let init = MaybeUninit::write_slice(&mut dst, &src); + /// + /// assert_eq!(init, src); + /// ``` + /// + /// ``` + /// #![feature(maybe_uninit_write_slice, vec_spare_capacity)] + /// use std::mem::MaybeUninit; + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = [0; 16]; + /// + /// MaybeUninit::write_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); + /// + /// // SAFETY: we have just copied all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); + /// } + /// + /// assert_eq!(vec, src); + /// ``` + /// + /// [`write_slice_cloned`]: MaybeUninit::write_slice_cloned + /// [`slice::copy_from_slice`]: ../../std/primitive.slice.html#method.copy_from_slice + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + pub fn write_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] + where + T: Copy, + { + // SAFETY: &[T] and &[MaybeUninit] have the same layout + let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; + + this.copy_from_slice(uninit_src); + + // SAFETY: Valid elements have just been copied into `this` so it is initalized + unsafe { MaybeUninit::slice_assume_init_mut(this) } + } + + /// Clones the elements from `src` to `this`, returning a mutable reference to the now initalized contents of `this`. + /// Any already initalized elements will not be dropped. + /// + /// If `T` implements `Copy`, use [`write_slice`] + /// + /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. + /// + /// If there is a panic, the already cloned elements will be dropped. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; + /// let src = ["wibbly".to_string(), "wobbly".to_string(), "timey".to_string(), "wimey".to_string(), "stuff".to_string()]; + /// + /// let init = MaybeUninit::write_slice_cloned(&mut dst, &src); + /// + /// assert_eq!(init, src); + /// ``` + /// + /// ``` + /// #![feature(maybe_uninit_write_slice, vec_spare_capacity)] + /// use std::mem::MaybeUninit; + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = ["rust", "is", "a", "pretty", "cool", "language"]; + /// + /// MaybeUninit::write_slice_cloned(&mut vec.spare_capacity_mut()[..src.len()], &src); + /// + /// // SAFETY: we have just cloned all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); + /// } + /// + /// assert_eq!(vec, src); + /// ``` + /// + /// [`write_slice`]: MaybeUninit::write_slice + /// [`slice::clone_from_slice`]: ../../std/primitive.slice.html#method.clone_from_slice + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + pub fn write_slice_cloned<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] + where + T: Clone, + { + // unlike copy_from_slice this does not call clone_from_slice on the slice + // this is because `MaybeUninit` does not implement Clone. + + struct Guard<'a, T> { + slice: &'a mut [MaybeUninit], + initialized: usize, + } + + impl<'a, T> Drop for Guard<'a, T> { + fn drop(&mut self) { + let initialized_part = &mut self.slice[..self.initialized]; + // SAFETY: this raw slice will contain only initialized objects + // that's why, it is allowed to drop it. + unsafe { + crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); + } + } + } + + assert_eq!(this.len(), src.len(), "destination and source slices have different lengths"); + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = this.len(); + let src = &src[..len]; + + // guard is needed b/c panic might happen during a clone + let mut guard = Guard { slice: this, initialized: 0 }; + + for i in 0..len { + guard.slice[i].write(src[i].clone()); + guard.initialized += 1; + } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `this` so it is initalized + unsafe { MaybeUninit::slice_assume_init_mut(this) } + } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index e84014c68a67..22e44a3c4090 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -151,9 +151,14 @@ pub const fn forget(t: T) { #[inline] #[unstable(feature = "forget_unsized", issue = "none")] pub fn forget_unsized(t: T) { + #[cfg(bootstrap)] // SAFETY: the forget intrinsic could be safe, but there's no point in making it safe since // we'll be implementing this function soon via `ManuallyDrop` - unsafe { intrinsics::forget(t) } + unsafe { + intrinsics::forget(t) + } + #[cfg(not(bootstrap))] + intrinsics::forget(t) } /// Returns the size of a type in bytes. @@ -374,7 +379,8 @@ pub const fn size_of_val(val: &T) -> usize { /// ``` #[inline] #[unstable(feature = "layout_for_ptr", issue = "69835")] -pub unsafe fn size_of_val_raw(val: *const T) -> usize { +#[rustc_const_unstable(feature = "const_size_of_val_raw", issue = "46571")] +pub const unsafe fn size_of_val_raw(val: *const T) -> usize { intrinsics::size_of_val(val) } @@ -505,7 +511,8 @@ pub const fn align_of_val(val: &T) -> usize { /// ``` #[inline] #[unstable(feature = "layout_for_ptr", issue = "69835")] -pub unsafe fn align_of_val_raw(val: *const T) -> usize { +#[rustc_const_unstable(feature = "const_align_of_val_raw", issue = "46571")] +pub const unsafe fn align_of_val_raw(val: *const T) -> usize { intrinsics::min_align_of_val(val) } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 33df175bfc54..4d876fd8c33e 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -961,4 +961,39 @@ impl f32 { left.cmp(&right) } + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. + /// + /// # Examples + /// + /// ``` + /// assert!((-3.0f32).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0); + /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "clamp", since = "1.50.0")] + #[inline] + pub fn clamp(self, min: f32, max: f32) -> f32 { + assert!(min <= max); + let mut x = self; + if x < min { + x = min; + } + if x > max { + x = max; + } + x + } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index b85e8deb6d22..3323b7d6774d 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -975,4 +975,39 @@ impl f64 { left.cmp(&right) } + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. + /// + /// # Examples + /// + /// ``` + /// assert!((-3.0f64).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0); + /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "clamp", since = "1.50.0")] + #[inline] + pub fn clamp(self, min: f64, max: f64) -> f64 { + assert!(min <= max); + let mut x = self; + if x < min { + x = min; + } + if x > max { + x = max; + } + x + } } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 33eefe5d2658..162ed7d1b8df 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1,2238 +1,2008 @@ macro_rules! int_impl { - ($SelfT:ty, $ActualT:ident, $UnsignedT:ty, $BITS:expr, $Min:expr, $Max:expr, $Feature:expr, - $EndFeature:expr, $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, + ($SelfT:ty, $ActualT:ident, $UnsignedT:ty, $BITS:expr, $Min:expr, $Max:expr, + $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, $reversed:expr, $le_bytes:expr, $be_bytes:expr, $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { - doc_comment! { - concat!("The smallest value that can be represented by this integer type. + /// The smallest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN, ", stringify!($Min), ");")] + /// ``` + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self; -# Examples + /// The largest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($Max), ");")] + /// ``` + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX: Self = !Self::MIN; -Basic usage: + /// The size of this integer type in bits. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_bits_const)] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");")] + /// ``` + #[unstable(feature = "int_bits_const", issue = "76904")] + pub const BITS: u32 = $BITS; -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MIN, ", stringify!($Min), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self; + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` or `-` sign followed by digits. + /// Leading and trailing whitespace represent an error. Digits are a subset of these characters, + /// depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_str_radix(src: &str, radix: u32) -> Result { + from_str_radix(src, radix) } - doc_comment! { - concat!("The largest value that can be represented by this integer type. + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0b100_0000", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.count_ones(), 1); + /// ``` + /// + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[inline] + pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($Max), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX: Self = !Self::MIN; + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 1);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn count_zeros(self) -> u32 { + (!self).count_ones() } - doc_comment! { - concat!("The size of this integer type in bits. - -# Examples - -``` -", $Feature, "#![feature(int_bits_const)] -assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");", -$EndFeature, " -```"), - #[unstable(feature = "int_bits_const", issue = "76904")] - pub const BITS: u32 = $BITS; + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = -1", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.leading_zeros(), 0); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn leading_zeros(self) -> u32 { + (self as $UnsignedT).leading_zeros() } - doc_comment! { - concat!("Converts a string slice in a given base to an integer. + /// Returns the number of trailing zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = -4", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.trailing_zeros(), 2); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + (self as $UnsignedT).trailing_zeros() + } -The string is expected to be an optional `+` or `-` sign followed by digits. -Leading and trailing whitespace represent an error. Digits are a subset of these characters, -depending on `radix`: + /// Returns the number of leading ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = -1", stringify!($SelfT), ";")] + /// + #[doc = concat!("assert_eq!(n.leading_ones(), ", stringify!($BITS), ");")] + /// ``` + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn leading_ones(self) -> u32 { + (self as $UnsignedT).leading_ones() + } - * `0-9` - * `a-z` - * `A-Z` + /// Returns the number of trailing ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 3", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.trailing_ones(), 2); + /// ``` + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn trailing_ones(self) -> u32 { + (self as $UnsignedT).trailing_ones() + } -# Panics + /// Shifts the bits to the left by a specified amount, `n`, + /// wrapping the truncated bits to the end of the resulting integer. + /// + /// Please note this isn't the same operation as the `<<` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $rot_op, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $rot_result, ";")] + /// + #[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_left(self, n: u32) -> Self { + (self as $UnsignedT).rotate_left(n) as Self + } -This function panics if `radix` is not in the range from 2 to 36. + /// Shifts the bits to the right by a specified amount, `n`, + /// wrapping the truncated bits to the beginning of the resulting + /// integer. + /// + /// Please note this isn't the same operation as the `>>` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $rot_result, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $rot_op, ";")] + /// + #[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_right(self, n: u32) -> Self { + (self as $UnsignedT).rotate_right(n) as Self + } -# Examples + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")] + /// + /// let m = n.swap_bytes(); + /// + #[doc = concat!("assert_eq!(m, ", $swapped, ");")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn swap_bytes(self) -> Self { + (self as $UnsignedT).swap_bytes() as Self + } -Basic usage: + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, + /// second least-significant bit becomes second most-significant bit, etc. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")] + /// let m = n.reverse_bits(); + /// + #[doc = concat!("assert_eq!(m, ", $reversed, ");")] + #[doc = concat!("assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits());")] + /// ``` + #[stable(feature = "reverse_bits", since = "1.37.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + #[must_use] + pub const fn reverse_bits(self) -> Self { + (self as $UnsignedT).reverse_bits() as Self + } -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "big") { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_be(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes())")] + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn from_be(x: Self) -> Self { + #[cfg(target_endian = "big")] + { + x + } + #[cfg(not(target_endian = "big"))] + { + x.swap_bytes() } } - doc_comment! { - concat!("Returns the number of ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b100_0000", stringify!($SelfT), "; - -assert_eq!(n.count_ones(), 1);", -$EndFeature, " -``` -"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } - } - - doc_comment! { - concat!("Returns the number of zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 1);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn count_zeros(self) -> u32 { - (!self).count_ones() + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "little") { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_le(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes())")] + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn from_le(x: Self) -> Self { + #[cfg(target_endian = "little")] + { + x + } + #[cfg(not(target_endian = "little"))] + { + x.swap_bytes() } } - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = -1", stringify!($SelfT), "; - -assert_eq!(n.leading_zeros(), 0);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn leading_zeros(self) -> u32 { - (self as $UnsignedT).leading_zeros() + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn to_be(self) -> Self { // or not to be? + #[cfg(target_endian = "big")] + { + self + } + #[cfg(not(target_endian = "big"))] + { + self.swap_bytes() } } - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = -4", stringify!($SelfT), "; - -assert_eq!(n.trailing_zeros(), 2);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn trailing_zeros(self) -> u32 { - (self as $UnsignedT).trailing_zeros() + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn to_le(self) -> Self { + #[cfg(target_endian = "little")] + { + self + } + #[cfg(not(target_endian = "little"))] + { + self.swap_bytes() } } - doc_comment! { - concat!("Returns the number of leading ones in the binary representation of `self`. + /// Checked integer addition. Computes `self + rhs`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), Some(", stringify!($SelfT), "::MAX - 1));")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); + if unlikely!(b) {None} else {Some(a)} + } -# Examples + /// Unchecked integer addition. Computes `self + rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_add`. + unsafe { intrinsics::unchecked_add(self, rhs) } + } -Basic usage: + /// Checked integer subtraction. Computes `self - rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(1), Some(", stringify!($SelfT), "::MIN + 1));")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(3), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); + if unlikely!(b) {None} else {Some(a)} + } -``` -", $Feature, "let n = -1", stringify!($SelfT), "; + /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_sub`. + unsafe { intrinsics::unchecked_sub(self, rhs) } + } -assert_eq!(n.leading_ones(), ", stringify!($BITS), ");", -$EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn leading_ones(self) -> u32 { - (self as $UnsignedT).leading_ones() + /// Checked integer multiplication. Computes `self * rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(1), Some(", stringify!($SelfT), "::MAX));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_mul`. + unsafe { intrinsics::unchecked_mul(self, rhs) } + } + + /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` + /// or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_div(-1), Some(", stringify!($Max), "));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_div(-1), None);")] + #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_div(0), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + // SAFETY: div by zero and by INT_MIN have been checked above + Some(unsafe { intrinsics::unchecked_div(self, rhs) }) } } - doc_comment! { - concat!("Returns the number of trailing ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 3", stringify!($SelfT), "; - -assert_eq!(n.trailing_ones(), 2);", -$EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn trailing_ones(self) -> u32 { - (self as $UnsignedT).trailing_ones() + /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, + /// returning `None` if `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_div_euclid(-1), Some(", stringify!($Max), "));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_div_euclid(-1), None);")] + #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_div_euclid(0), None);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + Some(self.div_euclid(rhs)) } } - doc_comment! { - concat!("Shifts the bits to the left by a specified amount, `n`, -wrapping the truncated bits to the end of the resulting integer. - -Please note this isn't the same operation as the `<<` shifting operator! - -# Examples - -Basic usage: - -``` -let n = ", $rot_op, stringify!($SelfT), "; -let m = ", $rot_result, "; - -assert_eq!(n.rotate_left(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_left(self, n: u32) -> Self { - (self as $UnsignedT).rotate_left(n) as Self + /// Checked integer remainder. Computes `self % rhs`, returning `None` if + /// `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1));")] + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_rem(-1), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + // SAFETY: div by zero and by INT_MIN have been checked above + Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) } } - doc_comment! { - concat!("Shifts the bits to the right by a specified amount, `n`, -wrapping the truncated bits to the beginning of the resulting -integer. - -Please note this isn't the same operation as the `>>` shifting operator! - -# Examples - -Basic usage: - -``` -let n = ", $rot_result, stringify!($SelfT), "; -let m = ", $rot_op, "; - -assert_eq!(n.rotate_right(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_right(self, n: u32) -> Self { - (self as $UnsignedT).rotate_right(n) as Self + /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` + /// if `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1));")] + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_rem_euclid(-1), None);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + Some(self.rem_euclid(rhs)) } } - doc_comment! { - concat!("Reverses the byte order of the integer. + /// Checked negation. Computes `-self`, returning `None` if `self == MIN`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_neg(), Some(-5));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_neg(), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_neg(self) -> Option { + let (a, b) = self.overflowing_neg(); + if unlikely!(b) {None} else {Some(a)} + } -# Examples + /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger + /// than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(129), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shl(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shl(rhs); + if unlikely!(b) {None} else {Some(a)} + } -Basic usage: + /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is + /// larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(128), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shr(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shr(rhs); + if unlikely!(b) {None} else {Some(a)} + } -``` -let n = ", $swap_op, stringify!($SelfT), "; - -let m = n.swap_bytes(); - -assert_eq!(m, ", $swapped, "); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn swap_bytes(self) -> Self { - (self as $UnsignedT).swap_bytes() as Self + /// Checked absolute value. Computes `self.abs()`, returning `None` if + /// `self == MIN`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").checked_abs(), Some(5));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_abs(), None);")] + /// ``` + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_abs(self) -> Option { + if self.is_negative() { + self.checked_neg() + } else { + Some(self) } } - doc_comment! { - concat!("Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, - second least-significant bit becomes second most-significant bit, etc. + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")] + /// ``` -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.reverse_bits(); - -assert_eq!(m, ", $reversed, "); -assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits()); -```"), - #[stable(feature = "reverse_bits", since = "1.37.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - #[must_use] - pub const fn reverse_bits(self) -> Self { - (self as $UnsignedT).reverse_bits() as Self + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_pow(self, mut exp: u32) -> Option { + if exp == 0 { + return Some(1); } - } + let mut base = self; + let mut acc: Self = 1; - doc_comment! { - concat!("Converts an integer from big endian to the target's endianness. - -On big endian this is a no-op. On little endian the bytes are swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(", stringify!($SelfT), "::from_be(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn from_be(x: Self) -> Self { - #[cfg(target_endian = "big")] - { - x - } - #[cfg(not(target_endian = "big"))] - { - x.swap_bytes() + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); } + exp /= 2; + base = try_opt!(base.checked_mul(base)); + } + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + Some(try_opt!(acc.checked_mul(base))) + } + + /// Saturating integer addition. Computes `self + rhs`, saturating at the numeric + /// bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(100), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_add(-1), ", stringify!($SelfT), "::MIN);")] + /// ``` + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_add(self, rhs: Self) -> Self { + intrinsics::saturating_add(self, rhs) + } + + /// Saturating integer subtraction. Computes `self - rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub(100), ", stringify!($SelfT), "::MIN);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_sub(-1), ", stringify!($SelfT), "::MAX);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_sub(self, rhs: Self) -> Self { + intrinsics::saturating_sub(self, rhs) + } + + /// Saturating integer negation. Computes `-self`, returning `MAX` if `self == MIN` + /// instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100);")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").saturating_neg(), 100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_neg(), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_neg(), ", stringify!($SelfT), "::MIN + 1);")] + /// ``` + + #[stable(feature = "saturating_neg", since = "1.45.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_neg(self) -> Self { + intrinsics::saturating_sub(0, self) + } + + /// Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self == + /// MIN` instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100);")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").saturating_abs(), 100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_abs(), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).saturating_abs(), ", stringify!($SelfT), "::MAX);")] + /// ``` + + #[stable(feature = "saturating_neg", since = "1.45.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_abs(self) -> Self { + if self.is_negative() { + self.saturating_neg() + } else { + self } } - doc_comment! { - concat!("Converts an integer from little endian to the target's endianness. - -On little endian this is a no-op. On big endian the bytes are swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(", stringify!($SelfT), "::from_le(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn from_le(x: Self) -> Self { - #[cfg(target_endian = "little")] - { - x - } - #[cfg(not(target_endian = "little"))] - { - x.swap_bytes() - } - } - } - - doc_comment! { - concat!("Converts `self` to big endian from the target's endianness. - -On big endian this is a no-op. On little endian the bytes are swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(n.to_be(), n) -} else { - assert_eq!(n.to_be(), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn to_be(self) -> Self { // or not to be? - #[cfg(target_endian = "big")] - { - self - } - #[cfg(not(target_endian = "big"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("Converts `self` to little endian from the target's endianness. - -On little endian this is a no-op. On big endian the bytes are swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(n.to_le(), n) -} else { - assert_eq!(n.to_le(), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn to_le(self) -> Self { - #[cfg(target_endian = "little")] - { - self - } - #[cfg(not(target_endian = "little"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("Checked integer addition. Computes `self + rhs`, returning `None` -if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MAX - 2).checked_add(1), Some(", stringify!($SelfT), "::MAX - 1)); -assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_add(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), -"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer subtraction. Computes `self - rhs`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MIN + 2).checked_sub(1), Some(", stringify!($SelfT), "::MIN + 1)); -assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(3), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_sub(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), -"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer multiplication. Computes `self * rhs`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), -"::MAX.checked_mul(1), Some(", stringify!($SelfT), "::MAX)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_mul(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_mul(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), -"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` -or the division results in overflow. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MIN + 1).checked_div(-1), Some(", stringify!($Max), ")); -assert_eq!(", stringify!($SelfT), "::MIN.checked_div(-1), None); -assert_eq!((1", stringify!($SelfT), ").checked_div(0), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None + /// Saturating integer multiplication. Computes `self * rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".saturating_mul(12), 120);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_mul(10), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_mul(10), ", stringify!($SelfT), "::MIN);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_mul(self, rhs: Self) -> Self { + match self.checked_mul(rhs) { + Some(x) => x, + None => if (self < 0) == (rhs < 0) { + Self::MAX } else { - // SAFETY: div by zero and by INT_MIN have been checked above - Some(unsafe { intrinsics::unchecked_div(self, rhs) }) + Self::MIN } } } - doc_comment! { - concat!("Checked Euclidean division. Computes `self.div_euclid(rhs)`, -returning `None` if `rhs == 0` or the division results in overflow. + /// Saturating integer exponentiation. Computes `self.pow(exp)`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(3), ", stringify!($SelfT), "::MIN);")] + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Some(x) => x, + None if self < 0 && exp % 2 == 1 => Self::MIN, + None => Self::MAX, + } + } -# Examples + /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the + /// boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_add(27), 127);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add(2), ", stringify!($SelfT), "::MIN + 1);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_add(self, rhs: Self) -> Self { + intrinsics::wrapping_add(self, rhs) + } -Basic usage: + /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the + /// boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".wrapping_sub(127), -127);")] + #[doc = concat!("assert_eq!((-2", stringify!($SelfT), ").wrapping_sub(", stringify!($SelfT), "::MAX), ", stringify!($SelfT), "::MAX);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_sub(self, rhs: Self) -> Self { + intrinsics::wrapping_sub(self, rhs) + } -``` -assert_eq!((", stringify!($SelfT), -"::MIN + 1).checked_div_euclid(-1), Some(", stringify!($Max), ")); -assert_eq!(", stringify!($SelfT), "::MIN.checked_div_euclid(-1), None); -assert_eq!((1", stringify!($SelfT), ").checked_div_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - Some(self.div_euclid(rhs)) + /// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at + /// the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".wrapping_mul(12), 120);")] + /// assert_eq!(11i8.wrapping_mul(12), -124); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_mul(self, rhs: Self) -> Self { + intrinsics::wrapping_mul(self, rhs) + } + + /// Wrapping (modular) division. Computes `self / rhs`, wrapping around at the + /// boundary of the type. + /// + /// The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where + /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// that is too large to represent in the type. In such a case, this function returns `MIN` itself. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);")] + /// assert_eq!((-128i8).wrapping_div(-1), -128); + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div(self, rhs: Self) -> Self { + self.overflowing_div(rhs).0 + } + + /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, + /// wrapping around at the boundary of the type. + /// + /// Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value + /// for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the + /// type. In this case, this method returns `MIN` itself. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10);")] + /// assert_eq!((-128i8).wrapping_div_euclid(-1), -128); + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { + self.overflowing_div_euclid(rhs).0 + } + + /// Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the + /// boundary of the type. + /// + /// Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` + /// invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, + /// this function returns `0`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);")] + /// assert_eq!((-128i8).wrapping_rem(-1), 0); + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem(self, rhs: Self) -> Self { + self.overflowing_rem(rhs).0 + } + + /// Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around + /// at the boundary of the type. + /// + /// Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value + /// for the type). In this case, this method returns 0. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0);")] + /// assert_eq!((-128i8).wrapping_rem_euclid(-1), 0); + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { + self.overflowing_rem_euclid(rhs).0 + } + + /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary + /// of the type. + /// + /// The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` + /// is the negative minimal value for the type); this is a positive value that is too large to represent + /// in the type. In such a case, this function returns `MIN` itself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_neg(), -100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.wrapping_neg(), ", stringify!($SelfT), "::MIN);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn wrapping_neg(self) -> Self { + self.overflowing_neg().0 + } + + /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes + /// any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to + /// the range of the type, rather than the bits shifted out of the LHS being returned to the other end. + /// The primitive integer types all implement a [`rotate_left`](#method.rotate_left) function, + /// which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128);")] + #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shl(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) + } + } + + /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` + /// removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted + /// to the range of the type, rather than the bits shifted out of the LHS being returned to the other + /// end. The primitive integer types all implement a [`rotate_right`](#method.rotate_right) function, + /// which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1);")] + /// assert_eq!((-128i16).wrapping_shr(64), -128); + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shr(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) + } + } + + /// Wrapping (modular) absolute value. Computes `self.abs()`, wrapping around at + /// the boundary of the type. + /// + /// The only case where such wrapping can occur is when one takes the absolute value of the negative + /// minimal value for the type; this is a positive value that is too large to represent in the type. In + /// such a case, this function returns `MIN` itself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_abs(), 100);")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").wrapping_abs(), 100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.wrapping_abs(), ", stringify!($SelfT), "::MIN);")] + /// assert_eq!((-128i8).wrapping_abs() as u8, 128); + /// ``` + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + #[inline] + pub const fn wrapping_abs(self) -> Self { + if self.is_negative() { + self.wrapping_neg() + } else { + self + } + } + + /// Computes the absolute value of `self` without any wrapping + /// or panicking. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(unsigned_abs)] + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".unsigned_abs(), 100", stringify!($UnsignedT), ");")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").unsigned_abs(), 100", stringify!($UnsignedT), ");")] + /// assert_eq!((-128i8).unsigned_abs(), 128u8); + /// ``` + #[unstable(feature = "unsigned_abs", issue = "74913")] + #[inline] + pub const fn unsigned_abs(self) -> $UnsignedT { + self.wrapping_abs() as $UnsignedT + } + + /// Wrapping (modular) exponentiation. Computes `self.pow(exp)`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81);")] + /// assert_eq!(3i8.wrapping_pow(5), -13); + /// assert_eq!(3i8.wrapping_pow(6), -39); + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); } + exp /= 2; + base = base.wrapping_mul(base); + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) + } + + /// Calculates `self` + `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating whether an arithmetic overflow would + /// occur. If an overflow would have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + + /// Calculates `self` - `rhs` + /// + /// Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic overflow + /// would occur. If an overflow would have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + + /// Calculates the multiplication of `self` and `rhs`. + /// + /// Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic overflow + /// would occur. If an overflow would have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_mul(2), (10, false));")] + /// assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true)); + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + + /// Calculates the divisor when `self` is divided by `rhs`. + /// + /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would + /// occur. If an overflow would occur then self is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div(-1), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (self, true) + } else { + (self / rhs, false) } } - doc_comment! { - concat!("Checked integer remainder. Computes `self % rhs`, returning `None` if -`rhs == 0` or the division results in overflow. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None); -assert_eq!(", stringify!($SelfT), "::MIN.checked_rem(-1), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - // SAFETY: div by zero and by INT_MIN have been checked above - Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) - } + /// Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. + /// + /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would + /// occur. If an overflow would occur then `self` is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (self, true) + } else { + (self.div_euclid(rhs), false) } } - doc_comment! { - concat!("Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` -if `rhs == 0` or the division results in overflow. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None); -assert_eq!(", stringify!($SelfT), "::MIN.checked_rem_euclid(-1), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - Some(self.rem_euclid(rhs)) - } - } - } - - doc_comment! { - concat!("Checked negation. Computes `-self`, returning `None` if `self == MIN`. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".checked_neg(), Some(-5)); -assert_eq!(", stringify!($SelfT), "::MIN.checked_neg(), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[inline] - pub const fn checked_neg(self) -> Option { - let (a, b) = self.overflowing_neg(); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger -than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10)); -assert_eq!(0x1", stringify!($SelfT), ".checked_shl(129), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is -larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shr(128), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked absolute value. Computes `self.abs()`, returning `None` if -`self == MIN`. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!((-5", stringify!($SelfT), ").checked_abs(), Some(5)); -assert_eq!(", stringify!($SelfT), "::MIN.checked_abs(), None);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[inline] - pub const fn checked_abs(self) -> Option { - if self.is_negative() { - self.checked_neg() - } else { - Some(self) - } - } - } - - doc_comment! { - concat!("Checked exponentiation. Computes `self.pow(exp)`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);", -$EndFeature, " -```"), - - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_pow(self, mut exp: u32) -> Option { - if exp == 0 { - return Some(1); - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - exp /= 2; - base = try_opt!(base.checked_mul(base)); - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - Some(try_opt!(acc.checked_mul(base))) - } - } - - doc_comment! { - concat!("Saturating integer addition. Computes `self + rhs`, saturating at the numeric -bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(100), ", stringify!($SelfT), -"::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_add(-1), ", stringify!($SelfT), -"::MIN);", -$EndFeature, " -```"), - - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_add(self, rhs: Self) -> Self { - intrinsics::saturating_add(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the -numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub(100), ", stringify!($SelfT), -"::MIN); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_sub(-1), ", stringify!($SelfT), -"::MAX);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_sub(self, rhs: Self) -> Self { - intrinsics::saturating_sub(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer negation. Computes `-self`, returning `MAX` if `self == MIN` -instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100); -assert_eq!((-100", stringify!($SelfT), ").saturating_neg(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_neg(), ", stringify!($SelfT), -"::MAX); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_neg(), ", stringify!($SelfT), -"::MIN + 1);", -$EndFeature, " -```"), - - #[stable(feature = "saturating_neg", since = "1.45.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] - pub const fn saturating_neg(self) -> Self { - intrinsics::saturating_sub(0, self) - } - } - - doc_comment! { - concat!("Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self == -MIN` instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100); -assert_eq!((-100", stringify!($SelfT), ").saturating_abs(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_abs(), ", stringify!($SelfT), -"::MAX); -assert_eq!((", stringify!($SelfT), "::MIN + 1).saturating_abs(), ", stringify!($SelfT), -"::MAX);", -$EndFeature, " -```"), - - #[stable(feature = "saturating_neg", since = "1.45.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] - pub const fn saturating_abs(self) -> Self { - if self.is_negative() { - self.saturating_neg() - } else { - self - } - } - } - - doc_comment! { - concat!("Saturating integer multiplication. Computes `self * rhs`, saturating at the -numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(10", stringify!($SelfT), ".saturating_mul(12), 120); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_mul(10), ", stringify!($SelfT), "::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_mul(10), ", stringify!($SelfT), "::MIN);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_mul(self, rhs: Self) -> Self { - match self.checked_mul(rhs) { - Some(x) => x, - None => if (self < 0) == (rhs < 0) { - Self::MAX - } else { - Self::MIN - } - } - } - } - - doc_comment! { - concat!("Saturating integer exponentiation. Computes `self.pow(exp)`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(3), ", stringify!($SelfT), "::MIN);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Some(x) => x, - None if self < 0 && exp % 2 == 1 => Self::MIN, - None => Self::MAX, - } - } - } - - doc_comment! { - concat!("Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the -boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_add(27), 127); -assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add(2), ", stringify!($SelfT), -"::MIN + 1);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_add(self, rhs: Self) -> Self { - intrinsics::wrapping_add(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the -boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".wrapping_sub(127), -127); -assert_eq!((-2", stringify!($SelfT), ").wrapping_sub(", stringify!($SelfT), "::MAX), ", -stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_sub(self, rhs: Self) -> Self { - intrinsics::wrapping_sub(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at -the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".wrapping_mul(12), 120); -assert_eq!(11i8.wrapping_mul(12), -124);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_mul(self, rhs: Self) -> Self { - intrinsics::wrapping_mul(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) division. Computes `self / rhs`, wrapping around at the -boundary of the type. - -The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where -`MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value -that is too large to represent in the type. In such a case, this function returns `MIN` itself. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10); -assert_eq!((-128i8).wrapping_div(-1), -128);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div(self, rhs: Self) -> Self { - self.overflowing_div(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, -wrapping around at the boundary of the type. - -Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value -for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the -type. In this case, this method returns `MIN` itself. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10); -assert_eq!((-128i8).wrapping_div_euclid(-1), -128); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { - self.overflowing_div_euclid(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the -boundary of the type. - -Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` -invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, -this function returns `0`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0); -assert_eq!((-128i8).wrapping_rem(-1), 0);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem(self, rhs: Self) -> Self { - self.overflowing_rem(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around -at the boundary of the type. - -Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value -for the type). In this case, this method returns 0. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); -assert_eq!((-128i8).wrapping_rem_euclid(-1), 0); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { - self.overflowing_rem_euclid(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary -of the type. - -The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` -is the negative minimal value for the type); this is a positive value that is too large to represent -in the type. In such a case, this function returns `MIN` itself. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_neg(), -100); -assert_eq!(", stringify!($SelfT), "::MIN.wrapping_neg(), ", stringify!($SelfT), -"::MIN);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn wrapping_neg(self) -> Self { - self.overflowing_neg().0 - } - } - - doc_comment! { - concat!("Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes -any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to -the range of the type, rather than the bits shifted out of the LHS being returned to the other end. -The primitive integer types all implement a `[`rotate_left`](#method.rotate_left) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128); -assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shl(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) - } - } - } - - doc_comment! { - concat!("Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` -removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted -to the range of the type, rather than the bits shifted out of the LHS being returned to the other -end. The primitive integer types all implement a [`rotate_right`](#method.rotate_right) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1); -assert_eq!((-128i16).wrapping_shr(64), -128);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shr(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) - } - } - } - - doc_comment! { - concat!("Wrapping (modular) absolute value. Computes `self.abs()`, wrapping around at -the boundary of the type. - -The only case where such wrapping can occur is when one takes the absolute value of the negative -minimal value for the type; this is a positive value that is too large to represent in the type. In -such a case, this function returns `MIN` itself. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_abs(), 100); -assert_eq!((-100", stringify!($SelfT), ").wrapping_abs(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.wrapping_abs(), ", stringify!($SelfT), -"::MIN); -assert_eq!((-128i8).wrapping_abs() as u8, 128);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - #[inline] - pub const fn wrapping_abs(self) -> Self { - if self.is_negative() { - self.wrapping_neg() - } else { - self - } - } - } - - doc_comment! { - concat!("Computes the absolute value of `self` without any wrapping -or panicking. - - -# Examples - -Basic usage: - -``` -", $Feature, "#![feature(unsigned_abs)] -assert_eq!(100", stringify!($SelfT), ".unsigned_abs(), 100", stringify!($UnsignedT), "); -assert_eq!((-100", stringify!($SelfT), ").unsigned_abs(), 100", stringify!($UnsignedT), "); -assert_eq!((-128i8).unsigned_abs(), 128u8);", -$EndFeature, " -```"), - #[unstable(feature = "unsigned_abs", issue = "74913")] - #[inline] - pub const fn unsigned_abs(self) -> $UnsignedT { - self.wrapping_abs() as $UnsignedT - } - } - - doc_comment! { - concat!("Wrapping (modular) exponentiation. Computes `self.pow(exp)`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81); -assert_eq!(3i8.wrapping_pow(5), -13); -assert_eq!(3i8.wrapping_pow(6), -39);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - } - exp /= 2; - base = base.wrapping_mul(base); - } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) - } - } - - doc_comment! { - concat!("Calculates `self` + `rhs` - -Returns a tuple of the addition along with a boolean indicating whether an arithmetic overflow would -occur. If an overflow would have occurred then the wrapped value is returned. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false)); -assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (", stringify!($SelfT), -"::MIN, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates `self` - `rhs` - -Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic overflow -would occur. If an overflow would have occurred then the wrapped value is returned. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_sub(1), (", stringify!($SelfT), -"::MAX, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates the multiplication of `self` and `rhs`. - -Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic overflow -would occur. If an overflow would have occurred then the wrapped value is returned. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_mul(2), (10, false)); -assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates the divisor when `self` is divided by `rhs`. - -Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would -occur. If an overflow would occur then self is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div(-1), (", stringify!($SelfT), -"::MIN, true));", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (self, true) - } else { - (self / rhs, false) - } - } - } - - doc_comment! { - concat!("Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. - -Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would -occur. If an overflow would occur then `self` is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringify!($SelfT), -"::MIN, true)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (self, true) - } else { - (self.div_euclid(rhs), false) - } - } - } - - doc_comment! { - concat!("Calculates the remainder when `self` is divided by `rhs`. - -Returns a tuple of the remainder after dividing along with a boolean indicating whether an -arithmetic overflow would occur. If an overflow would occur then 0 is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem(-1), (0, true));", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (0, true) - } else { - (self % rhs, false) - } + /// Calculates the remainder when `self` is divided by `rhs`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean indicating whether an + /// arithmetic overflow would occur. If an overflow would occur then 0 is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem(-1), (0, true));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (0, true) + } else { + (self % rhs, false) } } - doc_comment! { - concat!("Overflowing Euclidean remainder. Calculates `self.rem_euclid(rhs)`. - -Returns a tuple of the remainder after dividing along with a boolean indicating whether an -arithmetic overflow would occur. If an overflow would occur then 0 is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true)); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (0, true) - } else { - (self.rem_euclid(rhs), false) - } + /// Overflowing Euclidean remainder. Calculates `self.rem_euclid(rhs)`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean indicating whether an + /// arithmetic overflow would occur. If an overflow would occur then 0 is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true));")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (0, true) + } else { + (self.rem_euclid(rhs), false) } } - doc_comment! { - concat!("Negates self, overflowing if this is equal to the minimum value. - -Returns a tuple of the negated version of self along with a boolean indicating whether an overflow -happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the -minimum value will be returned again and `true` will be returned for an overflow happening. - -# Examples - -Basic usage: - -``` -assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($SelfT), -"::MIN, true));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - pub const fn overflowing_neg(self) -> (Self, bool) { - if unlikely!(self == Self::MIN) { - (Self::MIN, true) - } else { - (-self, false) - } + /// Negates self, overflowing if this is equal to the minimum value. + /// + /// Returns a tuple of the negated version of self along with a boolean indicating whether an overflow + /// happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the + /// minimum value will be returned again and `true` will be returned for an overflow happening. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + pub const fn overflowing_neg(self) -> (Self, bool) { + if unlikely!(self == Self::MIN) { + (Self::MIN, true) + } else { + (-self, false) } } - doc_comment! { - concat!("Shifts self left by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean indicating whether the shift -value was larger than or equal to the number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then used to perform the shift. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT),".overflowing_shl(4), (0x10, false)); -assert_eq!(0x1i32.overflowing_shl(36), (0x10, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) - } + /// Shifts self left by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean indicating whether the shift + /// value was larger than or equal to the number of bits. If the shift value is too large, then value is + /// masked (N-1) where N is the number of bits, and this value is then used to perform the shift. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT),".overflowing_shl(4), (0x10, false));")] + /// assert_eq!(0x1i32.overflowing_shl(36), (0x10, true)); + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) } - doc_comment! { - concat!("Shifts self right by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean indicating whether the shift -value was larger than or equal to the number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then used to perform the shift. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false)); -assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) - } + /// Shifts self right by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean indicating whether the shift + /// value was larger than or equal to the number of bits. If the shift value is too large, then value is + /// masked (N-1) where N is the number of bits, and this value is then used to perform the shift. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false));")] + /// assert_eq!(0x10i32.overflowing_shr(36), (0x1, true)); + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) } - doc_comment! { - concat!("Computes the absolute value of `self`. - -Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow -happened. If self is the minimum value (e.g., ", stringify!($SelfT), "::MIN for values of type - ", stringify!($SelfT), "), then the minimum value will be returned again and true will be returned -for an overflow happening. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".overflowing_abs(), (10, false)); -assert_eq!((-10", stringify!($SelfT), ").overflowing_abs(), (10, false)); -assert_eq!((", stringify!($SelfT), "::MIN).overflowing_abs(), (", stringify!($SelfT), -"::MIN, true));", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn overflowing_abs(self) -> (Self, bool) { - (self.wrapping_abs(), self == Self::MIN) - } + /// Computes the absolute value of `self`. + /// + /// Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow + /// happened. If self is the minimum value + #[doc = concat!("(e.g., ", stringify!($SelfT), "::MIN for values of type ", stringify!($SelfT), "),")] + /// then the minimum value will be returned again and true will be returned + /// for an overflow happening. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".overflowing_abs(), (10, false));")] + #[doc = concat!("assert_eq!((-10", stringify!($SelfT), ").overflowing_abs(), (10, false));")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN).overflowing_abs(), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn overflowing_abs(self) -> (Self, bool) { + (self.wrapping_abs(), self == Self::MIN) } - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// Returns a tuple of the exponentiation along with a bool indicating + /// whether an overflow happened. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false));")] + /// assert_eq!(3i8.overflowing_pow(5), (-13, true)); + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { + if exp == 0 { + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; -Returns a tuple of the exponentiation along with a bool indicating -whether an overflow happened. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false)); -assert_eq!(3i8.overflowing_pow(5), (-13, true));", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - if exp == 0 { - return (1,false); - } - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; overflown |= r.1; } + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; + overflown |= r.1; + } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; + r + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let x: ", stringify!($SelfT), " = 2; // or any other integer type")] + /// + /// assert_eq!(x.pow(5), 32); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; + } + exp /= 2; + base = base * base; + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } + + /// Calculates the quotient of Euclidean division of `self` by `rhs`. + /// + /// This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`, + /// with `0 <= self.rem_euclid(rhs) < rhs`. + /// + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// If `self > 0`, this is equal to round towards zero (the default in Rust); + /// if `self < 0`, this is equal to round towards +/- infinity. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0 or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let a: ", stringify!($SelfT), " = 7; // or any other integer type")] + /// let b = 4; + /// + /// assert_eq!(a.div_euclid(b), 1); // 7 >= 4 * 1 + /// assert_eq!(a.div_euclid(-b), -1); // 7 >= -4 * -1 + /// assert_eq!((-a).div_euclid(b), -2); // -7 >= 4 * -2 + /// assert_eq!((-a).div_euclid(-b), 2); // -7 >= -4 * 2 + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn div_euclid(self, rhs: Self) -> Self { + let q = self / rhs; + if self % rhs < 0 { + return if rhs > 0 { q - 1 } else { q + 1 } + } + q + } + + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// This is done as if by the Euclidean division algorithm -- given + /// `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and + /// `0 <= r < abs(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0 or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let a: ", stringify!($SelfT), " = 7; // or any other integer type")] + /// let b = 4; + /// + /// assert_eq!(a.rem_euclid(b), 3); + /// assert_eq!((-a).rem_euclid(b), 1); + /// assert_eq!(a.rem_euclid(-b), 3); + /// assert_eq!((-a).rem_euclid(-b), 1); + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn rem_euclid(self, rhs: Self) -> Self { + let r = self % rhs; + if r < 0 { + if rhs < 0 { + r - rhs + } else { + r + rhs + } + } else { r } } - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -# Examples - -Basic usage: - -``` -", $Feature, "let x: ", stringify!($SelfT), " = 2; // or any other integer type - -assert_eq!(x.pow(5), 32);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; - } - exp /= 2; - base = base * base; - } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base + /// Computes the absolute value of `self`. + /// + /// # Overflow behavior + /// + /// The absolute value of + #[doc = concat!("`", stringify!($SelfT), "::MIN`")] + /// cannot be represented as an + #[doc = concat!("`", stringify!($SelfT), "`,")] + /// and attempting to calculate it will cause an overflow. This means + /// that code in debug mode will trigger a panic on this case and + /// optimized code will return + #[doc = concat!("`", stringify!($SelfT), "::MIN`")] + /// without a panic. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".abs(), 10);")] + #[doc = concat!("assert_eq!((-10", stringify!($SelfT), ").abs(), 10);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn abs(self) -> Self { + // Note that the #[inline] above means that the overflow + // semantics of the subtraction depend on the crate we're being + // inlined into. + if self.is_negative() { + -self + } else { + self } } - doc_comment! { - concat!("Calculates the quotient of Euclidean division of `self` by `rhs`. - -This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`, -with `0 <= self.rem_euclid(rhs) < rhs`. - -In other words, the result is `self / rhs` rounded to the integer `n` -such that `self >= n * rhs`. -If `self > 0`, this is equal to round towards zero (the default in Rust); -if `self < 0`, this is equal to round towards +/- infinity. - -# Panics - -This function will panic if `rhs` is 0 or the division results in overflow. - -# Examples - -Basic usage: - -``` -let a: ", stringify!($SelfT), " = 7; // or any other integer type -let b = 4; - -assert_eq!(a.div_euclid(b), 1); // 7 >= 4 * 1 -assert_eq!(a.div_euclid(-b), -1); // 7 >= -4 * -1 -assert_eq!((-a).div_euclid(b), -2); // -7 >= 4 * -2 -assert_eq!((-a).div_euclid(-b), 2); // -7 >= -4 * 2 -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn div_euclid(self, rhs: Self) -> Self { - let q = self / rhs; - if self % rhs < 0 { - return if rhs > 0 { q - 1 } else { q + 1 } - } - q + /// Returns a number representing sign of `self`. + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".signum(), 1);")] + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".signum(), 0);")] + #[doc = concat!("assert_eq!((-10", stringify!($SelfT), ").signum(), -1);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")] + #[inline] + pub const fn signum(self) -> Self { + match self { + n if n > 0 => 1, + 0 => 0, + _ => -1, } } + /// Returns `true` if `self` is positive and `false` if the number is zero or + /// negative. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert!(10", stringify!($SelfT), ".is_positive());")] + #[doc = concat!("assert!(!(-10", stringify!($SelfT), ").is_positive());")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn is_positive(self) -> bool { self > 0 } - doc_comment! { - concat!("Calculates the least nonnegative remainder of `self (mod rhs)`. + /// Returns `true` if `self` is negative and `false` if the number is zero or + /// positive. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert!((-10", stringify!($SelfT), ").is_negative());")] + #[doc = concat!("assert!(!10", stringify!($SelfT), ".is_negative());")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn is_negative(self) -> bool { self < 0 } -This is done as if by the Euclidean division algorithm -- given -`r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and -`0 <= r < abs(rhs)`. - -# Panics - -This function will panic if `rhs` is 0 or the division results in overflow. - -# Examples - -Basic usage: - -``` -let a: ", stringify!($SelfT), " = 7; // or any other integer type -let b = 4; - -assert_eq!(a.rem_euclid(b), 3); -assert_eq!((-a).rem_euclid(b), 1); -assert_eq!(a.rem_euclid(-b), 3); -assert_eq!((-a).rem_euclid(-b), 1); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn rem_euclid(self, rhs: Self) -> Self { - let r = self % rhs; - if r < 0 { - if rhs < 0 { - r - rhs - } else { - r + rhs - } - } else { - r - } - } + /// Return the memory representation of this integer as a byte array in + /// big-endian (network) byte order. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", $be_bytes, ");")] + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_be().to_ne_bytes() } - doc_comment! { - concat!("Computes the absolute value of `self`. - -# Overflow behavior - -The absolute value of `", stringify!($SelfT), "::MIN` cannot be represented as an -`", stringify!($SelfT), "`, and attempting to calculate it will cause an overflow. This means that -code in debug mode will trigger a panic on this case and optimized code will return `", -stringify!($SelfT), "::MIN` without a panic. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".abs(), 10); -assert_eq!((-10", stringify!($SelfT), ").abs(), 10);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn abs(self) -> Self { - // Note that the #[inline] above means that the overflow - // semantics of the subtraction depend on the crate we're being - // inlined into. - if self.is_negative() { - -self - } else { - self - } - } + /// Return the memory representation of this integer as a byte array in + /// little-endian byte order. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", $le_bytes, ");")] + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_le().to_ne_bytes() } - doc_comment! { - concat!("Returns a number representing sign of `self`. - - - `0` if the number is zero - - `1` if the number is positive - - `-1` if the number is negative - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".signum(), 1); -assert_eq!(0", stringify!($SelfT), ".signum(), 0); -assert_eq!((-10", stringify!($SelfT), ").signum(), -1);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")] - #[inline] - pub const fn signum(self) -> Self { - match self { - n if n > 0 => 1, - 0 => 0, - _ => -1, - } - } + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, + /// instead. + /// + #[doc = $to_xe_bytes_doc] + /// + /// [`to_be_bytes`]: #method.to_be_bytes + /// [`to_le_bytes`]: #method.to_le_bytes + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes();")] + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" ", $be_bytes)] + /// } else { + #[doc = concat!(" ", $le_bytes)] + /// } + /// ); + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute them to arrays of bytes + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { mem::transmute(self) } } - doc_comment! { - concat!("Returns `true` if `self` is positive and `false` if the number is zero or -negative. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!(10", stringify!($SelfT), ".is_positive()); -assert!(!(-10", stringify!($SelfT), ").is_positive());", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn is_positive(self) -> bool { self > 0 } + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// [`to_ne_bytes`] should be preferred over this whenever possible. + /// + /// [`to_ne_bytes`]: #method.to_ne_bytes + /// + /// # Examples + /// + /// ``` + /// #![feature(num_as_ne_bytes)] + #[doc = concat!("let num = ", $swap_op, stringify!($SelfT), ";")] + /// let bytes = num.as_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" &", $be_bytes)] + /// } else { + #[doc = concat!(" &", $le_bytes)] + /// } + /// ); + /// ``` + #[unstable(feature = "num_as_ne_bytes", issue = "76976")] + #[inline] + pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { &*(self as *const Self as *const _) } } - doc_comment! { - concat!("Returns `true` if `self` is negative and `false` if the number is zero or -positive. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!((-10", stringify!($SelfT), ").is_negative()); -assert!(!10", stringify!($SelfT), ".is_negative());", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn is_negative(self) -> bool { self < 0 } + /// Create an integer value from its representation as a byte array in + /// big endian. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, ");")] + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_be(Self::from_ne_bytes(bytes)) } - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -big-endian (network) byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); -assert_eq!(bytes, ", $be_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { - self.to_be().to_ne_bytes() - } + /// Create an integer value from its representation as a byte array in + /// little endian. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, ");")] + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) } -doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -little-endian byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); -assert_eq!(bytes, ", $le_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { - self.to_le().to_ne_bytes() - } + /// Create an integer value from its memory representation as a byte + /// array in native endianness. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: #method.from_be_bytes + /// [`from_le_bytes`]: #method.from_le_bytes + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") {")] + #[doc = concat!(" ", $be_bytes)] + /// } else { + #[doc = concat!(" ", $le_bytes)] + /// }); + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute to them + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute to them + unsafe { mem::transmute(bytes) } } - doc_comment! { - concat!(" -Return the memory representation of this integer as a byte array in -native byte order. - -As the target platform's native endianness is used, portable code -should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, -instead. -", -$to_xe_bytes_doc, -" -[`to_be_bytes`]: #method.to_be_bytes -[`to_le_bytes`]: #method.to_le_bytes - -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - ", $be_bytes, " - } else { - ", $le_bytes, " - } -); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute them to arrays of bytes - #[rustc_allow_const_fn_unstable(const_fn_transmute)] - #[inline] - pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { mem::transmute(self) } - } + /// **This method is soft-deprecated.** + /// + /// Although using it won’t cause a compilation warning, new code should use + #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN)")] + /// instead. + /// + /// Returns the smallest value that can be represented by this integer type. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] + #[rustc_promotable] + #[rustc_const_stable(feature = "const_min_value", since = "1.32.0")] + pub const fn min_value() -> Self { + Self::MIN } - doc_comment! { - concat!(" -Return the memory representation of this integer as a byte array in -native byte order. - -[`to_ne_bytes`] should be preferred over this whenever possible. - -[`to_ne_bytes`]: #method.to_ne_bytes -", - -" -# Examples - -``` -#![feature(num_as_ne_bytes)] -let num = ", $swap_op, stringify!($SelfT), "; -let bytes = num.as_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - &", $be_bytes, " - } else { - &", $le_bytes, " - } -); -```"), - #[unstable(feature = "num_as_ne_bytes", issue = "76976")] - #[inline] - pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { &*(self as *const Self as *const _) } - } - } - -doc_comment! { - concat!("Create an integer value from its representation as a byte array in -big endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_be(Self::from_ne_bytes(bytes)) - } - } - -doc_comment! { - concat!(" -Create an integer value from its representation as a byte array in -little endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_le(Self::from_ne_bytes(bytes)) - } - } - - doc_comment! { - concat!("Create an integer value from its memory representation as a byte -array in native endianness. - -As the target platform's native endianness is used, portable code -likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as -appropriate instead. - -[`from_be_bytes`]: #method.from_be_bytes -[`from_le_bytes`]: #method.from_le_bytes -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { - ", $be_bytes, " -} else { - ", $le_bytes, " -}); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute to them - #[rustc_allow_const_fn_unstable(const_fn_transmute)] - #[inline] - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { - // SAFETY: integers are plain old datatypes so we can always transmute to them - unsafe { mem::transmute(bytes) } - } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause a compilation warning, -new code should use [`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN) instead. - -Returns the smallest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] - #[rustc_promotable] - #[rustc_const_stable(feature = "const_min_value", since = "1.32.0")] - pub const fn min_value() -> Self { - Self::MIN - } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause a compilation warning, -new code should use [`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX) instead. - -Returns the largest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] - #[rustc_promotable] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn max_value() -> Self { - Self::MAX - } + /// **This method is soft-deprecated.** + /// + /// Although using it won’t cause a compilation warning, new code should use + #[doc = concat!("[`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX)")] + /// instead. + /// + /// Returns the largest value that can be represented by this integer type. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] + #[rustc_promotable] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + pub const fn max_value() -> Self { + Self::MAX } } } diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 9f5ae57b74ad..6bdfa18fa434 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -23,13 +23,6 @@ macro_rules! unlikely { }; } -macro_rules! doc_comment { - ($x:expr, $($tt:tt)*) => { - #[doc = $x] - $($tt)* - }; -} - // All these modules are technically private and only exposed for coretests: pub mod bignum; pub mod dec2flt; @@ -95,26 +88,26 @@ depending on the target pointer size. #[lang = "i8"] impl i8 { - int_impl! { i8, i8, u8, 8, -128, 127, "", "", 2, "-0x7e", "0xa", "0x12", "0x12", "0x48", + int_impl! { i8, i8, u8, 8, -128, 127, 2, "-0x7e", "0xa", "0x12", "0x12", "0x48", "[0x12]", "[0x12]", "", "" } } #[lang = "i16"] impl i16 { - int_impl! { i16, i16, u16, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", + int_impl! { i16, i16, u16, 16, -32768, 32767, 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" } } #[lang = "i32"] impl i32 { - int_impl! { i32, i32, u32, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", + int_impl! { i32, i32, u32, 32, -2147483648, 2147483647, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } } #[lang = "i64"] impl i64 { - int_impl! { i64, i64, u64, 64, -9223372036854775808, 9223372036854775807, "", "", 12, + int_impl! { i64, i64, u64, 64, -9223372036854775808, 9223372036854775807, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", "", "" } @@ -123,7 +116,7 @@ impl i64 { #[lang = "i128"] impl i128 { int_impl! { i128, i128, u128, 128, -170141183460469231731687303715884105728, - 170141183460469231731687303715884105727, "", "", 16, + 170141183460469231731687303715884105727, 16, "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ @@ -135,7 +128,7 @@ impl i128 { #[cfg(target_pointer_width = "16")] #[lang = "isize"] impl isize { - int_impl! { isize, i16, usize, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", + int_impl! { isize, i16, usize, 16, -32768, 32767, 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } } @@ -143,7 +136,7 @@ impl isize { #[cfg(target_pointer_width = "32")] #[lang = "isize"] impl isize { - int_impl! { isize, i32, usize, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", + int_impl! { isize, i32, usize, 32, -2147483648, 2147483647, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } @@ -152,7 +145,7 @@ impl isize { #[cfg(target_pointer_width = "64")] #[lang = "isize"] impl isize { - int_impl! { isize, i64, usize, 64, -9223372036854775808, 9223372036854775807, "", "", + int_impl! { isize, i64, usize, 64, -9223372036854775808, 9223372036854775807, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", @@ -161,7 +154,7 @@ impl isize { #[lang = "u8"] impl u8 { - uint_impl! { u8, u8, 8, 255, "", "", 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]", + uint_impl! { u8, u8, 8, 255, 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]", "[0x12]", "", "" } /// Checks if the value is within the ASCII range. @@ -660,19 +653,19 @@ impl u8 { #[lang = "u16"] impl u16 { - uint_impl! { u16, u16, 16, 65535, "", "", 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", + uint_impl! { u16, u16, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" } } #[lang = "u32"] impl u32 { - uint_impl! { u32, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", + uint_impl! { u32, u32, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } } #[lang = "u64"] impl u64 { - uint_impl! { u64, u64, 64, 18446744073709551615, "", "", 12, "0xaa00000000006e1", "0x6e10aa", + uint_impl! { u64, u64, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", @@ -681,7 +674,7 @@ impl u64 { #[lang = "u128"] impl u128 { - uint_impl! { u128, u128, 128, 340282366920938463463374607431768211455, "", "", 16, + uint_impl! { u128, u128, 128, 340282366920938463463374607431768211455, 16, "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ @@ -694,14 +687,14 @@ impl u128 { #[cfg(target_pointer_width = "16")] #[lang = "usize"] impl usize { - uint_impl! { usize, u16, 16, 65535, "", "", 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", + uint_impl! { usize, u16, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } } #[cfg(target_pointer_width = "32")] #[lang = "usize"] impl usize { - uint_impl! { usize, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", + uint_impl! { usize, u32, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } } @@ -709,7 +702,7 @@ impl usize { #[cfg(target_pointer_width = "64")] #[lang = "usize"] impl usize { - uint_impl! { usize, u64, 64, 18446744073709551615, "", "", 12, "0xaa00000000006e1", "0x6e10aa", + uint_impl! { usize, u64, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 716b4a90e5ec..ba8918b192fa 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1,20 +1,13 @@ //! Definitions of integer that is known not to equal zero. use crate::fmt; -use crate::ops::{BitOr, BitOrAssign}; +use crate::ops::{BitOr, BitOrAssign, Div, Rem}; use crate::str::FromStr; use super::from_str_radix; use super::{IntErrorKind, ParseIntError}; use crate::intrinsics; -macro_rules! doc_comment { - ($x:expr, $($tt:tt)*) => { - #[doc = $x] - $($tt)* - }; -} - macro_rules! impl_nonzero_fmt { ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { $( @@ -32,24 +25,21 @@ macro_rules! impl_nonzero_fmt { macro_rules! nonzero_integers { ( $( #[$stability: meta] $Ty: ident($Int: ty); )+ ) => { $( - doc_comment! { - concat!("An integer that is known not to equal zero. - -This enables some memory layout optimization. -For example, `Option<", stringify!($Ty), ">` is the same size as `", stringify!($Int), "`: - -```rust -use std::mem::size_of; -assert_eq!(size_of::>(), size_of::<", stringify!($Int), -">()); -```"), - #[$stability] - #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] - #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - pub struct $Ty($Int); - } + /// An integer that is known not to equal zero. + /// + /// This enables some memory layout optimization. + #[doc = concat!("For example, `Option<", stringify!($Ty), ">` is the same size as `", stringify!($Int), "`:")] + /// + /// ```rust + /// use std::mem::size_of; + #[doc = concat!("assert_eq!(size_of::>(), size_of::<", stringify!($Int), ">());")] + /// ``` + #[$stability] + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] + #[repr(transparent)] + #[rustc_layout_scalar_valid_range_start(1)] + #[rustc_nonnull_optimization_guaranteed] + pub struct $Ty($Int); impl $Ty { /// Creates a non-zero without checking the value. @@ -90,13 +80,10 @@ assert_eq!(size_of::>(), size_of::<", s #[stable(feature = "from_nonzero", since = "1.31.0")] impl From<$Ty> for $Int { - doc_comment! { - concat!( -"Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`"), - #[inline] - fn from(nonzero: $Ty) -> Self { - nonzero.0 - } + #[doc = concat!("Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`")] + #[inline] + fn from(nonzero: $Ty) -> Self { + nonzero.0 } } @@ -195,53 +182,49 @@ macro_rules! nonzero_leading_trailing_zeros { ( $( $Ty: ident($Uint: ty) , $LeadingTestExpr:expr ;)+ ) => { $( impl $Ty { - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -On many architectures, this function can perform better than `leading_zeros()` on the underlying integer type, as special handling of zero can be avoided. - -# Examples - -Basic usage: - -``` -#![feature(nonzero_leading_trailing_zeros)] -let n = std::num::", stringify!($Ty), "::new(", stringify!($LeadingTestExpr), ").unwrap(); - -assert_eq!(n.leading_zeros(), 0); -```"), - #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[inline] - pub const fn leading_zeros(self) -> u32 { - // SAFETY: since `self` can not be zero it is safe to call ctlz_nonzero - unsafe { intrinsics::ctlz_nonzero(self.0 as $Uint) as u32 } - } + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// On many architectures, this function can perform better than `leading_zeros()` on the underlying integer type, as special handling of zero can be avoided. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_leading_trailing_zeros)] + #[doc = concat!("let n = std::num::", stringify!($Ty), "::new(", stringify!($LeadingTestExpr), ").unwrap();")] + /// + /// assert_eq!(n.leading_zeros(), 0); + /// ``` + #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[inline] + pub const fn leading_zeros(self) -> u32 { + // SAFETY: since `self` can not be zero it is safe to call ctlz_nonzero + unsafe { intrinsics::ctlz_nonzero(self.0 as $Uint) as u32 } } - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation -of `self`. - -On many architectures, this function can perform better than `trailing_zeros()` on the underlying integer type, as special handling of zero can be avoided. - -# Examples - -Basic usage: - -``` -#![feature(nonzero_leading_trailing_zeros)] -let n = std::num::", stringify!($Ty), "::new(0b0101000).unwrap(); - -assert_eq!(n.trailing_zeros(), 3); -```"), - #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[inline] - pub const fn trailing_zeros(self) -> u32 { - // SAFETY: since `self` can not be zero it is safe to call cttz_nonzero - unsafe { intrinsics::cttz_nonzero(self.0 as $Uint) as u32 } - } + /// Returns the number of trailing zeros in the binary representation + /// of `self`. + /// + /// On many architectures, this function can perform better than `trailing_zeros()` on the underlying integer type, as special handling of zero can be avoided. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_leading_trailing_zeros)] + #[doc = concat!("let n = std::num::", stringify!($Ty), "::new(0b0101000).unwrap();")] + /// + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + // SAFETY: since `self` can not be zero it is safe to call cttz_nonzero + unsafe { intrinsics::cttz_nonzero(self.0 as $Uint) as u32 } } } @@ -263,3 +246,43 @@ nonzero_leading_trailing_zeros! { NonZeroI128(u128), -1i128; NonZeroIsize(usize), -1isize; } + +macro_rules! nonzero_integers_div { + ( $( $Ty: ident($Int: ty); )+ ) => { + $( + #[stable(feature = "nonzero_div", since = "1.51.0")] + impl Div<$Ty> for $Int { + type Output = $Int; + /// This operation rounds towards zero, + /// truncating any fractional part of the exact result, and cannot panic. + #[inline] + fn div(self, other: $Ty) -> $Int { + // SAFETY: div by zero is checked because `other` is a nonzero, + // and MIN/-1 is checked because `self` is an unsigned int. + unsafe { crate::intrinsics::unchecked_div(self, other.get()) } + } + } + + #[stable(feature = "nonzero_div", since = "1.51.0")] + impl Rem<$Ty> for $Int { + type Output = $Int; + /// This operation satisfies `n % d == n - (n / d) * d`, and cannot panic. + #[inline] + fn rem(self, other: $Ty) -> $Int { + // SAFETY: rem by zero is checked because `other` is a nonzero, + // and MIN/-1 is checked because `self` is an unsigned int. + unsafe { crate::intrinsics::unchecked_rem(self, other.get()) } + } + } + )+ + } +} + +nonzero_integers_div! { + NonZeroU8(u8); + NonZeroU16(u16); + NonZeroU32(u32); + NonZeroU64(u64); + NonZeroU128(u128); + NonZeroUsize(usize); +} diff --git a/library/core/src/num/shells/int_macros.rs b/library/core/src/num/shells/int_macros.rs index ffd30b03f210..5f8bb648d04a 100644 --- a/library/core/src/num/shells/int_macros.rs +++ b/library/core/src/num/shells/int_macros.rs @@ -1,49 +1,44 @@ #![doc(hidden)] -macro_rules! doc_comment { - ($x:expr, $($tt:tt)*) => { - #[doc = $x] - $($tt)* - }; -} - macro_rules! int_module { ($T:ident) => (int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]);); ($T:ident, #[$attr:meta]) => ( - doc_comment! { - concat!("The smallest value that can be represented by this integer type. -Use [`", stringify!($T), "::MIN", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MIN) instead. + #[doc = concat!( + "The smallest value that can be represented by this integer type. Use ", + "[`", stringify!($T), "::MIN", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MIN)", + " instead.", + )] + /// + /// # Examples + /// + /// ```rust + /// // deprecated way + #[doc = concat!("let min = std::", stringify!($T), "::MIN;")] + /// + /// // intended way + #[doc = concat!("let min = ", stringify!($T), "::MIN;")] + /// ``` + /// + #[$attr] + pub const MIN: $T = $T::MIN; -# Examples - -```rust -// deprecated way -let min = std::", stringify!($T), "::MIN; - -// intended way -let min = ", stringify!($T), "::MIN; -``` -"), - #[$attr] - pub const MIN: $T = $T::MIN; - } - - doc_comment! { - concat!("The largest value that can be represented by this integer type. -Use [`", stringify!($T), "::MAX", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MAX) instead. - -# Examples - -```rust -// deprecated way -let max = std::", stringify!($T), "::MAX; - -// intended way -let max = ", stringify!($T), "::MAX; -``` -"), - #[$attr] - pub const MAX: $T = $T::MAX; - } + #[doc = concat!( + "The largest value that can be represented by this integer type. Use ", + "[`", stringify!($T), "::MAX", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MAX)", + " instead.", + )] + /// + /// # Examples + /// + /// ```rust + /// // deprecated way + #[doc = concat!("let max = std::", stringify!($T), "::MAX;")] + /// + /// // intended way + #[doc = concat!("let max = ", stringify!($T), "::MAX;")] + /// ``` + /// + #[$attr] + pub const MAX: $T = $T::MAX; ) } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 390c1b7e9e87..8f141a3ff9e9 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1,926 +1,842 @@ macro_rules! uint_impl { - ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, $Feature:expr, $EndFeature:expr, + ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, $reversed:expr, $le_bytes:expr, $be_bytes:expr, $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { - doc_comment! { - concat!("The smallest value that can be represented by this integer type. + /// The smallest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN, 0);")] + /// ``` + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN: Self = 0; -# Examples + /// The largest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($MaxV), ");")] + /// ``` + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX: Self = !0; -Basic usage: + /// The size of this integer type in bits. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_bits_const)] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");")] + /// ``` + #[unstable(feature = "int_bits_const", issue = "76904")] + pub const BITS: u32 = $BITS; -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MIN, 0);", $EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN: Self = 0; + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` sign + /// followed by digits. + /// Leading and trailing whitespace represent an error. + /// Digits are a subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_str_radix(src: &str, radix: u32) -> Result { + from_str_radix(src, radix) } - doc_comment! { - concat!("The largest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($MaxV), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX: Self = !0; + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0b01001100", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.count_ones(), 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[inline] + pub const fn count_ones(self) -> u32 { + intrinsics::ctpop(self as $ActualT) as u32 } - doc_comment! { - concat!("The size of this integer type in bits. - -# Examples - -``` -", $Feature, "#![feature(int_bits_const)] -assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");", -$EndFeature, " -```"), - #[unstable(feature = "int_bits_const", issue = "76904")] - pub const BITS: u32 = $BITS; + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 0);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn count_zeros(self) -> u32 { + (!self).count_ones() } - doc_comment! { - concat!("Converts a string slice in a given base to an integer. + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", stringify!($SelfT), "::MAX >> 2;")] + /// + /// assert_eq!(n.leading_zeros(), 2); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn leading_zeros(self) -> u32 { + intrinsics::ctlz(self as $ActualT) as u32 + } -The string is expected to be an optional `+` sign -followed by digits. -Leading and trailing whitespace represent an error. -Digits are a subset of these characters, depending on `radix`: + /// Returns the number of trailing zeros in the binary representation + /// of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0b0101000", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + intrinsics::cttz(self) as u32 + } -* `0-9` -* `a-z` -* `A-Z` + /// Returns the number of leading ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = !(", stringify!($SelfT), "::MAX >> 2);")] + /// + /// assert_eq!(n.leading_ones(), 2); + /// ``` + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn leading_ones(self) -> u32 { + (!self).leading_zeros() + } -# Panics + /// Returns the number of trailing ones in the binary representation + /// of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0b1010111", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.trailing_ones(), 3); + /// ``` + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn trailing_ones(self) -> u32 { + (!self).trailing_zeros() + } -This function panics if `radix` is not in the range from 2 to 36. + /// Shifts the bits to the left by a specified amount, `n`, + /// wrapping the truncated bits to the end of the resulting integer. + /// + /// Please note this isn't the same operation as the `<<` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $rot_op, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $rot_result, ";")] + /// + #[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_left(self, n: u32) -> Self { + intrinsics::rotate_left(self, n as $SelfT) + } -# Examples + /// Shifts the bits to the right by a specified amount, `n`, + /// wrapping the truncated bits to the beginning of the resulting + /// integer. + /// + /// Please note this isn't the same operation as the `>>` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $rot_result, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $rot_op, ";")] + /// + #[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_right(self, n: u32) -> Self { + intrinsics::rotate_right(self, n as $SelfT) + } -Basic usage: + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")] + /// let m = n.swap_bytes(); + /// + #[doc = concat!("assert_eq!(m, ", $swapped, ");")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn swap_bytes(self) -> Self { + intrinsics::bswap(self as $ActualT) as Self + } -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, + /// second least-significant bit becomes second most-significant bit, etc. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")] + /// let m = n.reverse_bits(); + /// + #[doc = concat!("assert_eq!(m, ", $reversed, ");")] + #[doc = concat!("assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits());")] + /// ``` + #[stable(feature = "reverse_bits", since = "1.37.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + #[must_use] + pub const fn reverse_bits(self) -> Self { + intrinsics::bitreverse(self as $ActualT) as Self + } + + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "big") { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_be(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes())")] + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn from_be(x: Self) -> Self { + #[cfg(target_endian = "big")] + { + x + } + #[cfg(not(target_endian = "big"))] + { + x.swap_bytes() } } - doc_comment! { - concat!("Returns the number of ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b01001100", stringify!($SelfT), "; - -assert_eq!(n.count_ones(), 3);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn count_ones(self) -> u32 { - intrinsics::ctpop(self as $ActualT) as u32 + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "little") { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_le(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes())")] + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn from_le(x: Self) -> Self { + #[cfg(target_endian = "little")] + { + x + } + #[cfg(not(target_endian = "little"))] + { + x.swap_bytes() } } - doc_comment! { - concat!("Returns the number of zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 0);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn count_zeros(self) -> u32 { - (!self).count_ones() + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn to_be(self) -> Self { // or not to be? + #[cfg(target_endian = "big")] + { + self + } + #[cfg(not(target_endian = "big"))] + { + self.swap_bytes() } } - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = ", stringify!($SelfT), "::MAX >> 2; - -assert_eq!(n.leading_zeros(), 2);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn leading_zeros(self) -> u32 { - intrinsics::ctlz(self as $ActualT) as u32 + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn to_le(self) -> Self { + #[cfg(target_endian = "little")] + { + self + } + #[cfg(not(target_endian = "little"))] + { + self.swap_bytes() } } - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation -of `self`. + /// Checked integer addition. Computes `self + rhs`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!( + "assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), ", + "Some(", stringify!($SelfT), "::MAX - 1));" + )] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); + if unlikely!(b) {None} else {Some(a)} + } -# Examples + /// Unchecked integer addition. Computes `self + rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_add`. + unsafe { intrinsics::unchecked_add(self, rhs) } + } -Basic usage: + /// Checked integer subtraction. Computes `self - rhs`, returning + /// `None` if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_sub(1), Some(0));")] + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); + if unlikely!(b) {None} else {Some(a)} + } -``` -", $Feature, "let n = 0b0101000", stringify!($SelfT), "; + /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_sub`. + unsafe { intrinsics::unchecked_sub(self, rhs) } + } -assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn trailing_zeros(self) -> u32 { - intrinsics::cttz(self) as u32 + /// Checked integer multiplication. Computes `self * rhs`, returning + /// `None` if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_mul(1), Some(5));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_mul`. + unsafe { intrinsics::unchecked_mul(self, rhs) } + } + + /// Checked integer division. Computes `self / rhs`, returning `None` + /// if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".checked_div(2), Some(64));")] + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_div(0), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + // SAFETY: div by zero has been checked above and unsigned types have no other + // failure modes for division + Some(unsafe { intrinsics::unchecked_div(self, rhs) }) } } - doc_comment! { - concat!("Returns the number of leading ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = !(", stringify!($SelfT), "::MAX >> 2); - -assert_eq!(n.leading_ones(), 2);", $EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn leading_ones(self) -> u32 { - (!self).leading_zeros() + /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` + /// if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".checked_div_euclid(2), Some(64));")] + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_div_euclid(0), None);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + Some(self.div_euclid(rhs)) } } - doc_comment! { - concat!("Returns the number of trailing ones in the binary representation -of `self`. -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b1010111", stringify!($SelfT), "; - -assert_eq!(n.trailing_ones(), 3);", $EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn trailing_ones(self) -> u32 { - (!self).trailing_zeros() + /// Checked integer remainder. Computes `self % rhs`, returning `None` + /// if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1));")] + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + // SAFETY: div by zero has been checked above and unsigned types have no other + // failure modes for division + Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) } } - doc_comment! { - concat!("Shifts the bits to the left by a specified amount, `n`, -wrapping the truncated bits to the end of the resulting integer. - -Please note this isn't the same operation as the `<<` shifting operator! - -# Examples - -Basic usage: - -``` -let n = ", $rot_op, stringify!($SelfT), "; -let m = ", $rot_result, "; - -assert_eq!(n.rotate_left(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_left(self, n: u32) -> Self { - intrinsics::rotate_left(self, n as $SelfT) + /// Checked Euclidean modulo. Computes `self.rem_euclid(rhs)`, returning `None` + /// if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1));")] + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + Some(self.rem_euclid(rhs)) } } - doc_comment! { - concat!("Shifts the bits to the right by a specified amount, `n`, -wrapping the truncated bits to the beginning of the resulting -integer. - -Please note this isn't the same operation as the `>>` shifting operator! - -# Examples - -Basic usage: - -``` -let n = ", $rot_result, stringify!($SelfT), "; -let m = ", $rot_op, "; - -assert_eq!(n.rotate_right(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_right(self, n: u32) -> Self { - intrinsics::rotate_right(self, n as $SelfT) - } + /// Checked negation. Computes `-self`, returning `None` unless `self == + /// 0`. + /// + /// Note that negating any positive integer will overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".checked_neg(), Some(0));")] + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_neg(), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_neg(self) -> Option { + let (a, b) = self.overflowing_neg(); + if unlikely!(b) {None} else {Some(a)} } - doc_comment! { - concat!(" -Reverses the byte order of the integer. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.swap_bytes(); - -assert_eq!(m, ", $swapped, "); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn swap_bytes(self) -> Self { - intrinsics::bswap(self as $ActualT) as Self - } + /// Checked shift left. Computes `self << rhs`, returning `None` + /// if `rhs` is larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shl(129), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shl(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shl(rhs); + if unlikely!(b) {None} else {Some(a)} } - doc_comment! { - concat!("Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, - second least-significant bit becomes second most-significant bit, etc. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.reverse_bits(); - -assert_eq!(m, ", $reversed, "); -assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits()); -```"), - #[stable(feature = "reverse_bits", since = "1.37.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - #[must_use] - pub const fn reverse_bits(self) -> Self { - intrinsics::bitreverse(self as $ActualT) as Self - } + /// Checked shift right. Computes `self >> rhs`, returning `None` + /// if `rhs` is larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(129), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shr(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shr(rhs); + if unlikely!(b) {None} else {Some(a)} } - doc_comment! { - concat!("Converts an integer from big endian to the target's endianness. + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")] + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_pow(self, mut exp: u32) -> Option { + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; -On big endian this is a no-op. On little endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(", stringify!($SelfT), "::from_be(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn from_be(x: Self) -> Self { - #[cfg(target_endian = "big")] - { - x - } - #[cfg(not(target_endian = "big"))] - { - x.swap_bytes() + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); } + exp /= 2; + base = try_opt!(base.checked_mul(base)); + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + + Some(try_opt!(acc.checked_mul(base))) + } + + /// Saturating integer addition. Computes `self + rhs`, saturating at + /// the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_add(self, rhs: Self) -> Self { + intrinsics::saturating_add(self, rhs) + } + + /// Saturating integer subtraction. Computes `self - rhs`, saturating + /// at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73);")] + #[doc = concat!("assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_sub(self, rhs: Self) -> Self { + intrinsics::saturating_sub(self, rhs) + } + + /// Saturating integer multiplication. Computes `self * rhs`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".saturating_mul(10), 20);")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($SelfT),"::MAX);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_mul(self, rhs: Self) -> Self { + match self.checked_mul(rhs) { + Some(x) => x, + None => Self::MAX, } } - doc_comment! { - concat!("Converts an integer from little endian to the target's endianness. - -On little endian this is a no-op. On big endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(", stringify!($SelfT), "::from_le(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn from_le(x: Self) -> Self { - #[cfg(target_endian = "little")] - { - x - } - #[cfg(not(target_endian = "little"))] - { - x.swap_bytes() - } + /// Saturating integer exponentiation. Computes `self.pow(exp)`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);")] + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Some(x) => x, + None => Self::MAX, } } - doc_comment! { - concat!("Converts `self` to big endian from the target's endianness. - -On big endian this is a no-op. On little endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(n.to_be(), n) -} else { - assert_eq!(n.to_be(), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn to_be(self) -> Self { // or not to be? - #[cfg(target_endian = "big")] - { - self - } - #[cfg(not(target_endian = "big"))] - { - self.swap_bytes() - } - } + /// Wrapping (modular) addition. Computes `self + rhs`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(200", stringify!($SelfT), ".wrapping_add(55), 255);")] + #[doc = concat!("assert_eq!(200", stringify!($SelfT), ".wrapping_add(", stringify!($SelfT), "::MAX), 199);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_add(self, rhs: Self) -> Self { + intrinsics::wrapping_add(self, rhs) } - doc_comment! { - concat!("Converts `self` to little endian from the target's endianness. - -On little endian this is a no-op. On big endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(n.to_le(), n) -} else { - assert_eq!(n.to_le(), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn to_le(self) -> Self { - #[cfg(target_endian = "little")] - { - self - } - #[cfg(not(target_endian = "little"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("Checked integer addition. Computes `self + rhs`, returning `None` -if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), ", -"Some(", stringify!($SelfT), "::MAX - 1)); -assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_add(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), -"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer subtraction. Computes `self - rhs`, returning -`None` if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(1", stringify!($SelfT), ".checked_sub(1), Some(0)); -assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_sub(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), -"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer multiplication. Computes `self * rhs`, returning -`None` if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".checked_mul(1), Some(5)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_mul(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_mul(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), -"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer division. Computes `self / rhs`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(128", stringify!($SelfT), ".checked_div(2), Some(64)); -assert_eq!(1", stringify!($SelfT), ".checked_div(0), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - // SAFETY: div by zero has been checked above and unsigned types have no other - // failure modes for division - Some(unsafe { intrinsics::unchecked_div(self, rhs) }) - } - } - } - - doc_comment! { - concat!("Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -assert_eq!(128", stringify!($SelfT), ".checked_div_euclid(2), Some(64)); -assert_eq!(1", stringify!($SelfT), ".checked_div_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - Some(self.div_euclid(rhs)) - } - } - } - - - doc_comment! { - concat!("Checked integer remainder. Computes `self % rhs`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - // SAFETY: div by zero has been checked above and unsigned types have no other - // failure modes for division - Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) - } - } - } - - doc_comment! { - concat!("Checked Euclidean modulo. Computes `self.rem_euclid(rhs)`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - Some(self.rem_euclid(rhs)) - } - } - } - - doc_comment! { - concat!("Checked negation. Computes `-self`, returning `None` unless `self == -0`. - -Note that negating any positive integer will overflow. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".checked_neg(), Some(0)); -assert_eq!(1", stringify!($SelfT), ".checked_neg(), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[inline] - pub const fn checked_neg(self) -> Option { - let (a, b) = self.overflowing_neg(); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift left. Computes `self << rhs`, returning `None` -if `rhs` is larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shl(129), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift right. Computes `self >> rhs`, returning `None` -if `rhs` is larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shr(129), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked exponentiation. Computes `self.pow(exp)`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_pow(self, mut exp: u32) -> Option { - if exp == 0 { - return Some(1); - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - exp /= 2; - base = try_opt!(base.checked_mul(base)); - } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - - Some(try_opt!(acc.checked_mul(base))) - } - } - - doc_comment! { - concat!("Saturating integer addition. Computes `self + rhs`, saturating at -the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), - - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] - pub const fn saturating_add(self, rhs: Self) -> Self { - intrinsics::saturating_add(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer subtraction. Computes `self - rhs`, saturating -at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73); -assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] - pub const fn saturating_sub(self, rhs: Self) -> Self { - intrinsics::saturating_sub(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer multiplication. Computes `self * rhs`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(2", stringify!($SelfT), ".saturating_mul(10), 20); -assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($SelfT), -"::MAX);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_mul(self, rhs: Self) -> Self { - match self.checked_mul(rhs) { - Some(x) => x, - None => Self::MAX, - } - } - } - - doc_comment! { - concat!("Saturating integer exponentiation. Computes `self.pow(exp)`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Some(x) => x, - None => Self::MAX, - } - } - } - - doc_comment! { - concat!("Wrapping (modular) addition. Computes `self + rhs`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(200", stringify!($SelfT), ".wrapping_add(55), 255); -assert_eq!(200", stringify!($SelfT), ".wrapping_add(", stringify!($SelfT), "::MAX), 199);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_add(self, rhs: Self) -> Self { - intrinsics::wrapping_add(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) subtraction. Computes `self - rhs`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_sub(100), 0); -assert_eq!(100", stringify!($SelfT), ".wrapping_sub(", stringify!($SelfT), "::MAX), 101);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_sub(self, rhs: Self) -> Self { - intrinsics::wrapping_sub(self, rhs) - } + /// Wrapping (modular) subtraction. Computes `self - rhs`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_sub(100), 0);")] + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_sub(", stringify!($SelfT), "::MAX), 101);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_sub(self, rhs: Self) -> Self { + intrinsics::wrapping_sub(self, rhs) } /// Wrapping (modular) multiplication. Computes `self * @@ -946,108 +862,100 @@ $EndFeature, " intrinsics::wrapping_mul(self, rhs) } - doc_comment! { - concat!("Wrapping (modular) division. Computes `self / rhs`. -Wrapped division on unsigned types is just normal division. -There's no way wrapping could ever happen. -This function exists, so that all operations -are accounted for in the wrapping operations. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div(self, rhs: Self) -> Self { - self / rhs - } + /// Wrapping (modular) division. Computes `self / rhs`. + /// Wrapped division on unsigned types is just normal division. + /// There's no way wrapping could ever happen. + /// This function exists, so that all operations + /// are accounted for in the wrapping operations. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div(self, rhs: Self) -> Self { + self / rhs } - doc_comment! { - concat!("Wrapping Euclidean division. Computes `self.div_euclid(rhs)`. -Wrapped division on unsigned types is just normal division. -There's no way wrapping could ever happen. -This function exists, so that all operations -are accounted for in the wrapping operations. -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self.wrapping_div(rhs)`. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { - self / rhs - } + /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`. + /// Wrapped division on unsigned types is just normal division. + /// There's no way wrapping could ever happen. + /// This function exists, so that all operations + /// are accounted for in the wrapping operations. + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self.wrapping_div(rhs)`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { + self / rhs } - doc_comment! { - concat!("Wrapping (modular) remainder. Computes `self % rhs`. -Wrapped remainder calculation on unsigned types is -just the regular remainder calculation. -There's no way wrapping could ever happen. -This function exists, so that all operations -are accounted for in the wrapping operations. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem(self, rhs: Self) -> Self { - self % rhs - } + /// Wrapping (modular) remainder. Computes `self % rhs`. + /// Wrapped remainder calculation on unsigned types is + /// just the regular remainder calculation. + /// There's no way wrapping could ever happen. + /// This function exists, so that all operations + /// are accounted for in the wrapping operations. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem(self, rhs: Self) -> Self { + self % rhs } - doc_comment! { - concat!("Wrapping Euclidean modulo. Computes `self.rem_euclid(rhs)`. -Wrapped modulo calculation on unsigned types is -just the regular remainder calculation. -There's no way wrapping could ever happen. -This function exists, so that all operations -are accounted for in the wrapping operations. -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self.wrapping_rem(rhs)`. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { - self % rhs - } + /// Wrapping Euclidean modulo. Computes `self.rem_euclid(rhs)`. + /// Wrapped modulo calculation on unsigned types is + /// just the regular remainder calculation. + /// There's no way wrapping could ever happen. + /// This function exists, so that all operations + /// are accounted for in the wrapping operations. + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self.wrapping_rem(rhs)`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { + self % rhs } /// Wrapping (modular) negation. Computes `-self`, @@ -1078,167 +986,156 @@ assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); self.overflowing_neg().0 } - doc_comment! { - concat!("Panic-free bitwise shift-left; yields `self << mask(rhs)`, -where `mask` removes any high-order bits of `rhs` that -would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-left; the -RHS of a wrapping shift-left is restricted to the range -of the type, rather than the bits shifted out of the LHS -being returned to the other end. The primitive integer -types all implement a [`rotate_left`](#method.rotate_left) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128); -assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shl(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) - } + /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, + /// where `mask` removes any high-order bits of `rhs` that + /// would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-left; the + /// RHS of a wrapping shift-left is restricted to the range + /// of the type, rather than the bits shifted out of the LHS + /// being returned to the other end. The primitive integer + /// types all implement a [`rotate_left`](#method.rotate_left) function, + /// which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128);")] + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shl(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) } } - doc_comment! { - concat!("Panic-free bitwise shift-right; yields `self >> mask(rhs)`, -where `mask` removes any high-order bits of `rhs` that -would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-right; the -RHS of a wrapping shift-right is restricted to the range -of the type, rather than the bits shifted out of the LHS -being returned to the other end. The primitive integer -types all implement a [`rotate_right`](#method.rotate_right) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1); -assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shr(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) - } + /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, + /// where `mask` removes any high-order bits of `rhs` that + /// would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-right; the + /// RHS of a wrapping shift-right is restricted to the range + /// of the type, rather than the bits shifted out of the LHS + /// being returned to the other end. The primitive integer + /// types all implement a [`rotate_right`](#method.rotate_right) function, + /// which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1);")] + #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shr(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) } } - doc_comment! { - concat!("Wrapping (modular) exponentiation. Computes `self.pow(exp)`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243); -assert_eq!(3u8.wrapping_pow(6), 217);", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - } - exp /= 2; - base = base.wrapping_mul(base); - } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) + /// Wrapping (modular) exponentiation. Computes `self.pow(exp)`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243);")] + /// assert_eq!(3u8.wrapping_pow(6), 217); + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) } - doc_comment! { - concat!("Calculates `self` + `rhs` - -Returns a tuple of the addition along with a boolean indicating -whether an arithmetic overflow would occur. If an overflow would -have occurred then the wrapped value is returned. - -# Examples - -Basic usage - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false)); -assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (0, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } + /// Calculates `self` + `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would + /// have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (0, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) } - doc_comment! { - concat!("Calculates `self` - `rhs` - -Returns a tuple of the subtraction along with a boolean indicating -whether an arithmetic overflow would occur. If an overflow would -have occurred then the wrapped value is returned. - -# Examples - -Basic usage - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false)); -assert_eq!(0", stringify!($SelfT), ".overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } + /// Calculates `self` - `rhs` + /// + /// Returns a tuple of the subtraction along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would + /// have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false));")] + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) } /// Calculates the multiplication of `self` and `rhs`. @@ -1268,269 +1165,251 @@ $EndFeature, " (a as Self, b) } - doc_comment! { - concat!("Calculates the divisor when `self` is divided by `rhs`. - -Returns a tuple of the divisor along with a boolean indicating -whether an arithmetic overflow would occur. Note that for unsigned -integers overflow never occurs, so the second value is always -`false`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - (self / rhs, false) - } + /// Calculates the divisor when `self` is divided by `rhs`. + /// + /// Returns a tuple of the divisor along with a boolean indicating + /// whether an arithmetic overflow would occur. Note that for unsigned + /// integers overflow never occurs, so the second value is always + /// `false`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { + (self / rhs, false) } - doc_comment! { - concat!("Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. - -Returns a tuple of the divisor along with a boolean indicating -whether an arithmetic overflow would occur. Note that for unsigned -integers overflow never occurs, so the second value is always -`false`. -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self.overflowing_div(rhs)`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - (self / rhs, false) - } + /// Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. + /// + /// Returns a tuple of the divisor along with a boolean indicating + /// whether an arithmetic overflow would occur. Note that for unsigned + /// integers overflow never occurs, so the second value is always + /// `false`. + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self.overflowing_div(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")] + /// ``` + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { + (self / rhs, false) } - doc_comment! { - concat!("Calculates the remainder when `self` is divided by `rhs`. - -Returns a tuple of the remainder after dividing along with a boolean -indicating whether an arithmetic overflow would occur. Note that for -unsigned integers overflow never occurs, so the second value is -always `false`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - (self % rhs, false) - } + /// Calculates the remainder when `self` is divided by `rhs`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean + /// indicating whether an arithmetic overflow would occur. Note that for + /// unsigned integers overflow never occurs, so the second value is + /// always `false`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { + (self % rhs, false) } - doc_comment! { - concat!("Calculates the remainder `self.rem_euclid(rhs)` as if by Euclidean division. - -Returns a tuple of the modulo after dividing along with a boolean -indicating whether an arithmetic overflow would occur. Note that for -unsigned integers overflow never occurs, so the second value is -always `false`. -Since, for the positive integers, all common -definitions of division are equal, this operation -is exactly equal to `self.overflowing_rem(rhs)`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - (self % rhs, false) - } + /// Calculates the remainder `self.rem_euclid(rhs)` as if by Euclidean division. + /// + /// Returns a tuple of the modulo after dividing along with a boolean + /// indicating whether an arithmetic overflow would occur. Note that for + /// unsigned integers overflow never occurs, so the second value is + /// always `false`. + /// Since, for the positive integers, all common + /// definitions of division are equal, this operation + /// is exactly equal to `self.overflowing_rem(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")] + /// ``` + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { + (self % rhs, false) } - doc_comment! { - concat!("Negates self in an overflowing fashion. - -Returns `!self + 1` using wrapping operations to return the value -that represents the negation of this unsigned value. Note that for -positive unsigned values overflow always occurs, but negating 0 does -not overflow. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false)); -assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), -", true));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - pub const fn overflowing_neg(self) -> (Self, bool) { - ((!self).wrapping_add(1), self != 0) - } + /// Negates self in an overflowing fashion. + /// + /// Returns `!self + 1` using wrapping operations to return the value + /// that represents the negation of this unsigned value. Note that for + /// positive unsigned values overflow always occurs, but negating 0 does + /// not overflow. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false));")] + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), ", true));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + pub const fn overflowing_neg(self) -> (Self, bool) { + ((!self).wrapping_add(1), self != 0) } - doc_comment! { - concat!("Shifts self left by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean -indicating whether the shift value was larger than or equal to the -number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then -used to perform the shift. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(4), (0x10, false)); -assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(132), (0x10, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) - } + /// Shifts self left by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean + /// indicating whether the shift value was larger than or equal to the + /// number of bits. If the shift value is too large, then value is + /// masked (N-1) where N is the number of bits, and this value is then + /// used to perform the shift. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(4), (0x10, false));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(132), (0x10, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) } - doc_comment! { - concat!("Shifts self right by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean -indicating whether the shift value was larger than or equal to the -number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then -used to perform the shift. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false)); -assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(132), (0x1, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) - } + /// Shifts self right by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean + /// indicating whether the shift value was larger than or equal to the + /// number of bits. If the shift value is too large, then value is + /// masked (N-1) where N is the number of bits, and this value is then + /// used to perform the shift. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(132), (0x1, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) } - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// Returns a tuple of the exponentiation along with a bool indicating + /// whether an overflow happened. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false));")] + /// assert_eq!(3u8.overflowing_pow(6), (217, true)); + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { + if exp == 0{ + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; -Returns a tuple of the exponentiation along with a bool indicating -whether an overflow happened. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false)); -assert_eq!(3u8.overflowing_pow(6), (217, true));", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - if exp == 0{ - return (1,false); - } - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; overflown |= r.1; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - - r + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; + overflown |= r.1; } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; + + r } - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".pow(5), 32);", $EndFeature, " -```"), + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".pow(5), 32);")] + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ @@ -1558,84 +1437,77 @@ Basic usage: // needless overflow. acc * base } - } - doc_comment! { - concat!("Performs Euclidean division. - -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self / rhs`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(7", stringify!($SelfT), ".div_euclid(4), 1); // or any other integer type -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn div_euclid(self, rhs: Self) -> Self { - self / rhs - } + /// Performs Euclidean division. + /// + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(7", stringify!($SelfT), ".div_euclid(4), 1); // or any other integer type")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn div_euclid(self, rhs: Self) -> Self { + self / rhs } - doc_comment! { - concat!("Calculates the least remainder of `self (mod rhs)`. - -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self % rhs`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer type -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn rem_euclid(self, rhs: Self) -> Self { - self % rhs - } + /// Calculates the least remainder of `self (mod rhs)`. + /// + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self % rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer type")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn rem_euclid(self, rhs: Self) -> Self { + self % rhs } - doc_comment! { - concat!("Returns `true` if and only if `self == 2^k` for some `k`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!(16", stringify!($SelfT), ".is_power_of_two()); -assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")] - #[inline] - pub const fn is_power_of_two(self) -> bool { - self.count_ones() == 1 - } + /// Returns `true` if and only if `self == 2^k` for some `k`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert!(16", stringify!($SelfT), ".is_power_of_two());")] + #[doc = concat!("assert!(!10", stringify!($SelfT), ".is_power_of_two());")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")] + #[inline] + pub const fn is_power_of_two(self) -> bool { + self.count_ones() == 1 } // Returns one less than next power of two. @@ -1661,332 +1533,302 @@ assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " <$SelfT>::MAX >> z } - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `self`. - -When return value overflows (i.e., `self > (1 << (N-1))` for type -`uN`), it panics in debug mode and return value is wrapped to 0 in -release mode (the only situation in which method can return 0). - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".next_power_of_two(), 2); -assert_eq!(3", stringify!($SelfT), ".next_power_of_two(), 4);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn next_power_of_two(self) -> Self { - self.one_less_than_next_power_of_two() + 1 - } + /// Returns the smallest power of two greater than or equal to `self`. + /// + /// When return value overflows (i.e., `self > (1 << (N-1))` for type + /// `uN`), it panics in debug mode and return value is wrapped to 0 in + /// release mode (the only situation in which method can return 0). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".next_power_of_two(), 2);")] + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".next_power_of_two(), 4);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn next_power_of_two(self) -> Self { + self.one_less_than_next_power_of_two() + 1 } - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `n`. If -the next power of two is greater than the type's maximum value, -`None` is returned, otherwise the power of two is wrapped in `Some`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), -".checked_next_power_of_two(), Some(2)); -assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_next_power_of_two(), None);", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - pub const fn checked_next_power_of_two(self) -> Option { - self.one_less_than_next_power_of_two().checked_add(1) - } + /// Returns the smallest power of two greater than or equal to `n`. If + /// the next power of two is greater than the type's maximum value, + /// `None` is returned, otherwise the power of two is wrapped in `Some`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_next_power_of_two(), Some(2));")] + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_next_power_of_two(), None);")] + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + pub const fn checked_next_power_of_two(self) -> Option { + self.one_less_than_next_power_of_two().checked_add(1) } - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `n`. If -the next power of two is greater than the type's maximum value, -the return value is wrapped to `0`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_next_power_of_two)] -", $Feature, " -assert_eq!(2", stringify!($SelfT), ".wrapping_next_power_of_two(), 2); -assert_eq!(3", stringify!($SelfT), ".wrapping_next_power_of_two(), 4); -assert_eq!(", stringify!($SelfT), "::MAX.wrapping_next_power_of_two(), 0);", -$EndFeature, " -```"), - #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", - reason = "needs decision on wrapping behaviour")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - pub const fn wrapping_next_power_of_two(self) -> Self { - self.one_less_than_next_power_of_two().wrapping_add(1) - } + /// Returns the smallest power of two greater than or equal to `n`. If + /// the next power of two is greater than the type's maximum value, + /// the return value is wrapped to `0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_next_power_of_two)] + /// + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".wrapping_next_power_of_two(), 2);")] + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_next_power_of_two(), 4);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_next_power_of_two(), 0);")] + /// ``` + #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", + reason = "needs decision on wrapping behaviour")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + pub const fn wrapping_next_power_of_two(self) -> Self { + self.one_less_than_next_power_of_two().wrapping_add(1) } - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -big-endian (network) byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); -assert_eq!(bytes, ", $be_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { - self.to_be().to_ne_bytes() - } + /// Return the memory representation of this integer as a byte array in + /// big-endian (network) byte order. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", $be_bytes, ");")] + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_be().to_ne_bytes() } - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -little-endian byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); -assert_eq!(bytes, ", $le_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { - self.to_le().to_ne_bytes() - } + /// Return the memory representation of this integer as a byte array in + /// little-endian byte order. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", $le_bytes, ");")] + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_le().to_ne_bytes() } - doc_comment! { - concat!(" -Return the memory representation of this integer as a byte array in -native byte order. - -As the target platform's native endianness is used, portable code -should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, -instead. -", -$to_xe_bytes_doc, -" -[`to_be_bytes`]: #method.to_be_bytes -[`to_le_bytes`]: #method.to_le_bytes - -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - ", $be_bytes, " - } else { - ", $le_bytes, " - } -); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute them to arrays of bytes - #[rustc_allow_const_fn_unstable(const_fn_transmute)] - #[inline] - pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { mem::transmute(self) } - } + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, + /// instead. + /// + #[doc = $to_xe_bytes_doc] + /// + /// [`to_be_bytes`]: #method.to_be_bytes + /// [`to_le_bytes`]: #method.to_le_bytes + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes();")] + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" ", $be_bytes)] + /// } else { + #[doc = concat!(" ", $le_bytes)] + /// } + /// ); + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute them to arrays of bytes + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { mem::transmute(self) } } - doc_comment! { - concat!(" -Return the memory representation of this integer as a byte array in -native byte order. - -[`to_ne_bytes`] should be preferred over this whenever possible. - -[`to_ne_bytes`]: #method.to_ne_bytes -", - -" -# Examples - -``` -#![feature(num_as_ne_bytes)] -let num = ", $swap_op, stringify!($SelfT), "; -let bytes = num.as_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - &", $be_bytes, " - } else { - &", $le_bytes, " - } -); -```"), - #[unstable(feature = "num_as_ne_bytes", issue = "76976")] - #[inline] - pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { &*(self as *const Self as *const _) } - } + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// [`to_ne_bytes`] should be preferred over this whenever possible. + /// + /// [`to_ne_bytes`]: #method.to_ne_bytes + /// + /// # Examples + /// + /// ``` + /// #![feature(num_as_ne_bytes)] + #[doc = concat!("let num = ", $swap_op, stringify!($SelfT), ";")] + /// let bytes = num.as_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" &", $be_bytes)] + /// } else { + #[doc = concat!(" &", $le_bytes)] + /// } + /// ); + /// ``` + #[unstable(feature = "num_as_ne_bytes", issue = "76976")] + #[inline] + pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { &*(self as *const Self as *const _) } } - doc_comment! { - concat!("Create a native endian integer value from its representation -as a byte array in big endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_be(Self::from_ne_bytes(bytes)) - } + /// Create a native endian integer value from its representation + /// as a byte array in big endian. + /// + #[doc = $from_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, ");")] + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_be(Self::from_ne_bytes(bytes)) } - doc_comment! { - concat!(" -Create a native endian integer value from its representation -as a byte array in little endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_le(Self::from_ne_bytes(bytes)) - } + /// Create a native endian integer value from its representation + /// as a byte array in little endian. + /// + #[doc = $from_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, ");")] + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) } - doc_comment! { - concat!("Create a native endian integer value from its memory representation -as a byte array in native endianness. - -As the target platform's native endianness is used, portable code -likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as -appropriate instead. - -[`from_be_bytes`]: #method.from_be_bytes -[`from_le_bytes`]: #method.from_le_bytes -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { - ", $be_bytes, " -} else { - ", $le_bytes, " -}); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute to them - #[rustc_allow_const_fn_unstable(const_fn_transmute)] - #[inline] - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { - // SAFETY: integers are plain old datatypes so we can always transmute to them - unsafe { mem::transmute(bytes) } - } + /// Create a native endian integer value from its memory representation + /// as a byte array in native endianness. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: #method.from_be_bytes + /// [`from_le_bytes`]: #method.from_le_bytes + /// + #[doc = $from_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") {")] + #[doc = concat!(" ", $be_bytes, "")] + /// } else { + #[doc = concat!(" ", $le_bytes, "")] + /// }); + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute to them + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute to them + unsafe { mem::transmute(bytes) } } - doc_comment! { - concat!("**This method is soft-deprecated.** + /// **This method is soft-deprecated.** + /// + /// Although using it won’t cause compilation warning, new code should use + #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN)")] + /// instead. + /// + /// Returns the smallest value that can be represented by this integer type. + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_promotable] + #[inline(always)] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + pub const fn min_value() -> Self { Self::MIN } -Although using it won’t cause compilation warning, -new code should use [`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN) instead. - -Returns the smallest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_promotable] - #[inline(always)] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn min_value() -> Self { Self::MIN } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause compilation warning, -new code should use [`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX) instead. - -Returns the largest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_promotable] - #[inline(always)] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn max_value() -> Self { Self::MAX } - } + /// **This method is soft-deprecated.** + /// + /// Although using it won’t cause compilation warning, new code should use + #[doc = concat!("[`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX)")] + /// instead. + /// + /// Returns the largest value that can be represented by this integer type. + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_promotable] + #[inline(always)] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + pub const fn max_value() -> Self { Self::MAX } } } diff --git a/library/core/src/num/wrapping.rs b/library/core/src/num/wrapping.rs index 5324dfdeddde..f1b9dabe7d6b 100644 --- a/library/core/src/num/wrapping.rs +++ b/library/core/src/num/wrapping.rs @@ -403,103 +403,94 @@ wrapping_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } macro_rules! wrapping_int_impl { ($($t:ty)*) => ($( impl Wrapping<$t> { - doc_comment! { - concat!("Returns the smallest value that can be represented by this integer type. + /// Returns the smallest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(>::MIN, Wrapping(", stringify!($t), "::MIN));")] + /// ``` + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const MIN: Self = Self(<$t>::MIN); -# Examples + /// Returns the largest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(>::MAX, Wrapping(", stringify!($t), "::MAX));")] + /// ``` + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const MAX: Self = Self(<$t>::MAX); -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(>::MIN, Wrapping(", stringify!($t), "::MIN)); -```"), - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const MIN: Self = Self(<$t>::MIN); + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0b01001100", stringify!($t), ");")] + /// + /// assert_eq!(n.count_ones(), 3); + /// ``` + #[inline] + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn count_ones(self) -> u32 { + self.0.count_ones() } - doc_comment! { - concat!("Returns the largest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(>::MAX, Wrapping(", stringify!($t), "::MAX)); -```"), - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const MAX: Self = Self(<$t>::MAX); + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(!0", stringify!($t), ").count_zeros(), 0);")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn count_zeros(self) -> u32 { + self.0.count_zeros() } - doc_comment! { - concat!("Returns the number of ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0b01001100", stringify!($t), "); - -assert_eq!(n.count_ones(), 3); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn count_ones(self) -> u32 { - self.0.count_ones() - } - } - - doc_comment! { - concat!("Returns the number of zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(!0", stringify!($t), ").count_zeros(), 0); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn count_zeros(self) -> u32 { - self.0.count_zeros() - } - } - - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation -of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0b0101000", stringify!($t), "); - -assert_eq!(n.trailing_zeros(), 3); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn trailing_zeros(self) -> u32 { - self.0.trailing_zeros() - } + /// Returns the number of trailing zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0b0101000", stringify!($t), ");")] + /// + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn trailing_zeros(self) -> u32 { + self.0.trailing_zeros() } /// Shifts the bits to the left by a specified amount, `n`, @@ -606,150 +597,140 @@ assert_eq!(n.trailing_zeros(), 3); Wrapping(self.0.reverse_bits()) } - doc_comment! { - concat!("Converts an integer from big endian to the target's endianness. - -On big endian this is a no-op. On little endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0x1A", stringify!($t), "); - -if cfg!(target_endian = \"big\") { - assert_eq!(>::from_be(n), n) -} else { - assert_eq!(>::from_be(n), n.swap_bytes()) -} -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn from_be(x: Self) -> Self { - Wrapping(<$t>::from_be(x.0)) - } + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0x1A", stringify!($t), ");")] + /// + /// if cfg!(target_endian = "big") { + #[doc = concat!(" assert_eq!(>::from_be(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(>::from_be(n), n.swap_bytes())")] + /// } + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn from_be(x: Self) -> Self { + Wrapping(<$t>::from_be(x.0)) } - doc_comment! { - concat!("Converts an integer from little endian to the target's endianness. - -On little endian this is a no-op. On big endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0x1A", stringify!($t), "); - -if cfg!(target_endian = \"little\") { - assert_eq!(>::from_le(n), n) -} else { - assert_eq!(>::from_le(n), n.swap_bytes()) -} -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn from_le(x: Self) -> Self { - Wrapping(<$t>::from_le(x.0)) - } + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0x1A", stringify!($t), ");")] + /// + /// if cfg!(target_endian = "little") { + #[doc = concat!(" assert_eq!(>::from_le(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(>::from_le(n), n.swap_bytes())")] + /// } + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn from_le(x: Self) -> Self { + Wrapping(<$t>::from_le(x.0)) } - doc_comment! { - concat!("Converts `self` to big endian from the target's endianness. - -On big endian this is a no-op. On little endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0x1A", stringify!($t), "); - -if cfg!(target_endian = \"big\") { - assert_eq!(n.to_be(), n) -} else { - assert_eq!(n.to_be(), n.swap_bytes()) -} -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn to_be(self) -> Self { - Wrapping(self.0.to_be()) - } + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0x1A", stringify!($t), ");")] + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn to_be(self) -> Self { + Wrapping(self.0.to_be()) } - doc_comment! { - concat!("Converts `self` to little endian from the target's endianness. - -On little endian this is a no-op. On big endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0x1A", stringify!($t), "); - -if cfg!(target_endian = \"little\") { - assert_eq!(n.to_le(), n) -} else { - assert_eq!(n.to_le(), n.swap_bytes()) -} -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn to_le(self) -> Self { - Wrapping(self.0.to_le()) - } + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0x1A", stringify!($t), ");")] + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn to_le(self) -> Self { + Wrapping(self.0.to_le()) } - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(3", stringify!($t), ").pow(4), Wrapping(81)); -``` - -Results that are too large are wrapped: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(3i8).pow(5), Wrapping(-13)); -assert_eq!(Wrapping(3i8).pow(6), Wrapping(-39)); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub fn pow(self, exp: u32) -> Self { - Wrapping(self.0.wrapping_pow(exp)) - } + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(3", stringify!($t), ").pow(4), Wrapping(81));")] + /// ``` + /// + /// Results that are too large are wrapped: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + /// assert_eq!(Wrapping(3i8).pow(5), Wrapping(-13)); + /// assert_eq!(Wrapping(3i8).pow(6), Wrapping(-39)); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub fn pow(self, exp: u32) -> Self { + Wrapping(self.0.wrapping_pow(exp)) } } )*) @@ -760,124 +741,114 @@ wrapping_int_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } macro_rules! wrapping_int_impl_signed { ($($t:ty)*) => ($( impl Wrapping<$t> { - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(", stringify!($t), "::MAX) >> 2; - -assert_eq!(n.leading_zeros(), 3); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn leading_zeros(self) -> u32 { - self.0.leading_zeros() - } + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(", stringify!($t), "::MAX) >> 2;")] + /// + /// assert_eq!(n.leading_zeros(), 3); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn leading_zeros(self) -> u32 { + self.0.leading_zeros() } - doc_comment! { - concat!("Computes the absolute value of `self`, wrapping around at -the boundary of the type. - -The only case where such wrapping can occur is when one takes the absolute value of the negative -minimal value for the type this is a positive value that is too large to represent in the type. In -such a case, this function returns `MIN` itself. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(100", stringify!($t), ").abs(), Wrapping(100)); -assert_eq!(Wrapping(-100", stringify!($t), ").abs(), Wrapping(100)); -assert_eq!(Wrapping(", stringify!($t), "::MIN).abs(), Wrapping(", stringify!($t), "::MIN)); -assert_eq!(Wrapping(-128i8).abs().0 as u8, 128u8); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub fn abs(self) -> Wrapping<$t> { - Wrapping(self.0.wrapping_abs()) - } + /// Computes the absolute value of `self`, wrapping around at + /// the boundary of the type. + /// + /// The only case where such wrapping can occur is when one takes the absolute value of the negative + /// minimal value for the type this is a positive value that is too large to represent in the type. In + /// such a case, this function returns `MIN` itself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(100", stringify!($t), ").abs(), Wrapping(100));")] + #[doc = concat!("assert_eq!(Wrapping(-100", stringify!($t), ").abs(), Wrapping(100));")] + #[doc = concat!("assert_eq!(Wrapping(", stringify!($t), "::MIN).abs(), Wrapping(", stringify!($t), "::MIN));")] + /// assert_eq!(Wrapping(-128i8).abs().0 as u8, 128u8); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub fn abs(self) -> Wrapping<$t> { + Wrapping(self.0.wrapping_abs()) } - doc_comment! { - concat!("Returns a number representing sign of `self`. - - - `0` if the number is zero - - `1` if the number is positive - - `-1` if the number is negative - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(10", stringify!($t), ").signum(), Wrapping(1)); -assert_eq!(Wrapping(0", stringify!($t), ").signum(), Wrapping(0)); -assert_eq!(Wrapping(-10", stringify!($t), ").signum(), Wrapping(-1)); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub fn signum(self) -> Wrapping<$t> { - Wrapping(self.0.signum()) - } + /// Returns a number representing sign of `self`. + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(10", stringify!($t), ").signum(), Wrapping(1));")] + #[doc = concat!("assert_eq!(Wrapping(0", stringify!($t), ").signum(), Wrapping(0));")] + #[doc = concat!("assert_eq!(Wrapping(-10", stringify!($t), ").signum(), Wrapping(-1));")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub fn signum(self) -> Wrapping<$t> { + Wrapping(self.0.signum()) } - doc_comment! { - concat!("Returns `true` if `self` is positive and `false` if the number is zero or -negative. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert!(Wrapping(10", stringify!($t), ").is_positive()); -assert!(!Wrapping(-10", stringify!($t), ").is_positive()); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn is_positive(self) -> bool { - self.0.is_positive() - } + /// Returns `true` if `self` is positive and `false` if the number is zero or + /// negative. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert!(Wrapping(10", stringify!($t), ").is_positive());")] + #[doc = concat!("assert!(!Wrapping(-10", stringify!($t), ").is_positive());")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn is_positive(self) -> bool { + self.0.is_positive() } - doc_comment! { - concat!("Returns `true` if `self` is negative and `false` if the number is zero or -positive. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert!(Wrapping(-10", stringify!($t), ").is_negative()); -assert!(!Wrapping(10", stringify!($t), ").is_negative()); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn is_negative(self) -> bool { - self.0.is_negative() - } + /// Returns `true` if `self` is negative and `false` if the number is zero or + /// positive. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert!(Wrapping(-10", stringify!($t), ").is_negative());")] + #[doc = concat!("assert!(!Wrapping(10", stringify!($t), ").is_negative());")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn is_negative(self) -> bool { + self.0.is_negative() } } )*) @@ -888,73 +859,67 @@ wrapping_int_impl_signed! { isize i8 i16 i32 i64 i128 } macro_rules! wrapping_int_impl_unsigned { ($($t:ty)*) => ($( impl Wrapping<$t> { - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(", stringify!($t), "::MAX) >> 2; - -assert_eq!(n.leading_zeros(), 2); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn leading_zeros(self) -> u32 { - self.0.leading_zeros() - } + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(", stringify!($t), "::MAX) >> 2;")] + /// + /// assert_eq!(n.leading_zeros(), 2); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn leading_zeros(self) -> u32 { + self.0.leading_zeros() } - doc_comment! { - concat!("Returns `true` if and only if `self == 2^k` for some `k`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert!(Wrapping(16", stringify!($t), ").is_power_of_two()); -assert!(!Wrapping(10", stringify!($t), ").is_power_of_two()); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub fn is_power_of_two(self) -> bool { - self.0.is_power_of_two() - } + /// Returns `true` if and only if `self == 2^k` for some `k`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert!(Wrapping(16", stringify!($t), ").is_power_of_two());")] + #[doc = concat!("assert!(!Wrapping(10", stringify!($t), ").is_power_of_two());")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub fn is_power_of_two(self) -> bool { + self.0.is_power_of_two() } - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `self`. - -When return value overflows (i.e., `self > (1 << (N-1))` for type -`uN`), overflows to `2^N = 0`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_next_power_of_two)] -use std::num::Wrapping; - -assert_eq!(Wrapping(2", stringify!($t), ").next_power_of_two(), Wrapping(2)); -assert_eq!(Wrapping(3", stringify!($t), ").next_power_of_two(), Wrapping(4)); -assert_eq!(Wrapping(200_u8).next_power_of_two(), Wrapping(0)); -```"), - #[inline] - #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", - reason = "needs decision on wrapping behaviour")] - pub fn next_power_of_two(self) -> Self { - Wrapping(self.0.wrapping_next_power_of_two()) - } + /// Returns the smallest power of two greater than or equal to `self`. + /// + /// When return value overflows (i.e., `self > (1 << (N-1))` for type + /// `uN`), overflows to `2^N = 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_next_power_of_two)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(2", stringify!($t), ").next_power_of_two(), Wrapping(2));")] + #[doc = concat!("assert_eq!(Wrapping(3", stringify!($t), ").next_power_of_two(), Wrapping(4));")] + #[doc = concat!("assert_eq!(Wrapping(200_u8).next_power_of_two(), Wrapping(0));")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", + reason = "needs decision on wrapping behaviour")] + pub fn next_power_of_two(self) -> Self { + Wrapping(self.0.wrapping_next_power_of_two()) } } )*) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 3daf26208b93..1afa30f5843f 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1695,7 +1695,9 @@ impl Option> { /// Converts from `Option>` to `Option` /// /// # Examples + /// /// Basic usage: + /// /// ``` /// let x: Option> = Some(Some(6)); /// assert_eq!(Some(6), x.flatten()); @@ -1706,7 +1708,9 @@ impl Option> { /// let x: Option> = None; /// assert_eq!(None, x.flatten()); /// ``` - /// Flattening once only removes one level of nesting: + /// + /// Flattening only removes one level of nesting at a time: + /// /// ``` /// let x: Option>> = Some(Some(Some(6))); /// assert_eq!(Some(Some(6)), x.flatten()); diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 8fd9ff768c4f..663001167865 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -232,23 +232,27 @@ impl *const T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. /// - /// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::())` - /// is *not* the same as `y`, and dereferencing it is undefined behavior - /// unless `x` and `y` point into the same allocated object. + /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. /// - /// Compared to [`offset`], this method basically delays the requirement of staying - /// within the same allocated object: [`offset`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_offset` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`offset`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// Compared to [`offset`], this method basically delays the requirement of staying within the + /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_offset` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`offset`] + /// can be optimized better and is thus preferable in performance-sensitive code. + /// + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other + /// words, leaving the allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -571,19 +575,27 @@ impl *const T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. /// - /// Compared to [`add`], this method basically delays the requirement of staying - /// within the same allocated object: [`add`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_add` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`add`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. + /// + /// Compared to [`add`], this method basically delays the requirement of staying within the + /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_add` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`add`] + /// can be optimized better and is thus preferable in performance-sensitive code. + /// + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the + /// allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -628,19 +640,27 @@ impl *const T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. /// - /// Compared to [`sub`], this method basically delays the requirement of staying - /// within the same allocated object: [`sub`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_sub` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`sub`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. + /// + /// Compared to [`sub`], this method basically delays the requirement of staying within the + /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_sub` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`sub`] + /// can be optimized better and is thus preferable in performance-sensitive code. + /// + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the + /// allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -725,8 +745,9 @@ impl *const T { /// /// [`ptr::read`]: crate::ptr::read() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] #[inline] - pub unsafe fn read(self) -> T + pub const unsafe fn read(self) -> T where T: Sized, { @@ -763,8 +784,9 @@ impl *const T { /// /// [`ptr::read_unaligned`]: crate::ptr::read_unaligned() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] #[inline] - pub unsafe fn read_unaligned(self) -> T + pub const unsafe fn read_unaligned(self) -> T where T: Sized, { diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 27d49529a5ec..807f114ea466 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -685,7 +685,8 @@ pub unsafe fn replace(dst: *mut T, mut src: T) -> T { /// [valid]: self#safety #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn read(src: *const T) -> T { +#[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] +pub const unsafe fn read(src: *const T) -> T { // `copy_nonoverlapping` takes care of debug_assert. let mut tmp = MaybeUninit::::uninit(); // SAFETY: the caller must guarantee that `src` is valid for reads. @@ -784,7 +785,8 @@ pub unsafe fn read(src: *const T) -> T { /// ``` #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] -pub unsafe fn read_unaligned(src: *const T) -> T { +#[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] +pub const unsafe fn read_unaligned(src: *const T) -> T { // `copy_nonoverlapping` takes care of debug_assert. let mut tmp = MaybeUninit::::uninit(); // SAFETY: the caller must guarantee that `src` is valid for reads. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5f94c2393aef..785bf70c2992 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -238,23 +238,27 @@ impl *mut T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. /// - /// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::())` - /// is *not* the same as `y`, and dereferencing it is undefined behavior - /// unless `x` and `y` point into the same allocated object. + /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. /// - /// Compared to [`offset`], this method basically delays the requirement of staying - /// within the same allocated object: [`offset`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_offset` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`offset`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// Compared to [`offset`], this method basically delays the requirement of staying within the + /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_offset` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`offset`] + /// can be optimized better and is thus preferable in performance-sensitive code. + /// + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other + /// words, leaving the allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -678,19 +682,27 @@ impl *mut T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. /// - /// Compared to [`add`], this method basically delays the requirement of staying - /// within the same allocated object: [`add`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_add` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`add`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. + /// + /// Compared to [`add`], this method basically delays the requirement of staying within the + /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_add` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`add`] + /// can be optimized better and is thus preferable in performance-sensitive code. + /// + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the + /// allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -735,19 +747,27 @@ impl *mut T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. /// - /// Compared to [`sub`], this method basically delays the requirement of staying - /// within the same allocated object: [`sub`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_sub` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`sub`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. + /// + /// Compared to [`sub`], this method basically delays the requirement of staying within the + /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_sub` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`sub`] + /// can be optimized better and is thus preferable in performance-sensitive code. + /// + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the + /// allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -832,8 +852,9 @@ impl *mut T { /// /// [`ptr::read`]: crate::ptr::read() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] #[inline] - pub unsafe fn read(self) -> T + pub const unsafe fn read(self) -> T where T: Sized, { @@ -870,8 +891,9 @@ impl *mut T { /// /// [`ptr::read_unaligned`]: crate::ptr::read_unaligned() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] #[inline] - pub unsafe fn read_unaligned(self) -> T + pub const unsafe fn read_unaligned(self) -> T where T: Sized, { diff --git a/library/core/src/result.rs b/library/core/src/result.rs index b6d9f13d881e..0b4ca2b72141 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1184,7 +1184,9 @@ impl Result, E> { /// Converts from `Result, E>` to `Result` /// /// # Examples + /// /// Basic usage: + /// /// ``` /// #![feature(result_flattening)] /// let x: Result, u32> = Ok(Ok("hello")); @@ -1197,7 +1199,7 @@ impl Result, E> { /// assert_eq!(Err(6), x.flatten()); /// ``` /// - /// Flattening once only removes one level of nesting: + /// Flattening only removes one level of nesting at a time: /// /// ``` /// #![feature(result_flattening)] diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 18073f4afedf..72af47c71dd6 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -75,28 +75,6 @@ where } } -// Use an equal-pointer optimization when types are `Eq` -// We can't make `A` and `B` the same type because `min_specialization` won't -// allow it. -impl SlicePartialEq for [A] -where - A: MarkerEq, -{ - default fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { - return false; - } - - // While performance would suffer if `guaranteed_eq` just returned `false` - // for all arguments, correctness and return value of this function are not affected. - if self.as_ptr().guaranteed_eq(other.as_ptr() as *const A) { - return true; - } - - self.iter().zip(other.iter()).all(|(x, y)| x == y) - } -} - // Use memcmp for bytewise equality when the types allow impl SlicePartialEq for [A] where @@ -107,11 +85,6 @@ where return false; } - // While performance would suffer if `guaranteed_eq` just returned `false` - // for all arguments, correctness and return value of this function are not affected. - if self.as_ptr().guaranteed_eq(other.as_ptr() as *const A) { - return true; - } // SAFETY: `self` and `other` are references and are thus guaranteed to be valid. // The two slices have been checked to have the same size above. unsafe { diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index e373936a6c74..a367b4737dba 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! Definitions of a bunch of iterators for `[T]`. #[macro_use] // import iterator! and forward_iterator! @@ -2967,3 +2968,176 @@ unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> { false } } + +/// An iterator over slice in (non-overlapping) chunks separated by a predicate. +/// +/// This struct is created by the [`group_by`] method on [slices]. +/// +/// [`group_by`]: ../../std/primitive.slice.html#method.group_by +/// [slices]: ../../std/primitive.slice.html +#[unstable(feature = "slice_group_by", issue = "80552")] +pub struct GroupBy<'a, T: 'a, P> { + slice: &'a [T], + predicate: P, +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> GroupBy<'a, T, P> { + pub(super) fn new(slice: &'a [T], predicate: P) -> Self { + GroupBy { slice, predicate } + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> Iterator for GroupBy<'a, T, P> +where + P: FnMut(&T, &T) -> bool, +{ + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option { + if self.slice.is_empty() { + None + } else { + let mut len = 1; + let mut iter = self.slice.windows(2); + while let Some([l, r]) = iter.next() { + if (self.predicate)(l, r) { len += 1 } else { break } + } + let (head, tail) = self.slice.split_at(len); + self.slice = tail; + Some(head) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.slice.is_empty() { (0, Some(0)) } else { (1, Some(self.slice.len())) } + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> DoubleEndedIterator for GroupBy<'a, T, P> +where + P: FnMut(&T, &T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option { + if self.slice.is_empty() { + None + } else { + let mut len = 1; + let mut iter = self.slice.windows(2); + while let Some([l, r]) = iter.next_back() { + if (self.predicate)(l, r) { len += 1 } else { break } + } + let (head, tail) = self.slice.split_at(self.slice.len() - len); + self.slice = head; + Some(tail) + } + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> FusedIterator for GroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool {} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupBy<'a, T, P> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GroupBy").field("slice", &self.slice).finish() + } +} + +/// An iterator over slice in (non-overlapping) mutable chunks separated +/// by a predicate. +/// +/// This struct is created by the [`group_by_mut`] method on [slices]. +/// +/// [`group_by_mut`]: ../../std/primitive.slice.html#method.group_by_mut +/// [slices]: ../../std/primitive.slice.html +#[unstable(feature = "slice_group_by", issue = "80552")] +pub struct GroupByMut<'a, T: 'a, P> { + slice: &'a mut [T], + predicate: P, +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> GroupByMut<'a, T, P> { + pub(super) fn new(slice: &'a mut [T], predicate: P) -> Self { + GroupByMut { slice, predicate } + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> Iterator for GroupByMut<'a, T, P> +where + P: FnMut(&T, &T) -> bool, +{ + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option { + if self.slice.is_empty() { + None + } else { + let mut len = 1; + let mut iter = self.slice.windows(2); + while let Some([l, r]) = iter.next() { + if (self.predicate)(l, r) { len += 1 } else { break } + } + let slice = mem::take(&mut self.slice); + let (head, tail) = slice.split_at_mut(len); + self.slice = tail; + Some(head) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.slice.is_empty() { (0, Some(0)) } else { (1, Some(self.slice.len())) } + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> DoubleEndedIterator for GroupByMut<'a, T, P> +where + P: FnMut(&T, &T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option { + if self.slice.is_empty() { + None + } else { + let mut len = 1; + let mut iter = self.slice.windows(2); + while let Some([l, r]) = iter.next_back() { + if (self.predicate)(l, r) { len += 1 } else { break } + } + let slice = mem::take(&mut self.slice); + let (head, tail) = slice.split_at_mut(slice.len() - len); + self.slice = head; + Some(tail) + } + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> FusedIterator for GroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool {} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupByMut<'a, T, P> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GroupByMut").field("slice", &self.slice).finish() + } +} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 44fe2ca88596..58bf74c8cf47 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -57,6 +57,9 @@ pub use iter::{ArrayChunks, ArrayChunksMut}; #[unstable(feature = "array_windows", issue = "75027")] pub use iter::ArrayWindows; +#[unstable(feature = "slice_group_by", issue = "80552")] +pub use iter::{GroupBy, GroupByMut}; + #[unstable(feature = "split_inclusive", issue = "72360")] pub use iter::{SplitInclusive, SplitInclusiveMut}; @@ -84,6 +87,7 @@ impl [T] { /// let a = [1, 2, 3]; /// assert_eq!(a.len(), 3); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_len", since = "1.32.0")] #[inline] @@ -1207,6 +1211,96 @@ impl [T] { RChunksExactMut::new(self, chunk_size) } + /// Returns an iterator over the slice producing non-overlapping runs + /// of elements using the predicate to separate them. + /// + /// The predicate is called on two elements following themselves, + /// it means the predicate is called on `slice[0]` and `slice[1]` + /// then on `slice[1]` and `slice[2]` and so on. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_group_by)] + /// + /// let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; + /// + /// let mut iter = slice.group_by(|a, b| a == b); + /// + /// assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + /// assert_eq!(iter.next(), Some(&[3, 3][..])); + /// assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// This method can be used to extract the sorted subslices: + /// + /// ``` + /// #![feature(slice_group_by)] + /// + /// let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4]; + /// + /// let mut iter = slice.group_by(|a, b| a <= b); + /// + /// assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..])); + /// assert_eq!(iter.next(), Some(&[2, 3][..])); + /// assert_eq!(iter.next(), Some(&[2, 3, 4][..])); + /// assert_eq!(iter.next(), None); + /// ``` + #[unstable(feature = "slice_group_by", issue = "80552")] + #[inline] + pub fn group_by(&self, pred: F) -> GroupBy<'_, T, F> + where + F: FnMut(&T, &T) -> bool, + { + GroupBy::new(self, pred) + } + + /// Returns an iterator over the slice producing non-overlapping mutable + /// runs of elements using the predicate to separate them. + /// + /// The predicate is called on two elements following themselves, + /// it means the predicate is called on `slice[0]` and `slice[1]` + /// then on `slice[1]` and `slice[2]` and so on. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_group_by)] + /// + /// let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2]; + /// + /// let mut iter = slice.group_by_mut(|a, b| a == b); + /// + /// assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); + /// assert_eq!(iter.next(), Some(&mut [3, 3][..])); + /// assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// This method can be used to extract the sorted subslices: + /// + /// ``` + /// #![feature(slice_group_by)] + /// + /// let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4]; + /// + /// let mut iter = slice.group_by_mut(|a, b| a <= b); + /// + /// assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..])); + /// assert_eq!(iter.next(), Some(&mut [2, 3][..])); + /// assert_eq!(iter.next(), Some(&mut [2, 3, 4][..])); + /// assert_eq!(iter.next(), None); + /// ``` + #[unstable(feature = "slice_group_by", issue = "80552")] + #[inline] + pub fn group_by_mut(&mut self, pred: F) -> GroupByMut<'_, T, F> + where + F: FnMut(&T, &T) -> bool, + { + GroupByMut::new(self, pred) + } + /// Divides one slice into two at an index. /// /// The first will contain all indices from `[0, mid)` (excluding @@ -2581,14 +2675,12 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_fill)] - /// /// let mut buf = vec![0; 10]; /// buf.fill(1); /// assert_eq!(buf, vec![1; 10]); /// ``` #[doc(alias = "memset")] - #[unstable(feature = "slice_fill", issue = "70758")] + #[stable(feature = "slice_fill", since = "1.50.0")] pub fn fill(&mut self, value: T) where T: Clone, diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 83cf47c8c8f6..ba495a1a9fbe 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -138,6 +138,7 @@ impl str { /// assert_eq!("ƒoo".len(), 4); // fancy f! /// assert_eq!("ƒoo".chars().count(), 3); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_len", since = "1.32.0")] #[inline] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index be6c86b51761..d03c19e51f3f 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -464,6 +464,23 @@ impl AtomicBool { /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// + /// # Migrating to `compare_exchange` and `compare_exchange_weak` + /// + /// `compare_and_swap` is equivalent to `compare_exchange` with the following mapping for + /// memory orderings: + /// + /// Original | Success | Failure + /// -------- | ------- | ------- + /// Relaxed | Relaxed | Relaxed + /// Acquire | Acquire | Acquire + /// Release | Release | Relaxed + /// AcqRel | AcqRel | Acquire + /// SeqCst | SeqCst | SeqCst + /// + /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, + /// which allows the compiler to generate better assembly code when the compare and swap + /// is used in a loop. + /// /// # Examples /// /// ``` @@ -479,6 +496,10 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated( + since = "1.50.0", + reason = "Use `compare_exchange` or `compare_exchange_weak` instead" + )] #[cfg(target_has_atomic = "8")] pub fn compare_and_swap(&self, current: bool, new: bool, order: Ordering) -> bool { match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { @@ -493,9 +514,10 @@ impl AtomicBool { /// the previous value. On success this value is guaranteed to be equal to `current`. /// /// `compare_exchange` takes two [`Ordering`] arguments to describe the memory - /// ordering of this operation. The first describes the required ordering if the - /// operation succeeds while the second describes the required ordering when the - /// operation fails. Using [`Acquire`] as success ordering makes the store part + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using [`Acquire`] as success ordering makes the store part /// of this operation [`Relaxed`], and using [`Release`] makes the successful load /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] /// and must be equivalent to or weaker than the success ordering. @@ -525,6 +547,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] + #[doc(alias = "compare_and_swap")] #[cfg(target_has_atomic = "8")] pub fn compare_exchange( &self, @@ -550,9 +573,10 @@ impl AtomicBool { /// previous value. /// /// `compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory - /// ordering of this operation. The first describes the required ordering if the - /// operation succeeds while the second describes the required ordering when the - /// operation fails. Using [`Acquire`] as success ordering makes the store part + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using [`Acquire`] as success ordering makes the store part /// of this operation [`Relaxed`], and using [`Release`] makes the successful load /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] /// and must be equivalent to or weaker than the success ordering. @@ -578,6 +602,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] + #[doc(alias = "compare_and_swap")] #[cfg(target_has_atomic = "8")] pub fn compare_exchange_weak( &self, @@ -966,16 +991,8 @@ impl AtomicPtr { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn load(&self, order: Ordering) -> *mut T { - #[cfg(not(bootstrap))] // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_load(self.p.get(), order) - } - #[cfg(bootstrap)] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_load(self.p.get() as *mut usize, order) as *mut T - } + unsafe { atomic_load(self.p.get(), order) } } /// Stores a value into the pointer. @@ -1002,16 +1019,10 @@ impl AtomicPtr { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn store(&self, ptr: *mut T, order: Ordering) { - #[cfg(not(bootstrap))] // SAFETY: data races are prevented by atomic intrinsics. unsafe { atomic_store(self.p.get(), ptr, order); } - #[cfg(bootstrap)] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_store(self.p.get() as *mut usize, ptr as usize, order); - } } /// Stores a value into the pointer, returning the previous value. @@ -1041,7 +1052,7 @@ impl AtomicPtr { #[cfg(target_has_atomic = "ptr")] pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T } + unsafe { atomic_swap(self.p.get(), ptr, order) } } /// Stores a value into the pointer if the current value is the same as the `current` value. @@ -1058,6 +1069,23 @@ impl AtomicPtr { /// **Note:** This method is only available on platforms that support atomic /// operations on pointers. /// + /// # Migrating to `compare_exchange` and `compare_exchange_weak` + /// + /// `compare_and_swap` is equivalent to `compare_exchange` with the following mapping for + /// memory orderings: + /// + /// Original | Success | Failure + /// -------- | ------- | ------- + /// Relaxed | Relaxed | Relaxed + /// Acquire | Acquire | Acquire + /// Release | Release | Relaxed + /// AcqRel | AcqRel | Acquire + /// SeqCst | SeqCst | SeqCst + /// + /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, + /// which allows the compiler to generate better assembly code when the compare and swap + /// is used in a loop. + /// /// # Examples /// /// ``` @@ -1072,6 +1100,10 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated( + since = "1.50.0", + reason = "Use `compare_exchange` or `compare_exchange_weak` instead" + )] #[cfg(target_has_atomic = "ptr")] pub fn compare_and_swap(&self, current: *mut T, new: *mut T, order: Ordering) -> *mut T { match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { @@ -1086,9 +1118,10 @@ impl AtomicPtr { /// the previous value. On success this value is guaranteed to be equal to `current`. /// /// `compare_exchange` takes two [`Ordering`] arguments to describe the memory - /// ordering of this operation. The first describes the required ordering if the - /// operation succeeds while the second describes the required ordering when the - /// operation fails. Using [`Acquire`] as success ordering makes the store part + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using [`Acquire`] as success ordering makes the store part /// of this operation [`Relaxed`], and using [`Release`] makes the successful load /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] /// and must be equivalent to or weaker than the success ordering. @@ -1119,26 +1152,8 @@ impl AtomicPtr { success: Ordering, failure: Ordering, ) -> Result<*mut T, *mut T> { - #[cfg(bootstrap)] // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - let res = atomic_compare_exchange( - self.p.get() as *mut usize, - current as usize, - new as usize, - success, - failure, - ); - match res { - Ok(x) => Ok(x as *mut T), - Err(x) => Err(x as *mut T), - } - } - #[cfg(not(bootstrap))] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_compare_exchange(self.p.get(), current, new, success, failure) - } + unsafe { atomic_compare_exchange(self.p.get(), current, new, success, failure) } } /// Stores a value into the pointer if the current value is the same as the `current` value. @@ -1149,9 +1164,10 @@ impl AtomicPtr { /// previous value. /// /// `compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory - /// ordering of this operation. The first describes the required ordering if the - /// operation succeeds while the second describes the required ordering when the - /// operation fails. Using [`Acquire`] as success ordering makes the store part + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using [`Acquire`] as success ordering makes the store part /// of this operation [`Relaxed`], and using [`Release`] makes the successful load /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] /// and must be equivalent to or weaker than the success ordering. @@ -1185,29 +1201,11 @@ impl AtomicPtr { success: Ordering, failure: Ordering, ) -> Result<*mut T, *mut T> { - #[cfg(bootstrap)] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - let res = atomic_compare_exchange_weak( - self.p.get() as *mut usize, - current as usize, - new as usize, - success, - failure, - ); - match res { - Ok(x) => Ok(x as *mut T), - Err(x) => Err(x as *mut T), - } - } - #[cfg(not(bootstrap))] // SAFETY: This intrinsic is unsafe because it operates on a raw pointer // but we know for sure that the pointer is valid (we just got it from // an `UnsafeCell` that we have by reference) and the atomic operation // itself allows us to safely mutate the `UnsafeCell` contents. - unsafe { - atomic_compare_exchange_weak(self.p.get(), current, new, success, failure) - } + unsafe { atomic_compare_exchange_weak(self.p.get(), current, new, success, failure) } } /// Fetches the value, and applies a function to it that returns an optional @@ -1374,12 +1372,9 @@ macro_rules! atomic_int { #[$stable_from] impl From<$int_type> for $atomic_type { - doc_comment! { - concat!( -"Converts an `", stringify!($int_type), "` into an `", stringify!($atomic_type), "`."), - #[inline] - fn from(v: $int_type) -> Self { Self::new(v) } - } + #[doc = concat!("Converts an `", stringify!($int_type), "` into an `", stringify!($atomic_type), "`.")] + #[inline] + fn from(v: $int_type) -> Self { Self::new(v) } } #[$stable_debug] @@ -1394,721 +1389,703 @@ macro_rules! atomic_int { unsafe impl Sync for $atomic_type {} impl $atomic_type { - doc_comment! { - concat!("Creates a new atomic integer. + /// Creates a new atomic integer. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::", stringify!($atomic_type), ";")] + /// + #[doc = concat!("let atomic_forty_two = ", stringify!($atomic_type), "::new(42);")] + /// ``` + #[inline] + #[$stable] + #[$const_stable] + pub const fn new(v: $int_type) -> Self { + Self {v: UnsafeCell::new(v)} + } -# Examples + /// Returns a mutable reference to the underlying integer. + /// + /// This is safe because the mutable reference guarantees that no other threads are + /// concurrently accessing the atomic data. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let mut some_var = ", stringify!($atomic_type), "::new(10);")] + /// assert_eq!(*some_var.get_mut(), 10); + /// *some_var.get_mut() = 5; + /// assert_eq!(some_var.load(Ordering::SeqCst), 5); + /// ``` + #[inline] + #[$stable_access] + pub fn get_mut(&mut self) -> &mut $int_type { + self.v.get_mut() + } -``` -", $extra_feature, "use std::sync::atomic::", stringify!($atomic_type), "; + #[doc = concat!("Get atomic access to a `&mut ", stringify!($int_type), "`.")] + /// + #[doc = if_not_8_bit! { + $int_type, + concat!( + "**Note:** This function is only available on targets where `", + stringify!($int_type), "` has an alignment of ", $align, " bytes." + ) + }] + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_from_mut)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + /// let mut some_int = 123; + #[doc = concat!("let a = ", stringify!($atomic_type), "::from_mut(&mut some_int);")] + /// a.store(100, Ordering::Relaxed); + /// assert_eq!(some_int, 100); + /// ``` + /// + #[inline] + #[$cfg_align] + #[unstable(feature = "atomic_from_mut", issue = "76314")] + pub fn from_mut(v: &mut $int_type) -> &Self { + use crate::mem::align_of; + let [] = [(); align_of::() - align_of::<$int_type>()]; + // SAFETY: + // - the mutable reference guarantees unique ownership. + // - the alignment of `$int_type` and `Self` is the + // same, as promised by $cfg_align and verified above. + unsafe { &*(v as *mut $int_type as *mut Self) } + } -let atomic_forty_two = ", stringify!($atomic_type), "::new(42); -```"), - #[inline] - #[$stable] - #[$const_stable] - pub const fn new(v: $int_type) -> Self { - Self {v: UnsafeCell::new(v)} + /// Consumes the atomic and returns the contained value. + /// + /// This is safe because passing `self` by value guarantees that no other threads are + /// concurrently accessing the atomic data. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::", stringify!($atomic_type), ";")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// assert_eq!(some_var.into_inner(), 5); + /// ``` + #[inline] + #[$stable_access] + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> $int_type { + self.v.into_inner() + } + + /// Loads a value from the atomic integer. + /// + /// `load` takes an [`Ordering`] argument which describes the memory ordering of this operation. + /// Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. + /// + /// # Panics + /// + /// Panics if `order` is [`Release`] or [`AcqRel`]. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// assert_eq!(some_var.load(Ordering::Relaxed), 5); + /// ``` + #[inline] + #[$stable] + pub fn load(&self, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_load(self.v.get(), order) } + } + + /// Stores a value into the atomic integer. + /// + /// `store` takes an [`Ordering`] argument which describes the memory ordering of this operation. + /// Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. + /// + /// # Panics + /// + /// Panics if `order` is [`Acquire`] or [`AcqRel`]. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// some_var.store(10, Ordering::Relaxed); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// ``` + #[inline] + #[$stable] + pub fn store(&self, val: $int_type, order: Ordering) { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_store(self.v.get(), val, order); } + } + + /// Stores a value into the atomic integer, returning the previous value. + /// + /// `swap` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// assert_eq!(some_var.swap(10, Ordering::Relaxed), 5); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_swap(self.v.get(), val, order) } + } + + /// Stores a value into the atomic integer if the current value is the same as + /// the `current` value. + /// + /// The return value is always the previous value. If it is equal to `current`, then the + /// value was updated. + /// + /// `compare_and_swap` also takes an [`Ordering`] argument which describes the memory + /// ordering of this operation. Notice that even when using [`AcqRel`], the operation + /// might fail and hence just perform an `Acquire` load, but not have `Release` semantics. + /// Using [`Acquire`] makes the store part of this operation [`Relaxed`] if it + /// happens, and using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Migrating to `compare_exchange` and `compare_exchange_weak` + /// + /// `compare_and_swap` is equivalent to `compare_exchange` with the following mapping for + /// memory orderings: + /// + /// Original | Success | Failure + /// -------- | ------- | ------- + /// Relaxed | Relaxed | Relaxed + /// Acquire | Acquire | Acquire + /// Release | Release | Relaxed + /// AcqRel | AcqRel | Acquire + /// SeqCst | SeqCst | SeqCst + /// + /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, + /// which allows the compiler to generate better assembly code when the compare and swap + /// is used in a loop. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// assert_eq!(some_var.compare_and_swap(5, 10, Ordering::Relaxed), 5); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// + /// assert_eq!(some_var.compare_and_swap(6, 12, Ordering::Relaxed), 10); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// ``` + #[inline] + #[$stable] + #[rustc_deprecated( + since = "1.50.0", + reason = "Use `compare_exchange` or `compare_exchange_weak` instead") + ] + #[$cfg_cas] + pub fn compare_and_swap(&self, + current: $int_type, + new: $int_type, + order: Ordering) -> $int_type { + match self.compare_exchange(current, + new, + order, + strongest_failure_ordering(order)) { + Ok(x) => x, + Err(x) => x, } } - doc_comment! { - concat!("Returns a mutable reference to the underlying integer. + /// Stores a value into the atomic integer if the current value is the same as + /// the `current` value. + /// + /// The return value is a result indicating whether the new value was written and + /// containing the previous value. On success this value is guaranteed to be equal to + /// `current`. + /// + /// `compare_exchange` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the successful load + /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] + /// and must be equivalent to or weaker than the success ordering. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// assert_eq!(some_var.compare_exchange(5, 10, + /// Ordering::Acquire, + /// Ordering::Relaxed), + /// Ok(5)); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// + /// assert_eq!(some_var.compare_exchange(6, 12, + /// Ordering::SeqCst, + /// Ordering::Acquire), + /// Err(10)); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// ``` + #[inline] + #[$stable_cxchg] + #[$cfg_cas] + pub fn compare_exchange(&self, + current: $int_type, + new: $int_type, + success: Ordering, + failure: Ordering) -> Result<$int_type, $int_type> { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_compare_exchange(self.v.get(), current, new, success, failure) } + } -This is safe because the mutable reference guarantees that no other threads are -concurrently accessing the atomic data. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let mut some_var = ", stringify!($atomic_type), "::new(10); -assert_eq!(*some_var.get_mut(), 10); -*some_var.get_mut() = 5; -assert_eq!(some_var.load(Ordering::SeqCst), 5); -```"), - #[inline] - #[$stable_access] - pub fn get_mut(&mut self) -> &mut $int_type { - self.v.get_mut() + /// Stores a value into the atomic integer if the current value is the same as + /// the `current` value. + /// + #[doc = concat!("Unlike [`", stringify!($atomic_type), "::compare_exchange`],")] + /// this function is allowed to spuriously fail even + /// when the comparison succeeds, which can result in more efficient code on some + /// platforms. The return value is a result indicating whether the new value was + /// written and containing the previous value. + /// + /// `compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the successful load + /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] + /// and must be equivalent to or weaker than the success ordering. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let val = ", stringify!($atomic_type), "::new(4);")] + /// + /// let mut old = val.load(Ordering::Relaxed); + /// loop { + /// let new = old * 2; + /// match val.compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed) { + /// Ok(_) => break, + /// Err(x) => old = x, + /// } + /// } + /// ``` + #[inline] + #[$stable_cxchg] + #[$cfg_cas] + pub fn compare_exchange_weak(&self, + current: $int_type, + new: $int_type, + success: Ordering, + failure: Ordering) -> Result<$int_type, $int_type> { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { + atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) } } - doc_comment! { - concat!("Get atomic access to a `&mut ", stringify!($int_type), "`. - -", -if_not_8_bit! { - $int_type, - concat!( - "**Note:** This function is only available on targets where `", - stringify!($int_type), "` has an alignment of ", $align, " bytes." - ) -}, -" - -# Examples - -``` -#![feature(atomic_from_mut)] -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let mut some_int = 123; -let a = ", stringify!($atomic_type), "::from_mut(&mut some_int); -a.store(100, Ordering::Relaxed); -assert_eq!(some_int, 100); -``` - "), - #[inline] - #[$cfg_align] - #[unstable(feature = "atomic_from_mut", issue = "76314")] - pub fn from_mut(v: &mut $int_type) -> &Self { - use crate::mem::align_of; - let [] = [(); align_of::() - align_of::<$int_type>()]; - // SAFETY: - // - the mutable reference guarantees unique ownership. - // - the alignment of `$int_type` and `Self` is the - // same, as promised by $cfg_align and verified above. - unsafe { &*(v as *mut $int_type as *mut Self) } - } + /// Adds to the current value, returning the previous value. + /// + /// This operation wraps around on overflow. + /// + /// `fetch_add` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0);")] + /// assert_eq!(foo.fetch_add(10, Ordering::SeqCst), 0); + /// assert_eq!(foo.load(Ordering::SeqCst), 10); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_add(self.v.get(), val, order) } } - doc_comment! { - concat!("Consumes the atomic and returns the contained value. - -This is safe because passing `self` by value guarantees that no other threads are -concurrently accessing the atomic data. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::", stringify!($atomic_type), "; - -let some_var = ", stringify!($atomic_type), "::new(5); -assert_eq!(some_var.into_inner(), 5); -```"), - #[inline] - #[$stable_access] - #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] - pub const fn into_inner(self) -> $int_type { - self.v.into_inner() - } + /// Subtracts from the current value, returning the previous value. + /// + /// This operation wraps around on overflow. + /// + /// `fetch_sub` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(20);")] + /// assert_eq!(foo.fetch_sub(10, Ordering::SeqCst), 20); + /// assert_eq!(foo.load(Ordering::SeqCst), 10); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_sub(self.v.get(), val, order) } } - doc_comment! { - concat!("Loads a value from the atomic integer. - -`load` takes an [`Ordering`] argument which describes the memory ordering of this operation. -Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. - -# Panics - -Panics if `order` is [`Release`] or [`AcqRel`]. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -assert_eq!(some_var.load(Ordering::Relaxed), 5); -```"), - #[inline] - #[$stable] - pub fn load(&self, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_load(self.v.get(), order) } - } + /// Bitwise "and" with the current value. + /// + /// Performs a bitwise "and" operation on the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_and` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0b101101);")] + /// assert_eq!(foo.fetch_and(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b100001); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_and(self.v.get(), val, order) } } - doc_comment! { - concat!("Stores a value into the atomic integer. - -`store` takes an [`Ordering`] argument which describes the memory ordering of this operation. - Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. - -# Panics - -Panics if `order` is [`Acquire`] or [`AcqRel`]. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -some_var.store(10, Ordering::Relaxed); -assert_eq!(some_var.load(Ordering::Relaxed), 10); -```"), - #[inline] - #[$stable] - pub fn store(&self, val: $int_type, order: Ordering) { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_store(self.v.get(), val, order); } - } + /// Bitwise "nand" with the current value. + /// + /// Performs a bitwise "nand" operation on the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_nand` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0x13);")] + /// assert_eq!(foo.fetch_nand(0x31, Ordering::SeqCst), 0x13); + /// assert_eq!(foo.load(Ordering::SeqCst), !(0x13 & 0x31)); + /// ``` + #[inline] + #[$stable_nand] + #[$cfg_cas] + pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_nand(self.v.get(), val, order) } } - doc_comment! { - concat!("Stores a value into the atomic integer, returning the previous value. - -`swap` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -assert_eq!(some_var.swap(10, Ordering::Relaxed), 5); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_swap(self.v.get(), val, order) } - } + /// Bitwise "or" with the current value. + /// + /// Performs a bitwise "or" operation on the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_or` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0b101101);")] + /// assert_eq!(foo.fetch_or(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b111111); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_or(self.v.get(), val, order) } } - doc_comment! { - concat!("Stores a value into the atomic integer if the current value is the same as -the `current` value. + /// Bitwise "xor" with the current value. + /// + /// Performs a bitwise "xor" operation on the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_xor` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0b101101);")] + /// assert_eq!(foo.fetch_xor(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b011110); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_xor(self.v.get(), val, order) } + } -The return value is always the previous value. If it is equal to `current`, then the -value was updated. - -`compare_and_swap` also takes an [`Ordering`] argument which describes the memory -ordering of this operation. Notice that even when using [`AcqRel`], the operation -might fail and hence just perform an `Acquire` load, but not have `Release` semantics. -Using [`Acquire`] makes the store part of this operation [`Relaxed`] if it -happens, and using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -assert_eq!(some_var.compare_and_swap(5, 10, Ordering::Relaxed), 5); -assert_eq!(some_var.load(Ordering::Relaxed), 10); - -assert_eq!(some_var.compare_and_swap(6, 12, Ordering::Relaxed), 10); -assert_eq!(some_var.load(Ordering::Relaxed), 10); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn compare_and_swap(&self, - current: $int_type, - new: $int_type, - order: Ordering) -> $int_type { - match self.compare_exchange(current, - new, - order, - strongest_failure_ordering(order)) { - Ok(x) => x, - Err(x) => x, + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else + /// `Err(previous_value)`. + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied + /// only once to the stored value. + /// + /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] + /// and must be equivalent to or weaker than the success ordering. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ```rust + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[stable(feature = "no_more_cas", since = "1.45.0")] + #[$cfg_cas] + pub fn fetch_update(&self, + set_order: Ordering, + fetch_order: Ordering, + mut f: F) -> Result<$int_type, $int_type> + where F: FnMut($int_type) -> Option<$int_type> { + let mut prev = self.load(fetch_order); + while let Some(next) = f(prev) { + match self.compare_exchange_weak(prev, next, set_order, fetch_order) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev } } + Err(prev) } - doc_comment! { - concat!("Stores a value into the atomic integer if the current value is the same as -the `current` value. - -The return value is a result indicating whether the new value was written and -containing the previous value. On success this value is guaranteed to be equal to -`current`. - -`compare_exchange` takes two [`Ordering`] arguments to describe the memory -ordering of this operation. The first describes the required ordering if the -operation succeeds while the second describes the required ordering when the -operation fails. Using [`Acquire`] as success ordering makes the store part -of this operation [`Relaxed`], and using [`Release`] makes the successful load -[`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] -and must be equivalent to or weaker than the success ordering. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -assert_eq!(some_var.compare_exchange(5, 10, - Ordering::Acquire, - Ordering::Relaxed), - Ok(5)); -assert_eq!(some_var.load(Ordering::Relaxed), 10); - -assert_eq!(some_var.compare_exchange(6, 12, - Ordering::SeqCst, - Ordering::Acquire), - Err(10)); -assert_eq!(some_var.load(Ordering::Relaxed), 10); -```"), - #[inline] - #[$stable_cxchg] - #[$cfg_cas] - pub fn compare_exchange(&self, - current: $int_type, - new: $int_type, - success: Ordering, - failure: Ordering) -> Result<$int_type, $int_type> { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_compare_exchange(self.v.get(), current, new, success, failure) } - } + /// Maximum with the current value. + /// + /// Finds the maximum of the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_max` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(23);")] + /// assert_eq!(foo.fetch_max(42, Ordering::SeqCst), 23); + /// assert_eq!(foo.load(Ordering::SeqCst), 42); + /// ``` + /// + /// If you want to obtain the maximum value in one step, you can use the following: + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(23);")] + /// let bar = 42; + /// let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar); + /// assert!(max_foo == 42); + /// ``` + #[inline] + #[stable(feature = "atomic_min_max", since = "1.45.0")] + #[$cfg_cas] + pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { $max_fn(self.v.get(), val, order) } } - doc_comment! { - concat!("Stores a value into the atomic integer if the current value is the same as -the `current` value. - -Unlike [`", stringify!($atomic_type), "::compare_exchange`], this function is allowed to spuriously fail even -when the comparison succeeds, which can result in more efficient code on some -platforms. The return value is a result indicating whether the new value was -written and containing the previous value. - -`compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory -ordering of this operation. The first describes the required ordering if the -operation succeeds while the second describes the required ordering when the -operation fails. Using [`Acquire`] as success ordering makes the store part -of this operation [`Relaxed`], and using [`Release`] makes the successful load -[`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] -and must be equivalent to or weaker than the success ordering. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let val = ", stringify!($atomic_type), "::new(4); - -let mut old = val.load(Ordering::Relaxed); -loop { - let new = old * 2; - match val.compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed) { - Ok(_) => break, - Err(x) => old = x, - } -} -```"), - #[inline] - #[$stable_cxchg] - #[$cfg_cas] - pub fn compare_exchange_weak(&self, - current: $int_type, - new: $int_type, - success: Ordering, - failure: Ordering) -> Result<$int_type, $int_type> { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) - } - } + /// Minimum with the current value. + /// + /// Finds the minimum of the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_min` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(23);")] + /// assert_eq!(foo.fetch_min(42, Ordering::Relaxed), 23); + /// assert_eq!(foo.load(Ordering::Relaxed), 23); + /// assert_eq!(foo.fetch_min(22, Ordering::Relaxed), 23); + /// assert_eq!(foo.load(Ordering::Relaxed), 22); + /// ``` + /// + /// If you want to obtain the minimum value in one step, you can use the following: + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(23);")] + /// let bar = 12; + /// let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar); + /// assert_eq!(min_foo, 12); + /// ``` + #[inline] + #[stable(feature = "atomic_min_max", since = "1.45.0")] + #[$cfg_cas] + pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { $min_fn(self.v.get(), val, order) } } - doc_comment! { - concat!("Adds to the current value, returning the previous value. - -This operation wraps around on overflow. - -`fetch_add` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0); -assert_eq!(foo.fetch_add(10, Ordering::SeqCst), 0); -assert_eq!(foo.load(Ordering::SeqCst), 10); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_add(self.v.get(), val, order) } - } - } - - doc_comment! { - concat!("Subtracts from the current value, returning the previous value. - -This operation wraps around on overflow. - -`fetch_sub` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(20); -assert_eq!(foo.fetch_sub(10, Ordering::SeqCst), 20); -assert_eq!(foo.load(Ordering::SeqCst), 10); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_sub(self.v.get(), val, order) } - } - } - - doc_comment! { - concat!("Bitwise \"and\" with the current value. - -Performs a bitwise \"and\" operation on the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_and` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0b101101); -assert_eq!(foo.fetch_and(0b110011, Ordering::SeqCst), 0b101101); -assert_eq!(foo.load(Ordering::SeqCst), 0b100001); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_and(self.v.get(), val, order) } - } - } - - doc_comment! { - concat!("Bitwise \"nand\" with the current value. - -Performs a bitwise \"nand\" operation on the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_nand` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, " -use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0x13); -assert_eq!(foo.fetch_nand(0x31, Ordering::SeqCst), 0x13); -assert_eq!(foo.load(Ordering::SeqCst), !(0x13 & 0x31)); -```"), - #[inline] - #[$stable_nand] - #[$cfg_cas] - pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_nand(self.v.get(), val, order) } - } - } - - doc_comment! { - concat!("Bitwise \"or\" with the current value. - -Performs a bitwise \"or\" operation on the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_or` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0b101101); -assert_eq!(foo.fetch_or(0b110011, Ordering::SeqCst), 0b101101); -assert_eq!(foo.load(Ordering::SeqCst), 0b111111); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_or(self.v.get(), val, order) } - } - } - - doc_comment! { - concat!("Bitwise \"xor\" with the current value. - -Performs a bitwise \"xor\" operation on the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_xor` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0b101101); -assert_eq!(foo.fetch_xor(0b110011, Ordering::SeqCst), 0b101101); -assert_eq!(foo.load(Ordering::SeqCst), 0b011110); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_xor(self.v.get(), val, order) } - } - } - - doc_comment! { - concat!("Fetches the value, and applies a function to it that returns an optional -new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else -`Err(previous_value)`. - -Note: This may call the function multiple times if the value has been changed from other threads in -the meantime, as long as the function returns `Some(_)`, but the function will have been applied -only once to the stored value. - -`fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. -The first describes the required ordering for when the operation finally succeeds while the second -describes the required ordering for loads. These correspond to the success and failure orderings of -[`", stringify!($atomic_type), "::compare_exchange`] respectively. - -Using [`Acquire`] as success ordering makes the store part -of this operation [`Relaxed`], and using [`Release`] makes the final successful load -[`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] -and must be equivalent to or weaker than the success ordering. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -```rust -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let x = ", stringify!($atomic_type), "::new(7); -assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); -assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); -assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); -assert_eq!(x.load(Ordering::SeqCst), 9); -```"), - #[inline] - #[stable(feature = "no_more_cas", since = "1.45.0")] - #[$cfg_cas] - pub fn fetch_update(&self, - set_order: Ordering, - fetch_order: Ordering, - mut f: F) -> Result<$int_type, $int_type> - where F: FnMut($int_type) -> Option<$int_type> { - let mut prev = self.load(fetch_order); - while let Some(next) = f(prev) { - match self.compare_exchange_weak(prev, next, set_order, fetch_order) { - x @ Ok(_) => return x, - Err(next_prev) => prev = next_prev - } - } - Err(prev) - } - } - - doc_comment! { - concat!("Maximum with the current value. - -Finds the maximum of the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_max` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(23); -assert_eq!(foo.fetch_max(42, Ordering::SeqCst), 23); -assert_eq!(foo.load(Ordering::SeqCst), 42); -``` - -If you want to obtain the maximum value in one step, you can use the following: - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(23); -let bar = 42; -let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar); -assert!(max_foo == 42); -```"), - #[inline] - #[stable(feature = "atomic_min_max", since = "1.45.0")] - #[$cfg_cas] - pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { $max_fn(self.v.get(), val, order) } - } - } - - doc_comment! { - concat!("Minimum with the current value. - -Finds the minimum of the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_min` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(23); -assert_eq!(foo.fetch_min(42, Ordering::Relaxed), 23); -assert_eq!(foo.load(Ordering::Relaxed), 23); -assert_eq!(foo.fetch_min(22, Ordering::Relaxed), 23); -assert_eq!(foo.load(Ordering::Relaxed), 22); -``` - -If you want to obtain the minimum value in one step, you can use the following: - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(23); -let bar = 12; -let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar); -assert_eq!(min_foo, 12); -```"), - #[inline] - #[stable(feature = "atomic_min_max", since = "1.45.0")] - #[$cfg_cas] - pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { $min_fn(self.v.get(), val, order) } - } - } - - doc_comment! { - concat!("Returns a mutable pointer to the underlying integer. - -Doing non-atomic reads and writes on the resulting integer can be a data race. -This method is mostly useful for FFI, where the function signature may use -`*mut ", stringify!($int_type), "` instead of `&", stringify!($atomic_type), "`. - -Returning an `*mut` pointer from a shared reference to this atomic is safe because the -atomic types work with interior mutability. All modifications of an atomic change the value -through a shared reference, and can do so safely as long as they use atomic operations. Any -use of the returned raw pointer requires an `unsafe` block and still has to uphold the same -restriction: operations on it must be atomic. - -# Examples - -```ignore (extern-declaration) -# fn main() { -", $extra_feature, "use std::sync::atomic::", stringify!($atomic_type), "; - -extern { - fn my_atomic_op(arg: *mut ", stringify!($int_type), "); -} - -let mut atomic = ", stringify!($atomic_type), "::new(1); -", -// SAFETY: Safe as long as `my_atomic_op` is atomic. -"unsafe { - my_atomic_op(atomic.as_mut_ptr()); -} -# } -```"), - #[inline] - #[unstable(feature = "atomic_mut_ptr", - reason = "recently added", - issue = "66893")] - pub fn as_mut_ptr(&self) -> *mut $int_type { - self.v.get() - } + /// Returns a mutable pointer to the underlying integer. + /// + /// Doing non-atomic reads and writes on the resulting integer can be a data race. + /// This method is mostly useful for FFI, where the function signature may use + #[doc = concat!("`*mut ", stringify!($int_type), "` instead of `&", stringify!($atomic_type), "`.")] + /// + /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the + /// atomic types work with interior mutability. All modifications of an atomic change the value + /// through a shared reference, and can do so safely as long as they use atomic operations. Any + /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same + /// restriction: operations on it must be atomic. + /// + /// # Examples + /// + /// ```ignore (extern-declaration) + /// # fn main() { + #[doc = concat!($extra_feature, "use std::sync::atomic::", stringify!($atomic_type), ";")] + /// + /// extern { + #[doc = concat!(" fn my_atomic_op(arg: *mut ", stringify!($int_type), ");")] + /// } + /// + #[doc = concat!("let mut atomic = ", stringify!($atomic_type), "::new(1);")] + /// + // SAFETY: Safe as long as `my_atomic_op` is atomic. + /// unsafe { + /// my_atomic_op(atomic.as_mut_ptr()); + /// } + /// # } + /// ``` + #[inline] + #[unstable(feature = "atomic_mut_ptr", + reason = "recently added", + issue = "66893")] + pub fn as_mut_ptr(&self) -> *mut $int_type { + self.v.get() } } } diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs index 75528ebb54ea..2d1e4496aeef 100644 --- a/library/core/tests/atomic.rs +++ b/library/core/tests/atomic.rs @@ -4,11 +4,11 @@ use core::sync::atomic::*; #[test] fn bool_() { let a = AtomicBool::new(false); - assert_eq!(a.compare_and_swap(false, true, SeqCst), false); - assert_eq!(a.compare_and_swap(false, true, SeqCst), true); + assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Ok(false)); + assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Err(true)); a.store(false, SeqCst); - assert_eq!(a.compare_and_swap(false, true, SeqCst), false); + assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Ok(false)); } #[test] diff --git a/library/core/tests/const_ptr.rs b/library/core/tests/const_ptr.rs new file mode 100644 index 000000000000..4acd059ab03d --- /dev/null +++ b/library/core/tests/const_ptr.rs @@ -0,0 +1,51 @@ +// Aligned to two bytes +const DATA: [u16; 2] = [u16::from_ne_bytes([0x01, 0x23]), u16::from_ne_bytes([0x45, 0x67])]; + +const fn unaligned_ptr() -> *const u16 { + // Since DATA.as_ptr() is aligned to two bytes, adding 1 byte to that produces an unaligned *const u16 + unsafe { (DATA.as_ptr() as *const u8).add(1) as *const u16 } +} + +#[test] +fn read() { + use core::ptr; + + const FOO: i32 = unsafe { ptr::read(&42 as *const i32) }; + assert_eq!(FOO, 42); + + const ALIGNED: i32 = unsafe { ptr::read_unaligned(&42 as *const i32) }; + assert_eq!(ALIGNED, 42); + + const UNALIGNED_PTR: *const u16 = unaligned_ptr(); + + const UNALIGNED: u16 = unsafe { ptr::read_unaligned(UNALIGNED_PTR) }; + assert_eq!(UNALIGNED, u16::from_ne_bytes([0x23, 0x45])); +} + +#[test] +fn const_ptr_read() { + const FOO: i32 = unsafe { (&42 as *const i32).read() }; + assert_eq!(FOO, 42); + + const ALIGNED: i32 = unsafe { (&42 as *const i32).read_unaligned() }; + assert_eq!(ALIGNED, 42); + + const UNALIGNED_PTR: *const u16 = unaligned_ptr(); + + const UNALIGNED: u16 = unsafe { UNALIGNED_PTR.read_unaligned() }; + assert_eq!(UNALIGNED, u16::from_ne_bytes([0x23, 0x45])); +} + +#[test] +fn mut_ptr_read() { + const FOO: i32 = unsafe { (&42 as *const i32 as *mut i32).read() }; + assert_eq!(FOO, 42); + + const ALIGNED: i32 = unsafe { (&42 as *const i32 as *mut i32).read_unaligned() }; + assert_eq!(ALIGNED, 42); + + const UNALIGNED_PTR: *mut u16 = unaligned_ptr() as *mut u16; + + const UNALIGNED: u16 = unsafe { UNALIGNED_PTR.read_unaligned() }; + assert_eq!(UNALIGNED, u16::from_ne_bytes([0x23, 0x45])); +} diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs index ec4b49da384c..7376e7848eff 100644 --- a/library/core/tests/iter.rs +++ b/library/core/tests/iter.rs @@ -3505,3 +3505,85 @@ pub fn extend_for_unit() { } assert_eq!(x, 5); } + +#[test] +fn test_intersperse() { + let xs = ["a", "", "b", "c"]; + let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect(); + let text: String = v.concat(); + assert_eq!(text, "a, , b, c".to_string()); + + let ys = [0, 1, 2, 3]; + let mut it = ys[..0].iter().map(|x| *x).intersperse(1); + assert!(it.next() == None); +} + +#[test] +fn test_intersperse_size_hint() { + let xs = ["a", "", "b", "c"]; + let mut iter = xs.iter().map(|x| x.clone()).intersperse(", "); + assert_eq!(iter.size_hint(), (7, Some(7))); + + assert_eq!(iter.next(), Some("a")); + assert_eq!(iter.size_hint(), (6, Some(6))); + assert_eq!(iter.next(), Some(", ")); + assert_eq!(iter.size_hint(), (5, Some(5))); + + assert_eq!([].iter().intersperse(&()).size_hint(), (0, Some(0))); +} + +#[test] +fn test_fold_specialization_intersperse() { + let mut iter = (1..2).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); + + let mut iter = (1..3).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); + + let mut iter = (1..4).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); +} + +#[test] +fn test_try_fold_specialization_intersperse_ok() { + let mut iter = (1..2).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); + + let mut iter = (1..3).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); + + let mut iter = (1..4).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); +} + +#[test] +fn test_try_fold_specialization_intersperse_err() { + let orig_iter = ["a", "b"].iter().copied().intersperse("-"); + + // Abort after the first item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|_| None::<()>); + assert_eq!(iter.next(), Some("-")); + assert_eq!(iter.next(), Some("b")); + assert_eq!(iter.next(), None); + + // Abort after the second item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|item| if item == "-" { None } else { Some(()) }); + assert_eq!(iter.next(), Some("b")); + assert_eq!(iter.next(), None); + + // Abort after the third item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|item| if item == "b" { None } else { Some(()) }); + assert_eq!(iter.next(), None); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 106c9fe5da3e..e01aaa4cbf17 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -8,9 +8,13 @@ #![feature(bound_cloned)] #![feature(box_syntax)] #![feature(cell_update)] +#![feature(cfg_panic)] #![feature(cfg_target_has_atomic)] #![feature(const_assume)] #![feature(const_cell_into_inner)] +#![feature(const_maybe_uninit_assume_init)] +#![feature(const_ptr_read)] +#![feature(const_ptr_offset)] #![feature(core_intrinsics)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] @@ -32,6 +36,8 @@ #![feature(raw)] #![feature(sort_internals)] #![feature(slice_partition_at_index)] +#![feature(maybe_uninit_extra)] +#![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(step_trait)] #![feature(step_trait_ext)] @@ -45,6 +51,7 @@ #![feature(array_value_iter)] #![feature(iter_advance_by)] #![feature(iter_partition_in_place)] +#![feature(iter_intersperse)] #![feature(iter_is_partitioned)] #![feature(iter_order_by)] #![feature(cmp_min_max_by)] @@ -65,6 +72,7 @@ #![feature(nonzero_leading_trailing_zeros)] #![feature(const_option)] #![feature(integer_atomics)] +#![feature(slice_group_by)] #![deny(unsafe_op_in_unsafe_fn)] extern crate test; @@ -79,6 +87,10 @@ mod cell; mod char; mod clone; mod cmp; + +#[cfg(not(bootstrap))] +mod const_ptr; + mod fmt; mod hash; mod intrinsics; diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index 59588d97787b..79ca2bba4038 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -1,5 +1,8 @@ use core::mem::*; +#[cfg(panic = "unwind")] +use std::rc::Rc; + #[test] fn size_of_basic() { assert_eq!(size_of::(), 1); @@ -129,3 +132,144 @@ fn test_discriminant_send_sync() { is_send_sync::>(); is_send_sync::>(); } + +#[test] +fn assume_init_good() { + const TRUE: bool = unsafe { MaybeUninit::::new(true).assume_init() }; + + assert!(TRUE); +} + +#[test] +fn uninit_write_slice() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 64]; + + assert_eq!(MaybeUninit::write_slice(&mut dst, &src), &src); +} + +#[test] +#[should_panic(expected = "source slice length (32) does not match destination slice length (64)")] +fn uninit_write_slice_panic_lt() { + let mut dst = [MaybeUninit::uninit(); 64]; + let src = [0; 32]; + + MaybeUninit::write_slice(&mut dst, &src); +} + +#[test] +#[should_panic(expected = "source slice length (128) does not match destination slice length (64)")] +fn uninit_write_slice_panic_gt() { + let mut dst = [MaybeUninit::uninit(); 64]; + let src = [0; 128]; + + MaybeUninit::write_slice(&mut dst, &src); +} + +#[test] +fn uninit_clone_from_slice() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 64]; + + assert_eq!(MaybeUninit::write_slice_cloned(&mut dst, &src), &src); +} + +#[test] +#[should_panic(expected = "destination and source slices have different lengths")] +fn uninit_write_slice_cloned_panic_lt() { + let mut dst = [MaybeUninit::uninit(); 64]; + let src = [0; 32]; + + MaybeUninit::write_slice_cloned(&mut dst, &src); +} + +#[test] +#[should_panic(expected = "destination and source slices have different lengths")] +fn uninit_write_slice_cloned_panic_gt() { + let mut dst = [MaybeUninit::uninit(); 64]; + let src = [0; 128]; + + MaybeUninit::write_slice_cloned(&mut dst, &src); +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_write_slice_cloned_mid_panic() { + use std::panic; + + enum IncrementOrPanic { + Increment(Rc<()>), + ExpectedPanic, + UnexpectedPanic, + } + + impl Clone for IncrementOrPanic { + fn clone(&self) -> Self { + match self { + Self::Increment(rc) => Self::Increment(rc.clone()), + Self::ExpectedPanic => panic!("expected panic on clone"), + Self::UnexpectedPanic => panic!("unexpected panic on clone"), + } + } + } + + let rc = Rc::new(()); + + let mut dst = [ + MaybeUninit::uninit(), + MaybeUninit::uninit(), + MaybeUninit::uninit(), + MaybeUninit::uninit(), + ]; + + let src = [ + IncrementOrPanic::Increment(rc.clone()), + IncrementOrPanic::Increment(rc.clone()), + IncrementOrPanic::ExpectedPanic, + IncrementOrPanic::UnexpectedPanic, + ]; + + let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { + MaybeUninit::write_slice_cloned(&mut dst, &src); + })); + + drop(src); + + match err { + Ok(_) => unreachable!(), + Err(payload) => { + payload + .downcast::<&'static str>() + .and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) }) + .unwrap_or_else(|p| panic::resume_unwind(p)); + + assert_eq!(Rc::strong_count(&rc), 1) + } + } +} + +#[test] +fn uninit_write_slice_cloned_no_drop() { + #[derive(Clone)] + struct Bomb; + + impl Drop for Bomb { + fn drop(&mut self) { + panic!("dropped a bomb! kaboom") + } + } + + let mut dst = [MaybeUninit::uninit()]; + let src = [Bomb]; + + MaybeUninit::write_slice_cloned(&mut dst, &src); + + forget(src); +} + +#[test] +#[cfg(not(bootstrap))] +fn uninit_const_assume_init_read() { + const FOO: u32 = unsafe { MaybeUninit::new(42).assume_init_read() }; + assert_eq!(FOO, 42); +} diff --git a/library/core/tests/nonzero.rs b/library/core/tests/nonzero.rs index b66c482c5e5e..c2c08522d0ca 100644 --- a/library/core/tests/nonzero.rs +++ b/library/core/tests/nonzero.rs @@ -312,3 +312,19 @@ fn nonzero_trailing_zeros() { const TRAILING_ZEROS: u32 = NonZeroU16::new(1 << 2).unwrap().trailing_zeros(); assert_eq!(TRAILING_ZEROS, 2); } + +#[test] +fn test_nonzero_uint_div() { + let nz = NonZeroU32::new(1).unwrap(); + + let x: u32 = 42u32 / nz; + assert_eq!(x, 42u32); +} + +#[test] +fn test_nonzero_uint_rem() { + let nz = NonZeroU32::new(10).unwrap(); + + let x: u32 = 42u32 % nz; + assert_eq!(x, 2u32); +} diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index a8ebb4b3219f..eb2277d8baac 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -14,11 +14,13 @@ #![feature(core_intrinsics)] #![feature(nll)] #![feature(panic_runtime)] +#![feature(std_internals)] #![feature(staged_api)] #![feature(rustc_attrs)] #![feature(asm)] use core::any::Any; +use core::panic::BoxMeUp; #[rustc_std_internal_symbol] #[allow(improper_ctypes_definitions)] @@ -28,7 +30,7 @@ pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Sen // "Leak" the payload and shim to the relevant abort on the platform in question. #[rustc_std_internal_symbol] -pub unsafe extern "C" fn __rust_start_panic(_payload: usize) -> u32 { +pub unsafe extern "C" fn __rust_start_panic(_payload: *mut &mut dyn BoxMeUp) -> u32 { abort(); cfg_if::cfg_if! { diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 0b74a844fec6..9ce9c477ec0f 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -104,9 +104,8 @@ pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any // implementation. #[rustc_std_internal_symbol] #[unwind(allowed)] -pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 { - let payload = payload as *mut &mut dyn BoxMeUp; - let payload = (*payload).take_box(); +pub unsafe extern "C" fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32 { + let payload = Box::from_raw((*payload).take_box()); - imp::panic(Box::from_raw(payload)) + imp::panic(payload) } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index ca40293ed582..a89e7b53e43c 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -28,8 +28,7 @@ #![feature(extern_types)] #![feature(in_band_lifetimes)] #![feature(negative_impls)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(min_specialization)] diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index b64f13dba4c2..c6a4548ec0c2 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -14,8 +14,7 @@ #![feature(no_core)] #![feature(lang_items)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index 18ee7c19ba0f..d5aca80edf9e 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -2,8 +2,7 @@ #![feature(no_core)] #![feature(lang_items)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![crate_type = "rlib"] #![no_core] diff --git a/library/rustc-std-workspace-core/README.md b/library/rustc-std-workspace-core/README.md index 9c2b1fa91d3b..40e0b62afabf 100644 --- a/library/rustc-std-workspace-core/README.md +++ b/library/rustc-std-workspace-core/README.md @@ -4,12 +4,12 @@ This crate is a shim and empty crate which simply depends on `libcore` and reexports all of its contents. The crate is the crux of empowering the standard library to depend on crates from crates.io -Crates on crates.io that the standard library depend on the -`rustc-std-workspace-core` crate from crates.io. On crates.io, however, this -crate is empty. We use `[patch]` to override it to this crate in this -repository. As a result, crates on crates.io will draw a dependency edge to -`libcore`, the version defined in this repository. That should draw all the -dependency edges to ensure Cargo builds crates successfully! +Crates on crates.io that the standard library depend on need to depend on the +`rustc-std-workspace-core` crate from crates.io, which is empty. We use +`[patch]` to override it to this crate in this repository. As a result, crates +on crates.io will draw a dependency edge to `libcore`, the version defined in +this repository. That should draw all the dependency edges to ensure Cargo +builds crates successfully! Note that crates on crates.io need to depend on this crate with the name `core` for everything to work correctly. To do that they can use: diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 819d57a934dc..8491ff400335 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -1,4 +1,4 @@ -//! Memory allocation APIs +//! Memory allocation APIs. //! //! In a given program, the standard library has one “global” memory allocator //! that is used for example by `Box` and `Vec`. diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 27d90e661374..0680b1fc3297 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -195,7 +195,6 @@ use crate::sys; /// // use the values stored in map /// ``` -#[derive(Clone)] #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashMap { @@ -449,6 +448,7 @@ impl HashMap { /// a.insert(1, "a"); /// assert_eq!(a.len(), 1); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { self.base.len() @@ -1029,6 +1029,24 @@ where } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for HashMap +where + K: Clone, + V: Clone, + S: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Self { base: self.base.clone() } + } + + #[inline] + fn clone_from(&mut self, other: &Self) { + self.base.clone_from(&other.base); + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for HashMap where @@ -2219,14 +2237,16 @@ impl<'a, K, V> Entry<'a, K, V> { } } - /// Ensures a value is in the entry by inserting, if empty, the result of the default function, - /// which takes the key as its argument, and returns a mutable reference to the value in the - /// entry. + /// Ensures a value is in the entry by inserting, if empty, the result of the default function. + /// This method allows for generating key-derived values for insertion by providing the default + /// function a reference to the key that was moved during the `.entry(key)` method call. + /// + /// The reference to the moved key is provided so that cloning or copying the key is + /// unnecessary, unlike with `.or_insert_with(|| ... )`. /// /// # Examples /// /// ``` - /// #![feature(or_insert_with_key)] /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, usize> = HashMap::new(); @@ -2236,7 +2256,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// assert_eq!(map["poneyland"], 9); /// ``` #[inline] - #[unstable(feature = "or_insert_with_key", issue = "71024")] + #[stable(feature = "or_insert_with_key", since = "1.50.0")] pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 3299fd12e024..f49e5801c353 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -106,7 +106,6 @@ use super::map::{map_try_reserve_error, RandomState}; /// [`HashMap`]: crate::collections::HashMap /// [`RefCell`]: crate::cell::RefCell /// [`Cell`]: crate::cell::Cell -#[derive(Clone)] #[cfg_attr(not(test), rustc_diagnostic_item = "hashset_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashSet { @@ -200,6 +199,7 @@ impl HashSet { /// v.insert(1); /// assert_eq!(v.len(), 1); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { @@ -932,6 +932,23 @@ where } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for HashSet +where + T: Clone, + S: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Self { base: self.base.clone() } + } + + #[inline] + fn clone_from(&mut self, other: &Self) { + self.base.clone_from(&other.base); + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for HashSet where diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 09a9b184e3a4..c30458c0545d 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -206,8 +206,10 @@ impl f32 { /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error, yielding a more accurate result than an unfused multiply-add. /// - /// Using `mul_add` can be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. /// /// # Examples /// @@ -877,39 +879,4 @@ impl f32 { pub fn atanh(self) -> f32 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } - - /// Restrict a value to a certain interval unless it is NaN. - /// - /// Returns `max` if `self` is greater than `max`, and `min` if `self` is - /// less than `min`. Otherwise this returns `self`. - /// - /// Note that this function returns NaN if the initial value was NaN as - /// well. - /// - /// # Panics - /// - /// Panics if `min > max`, `min` is NaN, or `max` is NaN. - /// - /// # Examples - /// - /// ``` - /// assert!((-3.0f32).clamp(-2.0, 1.0) == -2.0); - /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0); - /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0); - /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "clamp", since = "1.50.0")] - #[inline] - pub fn clamp(self, min: f32, max: f32) -> f32 { - assert!(min <= max); - let mut x = self; - if x < min { - x = min; - } - if x > max { - x = max; - } - x - } } diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 64bb7cd9fd16..f4cc53979d1a 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -206,8 +206,10 @@ impl f64 { /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error, yielding a more accurate result than an unfused multiply-add. /// - /// Using `mul_add` can be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. /// /// # Examples /// @@ -880,41 +882,6 @@ impl f64 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } - /// Restrict a value to a certain interval unless it is NaN. - /// - /// Returns `max` if `self` is greater than `max`, and `min` if `self` is - /// less than `min`. Otherwise this returns `self`. - /// - /// Note that this function returns NaN if the initial value was NaN as - /// well. - /// - /// # Panics - /// - /// Panics if `min > max`, `min` is NaN, or `max` is NaN. - /// - /// # Examples - /// - /// ``` - /// assert!((-3.0f64).clamp(-2.0, 1.0) == -2.0); - /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0); - /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0); - /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "clamp", since = "1.50.0")] - #[inline] - pub fn clamp(self, min: f64, max: f64) -> f64 { - assert!(min <= max); - let mut x = self; - if x < min { - x = min; - } - if x > max { - x = max; - } - x - } - // Solaris/Illumos requires a wrapper around log, log2, and log10 functions // because of their non-standard behavior (e.g., log(-n) returns -Inf instead // of expected NaN). diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 7e7a28be2b0e..2eef4d58507c 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -653,6 +653,7 @@ impl OsStr { /// let os_str = OsStr::new("foo"); /// assert_eq!(os_str.len(), 3); /// ``` + #[doc(alias = "length")] #[stable(feature = "osstring_simple_functions", since = "1.9.0")] pub fn len(&self) -> usize { self.inner.inner.len() @@ -667,10 +668,10 @@ impl OsStr { /// Gets the underlying byte representation. /// - /// Note: it is *crucial* that this API is private, to avoid + /// Note: it is *crucial* that this API is not externally public, to avoid /// revealing the internal, platform-specific encodings. #[inline] - fn bytes(&self) -> &[u8] { + pub(crate) fn bytes(&self) -> &[u8] { unsafe { &*(&self.inner as *const _ as *const [u8]) } } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 6ea7704d4221..1160011f3528 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -9,6 +9,7 @@ use crate::cell::{Cell, RefCell}; use crate::fmt; use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter}; use crate::lazy::SyncOnceCell; +use crate::pin::Pin; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard}; use crate::sys::stdio; @@ -490,7 +491,7 @@ pub struct Stdout { // FIXME: this should be LineWriter or BufWriter depending on the state of // stdout (tty or not). Note that if this is not line buffered it // should also flush-on-panic or some form of flush-on-abort. - inner: &'static ReentrantMutex>>, + inner: Pin<&'static ReentrantMutex>>>, } /// A locked reference to the `Stdout` handle. @@ -550,25 +551,29 @@ pub struct StdoutLock<'a> { pub fn stdout() -> Stdout { static INSTANCE: SyncOnceCell>>> = SyncOnceCell::new(); + + fn cleanup() { + if let Some(instance) = INSTANCE.get() { + // Flush the data and disable buffering during shutdown + // by replacing the line writer by one with zero + // buffering capacity. + // We use try_lock() instead of lock(), because someone + // might have leaked a StdoutLock, which would + // otherwise cause a deadlock here. + if let Some(lock) = Pin::static_ref(instance).try_lock() { + *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); + } + } + } + Stdout { - inner: INSTANCE.get_or_init(|| unsafe { - let _ = sys_common::at_exit(|| { - if let Some(instance) = INSTANCE.get() { - // Flush the data and disable buffering during shutdown - // by replacing the line writer by one with zero - // buffering capacity. - // We use try_lock() instead of lock(), because someone - // might have leaked a StdoutLock, which would - // otherwise cause a deadlock here. - if let Some(lock) = instance.try_lock() { - *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); - } - } - }); - let r = ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))); - r.init(); - r - }), + inner: Pin::static_ref(&INSTANCE).get_or_init_pin( + || unsafe { + let _ = sys_common::at_exit(cleanup); + ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) + }, + |mutex| unsafe { mutex.init() }, + ), } } @@ -700,7 +705,7 @@ impl fmt::Debug for StdoutLock<'_> { /// an error. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { - inner: &'static ReentrantMutex>, + inner: Pin<&'static ReentrantMutex>>, } /// A locked reference to the `Stderr` handle. @@ -756,21 +761,16 @@ pub struct StderrLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stderr() -> Stderr { - // Note that unlike `stdout()` we don't use `Lazy` here which registers a - // destructor. Stderr is not buffered nor does the `stderr_raw` type consume - // any owned resources, so there's no need to run any destructors at some - // point in the future. - // - // This has the added benefit of allowing `stderr` to be usable during - // process shutdown as well! + // Note that unlike `stdout()` we don't use `at_exit` here to register a + // destructor. Stderr is not buffered , so there's no need to run a + // destructor for flushing the buffer static INSTANCE: SyncOnceCell>> = SyncOnceCell::new(); Stderr { - inner: INSTANCE.get_or_init(|| unsafe { - let r = ReentrantMutex::new(RefCell::new(stderr_raw())); - r.init(); - r - }), + inner: Pin::static_ref(&INSTANCE).get_or_init_pin( + || unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) }, + |mutex| unsafe { mutex.init() }, + ), } } diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index dad3add5c55c..417a54e9dffb 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -565,8 +565,12 @@ mod fn_keyword {} /// /// For more information on for-loops, see the [Rust book] or the [Reference]. /// +/// See also, [`loop`], [`while`]. +/// /// [`in`]: keyword.in.html /// [`impl`]: keyword.impl.html +/// [`loop`]: keyword.loop.html +/// [`while`]: keyword.while.html /// [higher-ranked trait bounds]: ../reference/trait-bounds.html#higher-ranked-trait-bounds /// [Rust book]: /// ../book/ch03-05-control-flow.html#looping-through-a-collection-with-for @@ -842,6 +846,8 @@ mod let_keyword {} /// /// For more information on `while` and loops in general, see the [reference]. /// +/// See also, [`for`], [`loop`]. +/// /// [`for`]: keyword.for.html /// [`loop`]: keyword.loop.html /// [reference]: ../reference/expressions/loop-expr.html#predicate-loops @@ -890,6 +896,10 @@ mod while_keyword {} /// /// For more information on `loop` and loops in general, see the [Reference]. /// +/// See also, [`for`], [`while`]. +/// +/// [`for`]: keyword.for.html +/// [`while`]: keyword.while.html /// [Reference]: ../reference/expressions/loop-expr.html mod loop_keyword {} @@ -2186,6 +2196,7 @@ mod where_keyword {} // 2018 Edition keywords +#[doc(alias = "promise")] #[doc(keyword = "async")] // /// Return a [`Future`] instead of blocking the current thread. diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs index e0095e64faf3..68f57958bb23 100644 --- a/library/std/src/lazy.rs +++ b/library/std/src/lazy.rs @@ -10,6 +10,7 @@ use crate::{ mem::MaybeUninit, ops::{Deref, Drop}, panic::{RefUnwindSafe, UnwindSafe}, + pin::Pin, sync::Once, }; @@ -297,6 +298,60 @@ impl SyncOnceCell { Ok(unsafe { self.get_unchecked() }) } + /// Internal-only API that gets the contents of the cell, initializing it + /// in two steps with `f` and `g` if the cell was empty. + /// + /// `f` is called to construct the value, which is then moved into the cell + /// and given as a (pinned) mutable reference to `g` to finish + /// initialization. + /// + /// This allows `g` to inspect an manipulate the value after it has been + /// moved into its final place in the cell, but before the cell is + /// considered initialized. + /// + /// # Panics + /// + /// If `f` or `g` panics, the panic is propagated to the caller, and the + /// cell remains uninitialized. + /// + /// With the current implementation, if `g` panics, the value from `f` will + /// not be dropped. This should probably be fixed if this is ever used for + /// a type where this matters. + /// + /// It is an error to reentrantly initialize the cell from `f`. The exact + /// outcome is unspecified. Current implementation deadlocks, but this may + /// be changed to a panic in the future. + pub(crate) fn get_or_init_pin(self: Pin<&Self>, f: F, g: G) -> Pin<&T> + where + F: FnOnce() -> T, + G: FnOnce(Pin<&mut T>), + { + if let Some(value) = self.get_ref().get() { + // SAFETY: The inner value was already initialized, and will not be + // moved anymore. + return unsafe { Pin::new_unchecked(value) }; + } + + let slot = &self.value; + + // Ignore poisoning from other threads + // If another thread panics, then we'll be able to run our closure + self.once.call_once_force(|_| { + let value = f(); + // SAFETY: We use the Once (self.once) to guarantee unique access + // to the UnsafeCell (slot). + let value: &mut T = unsafe { (&mut *slot.get()).write(value) }; + // SAFETY: The value has been written to its final place in + // self.value. We do not to move it anymore, which we promise here + // with a Pin<&mut T>. + g(unsafe { Pin::new_unchecked(value) }); + }); + + // SAFETY: The inner value has been initialized, and will not be moved + // anymore. + unsafe { Pin::new_unchecked(self.get_ref().get_unchecked()) } + } + /// Consumes the `SyncOnceCell`, returning the wrapped value. Returns /// `None` if the cell was empty. /// diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6c240cb4c3ed..2c6f03fe2240 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -175,7 +175,7 @@ //! [`str`]: prim@str //! [`mpsc`]: sync::mpsc //! [`std::cmp`]: cmp -//! [`std::slice`]: slice +//! [`std::slice`]: mod@slice //! [`use std::env`]: env/index.html //! [`use`]: ../book/ch07-02-defining-modules-to-control-scope-and-privacy.html //! [crates.io]: https://crates.io @@ -185,7 +185,8 @@ //! [other]: #what-is-in-the-standard-library-documentation //! [primitive types]: ../book/ch03-02-data-types.html //! [rust-discord]: https://discord.gg/rust-lang - +#![cfg_attr(not(bootstrap), doc = "[array]: prim@array")] +#![cfg_attr(not(bootstrap), doc = "[slice]: prim@slice")] #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] #![doc( @@ -266,6 +267,7 @@ #![feature(format_args_nl)] #![feature(gen_future)] #![feature(generator_trait)] +#![feature(get_mut_unchecked)] #![feature(global_asm)] #![feature(hashmap_internals)] #![feature(int_error_internals)] @@ -287,12 +289,12 @@ #![feature(nll)] #![feature(nonnull_slice_from_raw_parts)] #![feature(once_cell)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(or_patterns)] #![feature(panic_info_message)] #![feature(panic_internals)] #![feature(panic_unwind)] +#![feature(pin_static_ref)] #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] @@ -302,7 +304,6 @@ #![feature(rustc_private)] #![feature(shrink_to)] #![feature(slice_concat_ext)] -#![feature(slice_fill)] #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] @@ -312,6 +313,7 @@ #![feature(stdsimd)] #![feature(stmt_expr_attributes)] #![feature(str_internals)] +#![feature(str_split_once)] #![feature(test)] #![feature(thread_local)] #![feature(thread_local_internals)] @@ -322,7 +324,6 @@ #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(unsafe_block_in_unsafe_fn)] -#![feature(unsafe_cell_get_mut)] #![feature(unsafe_cell_raw_get)] #![feature(unwind_attributes)] #![feature(vec_into_raw_parts)] diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index de072e83dfc4..5a70aa070e87 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -8,7 +8,7 @@ #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(libstd_sys_internals)] -#[cfg_attr(not(any(bootstrap, test)), rustc_diagnostic_item = "std_panic_macro")] +#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")] macro_rules! panic { () => ({ $crate::panic!("explicit panic") }); ($msg:expr $(,)?) => ({ $crate::rt::begin_panic($msg) }); diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index 87bbd33bc014..d33b772633d2 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -148,7 +148,7 @@ impl IpAddr { /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true); /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true); /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] + #[rustc_const_stable(feature = "const_ip", since = "1.50.0")] #[stable(feature = "ip_shared", since = "1.12.0")] pub const fn is_unspecified(&self) -> bool { match self { @@ -170,7 +170,7 @@ impl IpAddr { /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true); /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true); /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] + #[rustc_const_stable(feature = "const_ip", since = "1.50.0")] #[stable(feature = "ip_shared", since = "1.12.0")] pub const fn is_loopback(&self) -> bool { match self { @@ -215,7 +215,7 @@ impl IpAddr { /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true); /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true); /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] + #[rustc_const_stable(feature = "const_ip", since = "1.50.0")] #[stable(feature = "ip_shared", since = "1.12.0")] pub const fn is_multicast(&self) -> bool { match self { @@ -301,8 +301,8 @@ impl Ipv4Addr { /// /// let addr = Ipv4Addr::new(127, 0, 0, 1); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")] + #[stable(feature = "rust1", since = "1.0.0")] pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { // `s_addr` is stored as BE on all machine and the array is in BE order. // So the native endian conversion method is used so that it's never swapped. @@ -358,7 +358,7 @@ impl Ipv4Addr { /// let addr = Ipv4Addr::new(127, 0, 0, 1); /// assert_eq!(addr.octets(), [127, 0, 0, 1]); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] pub const fn octets(&self) -> [u8; 4] { // This returns the order we want because s_addr is stored in big-endian. @@ -380,8 +380,8 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true); /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false); /// ``` - #[stable(feature = "ip_shared", since = "1.12.0")] #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")] + #[stable(feature = "ip_shared", since = "1.12.0")] pub const fn is_unspecified(&self) -> bool { self.inner.s_addr == 0 } @@ -400,7 +400,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true); /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_loopback(&self) -> bool { self.octets()[0] == 127 @@ -429,7 +429,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true); /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_private(&self) -> bool { match self.octets() { @@ -455,7 +455,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true); /// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_link_local(&self) -> bool { matches!(self.octets(), [169, 254, ..]) @@ -675,7 +675,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true); /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_multicast(&self) -> bool { self.octets()[0] >= 224 && self.octets()[0] <= 239 @@ -695,7 +695,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true); /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_broadcast(&self) -> bool { u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) @@ -721,7 +721,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true); /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_documentation(&self) -> bool { match self.octets() { @@ -751,7 +751,7 @@ impl Ipv4Addr { /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 767) /// ); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); @@ -774,7 +774,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(), /// Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 49152, 767)); /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); @@ -1043,9 +1043,9 @@ impl Ipv6Addr { /// /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] + #[stable(feature = "rust1", since = "1.0.0")] pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { let addr16 = [ a.to_be(), @@ -1061,6 +1061,8 @@ impl Ipv6Addr { inner: c::in6_addr { // All elements in `addr16` are big endian. // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. + // rustc_allow_const_fn_unstable: the transmute could be written as stable const + // code, but that leads to worse code generation (#75085) s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) }, }, } @@ -1102,11 +1104,14 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(), /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] pub const fn segments(&self) -> [u16; 8] { // All elements in `s6_addr` must be big endian. // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`. + // rustc_allow_const_fn_unstable: the transmute could be written as stable const code, but + // that leads to worse code generation (#75085) let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) }; // We want native endian u16 [ @@ -1135,7 +1140,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_unspecified(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) @@ -1155,7 +1160,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_loopback(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) @@ -1465,7 +1470,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true); /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_multicast(&self) -> bool { (self.segments()[0] & 0xff00) == 0xff00 @@ -1520,7 +1525,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(), /// Some(Ipv4Addr::new(0, 0, 0, 1))); /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] pub const fn to_ipv4(&self) -> Option { if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { @@ -1540,8 +1545,8 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(), /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); /// ``` - #[stable(feature = "ipv6_to_octets", since = "1.12.0")] #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] + #[stable(feature = "ipv6_to_octets", since = "1.12.0")] pub const fn octets(&self) -> [u8; 16] { self.inner.s6_addr } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8ba3feccb6bc..6cd572cbe87c 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -44,11 +44,11 @@ use realstd::io::set_output_capture; extern "C" { fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); - /// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings. - /// It cannot be `Box` because the other end of this call does not depend - /// on liballoc, and thus cannot use `Box`. + /// `payload` is passed through another layer of raw pointers as `&mut dyn Trait` is not + /// FFI-safe. `BoxMeUp` lazily performs allocation only when needed (this avoids allocations + /// when using the "abort" panic runtime). #[unwind(allowed)] - fn __rust_start_panic(payload: usize) -> u32; + fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32; } /// This function is called by the panic runtime if FFI code catches a Rust @@ -637,7 +637,7 @@ pub fn rust_panic_without_hook(payload: Box) -> ! { fn rust_panic(mut msg: &mut dyn BoxMeUp) -> ! { let code = unsafe { let obj = &mut msg as *mut &mut dyn BoxMeUp; - __rust_start_panic(obj as usize) + __rust_start_panic(obj) }; rtabort!("failed to initiate panic, error {}", code) } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index ff94fda5a227..896d6c2a64c6 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -873,12 +873,12 @@ pub fn test_decompositions_windows() { ); t!("\\\\.\\foo/bar", - iter: ["\\\\.\\foo/bar", "\\"], + iter: ["\\\\.\\foo", "\\", "bar"], has_root: true, is_absolute: true, - parent: None, - file_name: None, - file_stem: None, + parent: Some("\\\\.\\foo/"), + file_name: Some("bar"), + file_stem: Some("bar"), extension: None ); diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index c7a7c779d3c5..a3776681d034 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -26,38 +26,37 @@ //! # Prelude contents //! //! The current version of the prelude (version 1) lives in -//! [`std::prelude::v1`], and re-exports the following. +//! [`std::prelude::v1`], and re-exports the following: //! -//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}. The -//! marker traits indicate fundamental properties of types. -//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}. Various +//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}, +//! marker traits that indicate fundamental properties of types. +//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}, various //! operations for both destructors and overloading `()`. //! * [`std::mem`]::[`drop`][`mem::drop`], a convenience function for explicitly //! dropping a value. //! * [`std::boxed`]::[`Box`], a way to allocate values on the heap. -//! * [`std::borrow`]::[`ToOwned`], The conversion trait that defines +//! * [`std::borrow`]::[`ToOwned`], the conversion trait that defines //! [`to_owned`], the generic method for creating an owned type from a //! borrowed type. //! * [`std::clone`]::[`Clone`], the ubiquitous trait that defines //! [`clone`][`Clone::clone`], the method for producing a copy of a value. -//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] }. The +//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] }, the //! comparison traits, which implement the comparison operators and are often //! seen in trait bounds. -//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}. Generic +//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}, generic //! conversions, used by savvy API authors to create overloaded methods. //! * [`std::default`]::[`Default`], types that have default values. -//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`], -//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}. Iterators of various +//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`] +//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}, iterators of various //! kinds. -//! * [`std::option`]::[`Option`]::{[`self`][`Option`], [`Some`], [`None`]}. A +//! * [`std::option`]::[`Option`]::{[`self`][`Option`], [`Some`], [`None`]}, a //! type which expresses the presence or absence of a value. This type is so //! commonly used, its variants are also exported. -//! * [`std::result`]::[`Result`]::{[`self`][`Result`], [`Ok`], [`Err`]}. A type +//! * [`std::result`]::[`Result`]::{[`self`][`Result`], [`Ok`], [`Err`]}, a type //! for functions that may succeed or fail. Like [`Option`], its variants are //! exported as well. //! * [`std::string`]::{[`String`], [`ToString`]}, heap allocated strings. -//! * [`std::vec`]::[`Vec`], a growable, heap-allocated -//! vector. +//! * [`std::vec`]::[`Vec`], a growable, heap-allocated vector. //! //! [`mem::drop`]: crate::mem::drop //! [`std::borrow`]: crate::borrow diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 55171ef2292d..ec12e9f09d8b 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -478,8 +478,10 @@ mod prim_unit {} #[stable(feature = "rust1", since = "1.0.0")] mod prim_pointer {} +#[doc(alias = "[]")] +#[doc(alias = "[T;N]")] // unfortunately, rustdoc doesn't have fuzzy search for aliases +#[doc(alias = "[T; N]")] #[doc(primitive = "array")] -// /// A fixed-size array, denoted `[T; N]`, for the element type, `T`, and the /// non-negative compile-time constant size, `N`. /// @@ -489,6 +491,10 @@ mod prim_pointer {} /// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`. /// The type of `x` must be [`Copy`]. /// +/// Note that `[expr; 0]` is allowed, and produces an empty array. +/// This will still evaluate `expr`, however, and immediately drop the resulting value, so +/// be mindful of side effects. +/// /// Arrays of *any* size implement the following traits if the element type allows it: /// /// - [`Copy`] @@ -920,6 +926,7 @@ mod prim_usize {} #[doc(primitive = "reference")] #[doc(alias = "&")] +#[doc(alias = "&mut")] // /// References, both shared and mutable. /// diff --git a/library/std/src/sync/mpsc/blocking.rs b/library/std/src/sync/mpsc/blocking.rs index d34de6a4fac3..4c852b8ee812 100644 --- a/library/std/src/sync/mpsc/blocking.rs +++ b/library/std/src/sync/mpsc/blocking.rs @@ -36,7 +36,11 @@ pub fn tokens() -> (WaitToken, SignalToken) { impl SignalToken { pub fn signal(&self) -> bool { - let wake = !self.inner.woken.compare_and_swap(false, true, Ordering::SeqCst); + let wake = self + .inner + .woken + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_ok(); if wake { self.inner.thread.unpark(); } diff --git a/library/std/src/sync/mpsc/oneshot.rs b/library/std/src/sync/mpsc/oneshot.rs index 75f5621fa127..3dcf03f579a0 100644 --- a/library/std/src/sync/mpsc/oneshot.rs +++ b/library/std/src/sync/mpsc/oneshot.rs @@ -129,7 +129,7 @@ impl Packet { let ptr = unsafe { signal_token.cast_to_usize() }; // race with senders to enter the blocking state - if self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) == EMPTY { + if self.state.compare_exchange(EMPTY, ptr, Ordering::SeqCst, Ordering::SeqCst).is_ok() { if let Some(deadline) = deadline { let timed_out = !wait_token.wait_max_until(deadline); // Try to reset the state @@ -161,7 +161,12 @@ impl Packet { // the state changes under our feet we'd rather just see that state // change. DATA => { - self.state.compare_and_swap(DATA, EMPTY, Ordering::SeqCst); + let _ = self.state.compare_exchange( + DATA, + EMPTY, + Ordering::SeqCst, + Ordering::SeqCst, + ); match (&mut *self.data.get()).take() { Some(data) => Ok(data), None => unreachable!(), @@ -264,7 +269,10 @@ impl Packet { // If we've got a blocked thread, then use an atomic to gain ownership // of it (may fail) - ptr => self.state.compare_and_swap(ptr, EMPTY, Ordering::SeqCst), + ptr => self + .state + .compare_exchange(ptr, EMPTY, Ordering::SeqCst, Ordering::SeqCst) + .unwrap_or_else(|x| x), }; // Now that we've got ownership of our state, figure out what to do diff --git a/library/std/src/sync/mpsc/shared.rs b/library/std/src/sync/mpsc/shared.rs index 898654f21f2e..0c32e636a563 100644 --- a/library/std/src/sync/mpsc/shared.rs +++ b/library/std/src/sync/mpsc/shared.rs @@ -385,8 +385,15 @@ impl Packet { self.port_dropped.store(true, Ordering::SeqCst); let mut steals = unsafe { *self.steals.get() }; while { - let cnt = self.cnt.compare_and_swap(steals, DISCONNECTED, Ordering::SeqCst); - cnt != DISCONNECTED && cnt != steals + match self.cnt.compare_exchange( + steals, + DISCONNECTED, + Ordering::SeqCst, + Ordering::SeqCst, + ) { + Ok(_) => false, + Err(old) => old != DISCONNECTED, + } } { // See the discussion in 'try_recv' for why we yield // control of this thread. diff --git a/library/std/src/sync/mpsc/stream.rs b/library/std/src/sync/mpsc/stream.rs index 9f7c1af89519..a652f24c58a1 100644 --- a/library/std/src/sync/mpsc/stream.rs +++ b/library/std/src/sync/mpsc/stream.rs @@ -322,12 +322,15 @@ impl Packet { // (because there is a bounded number of senders). let mut steals = unsafe { *self.queue.consumer_addition().steals.get() }; while { - let cnt = self.queue.producer_addition().cnt.compare_and_swap( + match self.queue.producer_addition().cnt.compare_exchange( steals, DISCONNECTED, Ordering::SeqCst, - ); - cnt != DISCONNECTED && cnt != steals + Ordering::SeqCst, + ) { + Ok(_) => false, + Err(old) => old != DISCONNECTED, + } } { while self.queue.pop().is_some() { steals += 1; diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index de5ddf1daf27..6a330834489d 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -65,7 +65,7 @@ // must do so with Release ordering to make the result available. // - `wait` inserts `Waiter` nodes as a pointer in `state_and_queue`, and // needs to make the nodes available with Release ordering. The load in -// its `compare_and_swap` can be Relaxed because it only has to compare +// its `compare_exchange` can be Relaxed because it only has to compare // the atomic, not to read other data. // - `WaiterQueue::Drop` must see the `Waiter` nodes, so it must load // `state_and_queue` with Acquire ordering. @@ -110,7 +110,7 @@ use crate::thread::{self, Thread}; /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Once { - // `state_and_queue` is actually an a pointer to a `Waiter` with extra state + // `state_and_queue` is actually a pointer to a `Waiter` with extra state // bits, so we add the `PhantomData` appropriately. state_and_queue: AtomicUsize, _marker: marker::PhantomData<*const Waiter>, @@ -395,12 +395,13 @@ impl Once { } POISONED | INCOMPLETE => { // Try to register this thread as the one RUNNING. - let old = self.state_and_queue.compare_and_swap( + let exchange_result = self.state_and_queue.compare_exchange( state_and_queue, RUNNING, Ordering::Acquire, + Ordering::Acquire, ); - if old != state_and_queue { + if let Err(old) = exchange_result { state_and_queue = old; continue; } @@ -452,8 +453,13 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { // Try to slide in the node at the head of the linked list, making sure // that another thread didn't just replace the head of the linked list. - let old = state_and_queue.compare_and_swap(current_state, me | RUNNING, Ordering::Release); - if old != current_state { + let exchange_result = state_and_queue.compare_exchange( + current_state, + me | RUNNING, + Ordering::Release, + Ordering::Relaxed, + ); + if let Err(old) = exchange_result { current_state = old; continue; } diff --git a/library/std/src/sys/sgx/abi/mod.rs b/library/std/src/sys/sgx/abi/mod.rs index a0eb12c3d154..a5e453034762 100644 --- a/library/std/src/sys/sgx/abi/mod.rs +++ b/library/std/src/sys/sgx/abi/mod.rs @@ -36,20 +36,20 @@ unsafe extern "C" fn tcs_init(secondary: bool) { } // Try to atomically swap UNINIT with BUSY. The returned state can be: - match RELOC_STATE.compare_and_swap(UNINIT, BUSY, Ordering::Acquire) { + match RELOC_STATE.compare_exchange(UNINIT, BUSY, Ordering::Acquire, Ordering::Acquire) { // This thread just obtained the lock and other threads will observe BUSY - UNINIT => { + Ok(_) => { reloc::relocate_elf_rela(); RELOC_STATE.store(DONE, Ordering::Release); } // We need to wait until the initialization is done. - BUSY => { + Err(BUSY) => { while RELOC_STATE.load(Ordering::Acquire) == BUSY { core::hint::spin_loop(); } } // Initialization is done. - DONE => {} + Err(DONE) => {} _ => unreachable!(), } } diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs index d99ce895da59..9140041c5841 100644 --- a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs +++ b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs @@ -42,7 +42,7 @@ impl SpinMutex { #[inline(always)] pub fn try_lock(&self) -> Option> { - if !self.lock.compare_and_swap(false, true, Ordering::Acquire) { + if self.lock.compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire).is_ok() { Some(SpinMutexGuard { mutex: self }) } else { None diff --git a/library/std/src/sys/unix/ext/net/ancillary.rs b/library/std/src/sys/unix/ext/net/ancillary.rs index 2c91ba70dd0b..0964b6335aa7 100644 --- a/library/std/src/sys/unix/ext/net/ancillary.rs +++ b/library/std/src/sys/unix/ext/net/ancillary.rs @@ -360,7 +360,11 @@ impl<'a> AncillaryData<'a> { fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Result { unsafe { cfg_if::cfg_if! { - if #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] { + if #[cfg(any( + target_os = "android", + all(target_os = "linux", target_env = "gnu"), + all(target_os = "linux", target_env = "uclibc"), + ))] { let cmsg_len_zero = libc::CMSG_LEN(0) as libc::size_t; } else if #[cfg(any( target_os = "dragonfly", diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index d3a279a23553..821851a6c65b 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -12,6 +12,11 @@ use crate::sys_common::AsInner; use libc::{c_int, c_void}; #[derive(Debug)] +#[rustc_layout_scalar_valid_range_start(0)] +// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a +// 32-bit c_int. Below is -2, in two's complement, but that only works out +// because c_int is 32 bits. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] pub struct FileDesc { fd: c_int, } @@ -63,7 +68,9 @@ const fn max_iov() -> usize { impl FileDesc { pub fn new(fd: c_int) -> FileDesc { - FileDesc { fd } + assert_ne!(fd, -1i32); + // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) + unsafe { FileDesc { fd } } } pub fn raw(&self) -> c_int { diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs index a932043cbc62..c9520485c3c7 100644 --- a/library/std/src/sys/unix/fd/tests.rs +++ b/library/std/src/sys/unix/fd/tests.rs @@ -3,7 +3,7 @@ use core::mem::ManuallyDrop; #[test] fn limit_vector_count() { - let stdout = ManuallyDrop::new(FileDesc { fd: 1 }); + let stdout = ManuallyDrop::new(unsafe { FileDesc { fd: 1 } }); let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::>(); assert!(stdout.write_vectored(&bufs).is_ok()); } diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 5bfac8031534..200dbf06ff8a 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -59,7 +59,7 @@ use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use crate::os::unix::net::UnixStream; use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; #[cfg(test)] @@ -491,8 +491,15 @@ impl CopyResult { } } -/// linux-specific implementation that will attempt to use copy_file_range for copy offloading -/// as the name says, it only works on regular files +/// Invalid file descriptor. +/// +/// Valid file descriptors are guaranteed to be positive numbers (see `open()` manpage) +/// while negative values are used to indicate errors. +/// Thus -1 will never be overlap with a valid open file. +const INVALID_FD: RawFd = -1; + +/// Linux-specific implementation that will attempt to use copy_file_range for copy offloading. +/// As the name says, it only works on regular files. /// /// Callers must handle fallback to a generic copy loop. /// `Fallback` may indicate non-zero number of bytes already written @@ -500,9 +507,13 @@ impl CopyResult { pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> CopyResult { use crate::cmp; + const NOT_PROBED: u8 = 0; + const UNAVAILABLE: u8 = 1; + const AVAILABLE: u8 = 2; + // Kernel prior to 4.5 don't have copy_file_range // We store the availability in a global to avoid unnecessary syscalls - static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true); + static HAS_COPY_FILE_RANGE: AtomicU8 = AtomicU8::new(NOT_PROBED); syscall! { fn copy_file_range( @@ -515,39 +526,39 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> ) -> libc::ssize_t } - let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed); + match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) { + NOT_PROBED => { + // EPERM can indicate seccomp filters or an immutable file. + // To distinguish these cases we probe with invalid file descriptors which should result in EBADF if the syscall is supported + // and some other error (ENOSYS or EPERM) if it's not available + let result = unsafe { + cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) + }; + + if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(libc::EBADF))) { + HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed); + } else { + HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed); + return CopyResult::Fallback(0); + } + } + UNAVAILABLE => return CopyResult::Fallback(0), + _ => {} + }; + let mut written = 0u64; while written < max_len { - let copy_result = if has_copy_file_range { - let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64); - // cap to 1GB chunks in case u64::MAX is passed as max_len and the file has a non-zero seek position - // this allows us to copy large chunks without hitting EOVERFLOW, - // unless someone sets a file offset close to u64::MAX - 1GB, in which case a fallback would be required - let bytes_to_copy = cmp::min(bytes_to_copy as usize, 0x4000_0000usize); - let copy_result = unsafe { - // We actually don't have to adjust the offsets, - // because copy_file_range adjusts the file offset automatically - cvt(copy_file_range( - reader, - ptr::null_mut(), - writer, - ptr::null_mut(), - bytes_to_copy, - 0, - )) - }; - if let Err(ref copy_err) = copy_result { - match copy_err.raw_os_error() { - Some(libc::ENOSYS | libc::EPERM | libc::EOPNOTSUPP) => { - HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed); - } - _ => {} - } - } - copy_result - } else { - Err(Error::from_raw_os_error(libc::ENOSYS)) + let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64); + // cap to 1GB chunks in case u64::MAX is passed as max_len and the file has a non-zero seek position + // this allows us to copy large chunks without hitting EOVERFLOW, + // unless someone sets a file offset close to u64::MAX - 1GB, in which case a fallback would be required + let bytes_to_copy = cmp::min(bytes_to_copy as usize, 0x4000_0000usize); + let copy_result = unsafe { + // We actually don't have to adjust the offsets, + // because copy_file_range adjusts the file offset automatically + cvt(copy_file_range(reader, ptr::null_mut(), writer, ptr::null_mut(), bytes_to_copy, 0)) }; + match copy_result { Ok(0) if written == 0 => { // fallback to work around several kernel bugs where copy_file_range will fail to @@ -567,11 +578,14 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP, ) => { // Try fallback io::copy if either: - // - Kernel version is < 4.5 (ENOSYS) + // - Kernel version is < 4.5 (ENOSYS¹) // - Files are mounted on different fs (EXDEV) // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP) - // - copy_file_range is disallowed, for example by seccomp (EPERM) + // - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM) // - copy_file_range cannot be used with pipes or device nodes (EINVAL) + // + // ¹ these cases should be detected by the initial probe but we handle them here + // anyway in case syscall interception changes during runtime assert_eq!(written, 0); CopyResult::Fallback(0) } diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs index 3937a1ffa38b..77369cdd35fb 100644 --- a/library/std/src/sys/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/unix/kernel_copy/tests.rs @@ -1,18 +1,18 @@ -use crate::env::temp_dir; use crate::fs::OpenOptions; use crate::io; use crate::io::Result; use crate::io::SeekFrom; use crate::io::{BufRead, Read, Seek, Write}; use crate::os::unix::io::AsRawFd; +use crate::sys_common::io::test::tmpdir; #[test] fn copy_specialization() -> Result<()> { use crate::io::{BufReader, BufWriter}; - let path = crate::env::temp_dir(); - let source_path = path.join("copy-spec.source"); - let sink_path = path.join("copy-spec.sink"); + let tmp_path = tmpdir(); + let source_path = tmp_path.join("copy-spec.source"); + let sink_path = tmp_path.join("copy-spec.sink"); let result: Result<()> = try { let mut source = crate::fs::OpenOptions::new() @@ -68,7 +68,8 @@ fn copy_specialization() -> Result<()> { #[bench] fn bench_file_to_file_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; - let src_path = temp_dir().join("file-copy-bench-src"); + let temp_path = tmpdir(); + let src_path = temp_path.join("file-copy-bench-src"); let mut src = crate::fs::OpenOptions::new() .create(true) .truncate(true) @@ -78,7 +79,7 @@ fn bench_file_to_file_copy(b: &mut test::Bencher) { .unwrap(); src.write(&vec![0u8; BYTES]).unwrap(); - let sink_path = temp_dir().join("file-copy-bench-sink"); + let sink_path = temp_path.join("file-copy-bench-sink"); let mut sink = crate::fs::OpenOptions::new() .create(true) .truncate(true) @@ -97,7 +98,8 @@ fn bench_file_to_file_copy(b: &mut test::Bencher) { #[bench] fn bench_file_to_socket_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; - let src_path = temp_dir().join("pipe-copy-bench-src"); + let temp_path = tmpdir(); + let src_path = temp_path.join("pipe-copy-bench-src"); let mut src = OpenOptions::new() .create(true) .truncate(true) @@ -128,7 +130,8 @@ fn bench_file_to_socket_copy(b: &mut test::Bencher) { #[bench] fn bench_file_to_uds_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; - let src_path = temp_dir().join("uds-copy-bench-src"); + let temp_path = tmpdir(); + let src_path = temp_path.join("uds-copy-bench-src"); let mut src = OpenOptions::new() .create(true) .truncate(true) diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index fac4b05ad0b5..23a5c81c0053 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -237,7 +237,7 @@ mod inner { // `denom` field. // // Encoding this as a single `AtomicU64` allows us to use `Relaxed` - // operations, as we are only interested in in the effects on a single + // operations, as we are only interested in the effects on a single // memory location. static INFO_BITS: AtomicU64 = AtomicU64::new(0); diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 657421e3fa4c..2b1bc92dc84a 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -31,6 +31,8 @@ pub type WORD = u16; pub type CHAR = c_char; pub type ULONG_PTR = usize; pub type ULONG = c_ulong; +pub type NTSTATUS = LONG; +pub type ACCESS_MASK = DWORD; pub type LPBOOL = *mut BOOL; pub type LPBYTE = *mut BYTE; @@ -285,6 +287,8 @@ pub const STACK_SIZE_PARAM_IS_A_RESERVATION: DWORD = 0x00010000; pub const HEAP_ZERO_MEMORY: DWORD = 0x00000008; +pub const STATUS_SUCCESS: NTSTATUS = 0x00000000; + #[repr(C)] #[cfg(not(target_pointer_width = "64"))] pub struct WSADATA { @@ -1085,3 +1089,46 @@ compat_fn! { panic!("rwlocks not available") } } +compat_fn! { + "api-ms-win-core-synch-l1-2-0": + pub fn WaitOnAddress( + Address: LPVOID, + CompareAddress: LPVOID, + AddressSize: SIZE_T, + dwMilliseconds: DWORD + ) -> BOOL { + panic!("WaitOnAddress not available") + } + pub fn WakeByAddressSingle(Address: LPVOID) -> () { + // If this api is unavailable, there cannot be anything waiting, because + // WaitOnAddress would've panicked. So it's fine to do nothing here. + } +} + +compat_fn! { + "ntdll": + pub fn NtCreateKeyedEvent( + KeyedEventHandle: LPHANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: LPVOID, + Flags: ULONG + ) -> NTSTATUS { + panic!("keyed events not available") + } + pub fn NtReleaseKeyedEvent( + EventHandle: HANDLE, + Key: LPVOID, + Alertable: BOOLEAN, + Timeout: PLARGE_INTEGER + ) -> NTSTATUS { + panic!("keyed events not available") + } + pub fn NtWaitForKeyedEvent( + EventHandle: HANDLE, + Key: LPVOID, + Alertable: BOOLEAN, + Timeout: PLARGE_INTEGER + ) -> NTSTATUS { + panic!("keyed events not available") + } +} diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index dd1523b422cc..e9588e297582 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -34,6 +34,7 @@ macro_rules! compat_fn { )*) => ($( $(#[$meta])* pub mod $symbol { + #[allow(unused_imports)] use super::*; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::mem; diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index c36c6196d79e..fcbff59dec00 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -35,6 +35,7 @@ pub mod rwlock; pub mod thread; pub mod thread_local_dtor; pub mod thread_local_key; +pub mod thread_parker; pub mod time; cfg_if::cfg_if! { if #[cfg(not(target_vendor = "uwp"))] { diff --git a/library/std/src/sys/windows/mutex.rs b/library/std/src/sys/windows/mutex.rs index fa51b006c346..d4cc56d4cb3e 100644 --- a/library/std/src/sys/windows/mutex.rs +++ b/library/std/src/sys/windows/mutex.rs @@ -123,9 +123,9 @@ impl Mutex { let inner = box Inner { remutex: ReentrantMutex::uninitialized(), held: Cell::new(false) }; inner.remutex.init(); let inner = Box::into_raw(inner); - match self.lock.compare_and_swap(0, inner as usize, Ordering::SeqCst) { - 0 => inner, - n => { + match self.lock.compare_exchange(0, inner as usize, Ordering::SeqCst, Ordering::SeqCst) { + Ok(_) => inner, + Err(n) => { Box::from_raw(inner).remutex.destroy(); n as *const _ } diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index dda3ed68cfc9..c10c0df4a3a9 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -8,15 +8,12 @@ mod tests; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; -// The unsafety here stems from converting between `&OsStr` and `&[u8]` -// and back. This is safe to do because (1) we only look at ASCII -// contents of the encoding and (2) new &OsStr values are produced -// only from ASCII-bounded slices of existing &OsStr values. -fn os_str_as_u8_slice(s: &OsStr) -> &[u8] { - unsafe { mem::transmute(s) } -} -unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { - mem::transmute(s) +// Safety: `bytes` must be a valid wtf8 encoded slice +#[inline] +unsafe fn bytes_as_os_str(bytes: &[u8]) -> &OsStr { + // &OsStr is layout compatible with &Slice, which is compatible with &Wtf8, + // which is compatible with &[u8]. + mem::transmute(bytes) } #[inline] @@ -29,79 +26,116 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'\\' } -// In most DOS systems, it is not possible to have more than 26 drive letters. -// See . -pub fn is_valid_drive_letter(disk: u8) -> bool { - disk.is_ascii_alphabetic() -} - pub fn parse_prefix(path: &OsStr) -> Option> { use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC}; - let path = os_str_as_u8_slice(path); + if let Some(path) = strip_prefix(path, r"\\") { + // \\ + if let Some(path) = strip_prefix(path, r"?\") { + // \\?\ + if let Some(path) = strip_prefix(path, r"UNC\") { + // \\?\UNC\server\share - // \\ - if let Some(path) = path.strip_prefix(br"\\") { - // \\?\ - if let Some(path) = path.strip_prefix(br"?\") { - // \\?\UNC\server\share - if let Some(path) = path.strip_prefix(br"UNC\") { - let (server, share) = match get_first_two_components(path, is_verbatim_sep) { - Some((server, share)) => unsafe { - (u8_slice_as_os_str(server), u8_slice_as_os_str(share)) - }, - None => (unsafe { u8_slice_as_os_str(path) }, OsStr::new("")), - }; - return Some(VerbatimUNC(server, share)); + let (server, path) = parse_next_component(path, true); + let (share, _) = parse_next_component(path, true); + + Some(VerbatimUNC(server, share)) } else { - // \\?\path - match path { - // \\?\C:\path - [c, b':', b'\\', ..] if is_valid_drive_letter(*c) => { - return Some(VerbatimDisk(c.to_ascii_uppercase())); - } - // \\?\cat_pics - _ => { - let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len()); - let slice = &path[..idx]; - return Some(Verbatim(unsafe { u8_slice_as_os_str(slice) })); - } + let (prefix, _) = parse_next_component(path, true); + + // in verbatim paths only recognize an exact drive prefix + if let Some(drive) = parse_drive_exact(prefix) { + // \\?\C: + Some(VerbatimDisk(drive)) + } else { + // \\?\prefix + Some(Verbatim(prefix)) } } - } else if let Some(path) = path.strip_prefix(b".\\") { + } else if let Some(path) = strip_prefix(path, r".\") { // \\.\COM42 - let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len()); - let slice = &path[..idx]; - return Some(DeviceNS(unsafe { u8_slice_as_os_str(slice) })); - } - match get_first_two_components(path, is_sep_byte) { - Some((server, share)) if !server.is_empty() && !share.is_empty() => { + let (prefix, _) = parse_next_component(path, false); + Some(DeviceNS(prefix)) + } else { + let (server, path) = parse_next_component(path, false); + let (share, _) = parse_next_component(path, false); + + if !server.is_empty() && !share.is_empty() { // \\server\share - return Some(unsafe { UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share)) }); + Some(UNC(server, share)) + } else { + // no valid prefix beginning with "\\" recognized + None } - _ => {} } - } else if let [c, b':', ..] = path { + } else if let Some(drive) = parse_drive(path) { // C: - if is_valid_drive_letter(*c) { - return Some(Disk(c.to_ascii_uppercase())); - } + Some(Disk(drive)) + } else { + // no prefix + None } - None } -/// Returns the first two path components with predicate `f`. -/// -/// The two components returned will be use by caller -/// to construct `VerbatimUNC` or `UNC` Windows path prefix. -/// -/// Returns [`None`] if there are no separators in path. -fn get_first_two_components(path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> { - let idx = path.iter().position(|&x| f(x))?; - // Panic safe - // The max `idx+1` is `path.len()` and `path[path.len()..]` is a valid index. - let (first, path) = (&path[..idx], &path[idx + 1..]); - let idx = path.iter().position(|&x| f(x)).unwrap_or(path.len()); - let second = &path[..idx]; - Some((first, second)) +// Parses a drive prefix, e.g. "C:" and "C:\whatever" +fn parse_drive(prefix: &OsStr) -> Option { + // In most DOS systems, it is not possible to have more than 26 drive letters. + // See . + fn is_valid_drive_letter(drive: &u8) -> bool { + drive.is_ascii_alphabetic() + } + + match prefix.bytes() { + [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()), + _ => None, + } +} + +// Parses a drive prefix exactly, e.g. "C:" +fn parse_drive_exact(prefix: &OsStr) -> Option { + // only parse two bytes: the drive letter and the drive separator + if prefix.len() == 2 { parse_drive(prefix) } else { None } +} + +fn strip_prefix<'a>(path: &'a OsStr, prefix: &str) -> Option<&'a OsStr> { + // `path` and `prefix` are valid wtf8 and utf8 encoded slices respectively, `path[prefix.len()]` + // is thus a code point boundary and `path[prefix.len()..]` is a valid wtf8 encoded slice. + match path.bytes().strip_prefix(prefix.as_bytes()) { + Some(path) => unsafe { Some(bytes_as_os_str(path)) }, + None => None, + } +} + +// Parse the next path component. +// +// Returns the next component and the rest of the path excluding the component and separator. +// Does not recognize `/` as a separator character if `verbatim` is true. +fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { + let separator = if verbatim { is_verbatim_sep } else { is_sep_byte }; + + match path.bytes().iter().position(|&x| separator(x)) { + Some(separator_start) => { + let mut separator_end = separator_start + 1; + + // a series of multiple separator characters is treated as a single separator, + // except in verbatim paths + while !verbatim && separator_end < path.len() && separator(path.bytes()[separator_end]) + { + separator_end += 1; + } + + let component = &path.bytes()[..separator_start]; + + // Panic safe + // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. + let path = &path.bytes()[separator_end..]; + + // Safety: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') + // is encoded in a single byte, therefore `bytes[separator_start]` and + // `bytes[separator_end]` must be code point boundaries and thus + // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices. + unsafe { (bytes_as_os_str(component), bytes_as_os_str(path)) } + } + None => (path, OsStr::new("")), + } } diff --git a/library/std/src/sys/windows/path/tests.rs b/library/std/src/sys/windows/path/tests.rs index fbac1dc1ca17..9675da6ff883 100644 --- a/library/std/src/sys/windows/path/tests.rs +++ b/library/std/src/sys/windows/path/tests.rs @@ -1,21 +1,44 @@ use super::*; #[test] -fn test_get_first_two_components() { +fn test_parse_next_component() { assert_eq!( - get_first_two_components(br"server\share", is_verbatim_sep), - Some((&b"server"[..], &b"share"[..])), + parse_next_component(OsStr::new(r"server\share"), true), + (OsStr::new(r"server"), OsStr::new(r"share")) ); assert_eq!( - get_first_two_components(br"server\", is_verbatim_sep), - Some((&b"server"[..], &b""[..])) + parse_next_component(OsStr::new(r"server/share"), true), + (OsStr::new(r"server/share"), OsStr::new(r"")) ); assert_eq!( - get_first_two_components(br"\server\", is_verbatim_sep), - Some((&b""[..], &b"server"[..])) + parse_next_component(OsStr::new(r"server/share"), false), + (OsStr::new(r"server"), OsStr::new(r"share")) ); - assert_eq!(get_first_two_components(br"there are no separators here", is_verbatim_sep), None,); + assert_eq!( + parse_next_component(OsStr::new(r"server\"), false), + (OsStr::new(r"server"), OsStr::new(r"")) + ); + + assert_eq!( + parse_next_component(OsStr::new(r"\server\"), false), + (OsStr::new(r""), OsStr::new(r"server\")) + ); + + assert_eq!( + parse_next_component(OsStr::new(r"servershare"), false), + (OsStr::new(r"servershare"), OsStr::new("")) + ); + + assert_eq!( + parse_next_component(OsStr::new(r"server/\//\/\\\\/////\/share"), false), + (OsStr::new(r"server"), OsStr::new(r"share")) + ); + + assert_eq!( + parse_next_component(OsStr::new(r"server\\\\\\\\\\\\\\share"), true), + (OsStr::new(r"server"), OsStr::new(r"\\\\\\\\\\\\\share")) + ); } diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs index fb2bb0613ee8..065365e5572f 100644 --- a/library/std/src/sys/windows/thread_local_key.rs +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -1,4 +1,4 @@ -use crate::mem; +use crate::mem::ManuallyDrop; use crate::ptr; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::SeqCst; @@ -111,16 +111,13 @@ struct Node { } unsafe fn register_dtor(key: Key, dtor: Dtor) { - let mut node = Box::new(Node { key, dtor, next: ptr::null_mut() }); + let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() })); let mut head = DTORS.load(SeqCst); loop { node.next = head; - match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) { - Ok(_) => { - mem::forget(node); - return; - } + match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) { + Ok(_) => return, // nothing to drop, we successfully added the node to the list Err(cur) => head = cur, } } diff --git a/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs new file mode 100644 index 000000000000..9e4c9aa0a512 --- /dev/null +++ b/library/std/src/sys/windows/thread_parker.rs @@ -0,0 +1,250 @@ +// Thread parker implementation for Windows. +// +// This uses WaitOnAddress and WakeByAddressSingle if available (Windows 8+). +// This modern API is exactly the same as the futex syscalls the Linux thread +// parker uses. When These APIs are available, the implementation of this +// thread parker matches the Linux thread parker exactly. +// +// However, when the modern API is not available, this implementation falls +// back to NT Keyed Events, which are similar, but have some important +// differences. These are available since Windows XP. +// +// WaitOnAddress first checks the state of the thread parker to make sure it no +// WakeByAddressSingle calls can be missed between updating the parker state +// and calling the function. +// +// NtWaitForKeyedEvent does not have this option, and unconditionally blocks +// without checking the parker state first. Instead, NtReleaseKeyedEvent +// (unlike WakeByAddressSingle) *blocks* until it woke up a thread waiting for +// it by NtWaitForKeyedEvent. This way, we can be sure no events are missed, +// but we need to be careful not to block unpark() if park_timeout() was woken +// up by a timeout instead of unpark(). +// +// Unlike WaitOnAddress, NtWaitForKeyedEvent/NtReleaseKeyedEvent operate on a +// HANDLE (created with NtCreateKeyedEvent). This means that we can be sure +// a succesfully awoken park() was awoken by unpark() and not a +// NtReleaseKeyedEvent call from some other code, as these events are not only +// matched by the key (address of the parker (state)), but also by this HANDLE. +// We lazily allocate this handle the first time it is needed. +// +// The fast path (calling park() after unpark() was already called) and the +// possible states are the same for both implementations. This is used here to +// make sure the fast path does not even check which API to use, but can return +// right away, independent of the used API. Only the slow paths (which will +// actually block/wake a thread) check which API is available and have +// different implementations. +// +// Unfortunately, NT Keyed Events are an undocumented Windows API. However: +// - This API is relatively simple with obvious behaviour, and there are +// several (unofficial) articles documenting the details. [1] +// - `parking_lot` has been using this API for years (on Windows versions +// before Windows 8). [2] Many big projects extensively use parking_lot, +// such as servo and the Rust compiler itself. +// - It is the underlying API used by Windows SRW locks and Windows critical +// sections. [3] [4] +// - The source code of the implementations of Wine, ReactOs, and Windows XP +// are available and match the expected behaviour. +// - The main risk with an undocumented API is that it might change in the +// future. But since we only use it for older versions of Windows, that's not +// a problem. +// - Even if these functions do not block or wake as we expect (which is +// unlikely, see all previous points), this implementation would still be +// memory safe. The NT Keyed Events API is only used to sleep/block in the +// right place. +// +// [1]: http://www.locklessinc.com/articles/keyed_events/ +// [2]: https://github.com/Amanieu/parking_lot/commit/43abbc964e +// [3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c +// [4]: Windows Internals, Part 1, ISBN 9780735671300 + +use crate::convert::TryFrom; +use crate::ptr; +use crate::sync::atomic::{ + AtomicI8, AtomicUsize, + Ordering::{Acquire, Relaxed, Release}, +}; +use crate::sys::{c, dur2timeout}; +use crate::time::Duration; + +pub struct Parker { + state: AtomicI8, +} + +const PARKED: i8 = -1; +const EMPTY: i8 = 0; +const NOTIFIED: i8 = 1; + +// Notes about memory ordering: +// +// Memory ordering is only relevant for the relative ordering of operations +// between different variables. Even Ordering::Relaxed guarantees a +// monotonic/consistent order when looking at just a single atomic variable. +// +// So, since this parker is just a single atomic variable, we only need to look +// at the ordering guarantees we need to provide to the 'outside world'. +// +// The only memory ordering guarantee that parking and unparking provide, is +// that things which happened before unpark() are visible on the thread +// returning from park() afterwards. Otherwise, it was effectively unparked +// before unpark() was called while still consuming the 'token'. +// +// In other words, unpark() needs to synchronize with the part of park() that +// consumes the token and returns. +// +// This is done with a release-acquire synchronization, by using +// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using +// Ordering::Acquire when reading this state in park() after waking up. +impl Parker { + pub fn new() -> Self { + Self { state: AtomicI8::new(EMPTY) } + } + + // Assumes this is only called by the thread that owns the Parker, + // which means that `self.state != PARKED`. + pub unsafe fn park(&self) { + // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the + // first case. + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + + if c::WaitOnAddress::is_available() { + loop { + // Wait for something to happen, assuming it's still set to PARKED. + c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE); + // Change NOTIFIED=>EMPTY but leave PARKED alone. + if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() { + // Actually woken up by unpark(). + return; + } else { + // Spurious wake up. We loop to try again. + } + } + } else { + // Wait for unpark() to produce this event. + c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut()); + // Set the state back to EMPTY (from either PARKED or NOTIFIED). + // Note that we don't just write EMPTY, but use swap() to also + // include an acquire-ordered read to synchronize with unpark()'s + // release-ordered write. + self.state.swap(EMPTY, Acquire); + } + } + + // Assumes this is only called by the thread that owns the Parker, + // which means that `self.state != PARKED`. + pub unsafe fn park_timeout(&self, timeout: Duration) { + // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the + // first case. + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + + if c::WaitOnAddress::is_available() { + // Wait for something to happen, assuming it's still set to PARKED. + c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout)); + // Set the state back to EMPTY (from either PARKED or NOTIFIED). + // Note that we don't just write EMPTY, but use swap() to also + // include an acquire-ordered read to synchronize with unpark()'s + // release-ordered write. + if self.state.swap(EMPTY, Acquire) == NOTIFIED { + // Actually woken up by unpark(). + } else { + // Timeout or spurious wake up. + // We return either way, because we can't easily tell if it was the + // timeout or not. + } + } else { + // Need to wait for unpark() using NtWaitForKeyedEvent. + let handle = keyed_event_handle(); + + // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative + // values to indicate a relative time on the monotonic clock. + // This is documented here for the underlying KeWaitForSingleObject function: + // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject + let mut timeout = match i64::try_from((timeout.as_nanos() + 99) / 100) { + Ok(t) => -t, + Err(_) => i64::MIN, + }; + + // Wait for unpark() to produce this event. + let unparked = + c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS; + + // Set the state back to EMPTY (from either PARKED or NOTIFIED). + let prev_state = self.state.swap(EMPTY, Acquire); + + if !unparked && prev_state == NOTIFIED { + // We were awoken by a timeout, not by unpark(), but the state + // was set to NOTIFIED, which means we *just* missed an + // unpark(), which is now blocked on us to wait for it. + // Wait for it to consume the event and unblock that thread. + c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut()); + } + } + } + + pub fn unpark(&self) { + // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and + // wake the thread in the first case. + // + // Note that even NOTIFIED=>NOTIFIED results in a write. This is on + // purpose, to make sure every unpark() has a release-acquire ordering + // with park(). + if self.state.swap(NOTIFIED, Release) == PARKED { + if c::WakeByAddressSingle::is_available() { + unsafe { + c::WakeByAddressSingle(self.ptr()); + } + } else { + // If we run NtReleaseKeyedEvent before the waiting thread runs + // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up. + // If the waiting thread wakes up before we run NtReleaseKeyedEvent + // (e.g. due to a timeout), this blocks until we do wake up a thread. + // To prevent this thread from blocking indefinitely in that case, + // park_impl() will, after seeing the state set to NOTIFIED after + // waking up, call NtWaitForKeyedEvent again to unblock us. + unsafe { + c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut()); + } + } + } + } + + fn ptr(&self) -> c::LPVOID { + &self.state as *const _ as c::LPVOID + } +} + +fn keyed_event_handle() -> c::HANDLE { + const INVALID: usize = !0; + static HANDLE: AtomicUsize = AtomicUsize::new(INVALID); + match HANDLE.load(Relaxed) { + INVALID => { + let mut handle = c::INVALID_HANDLE_VALUE; + unsafe { + match c::NtCreateKeyedEvent( + &mut handle, + c::GENERIC_READ | c::GENERIC_WRITE, + ptr::null_mut(), + 0, + ) { + c::STATUS_SUCCESS => {} + r => panic!("Unable to create keyed event handle: error {}", r), + } + } + match HANDLE.compare_exchange(INVALID, handle as usize, Relaxed, Relaxed) { + Ok(_) => handle, + Err(h) => { + // Lost the race to another thread initializing HANDLE before we did. + // Closing our handle and using theirs instead. + unsafe { + c::CloseHandle(handle); + } + h as c::HANDLE + } + } + } + handle => handle as c::HANDLE, + } +} diff --git a/library/std/src/sys_common/condvar/check.rs b/library/std/src/sys_common/condvar/check.rs index fecb732b910c..1578a2de60ce 100644 --- a/library/std/src/sys_common/condvar/check.rs +++ b/library/std/src/sys_common/condvar/check.rs @@ -23,9 +23,9 @@ impl SameMutexCheck { } pub fn verify(&self, mutex: &MovableMutex) { let addr = mutex.raw() as *const mutex_imp::Mutex as usize; - match self.addr.compare_and_swap(0, addr, Ordering::SeqCst) { - 0 => {} // Stored the address - n if n == addr => {} // Lost a race to store the same address + match self.addr.compare_exchange(0, addr, Ordering::SeqCst, Ordering::SeqCst) { + Ok(_) => {} // Stored the address + Err(n) if n == addr => {} // Lost a race to store the same address _ => panic!("attempted to use a condition variable with two mutexes"), } } diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 48ba4ddfc0b2..38ba0d2fbdb6 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -177,11 +177,8 @@ impl TryFrom<&str> for LookupHost { } // split the string by ':' and convert the second part to u16 - let mut parts_iter = s.rsplitn(2, ':'); - let port_str = try_opt!(parts_iter.next(), "invalid socket address"); - let host = try_opt!(parts_iter.next(), "invalid socket address"); + let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() } } diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs index 162eab2388d5..475bfca9b6dc 100644 --- a/library/std/src/sys_common/remutex.rs +++ b/library/std/src/sys_common/remutex.rs @@ -1,10 +1,10 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::fmt; -use crate::marker; +use crate::marker::PhantomPinned; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::pin::Pin; use crate::sys::mutex as sys; /// A re-entrant mutual exclusion @@ -15,6 +15,7 @@ use crate::sys::mutex as sys; pub struct ReentrantMutex { inner: sys::ReentrantMutex, data: T, + _pinned: PhantomPinned, } unsafe impl Send for ReentrantMutex {} @@ -37,10 +38,10 @@ impl RefUnwindSafe for ReentrantMutex {} /// guarded data. #[must_use = "if unused the ReentrantMutex will immediately unlock"] pub struct ReentrantMutexGuard<'a, T: 'a> { - lock: &'a ReentrantMutex, + lock: Pin<&'a ReentrantMutex>, } -impl !marker::Send for ReentrantMutexGuard<'_, T> {} +impl !Send for ReentrantMutexGuard<'_, T> {} impl ReentrantMutex { /// Creates a new reentrant mutex in an unlocked state. @@ -51,7 +52,11 @@ impl ReentrantMutex { /// once this mutex is in its final resting place, and only then are the /// lock/unlock methods safe. pub const unsafe fn new(t: T) -> ReentrantMutex { - ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t } + ReentrantMutex { + inner: sys::ReentrantMutex::uninitialized(), + data: t, + _pinned: PhantomPinned, + } } /// Initializes this mutex so it's ready for use. @@ -60,8 +65,8 @@ impl ReentrantMutex { /// /// Unsafe to call more than once, and must be called after this will no /// longer move in memory. - pub unsafe fn init(&self) { - self.inner.init(); + pub unsafe fn init(self: Pin<&mut Self>) { + self.get_unchecked_mut().inner.init() } /// Acquires a mutex, blocking the current thread until it is able to do so. @@ -76,9 +81,9 @@ impl ReentrantMutex { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn lock(&self) -> ReentrantMutexGuard<'_, T> { + pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> { unsafe { self.inner.lock() } - ReentrantMutexGuard::new(&self) + ReentrantMutexGuard { lock: self } } /// Attempts to acquire this lock. @@ -93,8 +98,12 @@ impl ReentrantMutex { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn try_lock(&self) -> Option> { - if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None } + pub fn try_lock(self: Pin<&Self>) -> Option> { + if unsafe { self.inner.try_lock() } { + Some(ReentrantMutexGuard { lock: self }) + } else { + None + } } } @@ -107,30 +116,6 @@ impl Drop for ReentrantMutex { } } -impl fmt::Debug for ReentrantMutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_lock() { - Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(), - None => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - - f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish() - } - } - } -} - -impl<'mutex, T> ReentrantMutexGuard<'mutex, T> { - fn new(lock: &'mutex ReentrantMutex) -> ReentrantMutexGuard<'mutex, T> { - ReentrantMutexGuard { lock } - } -} - impl Deref for ReentrantMutexGuard<'_, T> { type Target = T; diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs index 9c686e579d73..88453ded2f9f 100644 --- a/library/std/src/sys_common/remutex/tests.rs +++ b/library/std/src/sys_common/remutex/tests.rs @@ -1,4 +1,6 @@ +use crate::boxed::Box; use crate::cell::RefCell; +use crate::pin::Pin; use crate::sync::Arc; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::thread; @@ -6,10 +8,11 @@ use crate::thread; #[test] fn smoke() { let m = unsafe { - let m = ReentrantMutex::new(()); - m.init(); + let mut m = Box::pin(ReentrantMutex::new(())); + m.as_mut().init(); m }; + let m = m.as_ref(); { let a = m.lock(); { @@ -27,18 +30,19 @@ fn smoke() { #[test] fn is_mutex() { let m = unsafe { - let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); - m.init(); - m + // FIXME: Simplify this if Arc gets a Arc::get_pin_mut. + let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0))); + Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init(); + Pin::new_unchecked(m) }; let m2 = m.clone(); - let lock = m.lock(); + let lock = m.as_ref().lock(); let child = thread::spawn(move || { - let lock = m2.lock(); + let lock = m2.as_ref().lock(); assert_eq!(*lock.borrow(), 4950); }); for i in 0..100 { - let lock = m.lock(); + let lock = m.as_ref().lock(); *lock.borrow_mut() += i; } drop(lock); @@ -48,20 +52,21 @@ fn is_mutex() { #[test] fn trylock_works() { let m = unsafe { - let m = Arc::new(ReentrantMutex::new(())); - m.init(); - m + // FIXME: Simplify this if Arc gets a Arc::get_pin_mut. + let mut m = Arc::new(ReentrantMutex::new(())); + Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init(); + Pin::new_unchecked(m) }; let m2 = m.clone(); - let _lock = m.try_lock(); - let _lock2 = m.try_lock(); + let _lock = m.as_ref().try_lock(); + let _lock2 = m.as_ref().try_lock(); thread::spawn(move || { - let lock = m2.try_lock(); + let lock = m2.as_ref().try_lock(); assert!(lock.is_none()); }) .join() .unwrap(); - let _lock3 = m.try_lock(); + let _lock3 = m.as_ref().try_lock(); } pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell>); diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index dbcb7b36265f..32cd56416655 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -168,7 +168,7 @@ impl StaticKey { return key; } - // POSIX allows the key created here to be 0, but the compare_and_swap + // POSIX allows the key created here to be 0, but the compare_exchange // below relies on using 0 as a sentinel value to check who won the // race to set the shared TLS key. As far as I know, there is no // guaranteed value that cannot be returned as a posix_key_create key, @@ -186,11 +186,11 @@ impl StaticKey { key2 }; rtassert!(key != 0); - match self.key.compare_and_swap(0, key as usize, Ordering::SeqCst) { + match self.key.compare_exchange(0, key as usize, Ordering::SeqCst, Ordering::SeqCst) { // The CAS succeeded, so we've created the actual key - 0 => key as usize, + Ok(_) => key as usize, // If someone beat us to the punch, use their key instead - n => { + Err(n) => { imp::destroy(key); n } diff --git a/library/std/src/sys_common/thread_parker/futex.rs b/library/std/src/sys_common/thread_parker/futex.rs index a5d4927dcc5c..0132743b2440 100644 --- a/library/std/src/sys_common/thread_parker/futex.rs +++ b/library/std/src/sys_common/thread_parker/futex.rs @@ -49,7 +49,7 @@ impl Parker { // Wait for something to happen, assuming it's still set to PARKED. futex_wait(&self.state, PARKED, None); // Change NOTIFIED=>EMPTY and return in that case. - if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED { + if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() { return; } else { // Spurious wake up. We loop to try again. diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs index 5e75ac65de41..ba896bd676b6 100644 --- a/library/std/src/sys_common/thread_parker/mod.rs +++ b/library/std/src/sys_common/thread_parker/mod.rs @@ -6,6 +6,8 @@ cfg_if::cfg_if! { ))] { mod futex; pub use futex::Parker; + } else if #[cfg(windows)] { + pub use crate::sys::thread_parker::Parker; } else { mod generic; pub use generic::Parker; diff --git a/library/stdarch b/library/stdarch index 777efaf56447..9c732a56f67f 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 777efaf5644706b36706a7a5c51edb63835e05ca +Subproject commit 9c732a56f67f54d12a0b4fd99993154906c95ea6 diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 5c12a54eef12..656d9669e81d 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -30,6 +30,7 @@ #![feature(termination_trait_lib)] #![feature(test)] #![feature(total_cmp)] +#![feature(str_split_once)] // Public reexports pub use self::bench::{black_box, Bencher}; diff --git a/library/test/src/time.rs b/library/test/src/time.rs index 130792fa5d7e..e0b6eadffa11 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -105,30 +105,24 @@ impl TimeThreshold { /// value. pub fn from_env_var(env_var_name: &str) -> Option { let durations_str = env::var(env_var_name).ok()?; + let (warn_str, critical_str) = durations_str.split_once(',').unwrap_or_else(|| { + panic!( + "Duration variable {} expected to have 2 numbers separated by comma, but got {}", + env_var_name, durations_str + ) + }); - // Split string into 2 substrings by comma and try to parse numbers. - let mut durations = durations_str.splitn(2, ',').map(|v| { + let parse_u64 = |v| { u64::from_str(v).unwrap_or_else(|_| { panic!( "Duration value in variable {} is expected to be a number, but got {}", env_var_name, v ) }) - }); - - // Callback to be called if the environment variable has unexpected structure. - let panic_on_incorrect_value = || { - panic!( - "Duration variable {} expected to have 2 numbers separated by comma, but got {}", - env_var_name, durations_str - ); }; - let (warn, critical) = ( - durations.next().unwrap_or_else(panic_on_incorrect_value), - durations.next().unwrap_or_else(panic_on_incorrect_value), - ); - + let warn = parse_u64(warn_str); + let critical = parse_u64(critical_str); if warn > critical { panic!("Test execution warn time should be less or equal to the critical time"); } diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index a103c9fb0b78..f899f21080eb 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -4,7 +4,12 @@ All notable changes to bootstrap will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [Non-breaking changes since the last major version] + +## [Changes since the last major version] + +- `llvm-libunwind` now accepts `in-tree` (formerly true), `system` or `no` (formerly false) [#77703](https://github.com/rust-lang/rust/pull/77703) + +### Non-breaking changes - `x.py check` needs opt-in to check tests (--all-targets) [#77473](https://github.com/rust-lang/rust/pull/77473) - The default bootstrap profiles are now located at `bootstrap/defaults/config.$PROFILE.toml` (previously they were located at `bootstrap/defaults/config.toml.$PROFILE`) [#77558](https://github.com/rust-lang/rust/pull/77558) diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 84ed9446ae73..a2e596bf4e95 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -201,7 +201,6 @@ build/ # Output for all compiletest-based test suites test/ ui/ - compile-fail/ debuginfo/ ... diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index a819e1b6e2f1..b8bae69d0633 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -192,8 +192,10 @@ def default_build_triple(verbose): # If the user already has a host build triple with an existing `rustc` # install, use their preference. This fixes most issues with Windows builds # being detected as GNU instead of MSVC. + default_encoding = sys.getdefaultencoding() try: version = subprocess.check_output(["rustc", "--version", "--verbose"]) + version = version.decode(default_encoding) host = next(x for x in version.split('\n') if x.startswith("host: ")) triple = host.split("host: ")[1] if verbose: @@ -204,7 +206,6 @@ def default_build_triple(verbose): print("rustup not detected: {}".format(e)) print("falling back to auto-detect") - default_encoding = sys.getdefaultencoding() required = sys.platform != 'win32' ostype = require(["uname", "-s"], exit=required) cputype = require(['uname', '-m'], exit=required) @@ -350,11 +351,13 @@ def output(filepath): with open(tmp, 'w') as f: yield f try: - os.remove(filepath) # PermissionError/OSError on Win32 if in use - os.rename(tmp, filepath) + if os.path.exists(filepath): + os.remove(filepath) # PermissionError/OSError on Win32 if in use except OSError: shutil.copy2(tmp, filepath) os.remove(tmp) + return + os.rename(tmp, filepath) class RustBuild(object): @@ -794,7 +797,7 @@ class RustBuild(object): env.setdefault("RUSTFLAGS", "") env["RUSTFLAGS"] += " -Cdebuginfo=2" - build_section = "target.{}".format(self.build_triple()) + build_section = "target.{}".format(self.build) target_features = [] if self.get_toml("crt-static", build_section) == "true": target_features += ["+crt-static"] @@ -825,7 +828,11 @@ class RustBuild(object): run(args, env=env, verbose=self.verbose) def build_triple(self): - """Build triple as in LLVM""" + """Build triple as in LLVM + + Note that `default_build_triple` is moderately expensive, + so use `self.build` where possible. + """ config = self.get_toml('build') if config: return config @@ -892,13 +899,17 @@ class RustBuild(object): filtered_submodules = [] submodules_names = [] llvm_checked_out = os.path.exists(os.path.join(self.rust_root, "src/llvm-project/.git")) + external_llvm_provided = self.get_toml('llvm-config') or self.downloading_llvm() + llvm_needed = not self.get_toml('codegen-backends', 'rust') \ + or "llvm" in self.get_toml('codegen-backends', 'rust') for module in submodules: if module.endswith("llvm-project"): - # Don't sync the llvm-project submodule either if an external LLVM - # was provided, or if we are downloading LLVM. Also, if the - # submodule has been initialized already, sync it anyways so that - # it doesn't mess up contributor pull requests. - if self.get_toml('llvm-config') or self.downloading_llvm(): + # Don't sync the llvm-project submodule if an external LLVM was + # provided, if we are downloading LLVM or if the LLVM backend is + # not being built. Also, if the submodule has been initialized + # already, sync it anyways so that it doesn't mess up contributor + # pull requests. + if external_llvm_provided or not llvm_needed: if self.get_toml('lld') != 'true' and not llvm_checked_out: continue check = self.check_submodule(module, slow_submodules) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 6d97943548d5..c2abb01fa8c9 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -384,7 +384,6 @@ impl<'a> Builder<'a> { test::ExpandYamlAnchors, test::Tidy, test::Ui, - test::CompileFail, test::RunPassValgrind, test::MirOpt, test::Codegen, @@ -471,6 +470,7 @@ impl<'a> Builder<'a> { dist::RustDev, dist::Extended, dist::BuildManifest, + dist::ReproducibleArtifacts, ), Kind::Install => describe!( install::Docs, @@ -732,11 +732,11 @@ impl<'a> Builder<'a> { .env("CFG_RELEASE_CHANNEL", &self.config.channel) .env("RUSTDOC_REAL", self.rustdoc(compiler)) .env("RUSTC_BOOTSTRAP", "1") - .arg("-Znormalize_docs") .arg("-Winvalid_codeblock_attributes"); if self.config.deny_warnings { cmd.arg("-Dwarnings"); } + cmd.arg("-Znormalize-docs"); // Remove make-related flags that can cause jobserver problems. cmd.env_remove("MAKEFLAGS"); @@ -1123,6 +1123,19 @@ impl<'a> Builder<'a> { }, ); + // `dsymutil` adds time to builds on Apple platforms for no clear benefit, and also makes + // it more difficult for debuggers to find debug info. The compiler currently defaults to + // running `dsymutil` to preserve its historical default, but when compiling the compiler + // itself, we skip it by default since we know it's safe to do so in that case. + // See https://github.com/rust-lang/rust/issues/79361 for more info on this flag. + if target.contains("apple") { + if self.config.rust_run_dsymutil { + rustflags.arg("-Zrun-dsymutil=yes"); + } else { + rustflags.arg("-Zrun-dsymutil=no"); + } + } + if self.config.cmd.bless() { // Bless `expect!` tests. cargo.env("UPDATE_EXPECT", "1"); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 3cfb2f2836d5..9477a7cb3547 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -7,6 +7,7 @@ //! goes along from the output of the previous stage. use std::borrow::Cow; +use std::collections::HashSet; use std::env; use std::fs; use std::io::prelude::*; @@ -519,6 +520,41 @@ impl Step for Rustc { let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "build"); rustc_cargo(builder, &mut cargo, target); + if builder.config.rust_profile_use.is_some() + && builder.config.rust_profile_generate.is_some() + { + panic!("Cannot use and generate PGO profiles at the same time"); + } + + let is_collecting = if let Some(path) = &builder.config.rust_profile_generate { + if compiler.stage == 1 { + cargo.rustflag(&format!("-Cprofile-generate={}", path)); + // Apparently necessary to avoid overflowing the counters during + // a Cargo build profile + cargo.rustflag("-Cllvm-args=-vp-counters-per-site=4"); + true + } else { + false + } + } else if let Some(path) = &builder.config.rust_profile_use { + if compiler.stage == 1 { + cargo.rustflag(&format!("-Cprofile-use={}", path)); + cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function"); + true + } else { + false + } + } else { + false + }; + if is_collecting { + // Ensure paths to Rust sources are relative, not absolute. + cargo.rustflag(&format!( + "-Cllvm-args=-static-func-strip-dirname-prefix={}", + builder.config.src.components().count() + )); + } + builder.info(&format!( "Building stage{} compiler artifacts ({} -> {})", compiler.stage, &compiler.host, target @@ -770,7 +806,7 @@ fn copy_codegen_backends_to_sysroot( // Here we're looking for the output dylib of the `CodegenBackend` step and // we're copying that into the `codegen-backends` folder. let dst = builder.sysroot_codegen_backends(target_compiler); - t!(fs::create_dir_all(&dst)); + t!(fs::create_dir_all(&dst), dst); if builder.config.dry_run { return; @@ -974,28 +1010,51 @@ impl Step for Assemble { builder.info(&format!("Assembling stage{} compiler ({})", stage, host)); // Link in all dylibs to the libdir + let stamp = librustc_stamp(builder, build_compiler, target_compiler.host); + let proc_macros = builder + .read_stamp_file(&stamp) + .into_iter() + .filter_map(|(path, dependency_type)| { + if dependency_type == DependencyType::Host { + Some(path.file_name().unwrap().to_owned().into_string().unwrap()) + } else { + None + } + }) + .collect::>(); + let sysroot = builder.sysroot(target_compiler); let rustc_libdir = builder.rustc_libdir(target_compiler); t!(fs::create_dir_all(&rustc_libdir)); let src_libdir = builder.sysroot_libdir(build_compiler, host); for f in builder.read_dir(&src_libdir) { let filename = f.file_name().into_string().unwrap(); - if is_dylib(&filename) { + if is_dylib(&filename) && !proc_macros.contains(&filename) { builder.copy(&f.path(), &rustc_libdir.join(&filename)); } } copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler); + // We prepend this bin directory to the user PATH when linking Rust binaries. To + // avoid shadowing the system LLD we rename the LLD we provide to `rust-lld`. let libdir = builder.sysroot_libdir(target_compiler, target_compiler.host); + let libdir_bin = libdir.parent().unwrap().join("bin"); + t!(fs::create_dir_all(&libdir_bin)); + if let Some(lld_install) = lld_install { let src_exe = exe("lld", target_compiler.host); let dst_exe = exe("rust-lld", target_compiler.host); - // we prepend this bin directory to the user PATH when linking Rust binaries. To - // avoid shadowing the system LLD we rename the LLD we provide to `rust-lld`. - let dst = libdir.parent().unwrap().join("bin"); - t!(fs::create_dir_all(&dst)); - builder.copy(&lld_install.join("bin").join(&src_exe), &dst.join(&dst_exe)); + builder.copy(&lld_install.join("bin").join(&src_exe), &libdir_bin.join(&dst_exe)); + } + + // Similarly, copy `llvm-dwp` into libdir for Split DWARF. + { + let src_exe = exe("llvm-dwp", target_compiler.host); + let dst_exe = exe("rust-llvm-dwp", target_compiler.host); + let llvm_config_bin = builder.ensure(native::Llvm { target: target_compiler.host }); + let llvm_bin_dir = llvm_config_bin.parent().unwrap(); + builder.copy(&llvm_bin_dir.join(&src_exe), &libdir_bin.join(&dst_exe)); } // Ensure that `libLLVM.so` ends up in the newly build compiler directory, diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index fb2c6d1f92a8..f4d89a89c146 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -123,6 +123,7 @@ pub struct Config { pub rust_debuginfo_level_std: u32, pub rust_debuginfo_level_tools: u32, pub rust_debuginfo_level_tests: u32, + pub rust_run_dsymutil: bool, pub rust_rpath: bool, pub rustc_parallel: bool, pub rustc_default_linker: Option, @@ -133,6 +134,8 @@ pub struct Config { pub rust_thin_lto_import_instr_limit: Option, pub rust_remap_debuginfo: bool, pub rust_new_symbol_mangling: bool, + pub rust_profile_use: Option, + pub rust_profile_generate: Option, pub build: TargetSelection, pub hosts: Vec, @@ -145,6 +148,7 @@ pub struct Config { pub dist_sign_folder: Option, pub dist_upload_addr: Option, pub dist_gpg_password_file: Option, + pub dist_compression_formats: Option>, // libstd features pub backtrace: bool, // support for RUST_BACKTRACE @@ -331,7 +335,7 @@ impl Merge for TomlConfig { *x = Some(new); } } - }; + } do_merge(&mut self.build, build); do_merge(&mut self.install, install); do_merge(&mut self.llvm, llvm); @@ -435,6 +439,7 @@ struct Dist { upload_addr: Option, src_tarball: Option, missing_tools: Option, + compression_formats: Option>, } #[derive(Deserialize)] @@ -466,6 +471,7 @@ struct Rust { debuginfo_level_std: Option, debuginfo_level_tools: Option, debuginfo_level_tests: Option, + run_dsymutil: Option, backtrace: Option, incremental: Option, parallel_compiler: Option, @@ -494,6 +500,8 @@ struct Rust { llvm_libunwind: Option, control_flow_guard: Option, new_symbol_mangling: Option, + profile_generate: Option, + profile_use: Option, } /// TOML representation of how each build target is configured. @@ -830,6 +838,7 @@ impl Config { debuginfo_level_std = rust.debuginfo_level_std; debuginfo_level_tools = rust.debuginfo_level_tools; debuginfo_level_tests = rust.debuginfo_level_tests; + config.rust_run_dsymutil = rust.run_dsymutil.unwrap_or(false); optimize = rust.optimize; ignore_git = rust.ignore_git; set(&mut config.rust_new_symbol_mangling, rust.new_symbol_mangling); @@ -871,6 +880,11 @@ impl Config { config.rust_codegen_units = rust.codegen_units.map(threads_from_config); config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config); + config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use); + config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate); + } else { + config.rust_profile_use = flags.rust_profile_use; + config.rust_profile_generate = flags.rust_profile_generate; } if let Some(t) = toml.target { @@ -924,6 +938,7 @@ impl Config { config.dist_sign_folder = t.sign_folder.map(PathBuf::from); config.dist_gpg_password_file = t.gpg_password_file.map(PathBuf::from); config.dist_upload_addr = t.upload_addr; + config.dist_compression_formats = t.compression_formats; set(&mut config.rust_dist_src, t.src_tarball); set(&mut config.missing_tools, t.missing_tools); } diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 42f00ce96217..2cabaee68ea6 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -147,6 +147,8 @@ v("experimental-targets", "llvm.experimental-targets", "experimental LLVM targets to build") v("release-channel", "rust.channel", "the name of the release channel to build") v("release-description", "rust.description", "optional descriptive string for version output") +v("dist-compression-formats", None, + "comma-separated list of compression formats to use") # Used on systems where "cc" is unavailable v("default-linker", "rust.default-linker", "the default linker") @@ -349,6 +351,8 @@ for key in known_args: elif option.name == 'option-checking': # this was handled above pass + elif option.name == 'dist-compression-formats': + set('dist.compression-formats', value.split(',')) else: raise RuntimeError("unhandled option {}".format(option.name)) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 354be109cf2f..daec1656b27c 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -19,6 +19,7 @@ use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; use crate::compile; use crate::config::TargetSelection; +use crate::tarball::{GeneratedTarball, OverlayKind, Tarball}; use crate::tool::{self, Tool}; use crate::util::{exe, is_dylib, timeit}; use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS}; @@ -36,10 +37,6 @@ pub fn tmpdir(builder: &Builder<'_>) -> PathBuf { builder.out.join("tmp/dist") } -fn rust_installer(builder: &Builder<'_>) -> Command { - builder.tool_cmd(Tool::RustInstaller) -} - fn missing_tool(tool_name: &str, skip: bool) { if skip { println!("Unable to build {}, skipping dist", tool_name) @@ -54,7 +51,7 @@ pub struct Docs { } impl Step for Docs { - type Output = PathBuf; + type Output = Option; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -66,48 +63,20 @@ impl Step for Docs { } /// Builds the `rust-docs` installer component. - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> Option { let host = self.host; - - let name = pkgname(builder, "rust-docs"); - if !builder.config.docs { - return distdir(builder).join(format!("{}-{}.tar.gz", name, host.triple)); + return None; } - builder.default_doc(None); - builder.info(&format!("Dist docs ({})", host)); - let _time = timeit(builder); + let dest = "share/doc/rust/html"; - let image = tmpdir(builder).join(format!("{}-{}-image", name, host.triple)); - let _ = fs::remove_dir_all(&image); - - let dst = image.join("share/doc/rust/html"); - t!(fs::create_dir_all(&dst)); - let src = builder.doc_out(host); - builder.cp_r(&src, &dst); - builder.install(&builder.src.join("src/doc/robots.txt"), &dst, 0o644); - - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust-Documentation") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=Rust-documentation-is-installed.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg(format!("--package-name={}-{}", name, host.triple)) - .arg("--component-name=rust-docs") - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--bulk-dirs=share/doc/rust/html"); - builder.run(&mut cmd); - builder.remove_dir(&image); - - distdir(builder).join(format!("{}-{}.tar.gz", name, host.triple)) + let mut tarball = Tarball::new(builder, "rust-docs", &host.triple); + tarball.set_product_name("Rust Documentation"); + tarball.add_dir(&builder.doc_out(host), dest); + tarball.add_file(&builder.src.join("src/doc/robots.txt"), dest, 0o644); + Some(tarball.generate()) } } @@ -117,7 +86,7 @@ pub struct RustcDocs { } impl Step for RustcDocs { - type Output = PathBuf; + type Output = Option; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -129,47 +98,17 @@ impl Step for RustcDocs { } /// Builds the `rustc-docs` installer component. - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> Option { let host = self.host; - - let name = pkgname(builder, "rustc-docs"); - if !builder.config.compiler_docs { - return distdir(builder).join(format!("{}-{}.tar.gz", name, host.triple)); + return None; } - builder.default_doc(None); - let image = tmpdir(builder).join(format!("{}-{}-image", name, host.triple)); - let _ = fs::remove_dir_all(&image); - - let dst = image.join("share/doc/rust/html/rustc"); - t!(fs::create_dir_all(&dst)); - let src = builder.compiler_doc_out(host); - builder.cp_r(&src, &dst); - - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rustc-Documentation") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=Rustc-documentation-is-installed.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg(format!("--package-name={}-{}", name, host.triple)) - .arg("--component-name=rustc-docs") - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--bulk-dirs=share/doc/rust/html/rustc"); - - builder.info(&format!("Dist compiler docs ({})", host)); - let _time = timeit(builder); - builder.run(&mut cmd); - builder.remove_dir(&image); - - distdir(builder).join(format!("{}-{}.tar.gz", name, host.triple)) + let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple); + tarball.set_product_name("Rustc Documentation"); + tarball.add_dir(&builder.compiler_doc_out(host), "share/doc/rust/html/rustc"); + Some(tarball.generate()) } } @@ -328,7 +267,7 @@ pub struct Mingw { } impl Step for Mingw { - type Output = Option; + type Output = Option; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -343,43 +282,22 @@ impl Step for Mingw { /// /// This contains all the bits and pieces to run the MinGW Windows targets /// without any extra installed software (e.g., we bundle gcc, libraries, etc). - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> Option { let host = self.host; - if !host.contains("pc-windows-gnu") { return None; } - builder.info(&format!("Dist mingw ({})", host)); - let _time = timeit(builder); - let name = pkgname(builder, "rust-mingw"); - let image = tmpdir(builder).join(format!("{}-{}-image", name, host.triple)); - let _ = fs::remove_dir_all(&image); - t!(fs::create_dir_all(&image)); + let mut tarball = Tarball::new(builder, "rust-mingw", &host.triple); + tarball.set_product_name("Rust MinGW"); // The first argument is a "temporary directory" which is just // thrown away (this contains the runtime DLLs included in the rustc package // above) and the second argument is where to place all the MinGW components // (which is what we want). - make_win_dist(&tmpdir(builder), &image, host, &builder); + make_win_dist(&tmpdir(builder), tarball.image_dir(), host, &builder); - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust-MinGW") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=Rust-MinGW-is-installed.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg(format!("--package-name={}-{}", name, host.triple)) - .arg("--component-name=rust-mingw") - .arg("--legacy-manifest-dirs=rustlib,cargo"); - builder.run(&mut cmd); - t!(fs::remove_dir_all(&image)); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, host.triple))) + Some(tarball.generate()) } } @@ -389,7 +307,7 @@ pub struct Rustc { } impl Step for Rustc { - type Output = PathBuf; + type Output = GeneratedTarball; const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; @@ -403,34 +321,14 @@ impl Step for Rustc { } /// Creates the `rustc` installer component. - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> GeneratedTarball { let compiler = self.compiler; let host = self.compiler.host; - let name = pkgname(builder, "rustc"); - let image = tmpdir(builder).join(format!("{}-{}-image", name, host.triple)); - let _ = fs::remove_dir_all(&image); - let overlay = tmpdir(builder).join(format!("{}-{}-overlay", name, host.triple)); - let _ = fs::remove_dir_all(&overlay); + let tarball = Tarball::new(builder, "rustc", &host.triple); // Prepare the rustc "image", what will actually end up getting installed - prepare_image(builder, compiler, &image); - - // Prepare the overlay which is part of the tarball but won't actually be - // installed - let cp = |file: &str| { - builder.install(&builder.src.join(file), &overlay, 0o644); - }; - cp("COPYRIGHT"); - cp("LICENSE-APACHE"); - cp("LICENSE-MIT"); - cp("README.md"); - // tiny morsel of metadata is used by rust-packaging - let version = builder.rust_version(); - builder.create(&overlay.join("version"), &version); - if let Some(sha) = builder.rust_sha() { - builder.create(&overlay.join("git-commit-hash"), &sha); - } + prepare_image(builder, compiler, tarball.image_dir()); // On MinGW we've got a few runtime DLL dependencies that we need to // include. The first argument to this script is where to put these DLLs @@ -443,38 +341,11 @@ impl Step for Rustc { // install will *also* include the rust-mingw package, which also needs // licenses, so to be safe we just include it here in all MinGW packages. if host.contains("pc-windows-gnu") { - make_win_dist(&image, &tmpdir(builder), host, builder); - - let dst = image.join("share/doc"); - t!(fs::create_dir_all(&dst)); - builder.cp_r(&builder.src.join("src/etc/third-party"), &dst); + make_win_dist(tarball.image_dir(), &tmpdir(builder), host, builder); + tarball.add_dir(builder.src.join("src/etc/third-party"), "share/doc"); } - // Finally, wrap everything up in a nice tarball! - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=Rust-is-ready-to-roll.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, host.triple)) - .arg("--component-name=rustc") - .arg("--legacy-manifest-dirs=rustlib,cargo"); - - builder.info(&format!("Dist rustc stage{} ({})", compiler.stage, host.triple)); - let _time = timeit(builder); - builder.run(&mut cmd); - builder.remove_dir(&image); - builder.remove_dir(&overlay); - - return distdir(builder).join(format!("{}-{}.tar.gz", name, host.triple)); + return tarball.generate(); fn prepare_image(builder: &Builder<'_>, compiler: Compiler, image: &Path) { let host = compiler.host; @@ -523,17 +394,20 @@ impl Step for Rustc { // component for now. maybe_install_llvm_runtime(builder, host, image); + let src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin"); + let dst_dir = image.join("lib/rustlib").join(&*host.triple).join("bin"); + t!(fs::create_dir_all(&dst_dir)); + // Copy over lld if it's there if builder.config.lld_enabled { let exe = exe("rust-lld", compiler.host); - let src = - builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin").join(&exe); - // for the rationale about this rename check `compile::copy_lld_to_sysroot` - let dst = image.join("lib/rustlib").join(&*host.triple).join("bin").join(&exe); - t!(fs::create_dir_all(&dst.parent().unwrap())); - builder.copy(&src, &dst); + builder.copy(&src_dir.join(&exe), &dst_dir.join(&exe)); } + // Copy over llvm-dwp if it's there + let exe = exe("rust-llvm-dwp", compiler.host); + builder.copy(&src_dir.join(&exe), &dst_dir.join(&exe)); + // Man pages t!(fs::create_dir_all(image.join("share/man/man1"))); let man_src = builder.src.join("src/doc/man"); @@ -681,7 +555,7 @@ pub struct Std { } impl Step for Std { - type Output = PathBuf; + type Output = Option; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -699,46 +573,24 @@ impl Step for Std { }); } - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; - let name = pkgname(builder, "rust-std"); - let archive = distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple)); if skip_host_target_lib(builder, compiler) { - return archive; + return None; } builder.ensure(compile::Std { compiler, target }); - let image = tmpdir(builder).join(format!("{}-{}-image", name, target.triple)); - let _ = fs::remove_dir_all(&image); + let mut tarball = Tarball::new(builder, "rust-std", &target.triple); + tarball.include_target_in_component_name(true); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); let stamp = compile::libstd_stamp(builder, compiler_to_use, target); - copy_target_libs(builder, target, &image, &stamp); + copy_target_libs(builder, target, &tarball.image_dir(), &stamp); - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=std-is-standing-at-the-ready.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg(format!("--component-name=rust-std-{}", target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo"); - - builder - .info(&format!("Dist std stage{} ({} -> {})", compiler.stage, &compiler.host, target)); - let _time = timeit(builder); - builder.run(&mut cmd); - builder.remove_dir(&image); - archive + Some(tarball.generate()) } } @@ -749,7 +601,7 @@ pub struct RustcDev { } impl Step for RustcDev { - type Output = PathBuf; + type Output = Option; const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; @@ -768,60 +620,36 @@ impl Step for RustcDev { }); } - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; - - let name = pkgname(builder, "rustc-dev"); - let archive = distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple)); if skip_host_target_lib(builder, compiler) { - return archive; + return None; } builder.ensure(compile::Rustc { compiler, target }); - let image = tmpdir(builder).join(format!("{}-{}-image", name, target.triple)); - let _ = fs::remove_dir_all(&image); + let tarball = Tarball::new(builder, "rustc-dev", &target.triple); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); let stamp = compile::librustc_stamp(builder, compiler_to_use, target); - copy_target_libs(builder, target, &image, &stamp); + copy_target_libs(builder, target, tarball.image_dir(), &stamp); - // Copy compiler sources. - let dst_src = image.join("lib/rustlib/rustc-src/rust"); - t!(fs::create_dir_all(&dst_src)); - - let src_files = ["Cargo.lock"]; + let src_files = &["Cargo.lock"]; // This is the reduced set of paths which will become the rustc-dev component // (essentially the compiler crates and all of their path dependencies). - copy_src_dirs(builder, &builder.src, &["compiler"], &[], &dst_src); - for file in src_files.iter() { - builder.copy(&builder.src.join(file), &dst_src.join(file)); + copy_src_dirs( + builder, + &builder.src, + &["compiler"], + &[], + &tarball.image_dir().join("lib/rustlib/rustc-src/rust"), + ); + for file in src_files { + tarball.add_file(builder.src.join(file), "lib/rustlib/rustc-src/rust", 0o644); } - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=Rust-is-ready-to-develop.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg(format!("--component-name=rustc-dev-{}", target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo"); - - builder.info(&format!( - "Dist rustc-dev stage{} ({} -> {})", - compiler.stage, &compiler.host, target - )); - let _time = timeit(builder); - builder.run(&mut cmd); - builder.remove_dir(&image); - archive + Some(tarball.generate()) } } @@ -832,7 +660,7 @@ pub struct Analysis { } impl Step for Analysis { - type Output = PathBuf; + type Output = Option; const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -855,52 +683,26 @@ impl Step for Analysis { } /// Creates a tarball of save-analysis metadata, if available. - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; assert!(builder.config.extended); - let name = pkgname(builder, "rust-analysis"); - if compiler.host != builder.config.build { - return distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple)); + return None; } builder.ensure(compile::Std { compiler, target }); - - let image = tmpdir(builder).join(format!("{}-{}-image", name, target.triple)); - let src = builder .stage_out(compiler, Mode::Std) .join(target.triple) .join(builder.cargo_dir()) - .join("deps"); + .join("deps") + .join("save-analysis"); - let image_src = src.join("save-analysis"); - let dst = image.join("lib/rustlib").join(target.triple).join("analysis"); - t!(fs::create_dir_all(&dst)); - builder.info(&format!("image_src: {:?}, dst: {:?}", image_src, dst)); - builder.cp_r(&image_src, &dst); - - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=save-analysis-saved.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg(format!("--component-name=rust-analysis-{}", target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo"); - - builder.info("Dist analysis"); - let _time = timeit(builder); - builder.run(&mut cmd); - builder.remove_dir(&image); - distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple)) + let mut tarball = Tarball::new(builder, "rust-analysis", &target.triple); + tarball.include_target_in_component_name(true); + tarball.add_dir(src, format!("lib/rustlib/{}/analysis", target.triple)); + Some(tarball.generate()) } } @@ -994,7 +796,7 @@ pub struct Src; impl Step for Src { /// The output path of the src installer tarball - type Output = PathBuf; + type Output = GeneratedTarball; const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; @@ -1007,10 +809,8 @@ impl Step for Src { } /// Creates the `rust-src` installer component - fn run(self, builder: &Builder<'_>) -> PathBuf { - let name = pkgname(builder, "rust-src"); - let image = tmpdir(builder).join(format!("{}-image", name)); - let _ = fs::remove_dir_all(&image); + fn run(self, builder: &Builder<'_>) -> GeneratedTarball { + let tarball = Tarball::new_targetless(builder, "rust-src"); // A lot of tools expect the rust-src component to be entirely in this directory, so if you // change that (e.g. by adding another directory `lib/rustlib/src/foo` or @@ -1019,8 +819,7 @@ impl Step for Src { // // NOTE: if you update the paths here, you also should update the "virtual" path // translation code in `imported_source_files` in `src/librustc_metadata/rmeta/decoder.rs` - let dst_src = image.join("lib/rustlib/src/rust"); - t!(fs::create_dir_all(&dst_src)); + let dst_src = tarball.image_dir().join("lib/rustlib/src/rust"); let src_files = ["Cargo.lock"]; // This is the reduced set of paths which will become the rust-src component @@ -1040,50 +839,7 @@ impl Step for Src { builder.copy(&builder.src.join(file), &dst_src.join(file)); } - // libtest includes std and everything else, so vendoring it - // creates exactly what's needed for `cargo -Zbuild-std` or any - // other analysis of the stdlib's source. Cargo also needs help - // finding the lock, so we copy it to libtest temporarily. - // - // Note that this requires std to only have one version of each - // crate. e.g. two versions of getopts won't be patchable. - let dst_libtest = dst_src.join("library/test"); - let dst_vendor = dst_src.join("vendor"); - let root_lock = dst_src.join("Cargo.lock"); - let temp_lock = dst_libtest.join("Cargo.lock"); - - // `cargo vendor` will delete everything from the lockfile that - // isn't used by libtest, so we need to not use any links! - builder.really_copy(&root_lock, &temp_lock); - - let mut cmd = Command::new(&builder.initial_cargo); - cmd.arg("vendor").arg(dst_vendor).current_dir(&dst_libtest); - builder.info("Dist src"); - let _time = timeit(builder); - builder.run(&mut cmd); - - builder.remove(&temp_lock); - - // Create source tarball in rust-installer format - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=Awesome-Source.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg(format!("--package-name={}", name)) - .arg("--component-name=rust-src") - .arg("--legacy-manifest-dirs=rustlib,cargo"); - - builder.run(&mut cmd); - - builder.remove_dir(&image); - distdir(builder).join(&format!("{}.tar.gz", name)) + tarball.generate() } } @@ -1092,7 +848,7 @@ pub struct PlainSourceTarball; impl Step for PlainSourceTarball { /// Produces the location of the tarball generated - type Output = PathBuf; + type Output = GeneratedTarball; const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; @@ -1106,12 +862,9 @@ impl Step for PlainSourceTarball { } /// Creates the plain source tarball - fn run(self, builder: &Builder<'_>) -> PathBuf { - // Make sure that the root folder of tarball has the correct name - let plain_name = format!("{}-src", pkgname(builder, "rustc")); - let plain_dst_src = tmpdir(builder).join(&plain_name); - let _ = fs::remove_dir_all(&plain_dst_src); - t!(fs::create_dir_all(&plain_dst_src)); + fn run(self, builder: &Builder<'_>) -> GeneratedTarball { + let tarball = Tarball::new(builder, "rustc", "src"); + let plain_dst_src = tarball.image_dir(); // This is the set of root paths which will become part of the source package let src_files = [ @@ -1154,28 +907,7 @@ impl Step for PlainSourceTarball { builder.run(&mut cmd); } - // Create plain source tarball - let plain_name = format!("rustc-{}-src", builder.rust_package_vers()); - let mut tarball = distdir(builder).join(&format!("{}.tar.gz", plain_name)); - tarball.set_extension(""); // strip .gz - tarball.set_extension(""); // strip .tar - if let Some(dir) = tarball.parent() { - builder.create_dir(&dir); - } - builder.info("running installer"); - let mut cmd = rust_installer(builder); - cmd.arg("tarball") - .arg("--input") - .arg(&plain_name) - .arg("--output") - .arg(&tarball) - .arg("--work-dir=.") - .current_dir(tmpdir(builder)); - - builder.info("Create plain source tarball"); - let _time = timeit(builder); - builder.run(&mut cmd); - distdir(builder).join(&format!("{}.tar.gz", plain_name)) + tarball.bare() } } @@ -1186,7 +918,7 @@ pub fn sanitize_sh(path: &Path) -> String { return change_drive(unc_to_lfs(&path)).unwrap_or(path); fn unc_to_lfs(s: &str) -> &str { - if s.starts_with("//?/") { &s[4..] } else { s } + s.strip_prefix("//?/").unwrap_or(s) } fn change_drive(s: &str) -> Option { @@ -1209,7 +941,7 @@ pub struct Cargo { } impl Step for Cargo { - type Output = PathBuf; + type Output = GeneratedTarball; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1227,70 +959,32 @@ impl Step for Cargo { }); } - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> GeneratedTarball { let compiler = self.compiler; let target = self.target; + let cargo = builder.ensure(tool::Cargo { compiler, target }); let src = builder.src.join("src/tools/cargo"); let etc = src.join("src/etc"); - let release_num = builder.release_num("cargo"); - let name = pkgname(builder, "cargo"); - let version = builder.cargo_info.version(builder, &release_num); - - let tmp = tmpdir(builder); - let image = tmp.join("cargo-image"); - drop(fs::remove_dir_all(&image)); - builder.create_dir(&image); // Prepare the image directory - builder.create_dir(&image.join("share/zsh/site-functions")); - builder.create_dir(&image.join("etc/bash_completion.d")); - let cargo = builder.ensure(tool::Cargo { compiler, target }); - builder.install(&cargo, &image.join("bin"), 0o755); - for man in t!(etc.join("man").read_dir()) { - let man = t!(man); - builder.install(&man.path(), &image.join("share/man/man1"), 0o644); + let mut tarball = Tarball::new(builder, "cargo", &target.triple); + tarball.set_overlay(OverlayKind::Cargo); + + tarball.add_file(&cargo, "bin", 0o755); + tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", 0o644); + tarball.add_renamed_file(etc.join("cargo.bashcomp.sh"), "etc/bash_completion.d", "cargo"); + tarball.add_dir(etc.join("man"), "share/man/man1"); + tarball.add_legal_and_readme_to("share/doc/cargo"); + + for dirent in fs::read_dir(cargo.parent().unwrap()).expect("read_dir") { + let dirent = dirent.expect("read dir entry"); + if dirent.file_name().to_str().expect("utf8").starts_with("cargo-credential-") { + tarball.add_file(&dirent.path(), "libexec", 0o755); + } } - builder.install(&etc.join("_cargo"), &image.join("share/zsh/site-functions"), 0o644); - builder.copy(&etc.join("cargo.bashcomp.sh"), &image.join("etc/bash_completion.d/cargo")); - let doc = image.join("share/doc/cargo"); - builder.install(&src.join("README.md"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - builder.install(&src.join("LICENSE-THIRD-PARTY"), &doc, 0o644); - // Prepare the overlay - let overlay = tmp.join("cargo-overlay"); - drop(fs::remove_dir_all(&overlay)); - builder.create_dir(&overlay); - builder.install(&src.join("README.md"), &overlay, 0o644); - builder.install(&src.join("LICENSE-MIT"), &overlay, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &overlay, 0o644); - builder.install(&src.join("LICENSE-THIRD-PARTY"), &overlay, 0o644); - builder.create(&overlay.join("version"), &version); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=Rust-is-ready-to-roll.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg("--component-name=cargo") - .arg("--legacy-manifest-dirs=rustlib,cargo"); - - builder.info(&format!("Dist cargo stage{} ({})", compiler.stage, target)); - let _time = timeit(builder); - builder.run(&mut cmd); - distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple)) + tarball.generate() } } @@ -1301,7 +995,7 @@ pub struct Rls { } impl Step for Rls { - type Output = Option; + type Output = Option; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1319,24 +1013,11 @@ impl Step for Rls { }); } - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; assert!(builder.config.extended); - let src = builder.src.join("src/tools/rls"); - let release_num = builder.release_num("rls"); - let name = pkgname(builder, "rls"); - let version = builder.rls_info.version(builder, &release_num); - - let tmp = tmpdir(builder); - let image = tmp.join("rls-image"); - drop(fs::remove_dir_all(&image)); - t!(fs::create_dir_all(&image)); - - // Prepare the image directory - // We expect RLS to build, because we've exited this step above if tool - // state for RLS isn't testing. let rls = builder .ensure(tool::Rls { compiler, target, extra_features: Vec::new() }) .or_else(|| { @@ -1344,43 +1025,12 @@ impl Step for Rls { None })?; - builder.install(&rls, &image.join("bin"), 0o755); - let doc = image.join("share/doc/rls"); - builder.install(&src.join("README.md"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - - // Prepare the overlay - let overlay = tmp.join("rls-overlay"); - drop(fs::remove_dir_all(&overlay)); - t!(fs::create_dir_all(&overlay)); - builder.install(&src.join("README.md"), &overlay, 0o644); - builder.install(&src.join("LICENSE-MIT"), &overlay, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &overlay, 0o644); - builder.create(&overlay.join("version"), &version); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=RLS-ready-to-serve.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=rls-preview"); - - builder.info(&format!("Dist RLS stage{} ({})", compiler.stage, target.triple)); - let _time = timeit(builder); - builder.run(&mut cmd); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) + let mut tarball = Tarball::new(builder, "rls", &target.triple); + tarball.set_overlay(OverlayKind::RLS); + tarball.is_preview(true); + tarball.add_file(rls, "bin", 0o755); + tarball.add_legal_and_readme_to("share/doc/rls"); + Some(tarball.generate()) } } @@ -1391,7 +1041,7 @@ pub struct RustAnalyzer { } impl Step for RustAnalyzer { - type Output = Option; + type Output = Option; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1409,7 +1059,7 @@ impl Step for RustAnalyzer { }); } - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; assert!(builder.config.extended); @@ -1420,60 +1070,16 @@ impl Step for RustAnalyzer { return None; } - let src = builder.src.join("src/tools/rust-analyzer"); - let release_num = builder.release_num("rust-analyzer/crates/rust-analyzer"); - let name = pkgname(builder, "rust-analyzer"); - let version = builder.rust_analyzer_info.version(builder, &release_num); - - let tmp = tmpdir(builder); - let image = tmp.join("rust-analyzer-image"); - drop(fs::remove_dir_all(&image)); - builder.create_dir(&image); - - // Prepare the image directory - // We expect rust-analyer to always build, as it doesn't depend on rustc internals - // and doesn't have associated toolstate. let rust_analyzer = builder .ensure(tool::RustAnalyzer { compiler, target, extra_features: Vec::new() }) .expect("rust-analyzer always builds"); - builder.install(&rust_analyzer, &image.join("bin"), 0o755); - let doc = image.join("share/doc/rust-analyzer"); - builder.install(&src.join("README.md"), &doc, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - - // Prepare the overlay - let overlay = tmp.join("rust-analyzer-overlay"); - drop(fs::remove_dir_all(&overlay)); - t!(fs::create_dir_all(&overlay)); - builder.install(&src.join("README.md"), &overlay, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - builder.create(&overlay.join("version"), &version); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=rust-analyzer-ready-to-serve.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=rust-analyzer-preview"); - - builder.info(&format!("Dist rust-analyzer stage{} ({})", compiler.stage, target)); - let _time = timeit(builder); - builder.run(&mut cmd); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) + let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple); + tarball.set_overlay(OverlayKind::RustAnalyzer); + tarball.is_preview(true); + tarball.add_file(rust_analyzer, "bin", 0o755); + tarball.add_legal_and_readme_to("share/doc/rust-analyzer"); + Some(tarball.generate()) } } @@ -1484,7 +1090,7 @@ pub struct Clippy { } impl Step for Clippy { - type Output = PathBuf; + type Output = GeneratedTarball; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1502,21 +1108,11 @@ impl Step for Clippy { }); } - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> GeneratedTarball { let compiler = self.compiler; let target = self.target; assert!(builder.config.extended); - let src = builder.src.join("src/tools/clippy"); - let release_num = builder.release_num("clippy"); - let name = pkgname(builder, "clippy"); - let version = builder.clippy_info.version(builder, &release_num); - - let tmp = tmpdir(builder); - let image = tmp.join("clippy-image"); - drop(fs::remove_dir_all(&image)); - builder.create_dir(&image); - // Prepare the image directory // We expect clippy to build, because we've exited this step above if tool // state for clippy isn't testing. @@ -1527,44 +1123,13 @@ impl Step for Clippy { .ensure(tool::CargoClippy { compiler, target, extra_features: Vec::new() }) .expect("clippy expected to build - essential tool"); - builder.install(&clippy, &image.join("bin"), 0o755); - builder.install(&cargoclippy, &image.join("bin"), 0o755); - let doc = image.join("share/doc/clippy"); - builder.install(&src.join("README.md"), &doc, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - - // Prepare the overlay - let overlay = tmp.join("clippy-overlay"); - drop(fs::remove_dir_all(&overlay)); - t!(fs::create_dir_all(&overlay)); - builder.install(&src.join("README.md"), &overlay, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - builder.create(&overlay.join("version"), &version); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=clippy-ready-to-serve.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=clippy-preview"); - - builder.info(&format!("Dist clippy stage{} ({})", compiler.stage, target)); - let _time = timeit(builder); - builder.run(&mut cmd); - distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple)) + let mut tarball = Tarball::new(builder, "clippy", &target.triple); + tarball.set_overlay(OverlayKind::Clippy); + tarball.is_preview(true); + tarball.add_file(clippy, "bin", 0o755); + tarball.add_file(cargoclippy, "bin", 0o755); + tarball.add_legal_and_readme_to("share/doc/clippy"); + tarball.generate() } } @@ -1575,7 +1140,7 @@ pub struct Miri { } impl Step for Miri { - type Output = Option; + type Output = Option; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1593,24 +1158,11 @@ impl Step for Miri { }); } - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; assert!(builder.config.extended); - let src = builder.src.join("src/tools/miri"); - let release_num = builder.release_num("miri"); - let name = pkgname(builder, "miri"); - let version = builder.miri_info.version(builder, &release_num); - - let tmp = tmpdir(builder); - let image = tmp.join("miri-image"); - drop(fs::remove_dir_all(&image)); - builder.create_dir(&image); - - // Prepare the image directory - // We expect miri to build, because we've exited this step above if tool - // state for miri isn't testing. let miri = builder .ensure(tool::Miri { compiler, target, extra_features: Vec::new() }) .or_else(|| { @@ -1624,44 +1176,13 @@ impl Step for Miri { None })?; - builder.install(&miri, &image.join("bin"), 0o755); - builder.install(&cargomiri, &image.join("bin"), 0o755); - let doc = image.join("share/doc/miri"); - builder.install(&src.join("README.md"), &doc, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - - // Prepare the overlay - let overlay = tmp.join("miri-overlay"); - drop(fs::remove_dir_all(&overlay)); - t!(fs::create_dir_all(&overlay)); - builder.install(&src.join("README.md"), &overlay, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - builder.create(&overlay.join("version"), &version); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=miri-ready-to-serve.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=miri-preview"); - - builder.info(&format!("Dist miri stage{} ({})", compiler.stage, target)); - let _time = timeit(builder); - builder.run(&mut cmd); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) + let mut tarball = Tarball::new(builder, "miri", &target.triple); + tarball.set_overlay(OverlayKind::Miri); + tarball.is_preview(true); + tarball.add_file(miri, "bin", 0o755); + tarball.add_file(cargomiri, "bin", 0o755); + tarball.add_legal_and_readme_to("share/doc/miri"); + Some(tarball.generate()) } } @@ -1672,7 +1193,7 @@ pub struct Rustfmt { } impl Step for Rustfmt { - type Output = Option; + type Output = Option; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1690,21 +1211,10 @@ impl Step for Rustfmt { }); } - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; - let src = builder.src.join("src/tools/rustfmt"); - let release_num = builder.release_num("rustfmt"); - let name = pkgname(builder, "rustfmt"); - let version = builder.rustfmt_info.version(builder, &release_num); - - let tmp = tmpdir(builder); - let image = tmp.join("rustfmt-image"); - drop(fs::remove_dir_all(&image)); - builder.create_dir(&image); - - // Prepare the image directory let rustfmt = builder .ensure(tool::Rustfmt { compiler, target, extra_features: Vec::new() }) .or_else(|| { @@ -1718,44 +1228,13 @@ impl Step for Rustfmt { None })?; - builder.install(&rustfmt, &image.join("bin"), 0o755); - builder.install(&cargofmt, &image.join("bin"), 0o755); - let doc = image.join("share/doc/rustfmt"); - builder.install(&src.join("README.md"), &doc, 0o644); - builder.install(&src.join("LICENSE-MIT"), &doc, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &doc, 0o644); - - // Prepare the overlay - let overlay = tmp.join("rustfmt-overlay"); - drop(fs::remove_dir_all(&overlay)); - builder.create_dir(&overlay); - builder.install(&src.join("README.md"), &overlay, 0o644); - builder.install(&src.join("LICENSE-MIT"), &overlay, 0o644); - builder.install(&src.join("LICENSE-APACHE"), &overlay, 0o644); - builder.create(&overlay.join("version"), &version); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=rustfmt-ready-to-fmt.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=rustfmt-preview"); - - builder.info(&format!("Dist Rustfmt stage{} ({})", compiler.stage, target)); - let _time = timeit(builder); - builder.run(&mut cmd); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) + let mut tarball = Tarball::new(builder, "rustfmt", &target.triple); + tarball.set_overlay(OverlayKind::Rustfmt); + tarball.is_preview(true); + tarball.add_file(rustfmt, "bin", 0o755); + tarball.add_file(cargofmt, "bin", 0o755); + tarball.add_legal_and_readme_to("share/doc/rustfmt"); + Some(tarball.generate()) } } @@ -1804,24 +1283,14 @@ impl Step for Extended { let analysis_installer = builder.ensure(Analysis { compiler, target }); let docs_installer = builder.ensure(Docs { host: target }); - let std_installer = - builder.ensure(Std { compiler: builder.compiler(stage, target), target }); + let std_installer = builder.ensure(Std { compiler, target }); - let tmp = tmpdir(builder); - let overlay = tmp.join("extended-overlay"); let etc = builder.src.join("src/etc/installer"); - let work = tmp.join("work"); - let _ = fs::remove_dir_all(&overlay); - builder.install(&builder.src.join("COPYRIGHT"), &overlay, 0o644); - builder.install(&builder.src.join("LICENSE-APACHE"), &overlay, 0o644); - builder.install(&builder.src.join("LICENSE-MIT"), &overlay, 0o644); - let version = builder.rust_version(); - builder.create(&overlay.join("version"), &version); - if let Some(sha) = builder.rust_sha() { - builder.create(&overlay.join("git-commit-hash"), &sha); + // Avoid producing tarballs during a dry run. + if builder.config.dry_run { + return; } - builder.install(&etc.join("README.md"), &overlay, 0o644); // When rust-std package split from rustc, we needed to ensure that during // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering @@ -1836,39 +1305,22 @@ impl Step for Extended { tarballs.extend(miri_installer.clone()); tarballs.extend(rustfmt_installer.clone()); tarballs.extend(llvm_tools_installer); - tarballs.push(analysis_installer); - tarballs.push(std_installer); - if builder.config.docs { + if let Some(analysis_installer) = analysis_installer { + tarballs.push(analysis_installer); + } + tarballs.push(std_installer.expect("missing std")); + if let Some(docs_installer) = docs_installer { tarballs.push(docs_installer); } if target.contains("pc-windows-gnu") { tarballs.push(mingw_installer.unwrap()); } - let mut input_tarballs = tarballs[0].as_os_str().to_owned(); - for tarball in &tarballs[1..] { - input_tarballs.push(","); - input_tarballs.push(tarball); - } - builder.info("building combined installer"); - let mut cmd = rust_installer(builder); - cmd.arg("combine") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=Rust-is-ready-to-roll.") - .arg("--work-dir") - .arg(&work) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg(format!("--package-name={}-{}", pkgname(builder, "rust"), target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--input-tarballs") - .arg(input_tarballs) - .arg("--non-installed-overlay") - .arg(&overlay); - let time = timeit(&builder); - builder.run(&mut cmd); - drop(time); + let tarball = Tarball::new(builder, "rust", &target.triple); + let generated = tarball.combine(&tarballs); + + let tmp = tmpdir(builder).join("combined-tarball"); + let work = generated.work_dir(); let mut license = String::new(); license += &builder.read(&builder.src.join("COPYRIGHT")); @@ -2418,7 +1870,7 @@ pub struct LlvmTools { } impl Step for LlvmTools { - type Output = Option; + type Output = Option; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -2429,7 +1881,7 @@ impl Step for LlvmTools { run.builder.ensure(LlvmTools { target: run.target }); } - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> Option { let target = self.target; assert!(builder.config.extended); @@ -2441,58 +1893,25 @@ impl Step for LlvmTools { } } - builder.info(&format!("Dist LlvmTools ({})", target)); - let _time = timeit(builder); - let src = builder.src.join("src/llvm-project/llvm"); - let name = pkgname(builder, "llvm-tools"); - - let tmp = tmpdir(builder); - let image = tmp.join("llvm-tools-image"); - drop(fs::remove_dir_all(&image)); + let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple); + tarball.set_overlay(OverlayKind::LLVM); + tarball.is_preview(true); // Prepare the image directory let src_bindir = builder.llvm_out(target).join("bin"); - let dst_bindir = image.join("lib/rustlib").join(&*target.triple).join("bin"); - t!(fs::create_dir_all(&dst_bindir)); + let dst_bindir = format!("lib/rustlib/{}/bin", target.triple); for tool in LLVM_TOOLS { let exe = src_bindir.join(exe(tool, target)); - builder.install(&exe, &dst_bindir, 0o755); + tarball.add_file(&exe, &dst_bindir, 0o755); } // Copy libLLVM.so to the target lib dir as well, so the RPATH like // `$ORIGIN/../lib` can find it. It may also be used as a dependency // of `rustc-dev` to support the inherited `-lLLVM` when using the // compiler libraries. - maybe_install_llvm_target(builder, target, &image); + maybe_install_llvm_target(builder, target, tarball.image_dir()); - // Prepare the overlay - let overlay = tmp.join("llvm-tools-overlay"); - drop(fs::remove_dir_all(&overlay)); - builder.create_dir(&overlay); - builder.install(&src.join("README.txt"), &overlay, 0o644); - builder.install(&src.join("LICENSE.TXT"), &overlay, 0o644); - builder.create(&overlay.join("version"), &builder.llvm_tools_vers()); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=llvm-tools-installed.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=llvm-tools-preview"); - - builder.run(&mut cmd); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) + Some(tarball.generate()) } } @@ -2505,7 +1924,7 @@ pub struct RustDev { } impl Step for RustDev { - type Output = Option; + type Output = Option; const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; @@ -2517,7 +1936,7 @@ impl Step for RustDev { run.builder.ensure(RustDev { target: run.target }); } - fn run(self, builder: &Builder<'_>) -> Option { + fn run(self, builder: &Builder<'_>) -> Option { let target = self.target; /* run only if llvm-config isn't used */ @@ -2528,69 +1947,35 @@ impl Step for RustDev { } } - builder.info(&format!("Dist RustDev ({})", target)); - let _time = timeit(builder); - let src = builder.src.join("src/llvm-project/llvm"); - let name = pkgname(builder, "rust-dev"); - - let tmp = tmpdir(builder); - let image = tmp.join("rust-dev-image"); - drop(fs::remove_dir_all(&image)); - - // Prepare the image directory - let dst_bindir = image.join("bin"); - t!(fs::create_dir_all(&dst_bindir)); + let mut tarball = Tarball::new(builder, "rust-dev", &target.triple); + tarball.set_overlay(OverlayKind::LLVM); let src_bindir = builder.llvm_out(target).join("bin"); - let install_bin = - |name| builder.install(&src_bindir.join(exe(name, target)), &dst_bindir, 0o755); - install_bin("llvm-config"); - install_bin("llvm-ar"); - install_bin("llvm-objdump"); - install_bin("llvm-profdata"); - install_bin("llvm-bcanalyzer"); - install_bin("llvm-cov"); - builder.install(&builder.llvm_filecheck(target), &dst_bindir, 0o755); + for bin in &[ + "llvm-config", + "llvm-ar", + "llvm-objdump", + "llvm-profdata", + "llvm-bcanalyzer", + "llvm-cov", + "llvm-dwp", + ] { + tarball.add_file(src_bindir.join(exe(bin, target)), "bin", 0o755); + } + tarball.add_file(&builder.llvm_filecheck(target), "bin", 0o755); // Copy the include directory as well; needed mostly to build // librustc_llvm properly (e.g., llvm-config.h is in here). But also // just broadly useful to be able to link against the bundled LLVM. - builder.cp_r(&builder.llvm_out(target).join("include"), &image.join("include")); + tarball.add_dir(&builder.llvm_out(target).join("include"), "include"); // Copy libLLVM.so to the target lib dir as well, so the RPATH like // `$ORIGIN/../lib` can find it. It may also be used as a dependency // of `rustc-dev` to support the inherited `-lLLVM` when using the // compiler libraries. - maybe_install_llvm(builder, target, &image.join("lib")); + maybe_install_llvm(builder, target, &tarball.image_dir().join("lib")); - // Prepare the overlay - let overlay = tmp.join("rust-dev-overlay"); - drop(fs::remove_dir_all(&overlay)); - builder.create_dir(&overlay); - builder.install(&src.join("README.txt"), &overlay, 0o644); - builder.install(&src.join("LICENSE.TXT"), &overlay, 0o644); - builder.create(&overlay.join("version"), &builder.rust_version()); - - // Generate the installer tarball - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=rust-dev-installed.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=rust-dev"); - - builder.run(&mut cmd); - Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) + Some(tarball.generate()) } } @@ -2604,7 +1989,7 @@ pub struct BuildManifest { } impl Step for BuildManifest { - type Output = PathBuf; + type Output = GeneratedTarball; const DEFAULT: bool = false; const ONLY_HOSTS: bool = true; @@ -2616,47 +2001,43 @@ impl Step for BuildManifest { run.builder.ensure(BuildManifest { target: run.target }); } - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> GeneratedTarball { let build_manifest = builder.tool_exe(Tool::BuildManifest); - let name = pkgname(builder, "build-manifest"); - let tmp = tmpdir(builder); - - // Prepare the image. - let image = tmp.join("build-manifest-image"); - let image_bin = image.join("bin"); - let _ = fs::remove_dir_all(&image); - t!(fs::create_dir_all(&image_bin)); - builder.install(&build_manifest, &image_bin, 0o755); - - // Prepare the overlay. - let overlay = tmp.join("build-manifest-overlay"); - let _ = fs::remove_dir_all(&overlay); - builder.create_dir(&overlay); - builder.create(&overlay.join("version"), &builder.rust_version()); - for file in &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"] { - builder.install(&builder.src.join(file), &overlay, 0o644); - } - - // Create the final tarball. - let mut cmd = rust_installer(builder); - cmd.arg("generate") - .arg("--product-name=Rust") - .arg("--rel-manifest-dir=rustlib") - .arg("--success-message=build-manifest installed.") - .arg("--image-dir") - .arg(&image) - .arg("--work-dir") - .arg(&tmpdir(builder)) - .arg("--output-dir") - .arg(&distdir(builder)) - .arg("--non-installed-overlay") - .arg(&overlay) - .arg(format!("--package-name={}-{}", name, self.target.triple)) - .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--component-name=build-manifest"); - - builder.run(&mut cmd); - distdir(builder).join(format!("{}-{}.tar.gz", name, self.target.triple)) + let tarball = Tarball::new(builder, "build-manifest", &self.target.triple); + tarball.add_file(&build_manifest, "bin", 0o755); + tarball.generate() + } +} + +/// Tarball containing artifacts necessary to reproduce the build of rustc. +/// +/// Currently this is the PGO profile data. +/// +/// Should not be considered stable by end users. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct ReproducibleArtifacts { + pub target: TargetSelection, +} + +impl Step for ReproducibleArtifacts { + type Output = Option; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("reproducible") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(ReproducibleArtifacts { target: run.target }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let path = builder.config.rust_profile_use.as_ref()?; + + let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple); + tarball.add_file(path, ".", 0o644); + Some(tarball.generate()) } } diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index a296a1fe3f4f..8c849846676f 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -500,18 +500,17 @@ impl Step for Rustc { let target = self.target; builder.info(&format!("Documenting stage{} compiler ({})", stage, target)); - // This is the intended out directory for compiler documentation. - let out = builder.compiler_doc_out(target); - t!(fs::create_dir_all(&out)); - - let compiler = builder.compiler(stage, builder.config.build); - if !builder.config.compiler_docs { builder.info("\tskipping - compiler/librustdoc docs disabled"); return; } + // This is the intended out directory for compiler documentation. + let out = builder.compiler_doc_out(target); + t!(fs::create_dir_all(&out)); + // Build rustc. + let compiler = builder.compiler(stage, builder.config.build); builder.ensure(compile::Rustc { compiler, target }); // This uses a shared directory so that librustdoc documentation gets @@ -521,6 +520,10 @@ impl Step for Rustc { // merging the search index, or generating local (relative) links. let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc"); t!(symlink_dir_force(&builder.config, &out, &out_dir)); + // Cargo puts proc macros in `target/doc` even if you pass `--target` + // explicitly (https://github.com/rust-lang/cargo/issues/7677). + let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc"); + t!(symlink_dir_force(&builder.config, &out, &proc_macro_out_dir)); // Build cargo command. let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc"); @@ -625,6 +628,8 @@ impl Step for Rustdoc { cargo.arg("-p").arg("rustdoc"); cargo.rustdocflag("--document-private-items"); + cargo.rustdocflag("--enable-index-page"); + cargo.rustdocflag("-Zunstable-options"); builder.run(&mut cargo.into()); } } diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index d857618eefa8..b29ecd65401d 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/78131 +Last change is for: https://github.com/rust-lang/rust/pull/80087 diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 5a8096674c6d..d6a45f1c1707 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -68,6 +68,9 @@ pub struct Flags { pub deny_warnings: Option, pub llvm_skip_rebuild: Option, + + pub rust_profile_use: Option, + pub rust_profile_generate: Option, } pub enum Subcommand { @@ -219,6 +222,8 @@ To learn more about a subcommand, run `./x.py -h`", VALUE overrides the skip-rebuild option in config.toml.", "VALUE", ); + opts.optopt("", "rust-profile-generate", "rustc error format", "FORMAT"); + opts.optopt("", "rust-profile-use", "rustc error format", "FORMAT"); // We can't use getopt to parse the options until we have completed specifying which // options are valid, but under the current implementation, some options are conditional on @@ -674,6 +679,8 @@ Arguments: color: matches .opt_get_default("color", Color::Auto) .expect("`color` should be `always`, `never`, or `auto`"), + rust_profile_use: matches.opt_str("rust-profile-use"), + rust_profile_generate: matches.opt_str("rust-profile-generate"), } } } diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 074f5cd73f32..96164947943b 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -10,60 +10,19 @@ use std::process::Command; use build_helper::t; -use crate::dist::{self, pkgname, sanitize_sh, tmpdir}; +use crate::dist::{self, sanitize_sh}; +use crate::tarball::GeneratedTarball; use crate::Compiler; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::config::{Config, TargetSelection}; -pub fn install_docs(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "docs", "rust-docs", stage, Some(host)); -} - -pub fn install_std(builder: &Builder<'_>, stage: u32, target: TargetSelection) { - install_sh(builder, "std", "rust-std", stage, Some(target)); -} - -pub fn install_cargo(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "cargo", "cargo", stage, Some(host)); -} - -pub fn install_rls(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "rls", "rls", stage, Some(host)); -} - -pub fn install_rust_analyzer(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "rust-analyzer", "rust-analyzer", stage, Some(host)); -} - -pub fn install_clippy(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "clippy", "clippy", stage, Some(host)); -} -pub fn install_miri(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "miri", "miri", stage, Some(host)); -} - -pub fn install_rustfmt(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "rustfmt", "rustfmt", stage, Some(host)); -} - -pub fn install_analysis(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "analysis", "rust-analysis", stage, Some(host)); -} - -pub fn install_src(builder: &Builder<'_>, stage: u32) { - install_sh(builder, "src", "rust-src", stage, None); -} -pub fn install_rustc(builder: &Builder<'_>, stage: u32, host: TargetSelection) { - install_sh(builder, "rustc", "rustc", stage, Some(host)); -} - fn install_sh( builder: &Builder<'_>, package: &str, - name: &str, stage: u32, host: Option, + tarball: &GeneratedTarball, ) { builder.info(&format!("Install {} stage{} ({:?})", package, stage, host)); @@ -73,12 +32,7 @@ fn install_sh( let docdir_default = datadir_default.join("doc/rust"); let libdir_default = PathBuf::from("lib"); let mandir_default = datadir_default.join("man"); - let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| { - fs::create_dir_all(p) - .unwrap_or_else(|err| panic!("could not create {}: {}", p.display(), err)); - fs::canonicalize(p) - .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", p.display(), err)) - }); + let prefix = builder.config.prefix.as_ref().unwrap_or(&prefix_default); let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default); let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default); let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default); @@ -103,18 +57,20 @@ fn install_sh( let libdir = add_destdir(&libdir, &destdir); let mandir = add_destdir(&mandir, &destdir); + let prefix = { + fs::create_dir_all(&prefix) + .unwrap_or_else(|err| panic!("could not create {}: {}", prefix.display(), err)); + fs::canonicalize(&prefix) + .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", prefix.display(), err)) + }; + let empty_dir = builder.out.join("tmp/empty_dir"); t!(fs::create_dir_all(&empty_dir)); - let package_name = if let Some(host) = host { - format!("{}-{}", pkgname(builder, name), host.triple) - } else { - pkgname(builder, name) - }; let mut cmd = Command::new("sh"); cmd.current_dir(&empty_dir) - .arg(sanitize_sh(&tmpdir(builder).join(&package_name).join("install.sh"))) + .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"))) .arg(format!("--prefix={}", sanitize_sh(&prefix))) .arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir))) .arg(format!("--datadir={}", sanitize_sh(&datadir))) @@ -189,25 +145,25 @@ macro_rules! install { install!((self, builder, _config), Docs, "src/doc", _config.docs, only_hosts: false, { - builder.ensure(dist::Docs { host: self.target }); - install_docs(builder, self.compiler.stage, self.target); + let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs"); + install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball); }; Std, "library/std", true, only_hosts: false, { for target in &builder.targets { - builder.ensure(dist::Std { + let tarball = builder.ensure(dist::Std { compiler: self.compiler, target: *target - }); - install_std(builder, self.compiler.stage, *target); + }).expect("missing std"); + install_sh(builder, "std", self.compiler.stage, Some(*target), &tarball); } }; Cargo, "cargo", Self::should_build(_config), only_hosts: true, { - builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target }); - install_cargo(builder, self.compiler.stage, self.target); + let tarball = builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target }); + install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball); }; Rls, "rls", Self::should_build(_config), only_hosts: true, { - if builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }).is_some() { - install_rls(builder, self.compiler.stage, self.target); + if let Some(tarball) = builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }) { + install_sh(builder, "rls", self.compiler.stage, Some(self.target), &tarball); } else { builder.info( &format!("skipping Install RLS stage{} ({})", self.compiler.stage, self.target), @@ -215,16 +171,18 @@ install!((self, builder, _config), } }; RustAnalyzer, "rust-analyzer", Self::should_build(_config), only_hosts: true, { - builder.ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target }); - install_rust_analyzer(builder, self.compiler.stage, self.target); + let tarball = builder + .ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target }) + .expect("missing rust-analyzer"); + install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball); }; Clippy, "clippy", Self::should_build(_config), only_hosts: true, { - builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target }); - install_clippy(builder, self.compiler.stage, self.target); + let tarball = builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target }); + install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball); }; Miri, "miri", Self::should_build(_config), only_hosts: true, { - if builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }).is_some() { - install_miri(builder, self.compiler.stage, self.target); + if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) { + install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball); } else { builder.info( &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target), @@ -232,11 +190,11 @@ install!((self, builder, _config), } }; Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, { - if builder.ensure(dist::Rustfmt { + if let Some(tarball) = builder.ensure(dist::Rustfmt { compiler: self.compiler, target: self.target - }).is_some() { - install_rustfmt(builder, self.compiler.stage, self.target); + }) { + install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball); } else { builder.info( &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target), @@ -244,20 +202,20 @@ install!((self, builder, _config), } }; Analysis, "analysis", Self::should_build(_config), only_hosts: false, { - builder.ensure(dist::Analysis { + let tarball = builder.ensure(dist::Analysis { // Find the actual compiler (handling the full bootstrap option) which // produced the save-analysis data because that data isn't copied // through the sysroot uplifting. compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target), target: self.target - }); - install_analysis(builder, self.compiler.stage, self.target); + }).expect("missing analysis"); + install_sh(builder, "analysis", self.compiler.stage, Some(self.target), &tarball); }; Rustc, "src/librustc", true, only_hosts: true, { - builder.ensure(dist::Rustc { + let tarball = builder.ensure(dist::Rustc { compiler: builder.compiler(builder.top_stage, self.target), }); - install_rustc(builder, self.compiler.stage, self.target); + install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball); }; ); @@ -282,7 +240,7 @@ impl Step for Src { } fn run(self, builder: &Builder<'_>) { - builder.ensure(dist::Src); - install_src(builder, self.stage); + let tarball = builder.ensure(dist::Src); + install_sh(builder, "src", self.stage, None, &tarball); } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 06ccd72186dd..a47ddfbcc1f1 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -142,6 +142,7 @@ mod native; mod run; mod sanity; mod setup; +mod tarball; mod test; mod tool; mod toolstate; @@ -1068,10 +1069,6 @@ impl Build { self.package_vers(&self.version) } - fn llvm_tools_vers(&self) -> String { - self.rust_version() - } - fn llvm_link_tools_dynamically(&self, target: TargetSelection) -> bool { target.contains("linux-gnu") || target.contains("apple-darwin") } @@ -1182,27 +1179,6 @@ impl Build { paths } - /// Copies a file from `src` to `dst` and doesn't use links, so - /// that the copy can be modified without affecting the original. - pub fn really_copy(&self, src: &Path, dst: &Path) { - if self.config.dry_run { - return; - } - self.verbose_than(1, &format!("Copy {:?} to {:?}", src, dst)); - if src == dst { - return; - } - let _ = fs::remove_file(&dst); - let metadata = t!(src.symlink_metadata()); - if let Err(e) = fs::copy(src, dst) { - panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e) - } - t!(fs::set_permissions(dst, metadata.permissions())); - let atime = FileTime::from_last_access_time(&metadata); - let mtime = FileTime::from_last_modification_time(&metadata); - t!(filetime::set_file_times(dst, atime, mtime)); - } - /// Copies a file from `src` to `dst` pub fn copy(&self, src: &Path, dst: &Path) { if self.config.dry_run { diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 1564cfb06199..fd39944e176f 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -66,7 +66,6 @@ check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu: TESTS_IN_2 := \ src/test/ui \ - src/test/compile-fail \ src/tools/linkchecker ci-subset-1: @@ -75,8 +74,7 @@ ci-subset-2: $(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_2) TESTS_IN_MINGW_2 := \ - src/test/ui \ - src/test/compile-fail + src/test/ui ci-mingw-subset-1: $(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_MINGW_2:%=--exclude %) diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 4cfcf6ca407b..acb941d95407 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -17,6 +17,7 @@ use std::process::Command; use build_helper::{output, t}; +use crate::cache::INTERNER; use crate::config::Target; use crate::Build; @@ -79,18 +80,19 @@ pub fn check(build: &mut Build) { } // We need cmake, but only if we're actually building LLVM or sanitizers. - let building_llvm = build - .hosts - .iter() - .map(|host| { - build - .config - .target_config - .get(host) - .map(|config| config.llvm_config.is_none()) - .unwrap_or(true) - }) - .any(|build_llvm_ourselves| build_llvm_ourselves); + let building_llvm = build.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) + && build + .hosts + .iter() + .map(|host| { + build + .config + .target_config + .get(host) + .map(|config| config.llvm_config.is_none()) + .unwrap_or(true) + }) + .any(|build_llvm_ourselves| build_llvm_ourselves); if building_llvm || build.config.any_sanitizers_enabled() { cmd_finder.must_have("cmake"); } @@ -147,10 +149,12 @@ pub fn check(build: &mut Build) { } } - // Externally configured LLVM requires FileCheck to exist - let filecheck = build.llvm_filecheck(build.build); - if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests { - panic!("FileCheck executable {:?} does not exist", filecheck); + if build.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) { + // Externally configured LLVM requires FileCheck to exist + let filecheck = build.llvm_filecheck(build.build); + if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests { + panic!("FileCheck executable {:?} does not exist", filecheck); + } } for target in &build.targets { @@ -159,11 +163,7 @@ pub fn check(build: &mut Build) { panic!("the iOS target is only supported on macOS"); } - build - .config - .target_config - .entry(target.clone()) - .or_insert(Target::from_triple(&target.triple)); + build.config.target_config.entry(*target).or_insert(Target::from_triple(&target.triple)); if target.contains("-none-") || target.contains("nvptx") { if build.no_std(*target) == Some(false) { @@ -176,7 +176,7 @@ pub fn check(build: &mut Build) { // If this is a native target (host is also musl) and no musl-root is given, // fall back to the system toolchain in /usr before giving up if build.musl_root(*target).is_none() && build.config.build == *target { - let target = build.config.target_config.entry(target.clone()).or_default(); + let target = build.config.target_config.entry(*target).or_default(); target.musl_root = Some("/usr".into()); } match build.musl_libdir(*target) { diff --git a/src/bootstrap/tarball.rs b/src/bootstrap/tarball.rs new file mode 100644 index 000000000000..7fb03056f1bd --- /dev/null +++ b/src/bootstrap/tarball.rs @@ -0,0 +1,333 @@ +use std::{ + path::{Path, PathBuf}, + process::Command, +}; + +use build_helper::t; + +use crate::builder::Builder; + +#[derive(Copy, Clone)] +pub(crate) enum OverlayKind { + Rust, + LLVM, + Cargo, + Clippy, + Miri, + Rustfmt, + RLS, + RustAnalyzer, +} + +impl OverlayKind { + fn legal_and_readme(&self) -> &[&str] { + match self { + OverlayKind::Rust => &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"], + OverlayKind::LLVM => { + &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"] + } + OverlayKind::Cargo => &[ + "src/tools/cargo/README.md", + "src/tools/cargo/LICENSE-MIT", + "src/tools/cargo/LICENSE-APACHE", + "src/tools/cargo/LICENSE-THIRD-PARTY", + ], + OverlayKind::Clippy => &[ + "src/tools/clippy/README.md", + "src/tools/clippy/LICENSE-APACHE", + "src/tools/clippy/LICENSE-MIT", + ], + OverlayKind::Miri => &[ + "src/tools/miri/README.md", + "src/tools/miri/LICENSE-APACHE", + "src/tools/miri/LICENSE-MIT", + ], + OverlayKind::Rustfmt => &[ + "src/tools/rustfmt/README.md", + "src/tools/rustfmt/LICENSE-APACHE", + "src/tools/rustfmt/LICENSE-MIT", + ], + OverlayKind::RLS => &[ + "src/tools/rls/README.md", + "src/tools/rls/LICENSE-APACHE", + "src/tools/rls/LICENSE-MIT", + ], + OverlayKind::RustAnalyzer => &[ + "src/tools/rust-analyzer/README.md", + "src/tools/rust-analyzer/LICENSE-APACHE", + "src/tools/rust-analyzer/LICENSE-MIT", + ], + } + } + + fn version(&self, builder: &Builder<'_>) -> String { + match self { + OverlayKind::Rust => builder.rust_version(), + OverlayKind::LLVM => builder.rust_version(), + OverlayKind::Cargo => { + builder.cargo_info.version(builder, &builder.release_num("cargo")) + } + OverlayKind::Clippy => { + builder.clippy_info.version(builder, &builder.release_num("clippy")) + } + OverlayKind::Miri => builder.miri_info.version(builder, &builder.release_num("miri")), + OverlayKind::Rustfmt => { + builder.rustfmt_info.version(builder, &builder.release_num("rustfmt")) + } + OverlayKind::RLS => builder.rls_info.version(builder, &builder.release_num("rls")), + OverlayKind::RustAnalyzer => builder + .rust_analyzer_info + .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")), + } + } +} + +pub(crate) struct Tarball<'a> { + builder: &'a Builder<'a>, + + pkgname: String, + component: String, + target: Option, + product_name: String, + overlay: OverlayKind, + + temp_dir: PathBuf, + image_dir: PathBuf, + overlay_dir: PathBuf, + + include_target_in_component_name: bool, + is_preview: bool, +} + +impl<'a> Tarball<'a> { + pub(crate) fn new(builder: &'a Builder<'a>, component: &str, target: &str) -> Self { + Self::new_inner(builder, component, Some(target.into())) + } + + pub(crate) fn new_targetless(builder: &'a Builder<'a>, component: &str) -> Self { + Self::new_inner(builder, component, None) + } + + fn new_inner(builder: &'a Builder<'a>, component: &str, target: Option) -> Self { + let pkgname = crate::dist::pkgname(builder, component); + + let mut temp_dir = builder.out.join("tmp").join("tarball").join(component); + if let Some(target) = &target { + temp_dir = temp_dir.join(target); + } + let _ = std::fs::remove_dir_all(&temp_dir); + + let image_dir = temp_dir.join("image"); + let overlay_dir = temp_dir.join("overlay"); + + Self { + builder, + + pkgname, + component: component.into(), + target, + product_name: "Rust".into(), + overlay: OverlayKind::Rust, + + temp_dir, + image_dir, + overlay_dir, + + include_target_in_component_name: false, + is_preview: false, + } + } + + pub(crate) fn set_overlay(&mut self, overlay: OverlayKind) { + self.overlay = overlay; + } + + pub(crate) fn set_product_name(&mut self, name: &str) { + self.product_name = name.into(); + } + + pub(crate) fn include_target_in_component_name(&mut self, include: bool) { + self.include_target_in_component_name = include; + } + + pub(crate) fn is_preview(&mut self, is: bool) { + self.is_preview = is; + } + + pub(crate) fn image_dir(&self) -> &Path { + t!(std::fs::create_dir_all(&self.image_dir)); + &self.image_dir + } + + pub(crate) fn add_file(&self, src: impl AsRef, destdir: impl AsRef, perms: u32) { + // create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply + // uses the base directory as the destination directory. + let destdir = if destdir.as_ref() == Path::new(".") { + self.image_dir.clone() + } else { + self.image_dir.join(destdir.as_ref()) + }; + + t!(std::fs::create_dir_all(&destdir)); + self.builder.install(src.as_ref(), &destdir, perms); + } + + pub(crate) fn add_renamed_file( + &self, + src: impl AsRef, + destdir: impl AsRef, + new_name: &str, + ) { + let destdir = self.image_dir.join(destdir.as_ref()); + t!(std::fs::create_dir_all(&destdir)); + self.builder.copy(src.as_ref(), &destdir.join(new_name)); + } + + pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef) { + for file in self.overlay.legal_and_readme() { + self.add_file(self.builder.src.join(file), destdir.as_ref(), 0o644); + } + } + + pub(crate) fn add_dir(&self, src: impl AsRef, dest: impl AsRef) { + let dest = self.image_dir.join(dest.as_ref()); + + t!(std::fs::create_dir_all(&dest)); + self.builder.cp_r(src.as_ref(), &dest); + } + + pub(crate) fn generate(self) -> GeneratedTarball { + let mut component_name = self.component.clone(); + if self.is_preview { + component_name.push_str("-preview"); + } + if self.include_target_in_component_name { + component_name.push('-'); + component_name.push_str( + &self + .target + .as_ref() + .expect("include_target_in_component_name used in a targetless tarball"), + ); + } + + self.run(|this, cmd| { + cmd.arg("generate") + .arg("--image-dir") + .arg(&this.image_dir) + .arg(format!("--component-name={}", &component_name)); + this.non_bare_args(cmd); + }) + } + + pub(crate) fn combine(self, tarballs: &[GeneratedTarball]) -> GeneratedTarball { + let mut input_tarballs = tarballs[0].path.as_os_str().to_os_string(); + for tarball in &tarballs[1..] { + input_tarballs.push(","); + input_tarballs.push(&tarball.path); + } + + self.run(|this, cmd| { + cmd.arg("combine").arg("--input-tarballs").arg(input_tarballs); + this.non_bare_args(cmd); + }) + } + + pub(crate) fn bare(self) -> GeneratedTarball { + // Bare tarballs should have the top level directory match the package + // name, not "image". We rename the image directory just before passing + // into rust-installer. + let dest = self.temp_dir.join(self.package_name()); + t!(std::fs::rename(&self.image_dir, &dest)); + + self.run(|this, cmd| { + cmd.arg("tarball") + .arg("--input") + .arg(&dest) + .arg("--output") + .arg(crate::dist::distdir(this.builder).join(this.package_name())); + }) + } + + fn package_name(&self) -> String { + if let Some(target) = &self.target { + format!("{}-{}", self.pkgname, target) + } else { + self.pkgname.clone() + } + } + + fn non_bare_args(&self, cmd: &mut Command) { + cmd.arg("--rel-manifest-dir=rustlib") + .arg("--legacy-manifest-dirs=rustlib,cargo") + .arg(format!("--product-name={}", self.product_name)) + .arg(format!("--success-message={} installed.", self.component)) + .arg(format!("--package-name={}", self.package_name())) + .arg("--non-installed-overlay") + .arg(&self.overlay_dir) + .arg("--output-dir") + .arg(crate::dist::distdir(self.builder)); + } + + fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> GeneratedTarball { + t!(std::fs::create_dir_all(&self.overlay_dir)); + self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder)); + if let Some(sha) = self.builder.rust_sha() { + self.builder.create(&self.overlay_dir.join("git-commit-hash"), &sha); + } + for file in self.overlay.legal_and_readme() { + self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644); + } + + let mut cmd = self.builder.tool_cmd(crate::tool::Tool::RustInstaller); + + let package_name = self.package_name(); + self.builder.info(&format!("Dist {}", package_name)); + let _time = crate::util::timeit(self.builder); + + build_cli(&self, &mut cmd); + cmd.arg("--work-dir").arg(&self.temp_dir); + if let Some(formats) = &self.builder.config.dist_compression_formats { + assert!(!formats.is_empty(), "dist.compression-formats can't be empty"); + cmd.arg("--compression-formats").arg(formats.join(",")); + } + self.builder.run(&mut cmd); + + // Use either the first compression format defined, or "gz" as the default. + let ext = self + .builder + .config + .dist_compression_formats + .as_ref() + .and_then(|formats| formats.get(0)) + .map(|s| s.as_str()) + .unwrap_or("gz"); + + GeneratedTarball { + path: crate::dist::distdir(self.builder).join(format!("{}.tar.{}", package_name, ext)), + decompressed_output: self.temp_dir.join(package_name), + work: self.temp_dir, + } + } +} + +#[derive(Debug, Clone)] +pub struct GeneratedTarball { + path: PathBuf, + decompressed_output: PathBuf, + work: PathBuf, +} + +impl GeneratedTarball { + pub(crate) fn tarball(&self) -> &Path { + &self.path + } + + pub(crate) fn decompressed_output(&self) -> &Path { + &self.decompressed_output + } + + pub(crate) fn work_dir(&self) -> &Path { + &self.work + } +} diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 78b5de7897d1..33e252a63c9a 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -869,12 +869,6 @@ default_test_with_compare_mode!(Ui { compare_mode: "nll" }); -default_test!(CompileFail { - path: "src/test/compile-fail", - mode: "compile-fail", - suite: "compile-fail" -}); - default_test!(RunPassValgrind { path: "src/test/run-pass-valgrind", mode: "run-pass-valgrind", @@ -897,7 +891,12 @@ default_test!(Incremental { suite: "incremental" }); -default_test!(Debuginfo { path: "src/test/debuginfo", mode: "debuginfo", suite: "debuginfo" }); +default_test_with_compare_mode!(Debuginfo { + path: "src/test/debuginfo", + mode: "debuginfo", + suite: "debuginfo", + compare_mode: "split-dwarf" +}); host_test!(UiFullDeps { path: "src/test/ui-fulldeps", mode: "ui", suite: "ui-fulldeps" }); @@ -1963,8 +1962,8 @@ impl Step for Distcheck { builder.ensure(dist::Src); let mut cmd = Command::new("tar"); - cmd.arg("-xzf") - .arg(builder.ensure(dist::PlainSourceTarball)) + cmd.arg("-xf") + .arg(builder.ensure(dist::PlainSourceTarball).tarball()) .arg("--strip-components=1") .current_dir(&dir); builder.run(&mut cmd); @@ -1987,8 +1986,8 @@ impl Step for Distcheck { t!(fs::create_dir_all(&dir)); let mut cmd = Command::new("tar"); - cmd.arg("-xzf") - .arg(builder.ensure(dist::Src)) + cmd.arg("-xf") + .arg(builder.ensure(dist::Src).tarball()) .arg("--strip-components=1") .current_dir(&dir); builder.run(&mut cmd); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 290e3744852f..dc786249d996 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -563,7 +563,7 @@ impl Step for Cargo { } fn run(self, builder: &Builder<'_>) -> PathBuf { - builder + let cargo_bin_path = builder .ensure(ToolBuild { compiler: self.compiler, target: self.target, @@ -574,7 +574,40 @@ impl Step for Cargo { source_type: SourceType::Submodule, extra_features: Vec::new(), }) - .expect("expected to build -- essential tool") + .expect("expected to build -- essential tool"); + + let build_cred = |name, path| { + // These credential helpers are currently experimental. + // Any build failures will be ignored. + let _ = builder.ensure(ToolBuild { + compiler: self.compiler, + target: self.target, + tool: name, + mode: Mode::ToolRustc, + path, + is_optional_tool: true, + source_type: SourceType::Submodule, + extra_features: Vec::new(), + }); + }; + + if self.target.contains("windows") { + build_cred( + "cargo-credential-wincred", + "src/tools/cargo/crates/credential/cargo-credential-wincred", + ); + } + if self.target.contains("apple-darwin") { + build_cred( + "cargo-credential-macos-keychain", + "src/tools/cargo/crates/credential/cargo-credential-macos-keychain", + ); + } + build_cred( + "cargo-credential-1password", + "src/tools/cargo/crates/credential/cargo-credential-1password", + ); + cargo_bin_path } } diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 14700aeea05a..d1b4bbf7fffe 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -85,6 +85,8 @@ ENV CC=clang CXX=clang++ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +ENV PGO_HOST=x86_64-unknown-linux-gnu + ENV HOSTS=x86_64-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS \ @@ -98,9 +100,10 @@ ENV RUST_CONFIGURE_ARGS \ --set llvm.thin-lto=true \ --set llvm.ninja=false \ --set rust.jemalloc -ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS \ - --include-default-paths \ - src/tools/build-manifest +ENV SCRIPT ../src/ci/pgo.sh python2.7 ../x.py dist \ + --host $HOSTS --target $HOSTS \ + --include-default-paths \ + src/tools/build-manifest ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang # This is the only builder which will create source tarballs diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 8653aecc12c5..147de5f80158 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -44,7 +44,6 @@ ENV WASM_TARGETS=wasm32-unknown-unknown ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_TARGETS \ src/test/run-make \ src/test/ui \ - src/test/compile-fail \ src/test/mir-opt \ src/test/codegen-units \ library/core diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile index f4071961f8e1..77510d7ac62d 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile @@ -1,6 +1,7 @@ -FROM ubuntu:16.04 +FROM ubuntu:20.04 -RUN apt-get update && apt-get install -y --no-install-recommends \ +# Avoid interactive prompts while installing `tzdata` dependency with `DEBIAN_FRONTEND`. +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ g++ \ make \ ninja-build \ diff --git a/src/ci/pgo.sh b/src/ci/pgo.sh new file mode 100755 index 000000000000..13b8ca91f890 --- /dev/null +++ b/src/ci/pgo.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +set -euxo pipefail + +rm -rf /tmp/rustc-pgo + +python2.7 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \ + --stage 2 library/std --rust-profile-generate=/tmp/rustc-pgo + +./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \ + --crate-type=lib ../library/core/src/lib.rs + +# Download and build a single-file stress test benchmark on perf.rust-lang.org. +function pgo_perf_benchmark { + local PERF=e095f5021bf01cf3800f50b3a9f14a9683eb3e4e + local github_prefix=https://raw.githubusercontent.com/rust-lang/rustc-perf/$PERF + local name=$1 + curl -o /tmp/$name.rs $github_prefix/collector/benchmarks/$name/src/lib.rs + ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 --crate-type=lib /tmp/$name.rs +} + +pgo_perf_benchmark externs +pgo_perf_benchmark ctfe-stress-4 + +cp -pri ../src/tools/cargo /tmp/cargo + +# Build cargo (with some flags) +function pgo_cargo { + RUSTC=./build/$PGO_HOST/stage2/bin/rustc \ + ./build/$PGO_HOST/stage0/bin/cargo $@ \ + --manifest-path /tmp/cargo/Cargo.toml +} + +# Build a couple different variants of Cargo +CARGO_INCREMENTAL=1 pgo_cargo check +echo 'pub fn barbarbar() {}' >> /tmp/cargo/src/cargo/lib.rs +CARGO_INCREMENTAL=1 pgo_cargo check +touch /tmp/cargo/src/cargo/lib.rs +CARGO_INCREMENTAL=1 pgo_cargo check +pgo_cargo build --release + +# Merge the profile data we gathered +./build/$PGO_HOST/llvm/bin/llvm-profdata \ + merge -o /tmp/rustc-pgo.profdata /tmp/rustc-pgo + +# This produces the actual final set of artifacts. +$@ --rust-profile-use=/tmp/rustc-pgo.profdata diff --git a/src/ci/run.sh b/src/ci/run.sh index 181a7fcb7326..1958b6ee41d7 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -53,6 +53,11 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-locked-deps" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-cargo-native-static" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-units-std=1" +# Only produce xz tarballs on CI. gz tarballs will be generated by the release +# process by recompressing the existing xz ones. This decreases the storage +# space required for CI artifacts. +RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --dist-compression-formats=xz" + if [ "$DIST_SRC" = "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-dist-src" fi diff --git a/src/doc/book b/src/doc/book index a190438d77d2..5bb44f8b5b0a 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit a190438d77d28041f24da4f6592e287fab073a61 +Subproject commit 5bb44f8b5b0aa105c8b22602e9b18800484afa21 diff --git a/src/doc/nomicon b/src/doc/nomicon index d8383b65f794..a5a48441d411 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit d8383b65f7948c2ca19191b3b4bd709b403aaf45 +Subproject commit a5a48441d411f61556b57d762b03d6874afe575d diff --git a/src/doc/reference b/src/doc/reference index a8afdca5d071..b278478b7661 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit a8afdca5d0715b2257b6f8b9a032fd4dd7dae855 +Subproject commit b278478b766178491a8b6f67afa4bcd6b64d977a diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 236c734a2cb3..1cce0737d6a7 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 236c734a2cb323541b3394f98682cb981b9ec086 +Subproject commit 1cce0737d6a7d3ceafb139b4a206861fb1dcb2ab diff --git a/src/doc/rustc/book.toml b/src/doc/rustc/book.toml index 8adc05c51372..21d127c39c90 100644 --- a/src/doc/rustc/book.toml +++ b/src/doc/rustc/book.toml @@ -3,3 +3,6 @@ authors = ["The Rust Project Developers"] multilingual = false src = "src" title = "The rustc book" + +[output.html] +git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc" diff --git a/src/doc/rustdoc/book.toml b/src/doc/rustdoc/book.toml index ba30c107667e..c2e7ff589066 100644 --- a/src/doc/rustdoc/book.toml +++ b/src/doc/rustdoc/book.toml @@ -2,3 +2,6 @@ authors = ["The Rust Project Developers"] src = "src" title = "The rustdoc book" + +[output.html] +git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustdoc" diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 31e002810ce4..80f7851debfb 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -237,6 +237,26 @@ for a target triple that's different than your host triple. All of the usual caveats of cross-compiling code apply. +## `--default-theme`: set the default theme + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs --default-theme=ayu +``` + +Sets the default theme (for users whose browser has not remembered a +previous theme selection from the on-page theme picker). + +The supplied value should be the lowercase version of the theme name. +The set of available themes can be seen in the theme picker in the +generated output. + +Note that the set of available themes - and their appearance - is not +necessarily stable from one rustdoc version to the next. If the +requested theme does not exist, the builtin default (currently +`light`) is used instead. + ## `--markdown-css`: include more CSS files when rendering markdown Using this flag looks like this: diff --git a/src/doc/rustdoc/src/what-is-rustdoc.md b/src/doc/rustdoc/src/what-is-rustdoc.md index 1f6dced180b9..32dc1e02bb3d 100644 --- a/src/doc/rustdoc/src/what-is-rustdoc.md +++ b/src/doc/rustdoc/src/what-is-rustdoc.md @@ -10,7 +10,7 @@ CSS, and JavaScript. Let's give it a try! Create a new project with Cargo: ```bash -$ cargo new docs +$ cargo new docs --lib $ cd docs ``` diff --git a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md index 6ca5ae40707c..98bcadd12ee2 100644 --- a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md +++ b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md @@ -118,7 +118,7 @@ LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process covera * If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`. * You can install compatible versions of these tools via `rustup`. -The `rustup` option is guaranteed to install a compatible version of the LLVM tools, but they can be hard to find. We recommend [`cargo-bintools`], which installs Rust-specific wrappers around these and other LLVM tools, so you can invoke them via `cargo` commands! +The `rustup` option is guaranteed to install a compatible version of the LLVM tools, but they can be hard to find. We recommend [`cargo-binutils`], which installs Rust-specific wrappers around these and other LLVM tools, so you can invoke them via `cargo` commands! ```shell $ rustup component add llvm-tools-preview @@ -320,8 +320,8 @@ Rust's implementation and workflow for source-based code coverage is based on th [rustc-dev-guide-how-to-build-and-run]: https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html [`rustfilt`]: https://crates.io/crates/rustfilt [`json5format`]: https://crates.io/crates/json5format -[`cargo-bintools`]: https://crates.io/crates/cargo-bintools +[`cargo-binutils`]: https://crates.io/crates/cargo-binutils [`llvm-profdata merge`]: https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge [`llvm-cov report`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report [`llvm-cov show`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show -[source-based code coverage in Clang]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html \ No newline at end of file +[source-based code coverage in Clang]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html diff --git a/src/doc/unstable-book/src/language-features/const-fn.md b/src/doc/unstable-book/src/language-features/const-fn.md index 50dbbaf56743..bcf7f78b8fe0 100644 --- a/src/doc/unstable-book/src/language-features/const-fn.md +++ b/src/doc/unstable-book/src/language-features/const-fn.md @@ -6,24 +6,5 @@ The tracking issue for this feature is: [#57563] ------------------------ -The `const_fn` feature allows marking free functions and inherent methods as -`const`, enabling them to be called in constants contexts, with constant -arguments. - -## Examples - -```rust -#![feature(const_fn)] - -const fn double(x: i32) -> i32 { - x * 2 -} - -const FIVE: i32 = 5; -const TEN: i32 = double(FIVE); - -fn main() { - assert_eq!(5, FIVE); - assert_eq!(10, TEN); -} -``` +The `const_fn` feature enables additional functionality not stabilized in the +[minimal subset of `const_fn`](https://github.com/rust-lang/rust/issues/53555) diff --git a/src/doc/unstable-book/src/language-features/ffi-pure.md b/src/doc/unstable-book/src/language-features/ffi-pure.md index 4aef4eeab553..236ccb9f9053 100644 --- a/src/doc/unstable-book/src/language-features/ffi-pure.md +++ b/src/doc/unstable-book/src/language-features/ffi-pure.md @@ -31,7 +31,7 @@ parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not referentially-transparent, and are therefore more relaxed than `#[ffi_const]` functions. -However, accesing global memory through volatile or atomic reads can violate the +However, accessing global memory through volatile or atomic reads can violate the requirement that two consecutive function calls shall return the same value. A `pure` function that returns unit has no effect on the abstract machine's diff --git a/src/etc/generate-deriving-span-tests.py b/src/etc/generate-deriving-span-tests.py index a0ba47e1dbe3..d38f5add7474 100755 --- a/src/etc/generate-deriving-span-tests.py +++ b/src/etc/generate-deriving-span-tests.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -This script creates a pile of compile-fail tests check that all the +This script creates a pile of UI tests check that all the derives have spans that point to the fields, rather than the #[derive(...)] line. diff --git a/src/etc/natvis/intrinsic.natvis b/src/etc/natvis/intrinsic.natvis index 874550da8b0c..030892a432b3 100644 --- a/src/etc/natvis/intrinsic.natvis +++ b/src/etc/natvis/intrinsic.natvis @@ -4,17 +4,21 @@ {data_ptr,[length]s8} data_ptr,[length]s8 - length - - length - data_ptr - + length + + + + length + data_ptr + + +
- {{ length={length} }} + {{ len={length} }} - length + length length data_ptr diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index de30b58526a1..cfaafc5734bc 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -1,9 +1,9 @@ - {{ size={len} }} + {{ len={len} }} - len + len buf.cap len @@ -12,9 +12,9 @@ - {{ size={tail <= head ? head - tail : buf.cap - tail + head} }} + {{ len={tail <= head ? head - tail : buf.cap - tail + head} }} - tail <= head ? head - tail : buf.cap - tail + head + tail <= head ? head - tail : buf.cap - tail + head buf.cap @@ -31,7 +31,7 @@ - {{ size={len} }} + {{ len={len} }} len @@ -42,15 +42,37 @@ - {*(char**)this,[vec.len]s8} - *(char**)this,[vec.len]s8 + {(char*)vec.buf.ptr.pointer,[vec.len]s8} + (char*)vec.buf.ptr.pointer,[vec.len]s8 - vec.len + vec.len vec.buf.cap - - vec.len - *(char**)this - + + + + vec.len + (char*)vec.buf.ptr.pointer + + + + + + + {ptr.pointer->value} + + ptr.pointer->value + + + + {ptr.pointer->data} + + ptr.pointer->data + + + + {ptr.pointer->data} + + ptr.pointer->data diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis index 0e703b3b9502..984a8bfb13c7 100644 --- a/src/etc/natvis/libcore.natvis +++ b/src/etc/natvis/libcore.natvis @@ -6,34 +6,28 @@ pointer + {{ Shared {pointer} }} pointer + - {{ None }} - {{ Some {__0} }} + None + Some({__0}) - (ULONG)(RUST$ENUM$DISR != 0) - __0 - - (ULONG)(RUST$ENUM$DISR != 0) - &__0 - + __0 + - {{ None }} - {{ Some {($T1 *)this} }} + None + Some({($T1 *)this}) - (ULONG)(*(PVOID *)this != nullptr) - ($T1 *)this - - (ULONG)(*(PVOID *)this != nullptr) - ($T1 *)this - + ($T1 *)this + \ No newline at end of file diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis index 9550c25f2fcf..7e5ee7b13daf 100644 --- a/src/etc/natvis/libstd.natvis +++ b/src/etc/natvis/libstd.natvis @@ -26,9 +26,9 @@ --> - {{ size={base.table.items} }} + {{ len={base.table.items} }} - base.table.items + base.table.items base.table.items + base.table.growth_left base.hash_builder @@ -50,9 +50,9 @@ - {{ size={base.map.table.items} }} + {{ len={base.map.table.items} }} - base.map.table.items + base.map.table.items base.map.table.items + base.map.table.growth_left base.map.hash_builder diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 8feef9c259c1..2a8b6a321f1c 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -61,10 +61,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { .params .iter() .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => Some(param.name.to_string()), + ty::GenericParamDefKind::Lifetime => Some(param.name), _ => None, }) - .map(|name| (name.clone(), Lifetime(name))) + .map(|name| (name, Lifetime(name))) .collect(); let lifetime_predicates = self.handle_lifetimes(®ion_data, &names_map); let new_generics = self.param_env_to_generics( @@ -118,15 +118,12 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { }; Some(Item { - source: Span::empty(), + source: Span::dummy(), name: None, attrs: Default::default(), visibility: Inherited, def_id: self.cx.next_def_id(param_env_def_id.krate), - stability: None, - const_stability: None, - deprecation: None, - kind: ImplItem(Impl { + kind: box ImplItem(Impl { unsafety: hir::Unsafety::Normal, generics: new_generics, provided_trait_methods: Default::default(), @@ -145,21 +142,21 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { fn get_lifetime( &self, region: Region<'_>, - names_map: &FxHashMap, + names_map: &FxHashMap, ) -> Lifetime { self.region_name(region) .map(|name| { names_map.get(&name).unwrap_or_else(|| { - panic!("Missing lifetime with name {:?} for {:?}", name, region) + panic!("Missing lifetime with name {:?} for {:?}", name.as_str(), region) }) }) .unwrap_or(&Lifetime::statik()) .clone() } - fn region_name(&self, region: Region<'_>) -> Option { + fn region_name(&self, region: Region<'_>) -> Option { match region { - &ty::ReEarlyBound(r) => Some(r.name.to_string()), + &ty::ReEarlyBound(r) => Some(r.name), _ => None, } } @@ -177,7 +174,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { fn handle_lifetimes<'cx>( &self, regions: &RegionConstraintData<'cx>, - names_map: &FxHashMap, + names_map: &FxHashMap, ) -> Vec { // Our goal is to 'flatten' the list of constraints by eliminating // all intermediate RegionVids. At the end, all constraints should @@ -333,10 +330,9 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { match br { // We only care about named late bound regions, as we need to add them // to the 'for<>' section - ty::BrNamed(_, name) => Some(GenericParamDef { - name: name.to_string(), - kind: GenericParamDefKind::Lifetime, - }), + ty::BrNamed(_, name) => { + Some(GenericParamDef { name, kind: GenericParamDefKind::Lifetime }) + } _ => None, } }) @@ -569,7 +565,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } WherePredicate::EqPredicate { lhs, rhs } => { match lhs { - Type::QPath { name: ref left_name, ref self_type, ref trait_ } => { + Type::QPath { name: left_name, ref self_type, ref trait_ } => { let ty = &*self_type; match **trait_ { Type::ResolvedPath { @@ -580,7 +576,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } => { let mut new_trait_path = trait_path.clone(); - if self.is_fn_ty(tcx, trait_) && left_name == FN_OUTPUT_NAME { + if self.is_fn_ty(tcx, trait_) && left_name == sym::Output { ty_to_fn .entry(*ty.clone()) .and_modify(|e| *e = (e.0.clone(), Some(rhs.clone()))) @@ -601,7 +597,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { ref mut bindings, .. } => { bindings.push(TypeBinding { - name: left_name.clone(), + name: left_name, kind: TypeBindingKind::Equality { ty: rhs }, }); } @@ -669,7 +665,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { GenericParamDefKind::Type { ref mut default, ref mut bounds, .. } => { // We never want something like `impl`. default.take(); - let generic_ty = Type::Generic(param.name.clone()); + let generic_ty = Type::Generic(param.name); if !has_sized.contains(&generic_ty) { bounds.insert(0, GenericBound::maybe_sized(self.cx)); } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 33b5e84c5e07..ba3eb007e384 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .cx .tcx .provided_trait_methods(trait_def_id) - .map(|meth| meth.ident.to_string()) + .map(|meth| meth.ident.name) .collect(); impls.push(Item { @@ -112,10 +112,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { attrs: Default::default(), visibility: Inherited, def_id: self.cx.next_def_id(impl_def_id.krate), - stability: None, - const_stability: None, - deprecation: None, - kind: ImplItem(Impl { + kind: box ImplItem(Impl { unsafety: hir::Unsafety::Normal, generics: ( self.cx.tcx.generics_of(impl_def_id), diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index f3067360f068..c168c56d30d0 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -12,7 +12,7 @@ use rustc_metadata::creader::LoadedMacro; use rustc_middle::ty; use rustc_mir::const_eval::is_min_const_fn; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind}; @@ -124,7 +124,7 @@ crate fn try_inline( let attrs = merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone); cx.renderinfo.borrow_mut().inlined.insert(did); - let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name.clean(cx)), kind, cx); + let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx); ret.push(clean::Item { attrs, ..what_rustc_thinks }); Some(ret) } @@ -339,9 +339,6 @@ crate fn build_impl( return; } - let attrs = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs); - debug!("merged_attrs={:?}", attrs); - let tcx = cx.tcx; let associated_trait = tcx.impl_trait_ref(did); @@ -430,12 +427,12 @@ crate fn build_impl( let provided = trait_ .def_id() - .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.ident.to_string()).collect()) + .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.ident.name).collect()) .unwrap_or_default(); debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id()); - ret.push(clean::Item::from_def_id_and_parts( + let mut item = clean::Item::from_def_id_and_parts( did, None, clean::ImplItem(clean::Impl { @@ -450,7 +447,10 @@ crate fn build_impl( blanket_impl: None, }), cx, - )); + ); + item.attrs = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs); + debug!("merged_attrs={:?}", item.attrs); + ret.push(item); } fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) -> clean::Module { @@ -479,20 +479,17 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) items.push(clean::Item { name: None, attrs: clean::Attributes::default(), - source: clean::Span::empty(), + source: clean::Span::dummy(), def_id: DefId::local(CRATE_DEF_INDEX), visibility: clean::Public, - stability: None, - const_stability: None, - deprecation: None, - kind: clean::ImportItem(clean::Import::new_simple( - item.ident.to_string(), + kind: box clean::ImportItem(clean::Import::new_simple( + item.ident.name, clean::ImportSource { path: clean::Path { global: false, res: item.res, segments: vec![clean::PathSegment { - name: clean::PrimitiveType::from(p).as_str().to_string(), + name: clean::PrimitiveType::from(p).as_sym(), args: clean::GenericArgs::AngleBracketed { args: Vec::new(), bindings: Vec::new(), @@ -562,11 +559,11 @@ fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemKind .collect::() ); - clean::MacroItem(clean::Macro { source, imported_from: Some(imported_from).clean(cx) }) + clean::MacroItem(clean::Macro { source, imported_from: Some(imported_from) }) } LoadedMacro::ProcMacro(ext) => clean::ProcMacroItem(clean::ProcMacro { kind: ext.macro_kind(), - helpers: ext.helper_attrs.clean(cx), + helpers: ext.helper_attrs, }), } } @@ -583,7 +580,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: for pred in &mut g.where_predicates { match *pred { clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref mut bounds } - if *s == "Self" => + if *s == kw::SelfUpper => { bounds.retain(|bound| match *bound { clean::GenericBound::TraitBound( @@ -606,7 +603,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: name: ref _name, }, ref bounds, - } => !(bounds.is_empty() || *s == "Self" && did == trait_did), + } => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did), _ => true, }); g @@ -621,7 +618,7 @@ fn separate_supertrait_bounds( let mut ty_bounds = Vec::new(); g.where_predicates.retain(|pred| match *pred { clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds } - if *s == "Self" => + if *s == kw::SelfUpper => { ty_bounds.extend(bounds.iter().cloned()); false diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 16274430902e..f4eb1924e6f7 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -25,7 +25,7 @@ use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt}; use rustc_mir::const_eval::{is_const_fn, is_min_const_fn, is_unstable_const_fn}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, ExpnKind, Pos}; +use rustc_span::{self, ExpnKind}; use rustc_typeck::hir_ty_to_ty; use std::collections::hash_map::Entry; @@ -48,8 +48,6 @@ crate use self::types::Type::*; crate use self::types::Visibility::{Inherited, Public}; crate use self::types::*; -const FN_OUTPUT_NAME: &str = "Output"; - crate trait Clean { fn clean(&self, cx: &DocContext<'_>) -> T; } @@ -169,7 +167,7 @@ impl Clean for CrateNum { for attr in attrs.lists(sym::doc) { if attr.has_name(sym::keyword) { if let Some(v) = attr.value_str() { - keyword = Some(v.to_string()); + keyword = Some(v); break; } } @@ -209,7 +207,7 @@ impl Clean for CrateNum { }; ExternalCrate { - name: cx.tcx.crate_name(*self).to_string(), + name: cx.tcx.crate_name(*self), src: krate_src, attrs: cx.tcx.get_attrs(root).clean(cx), primitives, @@ -220,11 +218,6 @@ impl Clean for CrateNum { impl Clean for doctree::Module<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { - // maintain a stack of mod ids, for doc comment path resolution - // but we also need to resolve the module's own docs based on whether its docs were written - // inside or outside the module, so check for that - let attrs = self.attrs.clean(cx); - let mut items: Vec = vec![]; items.extend(self.imports.iter().flat_map(|x| x.clean(cx))); items.extend(self.foreigns.iter().map(|x| x.clean(cx))); @@ -253,12 +246,7 @@ impl Clean for doctree::Module<'_> { ModuleItem(Module { is_crate: self.is_crate, items }), cx, ); - Item { - name: Some(what_rustc_thinks.name.unwrap_or_default()), - attrs, - source: span.clean(cx), - ..what_rustc_thinks - } + Item { source: span.clean(cx), ..what_rustc_thinks } } } @@ -334,10 +322,9 @@ impl Clean for (ty::PolyTraitRef<'_>, &[TypeBinding]) { .collect_referenced_late_bound_regions(&poly_trait_ref) .into_iter() .filter_map(|br| match br { - ty::BrNamed(_, name) => Some(GenericParamDef { - name: name.to_string(), - kind: GenericParamDefKind::Lifetime, - }), + ty::BrNamed(_, name) => { + Some(GenericParamDef { name, kind: GenericParamDefKind::Lifetime }) + } _ => None, }) .collect(); @@ -387,7 +374,7 @@ impl Clean for hir::Lifetime { } _ => {} } - Lifetime(self.name.ident().to_string()) + Lifetime(self.name.ident().name) } } @@ -405,9 +392,9 @@ impl Clean for hir::GenericParam<'_> { for bound in bounds { s.push_str(&format!(" + {}", bound.name.ident())); } - Lifetime(s) + Lifetime(Symbol::intern(&s)) } else { - Lifetime(self.name.ident().to_string()) + Lifetime(self.name.ident().name) } } _ => panic!(), @@ -431,16 +418,18 @@ impl Clean for hir::ConstArg { impl Clean for ty::GenericParamDef { fn clean(&self, _cx: &DocContext<'_>) -> Lifetime { - Lifetime(self.name.to_string()) + Lifetime(self.name) } } impl Clean> for ty::RegionKind { - fn clean(&self, cx: &DocContext<'_>) -> Option { + fn clean(&self, _cx: &DocContext<'_>) -> Option { match *self { ty::ReStatic => Some(Lifetime::statik()), - ty::ReLateBound(_, ty::BrNamed(_, name)) => Some(Lifetime(name.to_string())), - ty::ReEarlyBound(ref data) => Some(Lifetime(data.name.clean(cx))), + ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name) }) => { + Some(Lifetime(name)) + } + ty::ReEarlyBound(ref data) => Some(Lifetime(data.name)), ty::ReLateBound(..) | ty::ReFree(..) @@ -477,8 +466,9 @@ impl Clean for hir::WherePredicate<'_> { impl<'a> Clean> for ty::Predicate<'a> { fn clean(&self, cx: &DocContext<'_>) -> Option { - match self.skip_binders() { - ty::PredicateAtom::Trait(pred, _) => Some(ty::Binder::bind(pred).clean(cx)), + let bound_predicate = self.bound_atom(); + match bound_predicate.skip_binder() { + ty::PredicateAtom::Trait(pred, _) => Some(bound_predicate.rebind(pred).clean(cx)), ty::PredicateAtom::RegionOutlives(pred) => pred.clean(cx), ty::PredicateAtom::TypeOutlives(pred) => pred.clean(cx), ty::PredicateAtom::Projection(pred) => Some(pred.clean(cx)), @@ -551,7 +541,7 @@ impl<'tcx> Clean for ty::ProjectionTy<'tcx> { GenericBound::Outlives(_) => panic!("cleaning a trait got a lifetime"), }; Type::QPath { - name: cx.tcx.associated_item(self.item_def_id).ident.name.clean(cx), + name: cx.tcx.associated_item(self.item_def_id).ident.name, self_type: box self.self_ty().clean(cx), trait_: box trait_, } @@ -561,14 +551,12 @@ impl<'tcx> Clean for ty::ProjectionTy<'tcx> { impl Clean for ty::GenericParamDef { fn clean(&self, cx: &DocContext<'_>) -> GenericParamDef { let (name, kind) = match self.kind { - ty::GenericParamDefKind::Lifetime => { - (self.name.to_string(), GenericParamDefKind::Lifetime) - } + ty::GenericParamDefKind::Lifetime => (self.name, GenericParamDefKind::Lifetime), ty::GenericParamDefKind::Type { has_default, synthetic, .. } => { let default = if has_default { Some(cx.tcx.type_of(self.def_id).clean(cx)) } else { None }; ( - self.name.clean(cx), + self.name, GenericParamDefKind::Type { did: self.def_id, bounds: vec![], // These are filled in from the where-clauses. @@ -578,7 +566,7 @@ impl Clean for ty::GenericParamDef { ) } ty::GenericParamDefKind::Const { .. } => ( - self.name.clean(cx), + self.name, GenericParamDefKind::Const { did: self.def_id, ty: cx.tcx.type_of(self.def_id).clean(cx), @@ -604,14 +592,14 @@ impl Clean for hir::GenericParam<'_> { for bound in bounds { s.push_str(&format!(" + {}", bound.name.ident())); } - s + Symbol::intern(&s) } else { - self.name.ident().to_string() + self.name.ident().name }; (name, GenericParamDefKind::Lifetime) } hir::GenericParamKind::Type { ref default, synthetic } => ( - self.name.ident().name.clean(cx), + self.name.ident().name, GenericParamDefKind::Type { did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), bounds: self.bounds.clean(cx), @@ -620,7 +608,7 @@ impl Clean for hir::GenericParam<'_> { }, ), hir::GenericParamKind::Const { ref ty } => ( - self.name.ident().name.clean(cx), + self.name.ident().name, GenericParamDefKind::Const { did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), ty: ty.clean(cx), @@ -645,6 +633,18 @@ impl Clean for hir::Generics<'_> { _ => false, } } + /// This can happen for `async fn`, e.g. `async fn f<'_>(&'_ self)`. + /// + /// See [`lifetime_to_generic_param`] in [`rustc_ast_lowering`] for more information. + /// + /// [`lifetime_to_generic_param`]: rustc_ast_lowering::LoweringContext::lifetime_to_generic_param + fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { + match param.kind { + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided } => true, + _ => false, + } + } + let impl_trait_params = self .params .iter() @@ -663,7 +663,7 @@ impl Clean for hir::Generics<'_> { .collect::>(); let mut params = Vec::with_capacity(self.params.len()); - for p in self.params.iter().filter(|p| !is_impl_trait(p)) { + for p in self.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { let p = p.clean(cx); params.push(p); } @@ -735,7 +735,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx .collect::>(); // param index -> [(DefId of trait, associated type name, type)] - let mut impl_trait_proj = FxHashMap::)>>::default(); + let mut impl_trait_proj = FxHashMap::)>>::default(); let where_predicates = preds .predicates @@ -743,7 +743,8 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx .flat_map(|(p, _)| { let mut projection = None; let param_idx = (|| { - match p.skip_binders() { + let bound_p = p.bound_atom(); + match bound_p.skip_binder() { ty::PredicateAtom::Trait(pred, _constness) => { if let ty::Param(param) = pred.self_ty().kind() { return Some(param.index); @@ -756,7 +757,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx } ty::PredicateAtom::Projection(p) => { if let ty::Param(param) = p.projection_ty.self_ty().kind() { - projection = Some(ty::Binder::bind(p)); + projection = Some(bound_p.rebind(p)); return Some(param.index); } } @@ -783,11 +784,10 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx if let Some(((_, trait_did, name), rhs)) = proj.as_ref().and_then(|(lhs, rhs)| Some((lhs.projection()?, rhs))) { - impl_trait_proj.entry(param_idx).or_default().push(( - trait_did, - name.to_string(), - rhs, - )); + impl_trait_proj + .entry(param_idx) + .or_default() + .push((trait_did, name, rhs)); } return None; @@ -805,7 +805,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx if let crate::core::ImplTraitParam::ParamIndex(idx) = param { if let Some(proj) = impl_trait_proj.remove(&idx) { for (trait_did, name, rhs) in proj { - simplify::merge_bounds(cx, &mut bounds, trait_did, &name, &rhs.clean(cx)); + simplify::merge_bounds(cx, &mut bounds, trait_did, name, &rhs.clean(cx)); } } } else { @@ -831,7 +831,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx where_predicates.retain(|pred| match *pred { WP::BoundPredicate { ty: Generic(ref g), ref bounds } => { if bounds.iter().any(|b| b.is_sized_bound(cx)) { - sized_params.insert(g.clone()); + sized_params.insert(*g); false } else { true @@ -847,7 +847,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx && !sized_params.contains(&tp.name) { where_predicates.push(WP::BoundPredicate { - ty: Type::Generic(tp.name.clone()), + ty: Type::Generic(tp.name), bounds: vec![GenericBound::maybe_sized(cx)], }) } @@ -908,7 +908,7 @@ fn clean_fn_or_proc_macro( } } } - ProcMacroItem(ProcMacro { kind, helpers: helpers.clean(cx) }) + ProcMacroItem(ProcMacro { kind, helpers }) } None => { let mut func = (sig, generics, body_id).clean(cx); @@ -941,9 +941,9 @@ impl<'a> Clean for (&'a [hir::Ty<'a>], &'a [Ident]) { .iter() .enumerate() .map(|(i, ty)| { - let mut name = self.1.get(i).map(|ident| ident.to_string()).unwrap_or_default(); + let mut name = self.1.get(i).map(|ident| ident.name).unwrap_or(kw::Empty); if name.is_empty() { - name = "_".to_string(); + name = kw::Underscore; } Argument { name, type_: ty.clean(cx) } }) @@ -1000,7 +1000,7 @@ impl<'tcx> Clean for (DefId, ty::PolyFnSig<'tcx>) { .iter() .map(|t| Argument { type_: t.clean(cx), - name: names.next().map_or_else(|| String::new(), |name| name.to_string()), + name: names.next().map(|i| i.name).unwrap_or(kw::Empty), }) .collect(), }, @@ -1096,7 +1096,7 @@ impl Clean for hir::TraitItem<'_> { AssocTypeItem(bounds.clean(cx), default.clean(cx)) } }; - Item::from_def_id_and_parts(local_did, Some(self.ident.name.clean(cx)), inner, cx) + Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx) }) } } @@ -1124,7 +1124,7 @@ impl Clean for hir::ImplItem<'_> { TypedefItem(Typedef { type_, generics: Generics::default(), item_type }, true) } }; - Item::from_def_id_and_parts(local_did, Some(self.ident.name.clean(cx)), inner, cx) + Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx) }) } } @@ -1155,12 +1155,12 @@ impl Clean for ty::AssocItem { }; let self_arg_ty = sig.input(0).skip_binder(); if self_arg_ty == self_ty { - decl.inputs.values[0].type_ = Generic(String::from("Self")); + decl.inputs.values[0].type_ = Generic(kw::SelfUpper); } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() { if ty == self_ty { match decl.inputs.values[0].type_ { BorrowedRef { ref mut type_, .. } => { - **type_ = Generic(String::from("Self")) + **type_ = Generic(kw::SelfUpper) } _ => unreachable!(), } @@ -1215,7 +1215,7 @@ impl Clean for ty::AssocItem { } } ty::AssocKind::Type => { - let my_name = self.ident.name.clean(cx); + let my_name = self.ident.name; if let ty::TraitContainer(_) = self.container { let bounds = cx.tcx.explicit_item_bounds(self.def_id); @@ -1240,7 +1240,7 @@ impl Clean for ty::AssocItem { _ => return None, } match **self_type { - Generic(ref s) if *s == "Self" => {} + Generic(ref s) if *s == kw::SelfUpper => {} _ => return None, } Some(bounds) @@ -1281,7 +1281,7 @@ impl Clean for ty::AssocItem { } }; - Item::from_def_id_and_parts(self.def_id, Some(self.ident.name.clean(cx)), kind, cx) + Item::from_def_id_and_parts(self.def_id, Some(self.ident.name), kind, cx) } } @@ -1413,7 +1413,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { segments: trait_segments.clean(cx), }; Type::QPath { - name: p.segments.last().expect("segments were empty").ident.name.clean(cx), + name: p.segments.last().expect("segments were empty").ident.name, self_type: box qself.clean(cx), trait_: box resolve_type(cx, trait_path, hir_id), } @@ -1427,7 +1427,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { }; let trait_path = hir::Path { span, res, segments: &[] }; Type::QPath { - name: segment.ident.name.clean(cx), + name: segment.ident.name, self_type: box qself.clean(cx), trait_: box resolve_type(cx, trait_path.clean(cx), hir_id), } @@ -1444,7 +1444,16 @@ impl Clean for hir::Ty<'_> { TyKind::Never => Never, TyKind::Ptr(ref m) => RawPointer(m.mutbl, box m.ty.clean(cx)), TyKind::Rptr(ref l, ref m) => { - let lifetime = if l.is_elided() { None } else { Some(l.clean(cx)) }; + // There are two times a `Fresh` lifetime can be created: + // 1. For `&'_ x`, written by the user. This corresponds to `lower_lifetime` in `rustc_ast_lowering`. + // 2. For `&x` as a parameter to an `async fn`. This corresponds to `elided_ref_lifetime in `rustc_ast_lowering`. + // See #59286 for more information. + // Ideally we would only hide the `'_` for case 2., but I don't know a way to distinguish it. + // Turning `fn f(&'_ self)` into `fn f(&self)` isn't the worst thing in the world, though; + // there's no case where it could cause the function to fail to compile. + let elided = + l.is_elided() || matches!(l.name, LifetimeName::Param(ParamName::Fresh(_))); + let lifetime = if elided { None } else { Some(l.clean(cx)) }; BorrowedRef { lifetime, mutability: m.mutbl, type_: box m.ty.clean(cx) } } TyKind::Slice(ref ty) => Slice(box ty.clean(cx)), @@ -1630,7 +1639,7 @@ impl<'tcx> Clean for Ty<'tcx> { let mut bindings = vec![]; for pb in obj.projection_bounds() { bindings.push(TypeBinding { - name: cx.tcx.associated_item(pb.item_def_id()).ident.name.clean(cx), + name: cx.tcx.associated_item(pb.item_def_id()).ident.name, kind: TypeBindingKind::Equality { ty: pb.skip_binder().ty.clean(cx) }, }); } @@ -1649,7 +1658,7 @@ impl<'tcx> Clean for Ty<'tcx> { if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&p.index.into()) { ImplTrait(bounds) } else { - Generic(p.name.to_string()) + Generic(p.name) } } @@ -1670,12 +1679,10 @@ impl<'tcx> Clean for Ty<'tcx> { .filter_map(|bound| { // Note: The substs of opaque types can contain unbound variables, // meaning that we have to use `ignore_quantifiers_with_unbound_vars` here. - let trait_ref = match bound - .bound_atom_with_opt_escaping(cx.tcx) - .skip_binder() - { + let bound_predicate = bound.bound_atom_with_opt_escaping(cx.tcx); + let trait_ref = match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(tr, _constness) => { - ty::Binder::bind(tr.trait_ref) + bound_predicate.rebind(tr.trait_ref) } ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { if let Some(r) = reg.clean(cx) { @@ -1707,8 +1714,7 @@ impl<'tcx> Clean for Ty<'tcx> { .tcx .associated_item(proj.projection_ty.item_def_id) .ident - .name - .clean(cx), + .name, kind: TypeBindingKind::Equality { ty: proj.ty.clean(cx), }, @@ -1771,7 +1777,7 @@ impl Clean for ty::FieldDef { fn clean(&self, cx: &DocContext<'_>) -> Item { let what_rustc_thinks = Item::from_def_id_and_parts( self.did, - Some(self.ident.name.clean(cx)), + Some(self.ident.name), StructFieldItem(cx.tcx.type_of(self.did).clean(cx)), cx, ); @@ -1787,25 +1793,28 @@ impl Clean for hir::Visibility<'_> { hir::VisibilityKind::Inherited => Visibility::Inherited, hir::VisibilityKind::Crate(_) => { let krate = DefId::local(CRATE_DEF_INDEX); - Visibility::Restricted(krate, cx.tcx.def_path(krate)) + Visibility::Restricted(krate) } hir::VisibilityKind::Restricted { ref path, .. } => { let path = path.clean(cx); let did = register_res(cx, path.res); - Visibility::Restricted(did, cx.tcx.def_path(did)) + Visibility::Restricted(did) } } } } impl Clean for ty::Visibility { - fn clean(&self, cx: &DocContext<'_>) -> Visibility { + fn clean(&self, _cx: &DocContext<'_>) -> Visibility { match *self { ty::Visibility::Public => Visibility::Public, + // NOTE: this is not quite right: `ty` uses `Invisible` to mean 'private', + // while rustdoc really does mean inherited. That means that for enum variants, such as + // `pub enum E { V }`, `V` will be marked as `Public` by `ty`, but as `Inherited` by rustdoc. + // This is the main reason `impl Clean for hir::Visibility` still exists; various parts of clean + // override `tcx.visibility` explicitly to make sure this distinction is captured. ty::Visibility::Invisible => Visibility::Inherited, - ty::Visibility::Restricted(module) => { - Visibility::Restricted(module, cx.tcx.def_path(module)) - } + ty::Visibility::Restricted(module) => Visibility::Restricted(module), } } } @@ -1847,7 +1856,7 @@ impl Clean for ty::VariantDef { .fields .iter() .map(|field| { - let name = Some(field.ident.name.clean(cx)); + let name = Some(field.ident.name); let kind = StructFieldItem(cx.tcx.type_of(field.did).clean(cx)); let what_rustc_thinks = Item::from_def_id_and_parts(field.did, name, kind, cx); @@ -1859,7 +1868,7 @@ impl Clean for ty::VariantDef { }; let what_rustc_thinks = Item::from_def_id_and_parts( self.def_id, - Some(self.ident.name.clean(cx)), + Some(self.ident.name), VariantItem(Variant { kind }), cx, ); @@ -1881,29 +1890,8 @@ impl Clean for hir::VariantData<'_> { } impl Clean for rustc_span::Span { - fn clean(&self, cx: &DocContext<'_>) -> Span { - if self.is_dummy() { - return Span::empty(); - } - - // Get the macro invocation instead of the definition, - // in case the span is result of a macro expansion. - // (See rust-lang/rust#39726) - let span = self.source_callsite(); - - let sm = cx.sess().source_map(); - let filename = sm.span_to_filename(span); - let lo = sm.lookup_char_pos(span.lo()); - let hi = sm.lookup_char_pos(span.hi()); - Span { - filename, - cnum: lo.file.cnum, - loline: lo.line, - locol: lo.col.to_usize(), - hiline: hi.line, - hicol: hi.col.to_usize(), - original: span, - } + fn clean(&self, _cx: &DocContext<'_>) -> Span { + Span::from_rustc_span(*self) } } @@ -1947,7 +1935,7 @@ impl Clean for hir::GenericArgs<'_> { impl Clean for hir::PathSegment<'_> { fn clean(&self, cx: &DocContext<'_>) -> PathSegment { - PathSegment { name: self.ident.name.clean(cx), args: self.generic_args().clean(cx) } + PathSegment { name: self.ident.name, args: self.generic_args().clean(cx) } } } @@ -2054,7 +2042,7 @@ impl Clean> for (&hir::Item<'_>, Option) { _ => unreachable!("not yet converted"), }; - vec![Item::from_def_id_and_parts(def_id, Some(name.clean(cx)), kind, cx)] + vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)] }) } } @@ -2098,9 +2086,9 @@ fn clean_impl(impl_: &hir::Item<'_>, cx: &DocContext<'_>) -> Vec { build_deref_target_impls(cx, &items, &mut ret); } - let provided: FxHashSet = trait_ + let provided: FxHashSet = trait_ .def_id() - .map(|did| cx.tcx.provided_trait_methods(did).map(|meth| meth.ident.to_string()).collect()) + .map(|did| cx.tcx.provided_trait_methods(did).map(|meth| meth.ident.name).collect()) .unwrap_or_default(); let for_ = for_.clean(cx); @@ -2165,7 +2153,6 @@ fn clean_extern_crate( return items; } } - let path = orig_name.map(|x| x.to_string()); // FIXME: using `from_def_id_and_kind` breaks `rustdoc/masked` for some reason vec![Item { name: None, @@ -2173,10 +2160,7 @@ fn clean_extern_crate( source: krate.span.clean(cx), def_id: crate_def_id, visibility: krate.vis.clean(cx), - stability: None, - const_stability: None, - deprecation: None, - kind: ExternCrateItem(name.clean(cx), path), + kind: box ExternCrateItem(name, orig_name), }] } @@ -2189,11 +2173,26 @@ impl Clean> for doctree::Import<'_> { return Vec::new(); } + let (doc_meta_item, please_inline) = self.attrs.lists(sym::doc).get_word_attr(sym::inline); + let pub_underscore = self.vis.node.is_pub() && self.name == kw::Underscore; + + if pub_underscore && please_inline { + rustc_errors::struct_span_err!( + cx.tcx.sess, + doc_meta_item.unwrap().span(), + E0780, + "anonymous imports cannot be inlined" + ) + .span_label(self.span, "anonymous import") + .emit(); + } + // We consider inlining the documentation of `pub use` statements, but we // forcefully don't inline if this is not public or if the // #[doc(no_inline)] attribute is present. // Don't inline doc(hidden) imports so they can be stripped at a later stage. let mut denied = !self.vis.node.is_pub() + || pub_underscore || self.attrs.iter().any(|a| { a.has_name(sym::doc) && match a.meta_item_list() { @@ -2206,7 +2205,6 @@ impl Clean> for doctree::Import<'_> { }); // Also check whether imports were asked to be inlined, in case we're trying to re-export a // crate in Rust 2018+ - let please_inline = self.attrs.lists(sym::doc).has_word(sym::inline); let path = self.path.clean(cx); let inner = if self.glob { if !denied { @@ -2244,11 +2242,8 @@ impl Clean> for doctree::Import<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: None, - const_stability: None, - deprecation: None, - kind: ImportItem(Import::new_simple( - self.name.clean(cx), + kind: box ImportItem(Import::new_simple( + self.name, resolve_use_source(cx, path), false, )), @@ -2256,7 +2251,7 @@ impl Clean> for doctree::Import<'_> { return items; } } - Import::new_simple(name.clean(cx), resolve_use_source(cx, path), true) + Import::new_simple(name, resolve_use_source(cx, path), true) }; vec![Item { @@ -2265,10 +2260,7 @@ impl Clean> for doctree::Import<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: None, - const_stability: None, - deprecation: None, - kind: ImportItem(inner), + kind: box ImportItem(inner), }] } } @@ -2333,18 +2325,19 @@ impl Clean for (&hir::MacroDef<'_>, Option) { ) } else { let vis = item.vis.clean(cx); + let def_id = cx.tcx.hir().local_def_id(item.hir_id).to_def_id(); if matchers.len() <= 1 { format!( "{}macro {}{} {{\n ...\n}}", - vis.print_with_space(), + vis.print_with_space(cx.tcx, def_id), name, matchers.iter().map(|span| span.to_src(cx)).collect::(), ) } else { format!( "{}macro {} {{\n{}}}", - vis.print_with_space(), + vis.print_with_space(cx.tcx, def_id), name, matchers .iter() @@ -2363,19 +2356,9 @@ impl Clean for (&hir::MacroDef<'_>, Option) { } } -impl Clean for attr::Deprecation { - fn clean(&self, _: &DocContext<'_>) -> Deprecation { - Deprecation { - since: self.since.map(|s| s.to_string()).filter(|s| !s.is_empty()), - note: self.note.map(|n| n.to_string()).filter(|n| !n.is_empty()), - is_since_rustc_version: self.is_since_rustc_version, - } - } -} - impl Clean for hir::TypeBinding<'_> { fn clean(&self, cx: &DocContext<'_>) -> TypeBinding { - TypeBinding { name: self.ident.name.clean(cx), kind: self.kind.clean(cx) } + TypeBinding { name: self.ident.name, kind: self.kind.clean(cx) } } } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 121c9d2bc4cd..16aaa9cfd20e 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -15,6 +15,7 @@ use std::collections::BTreeMap; use rustc_hir::def_id::DefId; use rustc_middle::ty; +use rustc_span::Symbol; use crate::clean; use crate::clean::GenericArgs as PP; @@ -78,7 +79,7 @@ crate fn merge_bounds( cx: &clean::DocContext<'_>, bounds: &mut Vec, trait_did: DefId, - name: &str, + name: Symbol, rhs: &clean::Type, ) -> bool { !bounds.iter_mut().any(|b| { @@ -100,7 +101,7 @@ crate fn merge_bounds( match last.args { PP::AngleBracketed { ref mut bindings, .. } => { bindings.push(clean::TypeBinding { - name: name.to_string(), + name, kind: clean::TypeBindingKind::Equality { ty: rhs.clone() }, }); } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index d4796b7ed66c..0d33bc9afd5e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -12,20 +12,21 @@ use rustc_ast::attr; use rustc_ast::util::comments::beautify_doc_string; use rustc_ast::{self as ast, AttrStyle}; use rustc_ast::{FloatTy, IntTy, UintTy}; -use rustc_attr::{ConstStability, Stability, StabilityLevel}; +use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_feature::UnstableFeatures; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::Mutability; use rustc_index::vec::IndexVec; -use rustc_middle::ty::{AssocKind, TyCtxt}; +use rustc_middle::ty::TyCtxt; +use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, sym, Ident, Symbol, SymbolStr}; -use rustc_span::{self, FileName}; +use rustc_span::{self, FileName, Loc}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use smallvec::{smallvec, SmallVec}; @@ -50,7 +51,7 @@ thread_local!(crate static MAX_DEF_ID: RefCell> = Def #[derive(Clone, Debug)] crate struct Crate { - crate name: String, + crate name: Symbol, crate version: Option, crate src: FileName, crate module: Option, @@ -65,11 +66,11 @@ crate struct Crate { #[derive(Clone, Debug)] crate struct ExternalCrate { - crate name: String, + crate name: Symbol, crate src: FileName, crate attrs: Attributes, crate primitives: Vec<(DefId, PrimitiveType)>, - crate keywords: Vec<(DefId, String)>, + crate keywords: Vec<(DefId, Symbol)>, } /// Anything with a source location and set of attributes and, optionally, a @@ -80,16 +81,17 @@ crate struct Item { /// Stringified span crate source: Span, /// Not everything has a name. E.g., impls - crate name: Option, + crate name: Option, crate attrs: Attributes, crate visibility: Visibility, - crate kind: ItemKind, + crate kind: Box, crate def_id: DefId, - crate stability: Option, - crate deprecation: Option, - crate const_stability: Option, } +// `Item` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(Item, 136); + impl fmt::Debug for Item { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let def_id: &dyn fmt::Debug = if self.is_fake() { &"**FAKE**" } else { &self.def_id }; @@ -101,13 +103,23 @@ impl fmt::Debug for Item { .field("kind", &self.kind) .field("visibility", &self.visibility) .field("def_id", def_id) - .field("stability", &self.stability) - .field("deprecation", &self.deprecation) .finish() } } impl Item { + crate fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx Stability> { + if self.is_fake() { None } else { tcx.lookup_stability(self.def_id) } + } + + crate fn const_stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ConstStability> { + if self.is_fake() { None } else { tcx.lookup_const_stability(self.def_id) } + } + + crate fn deprecation(&self, tcx: TyCtxt<'_>) -> Option { + if self.is_fake() { None } else { tcx.lookup_deprecation(self.def_id) } + } + /// Finds the `doc` attribute as a NameValue and returns the corresponding /// value found. crate fn doc_value(&self) -> Option<&str> { @@ -122,17 +134,12 @@ impl Item { kind: ItemKind, cx: &DocContext<'_>, ) -> Item { - Item::from_def_id_and_parts( - cx.tcx.hir().local_def_id(hir_id).to_def_id(), - name.clean(cx), - kind, - cx, - ) + Item::from_def_id_and_parts(cx.tcx.hir().local_def_id(hir_id).to_def_id(), name, kind, cx) } pub fn from_def_id_and_parts( def_id: DefId, - name: Option, + name: Option, kind: ItemKind, cx: &DocContext<'_>, ) -> Item { @@ -149,14 +156,11 @@ impl Item { Item { def_id, - kind, + kind: box kind, name, source: source.clean(cx), attrs: cx.tcx.get_attrs(def_id).clean(cx), visibility: cx.tcx.visibility(def_id).clean(cx), - stability: cx.tcx.lookup_stability(def_id).cloned(), - deprecation: cx.tcx.lookup_deprecation(def_id).clean(cx), - const_stability: cx.tcx.lookup_const_stability(def_id).cloned(), } } @@ -171,7 +175,7 @@ impl Item { } crate fn is_crate(&self) -> bool { - match self.kind { + match *self.kind { StrippedItem(box ModuleItem(Module { is_crate: true, .. })) | ModuleItem(Module { is_crate: true, .. }) => true, _ => false, @@ -223,14 +227,14 @@ impl Item { self.type_() == ItemType::Keyword } crate fn is_stripped(&self) -> bool { - match self.kind { + match *self.kind { StrippedItem(..) => true, ImportItem(ref i) => !i.should_be_displayed, _ => false, } } crate fn has_stripped_fields(&self) -> Option { - match self.kind { + match *self.kind { StructItem(ref _struct) => Some(_struct.fields_stripped), UnionItem(ref union) => Some(union.fields_stripped), VariantItem(Variant { kind: VariantKind::Struct(ref vstruct) }) => { @@ -240,8 +244,8 @@ impl Item { } } - crate fn stability_class(&self) -> Option { - self.stability.as_ref().and_then(|ref s| { + crate fn stability_class(&self, tcx: TyCtxt<'_>) -> Option { + self.stability(tcx).as_ref().and_then(|ref s| { let mut classes = Vec::with_capacity(2); if s.level.is_unstable() { @@ -249,7 +253,7 @@ impl Item { } // FIXME: what about non-staged API items that are deprecated? - if self.deprecation.is_some() { + if self.deprecation(tcx).is_some() { classes.push("deprecated"); } @@ -257,15 +261,15 @@ impl Item { }) } - crate fn stable_since(&self) -> Option { - match self.stability?.level { + crate fn stable_since(&self, tcx: TyCtxt<'_>) -> Option { + match self.stability(tcx)?.level { StabilityLevel::Stable { since, .. } => Some(since.as_str()), StabilityLevel::Unstable { .. } => None, } } - crate fn const_stable_since(&self) -> Option { - match self.const_stability?.level { + crate fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option { + match self.const_stability(tcx)?.level { StabilityLevel::Stable { since, .. } => Some(since.as_str()), StabilityLevel::Unstable { .. } => None, } @@ -281,7 +285,7 @@ impl Item { } crate fn is_default(&self) -> bool { - match self.kind { + match *self.kind { ItemKind::MethodItem(_, Some(defaultness)) => { defaultness.has_value() && !defaultness.is_final() } @@ -299,7 +303,7 @@ impl Item { #[derive(Clone, Debug)] crate enum ItemKind { - ExternCrateItem(String, Option), + ExternCrateItem(Symbol, Option), ImportItem(Import), StructItem(Struct), UnionItem(Union), @@ -333,7 +337,7 @@ crate enum ItemKind { AssocTypeItem(Vec, Option), /// An item that has been stripped by a rustdoc pass StrippedItem(Box), - KeywordItem(String), + KeywordItem(Symbol), } impl ItemKind { @@ -379,15 +383,6 @@ impl ItemKind { _ => false, } } - - crate fn as_assoc_kind(&self) -> Option { - match *self { - ItemKind::AssocConstItem(..) => Some(AssocKind::Const), - ItemKind::AssocTypeItem(..) => Some(AssocKind::Type), - ItemKind::TyMethodItem(..) | ItemKind::MethodItem(..) => Some(AssocKind::Fn), - _ => None, - } - } } #[derive(Clone, Debug)] @@ -444,12 +439,22 @@ impl AttributesExt for [ast::Attribute] { crate trait NestedAttributesExt { /// Returns `true` if the attribute list contains a specific `Word` fn has_word(self, word: Symbol) -> bool; + fn get_word_attr(self, word: Symbol) -> (Option, bool); } -impl> NestedAttributesExt for I { +impl + IntoIterator> + NestedAttributesExt for I +{ fn has_word(self, word: Symbol) -> bool { self.into_iter().any(|attr| attr.is_word() && attr.has_name(word)) } + + fn get_word_attr(mut self, word: Symbol) -> (Option, bool) { + match self.find(|attr| attr.is_word() && attr.has_name(word)) { + Some(a) => (Some(a), true), + None => (None, false), + } + } } /// A portion of documentation, extracted from a `#[doc]` attribute. @@ -630,7 +635,7 @@ impl Attributes { let clean_attr = |(attr, parent_module): (&ast::Attribute, _)| { if let Some(value) = attr.doc_str() { trace!("got doc_str={:?}", value); - let value = beautify_doc_string(value); + let value = beautify_doc_string(value).to_string(); let kind = if attr.is_doc_comment() { DocFragmentKind::SugaredDoc } else { @@ -890,21 +895,19 @@ impl GenericBound { } #[derive(Clone, PartialEq, Eq, Debug, Hash)] -crate struct Lifetime(pub String); +crate struct Lifetime(pub Symbol); impl Lifetime { - crate fn get_ref<'a>(&'a self) -> &'a str { - let Lifetime(ref s) = *self; - let s: &'a str = s; - s + crate fn get_ref(&self) -> SymbolStr { + self.0.as_str() } crate fn statik() -> Lifetime { - Lifetime("'static".to_string()) + Lifetime(kw::StaticLifetime) } crate fn elided() -> Lifetime { - Lifetime("'_".to_string()) + Lifetime(kw::UnderscoreLifetime) } } @@ -962,7 +965,7 @@ impl GenericParamDefKind { #[derive(Clone, PartialEq, Eq, Debug, Hash)] crate struct GenericParamDef { - crate name: String, + crate name: Symbol, crate kind: GenericParamDefKind, } @@ -1050,7 +1053,7 @@ crate struct Arguments { #[derive(Clone, PartialEq, Eq, Debug, Hash)] crate struct Argument { crate type_: Type, - crate name: String, + crate name: Symbol, } #[derive(Clone, PartialEq, Debug)] @@ -1062,7 +1065,7 @@ crate enum SelfTy { impl Argument { crate fn to_self(&self) -> Option { - if self.name != "self" { + if self.name != kw::SelfLower { return None; } if self.type_.is_self_type() { @@ -1130,7 +1133,7 @@ crate enum Type { }, /// For parameterized types, so the consumer of the JSON don't go /// looking for types which don't exist anywhere. - Generic(String), + Generic(Symbol), /// Primitives are the fixed-size numeric types (plus int/usize/float), char, /// arrays, slices, and tuples. Primitive(PrimitiveType), @@ -1149,7 +1152,7 @@ crate enum Type { // `::Name` QPath { - name: String, + name: Symbol, self_type: Box, trait_: Box, }, @@ -1162,6 +1165,8 @@ crate enum Type { } #[derive(Clone, PartialEq, Eq, Hash, Copy, Debug)] +/// N.B. this has to be different from `hir::PrimTy` because it also includes types that aren't +/// paths, like `Unit`. crate enum PrimitiveType { Isize, I8, @@ -1248,7 +1253,7 @@ impl Type { crate fn is_self_type(&self) -> bool { match *self { - Generic(ref name) => name == "Self", + Generic(name) => name == kw::SelfUpper, _ => false, } } @@ -1293,16 +1298,16 @@ impl Type { } } - crate fn projection(&self) -> Option<(&Type, DefId, &str)> { + crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> { let (self_, trait_, name) = match self { - QPath { ref self_type, ref trait_, ref name } => (self_type, trait_, name), + QPath { ref self_type, ref trait_, name } => (self_type, trait_, name), _ => return None, }; let trait_did = match **trait_ { ResolvedPath { did, .. } => did, _ => return None, }; - Some((&self_, trait_did, name)) + Some((&self_, trait_did, *name)) } } @@ -1501,6 +1506,37 @@ impl PrimitiveType { crate fn to_url_str(&self) -> &'static str { self.as_str() } + + crate fn as_sym(&self) -> Symbol { + use PrimitiveType::*; + match self { + Isize => sym::isize, + I8 => sym::i8, + I16 => sym::i16, + I32 => sym::i32, + I64 => sym::i64, + I128 => sym::i128, + Usize => sym::usize, + U8 => sym::u8, + U16 => sym::u16, + U32 => sym::u32, + U64 => sym::u64, + U128 => sym::u128, + F32 => sym::f32, + F64 => sym::f64, + Str => sym::str, + Bool => sym::bool, + Char => sym::char, + Array => sym::array, + Slice => sym::slice, + Tuple => sym::tuple, + Unit => sym::unit, + RawPointer => sym::pointer, + Reference => sym::reference, + Fn => kw::Fn, + Never => sym::never, + } + } } impl From for PrimitiveType { @@ -1551,11 +1587,11 @@ impl From for PrimitiveType { } } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] crate enum Visibility { Public, Inherited, - Restricted(DefId, rustc_hir::definitions::DefPath), + Restricted(DefId), } impl Visibility { @@ -1609,32 +1645,41 @@ crate enum VariantKind { Struct(VariantStruct), } +/// Small wrapper around `rustc_span::Span` that adds helper methods and enforces calling `source_callsite`. #[derive(Clone, Debug)] -crate struct Span { - crate filename: FileName, - crate cnum: CrateNum, - crate loline: usize, - crate locol: usize, - crate hiline: usize, - crate hicol: usize, - crate original: rustc_span::Span, -} +crate struct Span(rustc_span::Span); impl Span { - crate fn empty() -> Span { - Span { - filename: FileName::Anon(0), - cnum: LOCAL_CRATE, - loline: 0, - locol: 0, - hiline: 0, - hicol: 0, - original: rustc_span::DUMMY_SP, - } + crate fn from_rustc_span(sp: rustc_span::Span) -> Self { + // Get the macro invocation instead of the definition, + // in case the span is result of a macro expansion. + // (See rust-lang/rust#39726) + Self(sp.source_callsite()) + } + + crate fn dummy() -> Self { + Self(rustc_span::DUMMY_SP) } crate fn span(&self) -> rustc_span::Span { - self.original + self.0 + } + + crate fn filename(&self, sess: &Session) -> FileName { + sess.source_map().span_to_filename(self.0) + } + + crate fn lo(&self, sess: &Session) -> Loc { + sess.source_map().lookup_char_pos(self.0.lo()) + } + + crate fn hi(&self, sess: &Session) -> Loc { + sess.source_map().lookup_char_pos(self.0.hi()) + } + + crate fn cnum(&self, sess: &Session) -> CrateNum { + // FIXME: is there a time when the lo and hi crate would be different? + self.lo(sess).file.cnum } } @@ -1646,13 +1691,17 @@ crate struct Path { } impl Path { - crate fn last_name(&self) -> &str { + crate fn last(&self) -> Symbol { + self.segments.last().expect("segments were empty").name + } + + crate fn last_name(&self) -> SymbolStr { self.segments.last().expect("segments were empty").name.as_str() } crate fn whole_name(&self) -> String { String::from(if self.global { "::" } else { "" }) - + &self.segments.iter().map(|s| s.name.clone()).collect::>().join("::") + + &self.segments.iter().map(|s| s.name.to_string()).collect::>().join("::") } } @@ -1671,7 +1720,7 @@ crate enum GenericArgs { #[derive(Clone, PartialEq, Eq, Debug, Hash)] crate struct PathSegment { - crate name: String, + crate name: Symbol, crate args: GenericArgs, } @@ -1731,7 +1780,7 @@ crate enum ImplPolarity { crate struct Impl { crate unsafety: hir::Unsafety, crate generics: Generics, - crate provided_trait_methods: FxHashSet, + crate provided_trait_methods: FxHashSet, crate trait_: Option, crate for_: Type, crate items: Vec, @@ -1748,7 +1797,7 @@ crate struct Import { } impl Import { - crate fn new_simple(name: String, source: ImportSource, should_be_displayed: bool) -> Self { + crate fn new_simple(name: Symbol, source: ImportSource, should_be_displayed: bool) -> Self { Self { kind: ImportKind::Simple(name), source, should_be_displayed } } @@ -1760,7 +1809,7 @@ impl Import { #[derive(Clone, Debug)] crate enum ImportKind { // use source as str; - Simple(String), + Simple(Symbol), // use source::*; Glob, } @@ -1774,27 +1823,20 @@ crate struct ImportSource { #[derive(Clone, Debug)] crate struct Macro { crate source: String, - crate imported_from: Option, + crate imported_from: Option, } #[derive(Clone, Debug)] crate struct ProcMacro { crate kind: MacroKind, - crate helpers: Vec, -} - -#[derive(Clone, Debug)] -crate struct Deprecation { - crate since: Option, - crate note: Option, - crate is_since_rustc_version: bool, + crate helpers: Vec, } /// An type binding on an associated type (e.g., `A = Bar` in `Foo` or /// `A: Send + Sync` in `Foo`). #[derive(Clone, PartialEq, Eq, Debug, Hash)] crate struct TypeBinding { - crate name: String, + crate name: Symbol, crate kind: TypeBindingKind, } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 1b22d26f49bd..d4482d6fa901 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -8,14 +8,13 @@ use crate::clean::{ }; use crate::core::DocContext; -use itertools::Itertools; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, DefIdTree, Ty}; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use std::mem; @@ -43,7 +42,7 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { let mut module = module.clean(cx); let mut masked_crates = FxHashSet::default(); - match module.kind { + match *module.kind { ItemKind::ModuleItem(ref module) => { for it in &module.items { // `compiler_builtins` should be masked too, but we can't apply @@ -61,20 +60,20 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx); { - let m = match module.kind { + let m = match *module.kind { ItemKind::ModuleItem(ref mut m) => m, _ => unreachable!(), }; m.items.extend(primitives.iter().map(|&(def_id, prim)| { Item::from_def_id_and_parts( def_id, - Some(prim.to_url_str().to_owned()), + Some(prim.as_sym()), ItemKind::PrimitiveItem(prim), cx, ) })); m.items.extend(keywords.into_iter().map(|(def_id, kw)| { - Item::from_def_id_and_parts(def_id, Some(kw.clone()), ItemKind::KeywordItem(kw), cx) + Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem(kw), cx) })); } @@ -104,7 +103,9 @@ fn external_generic_args( .iter() .filter_map(|kind| match kind.unpack() { GenericArgKind::Lifetime(lt) => match lt { - ty::ReLateBound(_, ty::BrAnon(_)) => Some(GenericArg::Lifetime(Lifetime::elided())), + ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrAnon(_) }) => { + Some(GenericArg::Lifetime(Lifetime::elided())) + } _ => lt.clean(cx).map(GenericArg::Lifetime), }, GenericArgKind::Type(_) if skip_self => { @@ -153,7 +154,7 @@ pub(super) fn external_path( global: false, res: Res::Err, segments: vec![PathSegment { - name: name.to_string(), + name, args: external_generic_args(cx, trait_did, has_self, bindings, substs), }], } @@ -170,13 +171,13 @@ crate fn get_real_types( cx: &DocContext<'_>, recurse: i32, ) -> FxHashSet<(Type, TypeKind)> { - let arg_s = arg.print().to_string(); let mut res = FxHashSet::default(); if recurse >= 10 { // FIXME: remove this whole recurse thing when the recursion bug is fixed return res; } if arg.is_full_generic() { + let arg_s = Symbol::intern(&arg.print().to_string()); if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g { &WherePredicate::BoundPredicate { ref ty, .. } => ty.def_id() == arg.def_id(), _ => false, @@ -305,7 +306,7 @@ crate fn strip_path(path: &Path) -> Path { .segments .iter() .map(|s| PathSegment { - name: s.name.clone(), + name: s.name, args: GenericArgs::AngleBracketed { args: vec![], bindings: vec![] }, }) .collect(); @@ -336,7 +337,7 @@ crate fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut let tcx = cx.tcx; for item in items { - let target = match item.kind { + let target = match *item.kind { ItemKind::TypedefItem(ref t, true) => &t.type_, _ => continue, }; @@ -375,13 +376,13 @@ impl ToSource for rustc_span::Span { } } -crate fn name_from_pat(p: &hir::Pat<'_>) -> String { +crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { use rustc_hir::*; debug!("trying to get a name from pattern: {:?}", p); - match p.kind { - PatKind::Wild => "_".to_string(), - PatKind::Binding(_, _, ident, _) => ident.to_string(), + Symbol::intern(&match p.kind { + PatKind::Wild => return kw::Underscore, + PatKind::Binding(_, _, ident, _) => return ident.name, PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), PatKind::Struct(ref name, ref fields, etc) => format!( "{} {{ {}{} }}", @@ -393,32 +394,37 @@ crate fn name_from_pat(p: &hir::Pat<'_>) -> String { .join(", "), if etc { ", .." } else { "" } ), - PatKind::Or(ref pats) => { - pats.iter().map(|p| name_from_pat(&**p)).collect::>().join(" | ") - } + PatKind::Or(ref pats) => pats + .iter() + .map(|p| name_from_pat(&**p).to_string()) + .collect::>() + .join(" | "), PatKind::Tuple(ref elts, _) => format!( "({})", - elts.iter().map(|p| name_from_pat(&**p)).collect::>().join(", ") + elts.iter() + .map(|p| name_from_pat(&**p).to_string()) + .collect::>() + .join(", ") ), - PatKind::Box(ref p) => name_from_pat(&**p), - PatKind::Ref(ref p, _) => name_from_pat(&**p), + PatKind::Box(ref p) => return name_from_pat(&**p), + PatKind::Ref(ref p, _) => return name_from_pat(&**p), PatKind::Lit(..) => { warn!( "tried to get argument name from PatKind::Lit, which is silly in function arguments" ); - "()".to_string() + return Symbol::intern("()"); } PatKind::Range(..) => panic!( "tried to get argument name from PatKind::Range, \ which is not allowed in function arguments" ), PatKind::Slice(ref begin, ref mid, ref end) => { - let begin = begin.iter().map(|p| name_from_pat(&**p)); + let begin = begin.iter().map(|p| name_from_pat(&**p).to_string()); let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter(); - let end = end.iter().map(|p| name_from_pat(&**p)); + let end = end.iter().map(|p| name_from_pat(&**p).to_string()); format!("[{}]", begin.chain(mid).chain(end).collect::>().join(", ")) } - } + }) } crate fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { @@ -534,10 +540,10 @@ crate fn resolve_type(cx: &DocContext<'_>, path: Path, id: hir::HirId) -> Type { let is_generic = match path.res { Res::PrimTy(p) => return Primitive(PrimitiveType::from(p)), Res::SelfTy(..) if path.segments.len() == 1 => { - return Generic(kw::SelfUpper.to_string()); + return Generic(kw::SelfUpper); } Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => { - return Generic(format!("{:#}", path.print())); + return Generic(Symbol::intern(&format!("{:#}", path.print()))); } Res::SelfTy(..) | Res::Def(DefKind::TyParam | DefKind::AssocTy, _) => true, _ => false, @@ -551,10 +557,13 @@ crate fn get_auto_trait_and_blanket_impls( ty: Ty<'tcx>, param_env_def_id: DefId, ) -> impl Iterator { - AutoTraitFinder::new(cx) - .get_auto_trait_impls(ty, param_env_def_id) - .into_iter() - .chain(BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id)) + let auto_impls = cx.sess().time("get_auto_trait_impls", || { + AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id) + }); + let blanket_impls = cx.sess().time("get_blanket_impls", || { + BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id) + }); + auto_impls.into_iter().chain(blanket_impls) } crate fn register_res(cx: &DocContext<'_>, res: Res) -> DefId { @@ -614,3 +623,24 @@ where *cx.impl_trait_bounds.borrow_mut() = old_bounds; r } + +/// Find the nearest parent module of a [`DefId`]. +/// +/// **Panics if the item it belongs to [is fake][Item::is_fake].** +crate fn find_nearest_parent_module(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + if def_id.is_top_level_module() { + // The crate root has no parent. Use it as the root instead. + Some(def_id) + } else { + let mut current = def_id; + // The immediate parent might not always be a module. + // Find the first parent which is. + while let Some(parent) = tcx.parent(current) { + if tcx.def_kind(parent) == DefKind::Mod { + return Some(parent); + } + current = parent; + } + None + } +} diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index e60970af0d34..2d58614b1397 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -397,12 +397,9 @@ impl Options { matches .opt_strs("default-setting") .iter() - .map(|s| { - let mut kv = s.splitn(2, '='); - // never panics because `splitn` always returns at least one element - let k = kv.next().unwrap().to_string(); - let v = kv.next().unwrap_or("true").to_string(); - (k, v) + .map(|s| match s.split_once('=') { + None => (s.clone(), "true".to_string()), + Some((k, v)) => (k.to_string(), v.to_string()), }) .collect(), ]; @@ -707,11 +704,9 @@ fn parse_extern_html_roots( ) -> Result, &'static str> { let mut externs = BTreeMap::new(); for arg in &matches.opt_strs("extern-html-root-url") { - let mut parts = arg.splitn(2, '='); - let name = parts.next().ok_or("--extern-html-root-url must not be empty")?; - let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?; + let (name, url) = + arg.split_once('=').ok_or("--extern-html-root-url must be of the form name=url")?; externs.insert(name.to_string(), url.to_string()); } - Ok(externs) } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b2eeaf584bf2..7e85342ac7d5 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -11,7 +11,7 @@ use rustc_hir::{ intravisit::{self, NestedVisitorMap, Visitor}, Path, }; -use rustc_interface::interface; +use rustc_interface::{interface, Queries}; use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; @@ -273,12 +273,9 @@ where (lint_opts, lint_caps) } -crate fn run_core( - options: RustdocOptions, -) -> (clean::Crate, RenderInfo, RenderOptions, Lrc) { - // Parse, resolve, and typecheck the given crate. - - let RustdocOptions { +/// Parse, resolve, and typecheck the given crate. +crate fn create_config( + RustdocOptions { input, crate_name, proc_macro_crate, @@ -294,21 +291,10 @@ crate fn run_core( lint_opts, describe_lints, lint_cap, - default_passes, - manual_passes, display_warnings, - render_options, - output_format, .. - } = options; - - let extern_names: Vec = externs - .iter() - .filter(|(_, entry)| entry.add_prelude) - .map(|(name, _)| name) - .cloned() - .collect(); - + }: RustdocOptions, +) -> rustc_interface::Config { // Add the doc cfg into the doc build. cfgs.push("doc".to_string()); @@ -374,7 +360,7 @@ crate fn run_core( ..Options::default() }; - let config = interface::Config { + interface::Config { opts: sessopts, crate_cfg: interface::parse_cfgspecs(cfgs), input, @@ -417,68 +403,50 @@ crate fn run_core( }), make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), - }; - - interface::create_compiler_and_run(config, |compiler| { - compiler.enter(|queries| { - let sess = compiler.session(); - - // We need to hold on to the complete resolver, so we cause everything to be - // cloned for the analysis passes to use. Suboptimal, but necessary in the - // current architecture. - let resolver = { - let parts = abort_on_err(queries.expansion(), sess).peek(); - let resolver = parts.1.borrow(); - - // Before we actually clone it, let's force all the extern'd crates to - // actually be loaded, just in case they're only referred to inside - // intra-doc-links - resolver.borrow_mut().access(|resolver| { - sess.time("load_extern_crates", || { - for extern_name in &extern_names { - debug!("loading extern crate {}", extern_name); - resolver - .resolve_str_path_error( - DUMMY_SP, - extern_name, - TypeNS, - LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), - ) - .unwrap_or_else(|()| { - panic!("Unable to resolve external crate {}", extern_name) - }); - } - }); - }); - - // Now we're good to clone the resolver because everything should be loaded - resolver.clone() - }; - - if sess.has_errors() { - sess.fatal("Compilation failed, aborting rustdoc"); - } - - let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess).take(); - - let (krate, render_info, opts) = sess.time("run_global_ctxt", || { - global_ctxt.enter(|tcx| { - run_global_ctxt( - tcx, - resolver, - default_passes, - manual_passes, - render_options, - output_format, - ) - }) - }); - (krate, render_info, opts, Lrc::clone(sess)) - }) - }) + } } -fn run_global_ctxt( +crate fn create_resolver<'a>( + externs: config::Externs, + queries: &Queries<'a>, + sess: &Session, +) -> Rc> { + let extern_names: Vec = externs + .iter() + .filter(|(_, entry)| entry.add_prelude) + .map(|(name, _)| name) + .cloned() + .collect(); + + let parts = abort_on_err(queries.expansion(), sess).peek(); + let resolver = parts.1.borrow(); + + // Before we actually clone it, let's force all the extern'd crates to + // actually be loaded, just in case they're only referred to inside + // intra-doc-links + resolver.borrow_mut().access(|resolver| { + sess.time("load_extern_crates", || { + for extern_name in &extern_names { + debug!("loading extern crate {}", extern_name); + resolver + .resolve_str_path_error( + DUMMY_SP, + extern_name, + TypeNS, + LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), + ) + .unwrap_or_else(|()| { + panic!("Unable to resolve external crate {}", extern_name) + }); + } + }); + }); + + // Now we're good to clone the resolver because everything should be loaded + resolver.clone() +} + +crate fn run_global_ctxt( tcx: TyCtxt<'_>, resolver: Rc>, mut default_passes: passes::DefaultPassOption, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 37fe13c32ce7..02dd42ce0c14 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -247,9 +247,10 @@ fn run_test( edition: Edition, outdir: DirState, path: PathBuf, + test_id: &str, ) -> Result<(), TestFailure> { let (test, line_offset, supports_color) = - make_test(test, Some(cratename), as_test_harness, opts, edition); + make_test(test, Some(cratename), as_test_harness, opts, edition, Some(test_id)); let output_file = outdir.path().join("rust_out"); @@ -387,6 +388,7 @@ crate fn make_test( dont_insert_main: bool, opts: &TestOptions, edition: Edition, + test_id: Option<&str>, ) -> (String, usize, bool) { let (crate_attrs, everything_else, crates) = partition_source(s); let everything_else = everything_else.trim(); @@ -542,16 +544,41 @@ crate fn make_test( prog.push_str(everything_else); } else { let returns_result = everything_else.trim_end().ends_with("(())"); + // Give each doctest main function a unique name. + // This is for example needed for the tooling around `-Z instrument-coverage`. + let inner_fn_name = if let Some(test_id) = test_id { + format!("_doctest_main_{}", test_id) + } else { + "_inner".into() + }; + let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; let (main_pre, main_post) = if returns_result { ( - "fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {", - "}\n_inner().unwrap() }", + format!( + "fn main() {{ {}fn {}() -> Result<(), impl core::fmt::Debug> {{\n", + inner_attr, inner_fn_name + ), + format!("\n}} {}().unwrap() }}", inner_fn_name), + ) + } else if test_id.is_some() { + ( + format!("fn main() {{ {}fn {}() {{\n", inner_attr, inner_fn_name), + format!("\n}} {}() }}", inner_fn_name), ) } else { - ("fn main() {\n", "\n}") + ("fn main() {\n".into(), "\n}".into()) }; - prog.extend([main_pre, everything_else, main_post].iter().cloned()); + // Note on newlines: We insert a line/newline *before*, and *after* + // the doctest and adjust the `line_offset` accordingly. + // In the case of `-Z instrument-coverage`, this means that the generated + // inner `main` function spans from the doctest opening codeblock to the + // closing one. For example + // /// ``` <- start of the inner main + // /// <- code under doctest + // /// ``` <- end of the inner main line_offset += 1; + + prog.extend([&main_pre, everything_else, &main_post].iter().cloned()); } debug!("final doctest:\n{}", prog); @@ -749,28 +776,24 @@ impl Tester for Collector { _ => PathBuf::from(r"doctest.rs"), }; + // For example `module/file.rs` would become `module_file_rs` + let file = filename + .to_string() + .chars() + .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }) + .collect::(); + let test_id = format!( + "{file}_{line}_{number}", + file = file, + line = line, + number = { + // Increases the current test number, if this file already + // exists or it creates a new entry with a test number of 0. + self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0) + }, + ); let outdir = if let Some(mut path) = options.persist_doctests.clone() { - // For example `module/file.rs` would become `module_file_rs` - let folder_name = filename - .to_string() - .chars() - .map(|c| if c == '\\' || c == '/' || c == '.' { '_' } else { c }) - .collect::(); - - path.push(format!( - "{krate}_{file}_{line}_{number}", - krate = cratename, - file = folder_name, - line = line, - number = { - // Increases the current test number, if this file already - // exists or it creates a new entry with a test number of 0. - self.visited_tests - .entry((folder_name.clone(), line)) - .and_modify(|v| *v += 1) - .or_insert(0) - }, - )); + path.push(&test_id); std::fs::create_dir_all(&path) .expect("Couldn't create directory for doctest executables"); @@ -817,6 +840,7 @@ impl Tester for Collector { edition, outdir, path, + &test_id, ); if let Err(err) = res { diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index a024e9c72a43..465b2b1d69b3 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -11,7 +11,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); } @@ -26,7 +26,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); } @@ -44,7 +44,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 3)); } @@ -61,7 +61,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); } @@ -79,7 +79,7 @@ use std::*; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); } @@ -98,7 +98,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); } @@ -115,7 +115,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); } @@ -134,7 +134,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 3)); // Adding more will also bump the returned line offset. @@ -147,7 +147,7 @@ use asdf::qwop; assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 4)); } @@ -164,7 +164,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); } @@ -180,7 +180,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 1)); } @@ -196,7 +196,7 @@ fn main() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); } @@ -210,7 +210,7 @@ assert_eq!(2+2, 4);"; //Ceci n'est pas une `fn main` assert_eq!(2+2, 4);" .to_string(); - let (output, len, _) = make_test(input, None, true, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, None, true, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 1)); } @@ -224,7 +224,7 @@ fn make_test_display_warnings() { assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 1)); } @@ -242,7 +242,7 @@ assert_eq!(2+2, 4); }" .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); let input = "extern crate hella_qwop; @@ -256,7 +256,7 @@ assert_eq!(asdf::foo, 4); }" .to_string(); - let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 3)); } @@ -274,6 +274,41 @@ test_wrapper! { }" .to_string(); - let (output, len, _) = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION); + let (output, len, _) = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 1)); } + +#[test] +fn make_test_returns_result() { + // creates an inner function and unwraps it + let opts = TestOptions::default(); + let input = "use std::io; +let mut input = String::new(); +io::stdin().read_line(&mut input)?; +Ok::<(), io:Error>(())"; + let expected = "#![allow(unused)] +fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> { +use std::io; +let mut input = String::new(); +io::stdin().read_line(&mut input)?; +Ok::<(), io:Error>(()) +} _inner().unwrap() }" + .to_string(); + let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); + assert_eq!((output, len), (expected, 2)); +} + +#[test] +fn make_test_named_wrapper() { + // creates an inner function with a specific name + let opts = TestOptions::default(); + let input = "assert_eq!(2+2, 4);"; + let expected = "#![allow(unused)] +fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() { +assert_eq!(2+2, 4); +} _doctest_main__some_unique_name() }" + .to_string(); + let (output, len, _) = + make_test(input, None, false, &opts, DEFAULT_EDITION, Some("_some_unique_name")); + assert_eq!((output, len), (expected, 2)); +} diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index ee9a69818579..bc9f1cf8806a 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -9,7 +9,6 @@ use rustc_hir as hir; crate struct Module<'hir> { crate name: Option, - crate attrs: &'hir [ast::Attribute], crate where_outer: Span, crate where_inner: Span, crate imports: Vec>, @@ -23,13 +22,12 @@ crate struct Module<'hir> { } impl Module<'hir> { - crate fn new(name: Option, attrs: &'hir [ast::Attribute]) -> Module<'hir> { + crate fn new(name: Option) -> Module<'hir> { Module { name, id: hir::CRATE_HIR_ID, where_outer: rustc_span::DUMMY_SP, where_inner: rustc_span::DUMMY_SP, - attrs, imports: Vec::new(), mods: Vec::new(), items: Vec::new(), diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 285fabdc3723..c39cc3ca3977 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -3,12 +3,12 @@ use crate::clean::*; crate struct StripItem(pub Item); impl StripItem { - crate fn strip(self) -> Option { + crate fn strip(self) -> Item { match self.0 { - Item { kind: StrippedItem(..), .. } => Some(self.0), + Item { kind: box StrippedItem(..), .. } => self.0, mut i => { - i.kind = StrippedItem(box i.kind); - Some(i) + i.kind = box StrippedItem(i.kind); + i } } } @@ -72,9 +72,9 @@ crate trait DocFolder: Sized { /// don't override! fn fold_item_recur(&mut self, mut item: Item) -> Item { - item.kind = match item.kind { + item.kind = box match *item.kind { StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)), - _ => self.fold_inner_recur(item.kind), + _ => self.fold_inner_recur(*item.kind), }; item } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index e82bc540e95a..899d61d8e43d 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -8,6 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_middle::middle::privacy::AccessLevels; use rustc_span::source_map::FileName; +use rustc_span::Symbol; use crate::clean::{self, GetDefId}; use crate::config::RenderInfo; @@ -74,7 +75,7 @@ crate struct Cache { crate implementors: FxHashMap>, /// Cache of where external crate documentation can be found. - crate extern_locations: FxHashMap, + crate extern_locations: FxHashMap, /// Cache of where documentation for primitives can be found. crate primitive_locations: FxHashMap, @@ -173,10 +174,10 @@ impl Cache { }, _ => PathBuf::new(), }; - let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u); + let extern_url = extern_html_root_urls.get(&*e.name.as_str()).map(|u| &**u); cache .extern_locations - .insert(n, (e.name.clone(), src_root, extern_location(e, extern_url, &dst))); + .insert(n, (e.name, src_root, extern_location(e, extern_url, &dst))); let did = DefId { krate: n, index: CRATE_DEF_INDEX }; cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); @@ -195,7 +196,7 @@ impl Cache { cache.primitive_locations.insert(prim, def_id); } - cache.stack.push(krate.name.clone()); + cache.stack.push(krate.name.to_string()); krate = cache.fold_crate(krate); for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) { @@ -218,7 +219,7 @@ impl DocFolder for Cache { // If this is a stripped module, // we don't want it or its children in the search index. - let orig_stripped_mod = match item.kind { + let orig_stripped_mod = match *item.kind { clean::StrippedItem(box clean::ModuleItem(..)) => { mem::replace(&mut self.stripped_mod, true) } @@ -227,7 +228,7 @@ impl DocFolder for Cache { // If the impl is from a masked crate or references something from a // masked crate then remove it completely. - if let clean::ImplItem(ref i) = item.kind { + if let clean::ImplItem(ref i) = *item.kind { if self.masked_crates.contains(&item.def_id.krate) || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) @@ -238,12 +239,12 @@ impl DocFolder for Cache { // Propagate a trait method's documentation to all implementors of the // trait. - if let clean::TraitItem(ref t) = item.kind { + if let clean::TraitItem(ref t) = *item.kind { self.traits.entry(item.def_id).or_insert_with(|| t.clone()); } // Collect all the implementors of traits. - if let clean::ImplItem(ref i) = item.kind { + if let clean::ImplItem(ref i) = *item.kind { if let Some(did) = i.trait_.def_id() { if i.blanket_impl.is_none() { self.implementors @@ -256,7 +257,7 @@ impl DocFolder for Cache { // Index this method for searching later on. if let Some(ref s) = item.name { - let (parent, is_inherent_impl_item) = match item.kind { + let (parent, is_inherent_impl_item) = match *item.kind { clean::StrippedItem(..) => ((None, None), false), clean::AssocConstItem(..) | clean::TypedefItem(_, true) if self.parent_is_trait_impl => @@ -340,14 +341,14 @@ impl DocFolder for Cache { // Keep track of the fully qualified path for this item. let pushed = match item.name { - Some(ref n) if !n.is_empty() => { + Some(n) if !n.is_empty() => { self.stack.push(n.to_string()); true } _ => false, }; - match item.kind { + match *item.kind { clean::StructItem(..) | clean::EnumItem(..) | clean::TypedefItem(..) @@ -386,7 +387,7 @@ impl DocFolder for Cache { // Maintain the parent stack let orig_parent_is_trait_impl = self.parent_is_trait_impl; - let parent_pushed = match item.kind { + let parent_pushed = match *item.kind { clean::TraitItem(..) | clean::EnumItem(..) | clean::ForeignTypeItem @@ -424,38 +425,33 @@ impl DocFolder for Cache { // Once we've recursively found all the generics, hoard off all the // implementations elsewhere. let item = self.fold_item_recur(item); - let ret = if let clean::Item { kind: clean::ImplItem(_), .. } = item { + let ret = if let clean::Item { kind: box clean::ImplItem(ref i), .. } = item { // Figure out the id of this impl. This may map to a // primitive rather than always to a struct/enum. // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxHashSet::default(); - if let clean::Item { kind: clean::ImplItem(ref i), .. } = item { - match i.for_ { - clean::ResolvedPath { did, .. } - | clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => { + match i.for_ { + clean::ResolvedPath { did, .. } + | clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => { + dids.insert(did); + } + ref t => { + let did = + t.primitive_type().and_then(|t| self.primitive_locations.get(&t).cloned()); + + if let Some(did) = did { dids.insert(did); } - ref t => { - let did = t - .primitive_type() - .and_then(|t| self.primitive_locations.get(&t).cloned()); + } + } - if let Some(did) = did { - dids.insert(did); - } + if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { + for bound in generics { + if let Some(did) = bound.def_id() { + dids.insert(did); } } - - if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { - for bound in generics { - if let Some(did) = bound.def_id() { - dids.insert(did); - } - } - } - } else { - unreachable!() - }; + } let impl_item = Impl { impl_item: item }; if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) { for did in dids { diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index af512e374609..ad51adf2fe3f 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -60,7 +60,7 @@ impl Serialize for ItemType { impl<'a> From<&'a clean::Item> for ItemType { fn from(item: &'a clean::Item) -> ItemType { - let kind = match item.kind { + let kind = match *item.kind { clean::StrippedItem(box ref item) => item, ref kind => kind, }; diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs index 55fd4948f452..58b9f5fbf0f6 100644 --- a/src/librustdoc/formats/mod.rs +++ b/src/librustdoc/formats/mod.rs @@ -32,7 +32,7 @@ crate struct Impl { impl Impl { crate fn inner_impl(&self) -> &clean::Impl { - match self.impl_item.kind { + match *self.impl_item.kind { clean::ImplItem(ref impl_) => impl_, _ => panic!("non-impl item found in impl"), } diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index d0fdc69cc193..e84a9853d9b7 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use rustc_middle::ty; use rustc_span::edition::Edition; use crate::clean; @@ -10,7 +11,7 @@ use crate::formats::cache::{Cache, CACHE_KEY}; /// Allows for different backends to rustdoc to be used with the `run_format()` function. Each /// backend renderer has hooks for initialization, documenting an item, entering and exiting a /// module, and cleanup/finalizing output. -crate trait FormatRenderer: Clone { +crate trait FormatRenderer<'tcx>: Clone { /// Sets up any state required for the renderer. When this is called the cache has already been /// populated. fn init( @@ -19,6 +20,7 @@ crate trait FormatRenderer: Clone { render_info: RenderInfo, edition: Edition, cache: &mut Cache, + tcx: ty::TyCtxt<'tcx>, ) -> Result<(Self, clean::Crate), Error>; /// Renders a single non-module item. This means no recursive sub-item rendering is required. @@ -43,12 +45,13 @@ crate trait FormatRenderer: Clone { } /// Main method for rendering a crate. -crate fn run_format( +crate fn run_format<'tcx, T: FormatRenderer<'tcx>>( krate: clean::Crate, options: RenderOptions, render_info: RenderInfo, diag: &rustc_errors::Handler, edition: Edition, + tcx: ty::TyCtxt<'tcx>, ) -> Result<(), Error> { let (krate, mut cache) = Cache::from_krate( render_info.clone(), @@ -59,7 +62,7 @@ crate fn run_format( ); let (mut format_renderer, mut krate) = - T::init(krate, options, render_info, edition, &mut cache)?; + T::init(krate, options, render_info, edition, &mut cache, tcx)?; let cache = Arc::new(cache); // Freeze the cache now that the index has been built. Put an Arc into TLS for future @@ -71,7 +74,7 @@ crate fn run_format( None => return Ok(()), }; - item.name = Some(krate.name.clone()); + item.name = Some(krate.name); // Render the crate documentation let mut work = vec![(format_renderer.clone(), item)]; @@ -86,7 +89,7 @@ crate fn run_format( } cx.mod_item_in(&item, &name, &cache)?; - let module = match item.kind { + let module = match *item.kind { clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m, _ => unreachable!(), }; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 536c2e08fdef..9b2fb8582f54 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -11,10 +11,11 @@ use std::fmt; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; +use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_target::spec::abi::Abi; -use crate::clean::{self, PrimitiveType}; +use crate::clean::{self, utils::find_nearest_parent_module, PrimitiveType}; use crate::formats::cache::cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; @@ -172,7 +173,7 @@ impl clean::GenericParamDef { display_fn(move |f| match self.kind { clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name), clean::GenericParamDefKind::Type { ref bounds, ref default, .. } => { - f.write_str(&self.name)?; + f.write_str(&*self.name.as_str())?; if !bounds.is_empty() { if f.alternate() { @@ -193,13 +194,10 @@ impl clean::GenericParamDef { Ok(()) } clean::GenericParamDefKind::Const { ref ty, .. } => { - f.write_str("const ")?; - f.write_str(&self.name)?; - if f.alternate() { - write!(f, ": {:#}", ty.print()) + write!(f, "const {}: {:#}", self.name, ty.print()) } else { - write!(f, ": {}", ty.print()) + write!(f, "const {}: {}", self.name, ty.print()) } } }) @@ -311,7 +309,7 @@ impl<'a> fmt::Display for WhereClause<'a> { } impl clean::Lifetime { - crate fn print(&self) -> &str { + crate fn print(&self) -> impl fmt::Display + '_ { self.get_ref() } } @@ -448,11 +446,10 @@ impl clean::GenericArgs { impl clean::PathSegment { crate fn print(&self) -> impl fmt::Display + '_ { display_fn(move |f| { - f.write_str(&self.name)?; if f.alternate() { - write!(f, "{:#}", self.args.print()) + write!(f, "{}{:#}", self.name, self.args.print()) } else { - write!(f, "{}", self.args.print()) + write!(f, "{}{}", self.name, self.args.print()) } }) } @@ -547,7 +544,7 @@ fn resolved_path( last.name.to_string() } } else { - anchor(did, &last.name).to_string() + anchor(did, &*last.name.as_str()).to_string() }; write!(w, "{}{}", path, last.args.print())?; } @@ -638,7 +635,7 @@ crate fn anchor(did: DefId, text: &str) -> impl fmt::Display + '_ { fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> fmt::Result { match *t { - clean::Generic(ref name) => f.write_str(name), + clean::Generic(name) => write!(f, "{}", name), clean::ResolvedPath { did, ref param_names, ref path, is_generic } => { if param_names.is_some() { f.write_str("dyn ")?; @@ -1088,32 +1085,54 @@ impl Function<'_> { } impl clean::Visibility { - crate fn print_with_space(&self) -> impl fmt::Display + '_ { + crate fn print_with_space<'tcx>( + self, + tcx: TyCtxt<'tcx>, + item_did: DefId, + ) -> impl fmt::Display + 'tcx { use rustc_span::symbol::kw; - display_fn(move |f| match *self { + display_fn(move |f| match self { clean::Public => f.write_str("pub "), clean::Inherited => Ok(()), - // If this is `pub(crate)`, `path` will be empty. - clean::Visibility::Restricted(did, _) if did.index == CRATE_DEF_INDEX => { - write!(f, "pub(crate) ") - } - clean::Visibility::Restricted(did, ref path) => { - f.write_str("pub(")?; - debug!("path={:?}", path); - let first_name = - path.data[0].data.get_opt_name().expect("modules are always named"); - if path.data.len() != 1 || (first_name != kw::SelfLower && first_name != kw::Super) + + clean::Visibility::Restricted(vis_did) => { + // FIXME(camelid): This may not work correctly if `item_did` is a module. + // However, rustdoc currently never displays a module's + // visibility, so it shouldn't matter. + let parent_module = find_nearest_parent_module(tcx, item_did); + + if vis_did.index == CRATE_DEF_INDEX { + write!(f, "pub(crate) ") + } else if parent_module == Some(vis_did) { + // `pub(in foo)` where `foo` is the parent module + // is the same as no visibility modifier + Ok(()) + } else if parent_module + .map(|parent| find_nearest_parent_module(tcx, parent)) + .flatten() + == Some(vis_did) { - f.write_str("in ")?; + write!(f, "pub(super) ") + } else { + f.write_str("pub(")?; + let path = tcx.def_path(vis_did); + debug!("path={:?}", path); + let first_name = + path.data[0].data.get_opt_name().expect("modules are always named"); + if path.data.len() != 1 + || (first_name != kw::SelfLower && first_name != kw::Super) + { + f.write_str("in ")?; + } + // modified from `resolved_path()` to work with `DefPathData` + let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); + for seg in &path.data[..path.data.len() - 1] { + write!(f, "{}::", seg.data.get_opt_name().unwrap())?; + } + let path = anchor(vis_did, &last_name.as_str()).to_string(); + write!(f, "{}) ", path) } - // modified from `resolved_path()` to work with `DefPathData` - let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); - for seg in &path.data[..path.data.len() - 1] { - write!(f, "{}::", seg.data.get_opt_name().unwrap())?; - } - let path = anchor(did, &last_name.as_str()).to_string(); - write!(f, "{}) ", path) } }) } @@ -1162,11 +1181,11 @@ impl PrintWithSpace for hir::Mutability { impl clean::Import { crate fn print(&self) -> impl fmt::Display + '_ { display_fn(move |f| match self.kind { - clean::ImportKind::Simple(ref name) => { - if *name == self.source.path.last_name() { + clean::ImportKind::Simple(name) => { + if name == self.source.path.last() { write!(f, "use {};", self.source.print()) } else { - write!(f, "use {} as {};", self.source.print(), *name) + write!(f, "use {} as {};", self.source.print(), name) } } clean::ImportKind::Glob => { @@ -1190,7 +1209,7 @@ impl clean::ImportSource { } let name = self.path.last_name(); if let hir::def::Res::PrimTy(p) = self.path.res { - primitive_link(f, PrimitiveType::from(p), name)?; + primitive_link(f, PrimitiveType::from(p), &*name)?; } else { write!(f, "{}", name)?; } @@ -1203,7 +1222,7 @@ impl clean::ImportSource { impl clean::TypeBinding { crate fn print(&self) -> impl fmt::Display + '_ { display_fn(move |f| { - f.write_str(&self.name)?; + f.write_str(&*self.name.as_str())?; match self.kind { clean::TypeBindingKind::Equality { ref ty } => { if f.alternate() { diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1cbfbf50dd74..d21998bb8cfe 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -11,7 +11,8 @@ use std::fmt::{Display, Write}; use std::iter::Peekable; use rustc_lexer::{LiteralKind, TokenKind}; -use rustc_span::symbol::Ident; +use rustc_span::edition::Edition; +use rustc_span::symbol::Symbol; use rustc_span::with_default_session_globals; /// Highlights `src`, returning the HTML output. @@ -19,22 +20,27 @@ crate fn render_with_highlighting( src: String, class: Option<&str>, playground_button: Option<&str>, - tooltip: Option<(&str, &str)>, + tooltip: Option<(Option, &str)>, + edition: Edition, ) -> String { debug!("highlighting: ================\n{}\n==============", src); let mut out = String::with_capacity(src.len()); - if let Some((tooltip, class)) = tooltip { + if let Some((edition_info, class)) = tooltip { write!( out, - "
{}
", - class, tooltip + "
", + class, + if let Some(edition_info) = edition_info { + format!(" data-edition=\"{}\"", edition_info) + } else { + String::new() + }, ) .unwrap(); } write_header(&mut out, class); - write_code(&mut out, &src); + write_code(&mut out, &src, edition); write_footer(&mut out, playground_button); out @@ -45,10 +51,10 @@ fn write_header(out: &mut String, class: Option<&str>) { .unwrap() } -fn write_code(out: &mut String, src: &str) { +fn write_code(out: &mut String, src: &str, edition: Edition) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); - Classifier::new(&src).highlight(&mut |highlight| { + Classifier::new(&src, edition).highlight(&mut |highlight| { match highlight { Highlight::Token { text, class } => string(out, Escape(text), class), Highlight::EnterSpan { class } => enter_span(out, class), @@ -139,12 +145,19 @@ struct Classifier<'a> { in_attribute: bool, in_macro: bool, in_macro_nonterminal: bool, + edition: Edition, } impl<'a> Classifier<'a> { - fn new(src: &str) -> Classifier<'_> { + fn new(src: &str, edition: Edition) -> Classifier<'_> { let tokens = TokenIter { src }.peekable(); - Classifier { tokens, in_attribute: false, in_macro: false, in_macro_nonterminal: false } + Classifier { + tokens, + in_attribute: false, + in_macro: false, + in_macro_nonterminal: false, + edition, + } } /// Exhausts the `Classifier` writing the output into `sink`. @@ -296,7 +309,7 @@ impl<'a> Classifier<'a> { "Option" | "Result" => Class::PreludeTy, "Some" | "None" | "Ok" | "Err" => Class::PreludeVal, // Keywords are also included in the identifier set. - _ if Ident::from_str(text).is_reserved() => Class::KeyWord, + _ if Symbol::intern(text).is_reserved(|| self.edition) => Class::KeyWord, _ if self.in_macro_nonterminal => { self.in_macro_nonterminal = false; Class::MacroNonTerminal diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index f57f52d6f087..f97c8a7ab714 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -1,5 +1,6 @@ use super::write_code; use expect_test::expect_file; +use rustc_span::edition::Edition; const STYLE: &str = r#" + + +
@0⦊fn main() ⦉@0{ + if @0⦊true⦉@0 { + @5⦊@4,6,7,8,9⦊assert_eq!(1, 1);⦉@4,6,7,8,9⦉@5 + } else { + @11⦊@10,12,13,14,15⦊assert_eq!(1, 2);⦉@10,12,13,14,15⦉@11 + } +}@16⦊⦉@16
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest_crate/doctest_crate.fn_run_in_doctests.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest_crate/doctest_crate.fn_run_in_doctests.-------.InstrumentCoverage.0.html new file mode 100644 index 000000000000..ae119d9ca9f2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest_crate/doctest_crate.fn_run_in_doctests.-------.InstrumentCoverage.0.html @@ -0,0 +1,173 @@ + + + + +doctest_crate.fn_run_in_doctests - Coverage Spans + + + +
@0⦊pub fn fn_run_in_doctests(conditional: usize) ⦉@0{ + match @0⦊conditional⦉@0 { + 1 => @7⦊@6,8,9,10,11⦊assert_eq!(1, 1)⦉@6,8,9,10,11⦉@7, // this is run, + 2 => @14⦊@13,15,16,17,18⦊assert_eq!(1, 1)⦉@13,15,16,17,18⦉@14, // this, + 3 => @21⦊@20,22,23,24,25⦊assert_eq!(1, 1)⦉@20,22,23,24,25⦉@21, // and this too + _ => @27⦊@26,28,29,30,31⦊assert_eq!(1, 2)⦉@26,28,29,30,31⦉@27, // however this is not + } +}@32⦊⦉@32
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.match_or_pattern/match_or_pattern.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.match_or_pattern/match_or_pattern.main.-------.InstrumentCoverage.0.html new file mode 100644 index 000000000000..133a85c83945 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.match_or_pattern/match_or_pattern.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,271 @@ + + + + +match_or_pattern.main - Coverage Spans + + + +
@0,1,2,3⦊fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut a: u8 = 0; + let mut b: u8 = 0; + if is_true⦉@0,1,2,3 @4,6⦊{ + a = 2; + b = 0; + }⦉@4,6@5⦊⦉@5 + match @7⦊(a, b)⦉@7 { + // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`. + // This test confirms a fix for Issue #79569. + (0 | 1, 2 | 3) => @10,11⦊{}⦉@10,11 + _ => @8⦊{}⦉@8 + } + if @12⦊is_true⦉@12 @13,15⦊{ + a = 0; + b = 0; + }⦉@13,15@14⦊⦉@14 + match @16⦊(a, b)⦉@16 { + (0 | 1, 2 | 3) => @19,20⦊{}⦉@19,20 + _ => @17⦊{}⦉@17 + } + if @21⦊is_true⦉@21 @22,24⦊{ + a = 2; + b = 2; + }⦉@22,24@23⦊⦉@23 + match @25⦊(a, b)⦉@25 { + (0 | 1, 2 | 3) => @28,29⦊{}⦉@28,29 + _ => @26⦊{}⦉@26 + } + if @30⦊is_true⦉@30 @31,33⦊{ + a = 0; + b = 2; + }⦉@31,33@32⦊⦉@32 + match @34⦊(a, b)⦉@34 { + (0 | 1, 2 | 3) => @37,38⦊{}⦉@37,38 + _ => @35⦊{}⦉@35 + } +}@39⦊⦉@39
+ + diff --git a/src/test/run-make-fulldeps/coverage/compiletest-ignore-dir b/src/test/run-make-fulldeps/coverage/compiletest-ignore-dir index abf8df8fdc9e..d1824d189e38 100644 --- a/src/test/run-make-fulldeps/coverage/compiletest-ignore-dir +++ b/src/test/run-make-fulldeps/coverage/compiletest-ignore-dir @@ -1,3 +1,3 @@ -# Directory "instrument-coverage" supports the tests at prefix ../instrument-coverage-* +# Directory "coverage" supports the tests at prefix ../coverage-* -# Use ./x.py [options] test src/test/run-make-fulldeps/instrument-coverage to run all related tests. +# Use ./x.py [options] test src/test/run-make-fulldeps/coverage to run all related tests. diff --git a/src/test/run-make-fulldeps/coverage/coverage_tools.mk b/src/test/run-make-fulldeps/coverage/coverage_tools.mk index 7dc485cd94d6..4d340d4b1dad 100644 --- a/src/test/run-make-fulldeps/coverage/coverage_tools.mk +++ b/src/test/run-make-fulldeps/coverage/coverage_tools.mk @@ -1,7 +1,7 @@ -# Common Makefile include for Rust `run-make-fulldeps/instrument-coverage-* tests. Include this +# Common Makefile include for Rust `run-make-fulldeps/coverage-* tests. Include this # file with the line: # -# -include ../instrument-coverage/coverage_tools.mk +# -include ../coverage/coverage_tools.mk -include ../tools.mk diff --git a/src/test/run-make-fulldeps/coverage/doctest.rs b/src/test/run-make-fulldeps/coverage/doctest.rs new file mode 100644 index 000000000000..e41d669bf0c7 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/doctest.rs @@ -0,0 +1,66 @@ +//! This test ensures that code from doctests is properly re-mapped. +//! See for more info. +//! +//! Just some random code: +//! ``` +//! if true { +//! // this is executed! +//! assert_eq!(1, 1); +//! } else { +//! // this is not! +//! assert_eq!(1, 2); +//! } +//! ``` +//! +//! doctest testing external code: +//! ``` +//! extern crate doctest_crate; +//! doctest_crate::fn_run_in_doctests(1); +//! ``` +//! +//! doctest returning a result: +//! ``` +//! #[derive(Debug)] +//! struct SomeError; +//! let mut res = Err(SomeError); +//! if res.is_ok() { +//! res?; +//! } else { +//! res = Ok(0); +//! } +//! // need to be explicit because rustdoc cant infer the return type +//! Ok::<(), SomeError>(()) +//! ``` +//! +//! doctest with custom main: +//! ``` +//! #[derive(Debug)] +//! struct SomeError; +//! +//! extern crate doctest_crate; +//! +//! fn doctest_main() -> Result<(), SomeError> { +//! doctest_crate::fn_run_in_doctests(2); +//! Ok(()) +//! } +//! +//! // this `main` is not shown as covered, as it clashes with all the other +//! // `main` functions that were automatically generated for doctests +//! fn main() -> Result<(), SomeError> { +//! doctest_main() +//! } +//! ``` + +/// doctest attached to fn testing external code: +/// ``` +/// extern crate doctest_crate; +/// doctest_crate::fn_run_in_doctests(3); +/// ``` +/// +fn main() { + if true { + assert_eq!(1, 1); + } else { + assert_eq!(1, 2); + } +} diff --git a/src/test/run-make-fulldeps/coverage/lib/doctest_crate.rs b/src/test/run-make-fulldeps/coverage/lib/doctest_crate.rs new file mode 100644 index 000000000000..c3210146d69b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/lib/doctest_crate.rs @@ -0,0 +1,9 @@ +/// A function run only from within doctests +pub fn fn_run_in_doctests(conditional: usize) { + match conditional { + 1 => assert_eq!(1, 1), // this is run, + 2 => assert_eq!(1, 1), // this, + 3 => assert_eq!(1, 1), // and this too + _ => assert_eq!(1, 2), // however this is not + } +} diff --git a/src/test/run-make-fulldeps/coverage/match_or_pattern.rs b/src/test/run-make-fulldeps/coverage/match_or_pattern.rs new file mode 100644 index 000000000000..4c6a8a9b7037 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/match_or_pattern.rs @@ -0,0 +1,45 @@ +#![feature(or_patterns)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut a: u8 = 0; + let mut b: u8 = 0; + if is_true { + a = 2; + b = 0; + } + match (a, b) { + // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`. + // This test confirms a fix for Issue #79569. + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 0; + b = 0; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 2; + b = 2; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 0; + b = 2; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } +} diff --git a/src/test/run-make-fulldeps/many-crates-but-no-match/Makefile b/src/test/run-make-fulldeps/many-crates-but-no-match/Makefile index 03a797d95f98..e7268311b131 100644 --- a/src/test/run-make-fulldeps/many-crates-but-no-match/Makefile +++ b/src/test/run-make-fulldeps/many-crates-but-no-match/Makefile @@ -1,9 +1,8 @@ -include ../tools.mk -# Modelled after compile-fail/changing-crates test, but this one puts +# Modelled after ui/changing-crates.rs test, but this one puts # more than one (mismatching) candidate crate into the search path, -# which did not appear directly expressible in compile-fail/aux-build -# infrastructure. +# which did not appear directly expressible in UI testing infrastructure. # # Note that we move the built libraries into target direcrtories rather than # use the `--out-dir` option because the `../tools.mk` file already bakes a @@ -33,4 +32,4 @@ all: 'crate `crateA`:' \ 'crate `crateB`:' \ < $(LOG) - # the 'crate `crateA`' will match two entries. \ No newline at end of file + # the 'crate `crateA`' will match two entries. diff --git a/src/test/run-make-fulldeps/separate-link/Makefile b/src/test/run-make-fulldeps/separate-link/Makefile new file mode 100644 index 000000000000..060484e89f93 --- /dev/null +++ b/src/test/run-make-fulldeps/separate-link/Makefile @@ -0,0 +1,6 @@ +-include ../tools.mk + +all: + echo 'fn main(){}' | $(RUSTC) -Z no-link - + $(RUSTC) -Z link-only $(TMPDIR)/rust_out.rlink + $(call RUN,rust_out) diff --git a/src/test/run-make-fulldeps/split-dwarf/Makefile b/src/test/run-make-fulldeps/split-dwarf/Makefile new file mode 100644 index 000000000000..e1a78e2edfc4 --- /dev/null +++ b/src/test/run-make-fulldeps/split-dwarf/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk + +# only-linux + +all: + $(RUSTC) -Z split-dwarf=split foo.rs + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) diff --git a/src/test/run-make-fulldeps/split-dwarf/foo.rs b/src/test/run-make-fulldeps/split-dwarf/foo.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/src/test/run-make-fulldeps/split-dwarf/foo.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/test/run-make-fulldeps/type-mismatch-same-crate-name/crateC.rs b/src/test/run-make-fulldeps/type-mismatch-same-crate-name/crateC.rs index 12898aa5c74b..71b38a9f8ca5 100644 --- a/src/test/run-make-fulldeps/type-mismatch-same-crate-name/crateC.rs +++ b/src/test/run-make-fulldeps/type-mismatch-same-crate-name/crateC.rs @@ -5,7 +5,7 @@ // causing a type mismatch. // The test is nearly the same as the one in -// compile-fail/type-mismatch-same-crate-name.rs +// ui/type/type-mismatch-same-crate-name.rs // but deals with the case where one of the crates // is only introduced as an indirect dependency. // and the type is accessed via a re-export. diff --git a/src/test/rustdoc-ui/auxiliary/issue-61592.rs b/src/test/rustdoc-ui/auxiliary/issue-61592.rs new file mode 100644 index 000000000000..57a365b3f386 --- /dev/null +++ b/src/test/rustdoc-ui/auxiliary/issue-61592.rs @@ -0,0 +1,3 @@ +#![crate_name = "foo"] + +pub trait Foo {} diff --git a/src/test/rustdoc-ui/error-in-impl-trait/const-generics.rs b/src/test/rustdoc-ui/error-in-impl-trait/const-generics.rs index 97760cbf8fb5..ed62f0208aa5 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/const-generics.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/const-generics.rs @@ -1,6 +1,5 @@ // check-pass // edition:2018 -#![feature(min_const_generics)] trait ValidTrait {} /// This has docs diff --git a/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs b/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs new file mode 100644 index 000000000000..114502b0ddf4 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs @@ -0,0 +1,34 @@ +#![deny(broken_intra_doc_links)] +// These are links that could reasonably expected to work, but don't. + +// `[]` isn't supported because it had too many false positives. +//! [X]([T]::not_here) +//! [Y](&[]::not_here) +//! [X]([]::not_here) +//! [Y]([T;N]::not_here) + +// These don't work because markdown syntax doesn't allow it. +//! [[T]::rotate_left] //~ ERROR unresolved link to `T` +//! [&[]::not_here] +//![Z]([T; N]::map) //~ ERROR unresolved link to `Z` +//! [`[T; N]::map`] +//! [[]::map] +//! [Z][] //~ ERROR unresolved link to `Z` +//! +//! [Z]: [T; N]::map //~ ERROR unresolved link to `Z` + +// `()` isn't supported because it had too many false positives. +//! [()::not_here] +//! [X]((,)::not_here) +//! [(,)::not_here] + +// FIXME: Associated items on some primitives aren't working, because the impls +// are part of the compiler instead of being part of the source code. +//! [unit::eq] //~ ERROR unresolved +//! [tuple::eq] //~ ERROR unresolved +//! [fn::eq] //~ ERROR unresolved +//! [never::eq] //~ ERROR unresolved + +// FIXME(#78800): This breaks because it's a blanket impl +// (I think? Might break for other reasons too.) +//! [reference::deref] //~ ERROR unresolved diff --git a/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr b/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr new file mode 100644 index 000000000000..ea831e648f63 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr @@ -0,0 +1,69 @@ +error: unresolved link to `T` + --> $DIR/non-path-primitives.rs:11:7 + | +LL | //! [[T]::rotate_left] + | ^ no item named `T` in scope + | +note: the lint level is defined here + --> $DIR/non-path-primitives.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `Z` + --> $DIR/non-path-primitives.rs:13:5 + | +LL | //![Z]([T; N]::map) + | ^ no item named `Z` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `Z` + --> $DIR/non-path-primitives.rs:16:6 + | +LL | //! [Z][] + | ^ no item named `Z` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `Z` + --> $DIR/non-path-primitives.rs:18:6 + | +LL | //! [Z]: [T; N]::map + | ^ no item named `Z` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `unit::eq` + --> $DIR/non-path-primitives.rs:27:6 + | +LL | //! [unit::eq] + | ^^^^^^^^ the builtin type `unit` has no associated item named `eq` + +error: unresolved link to `tuple::eq` + --> $DIR/non-path-primitives.rs:28:6 + | +LL | //! [tuple::eq] + | ^^^^^^^^^ the builtin type `tuple` has no associated item named `eq` + +error: unresolved link to `fn::eq` + --> $DIR/non-path-primitives.rs:29:6 + | +LL | //! [fn::eq] + | ^^^^^^ the builtin type `fn` has no associated item named `eq` + +error: unresolved link to `never::eq` + --> $DIR/non-path-primitives.rs:30:6 + | +LL | //! [never::eq] + | ^^^^^^^^^ the builtin type `never` has no associated item named `eq` + +error: unresolved link to `reference::deref` + --> $DIR/non-path-primitives.rs:34:6 + | +LL | //! [reference::deref] + | ^^^^^^^^^^^^^^^^ the builtin type `reference` has no associated item named `deref` + +error: aborting due to 9 previous errors + diff --git a/src/test/rustdoc-ui/issue-61592-2.rs b/src/test/rustdoc-ui/issue-61592-2.rs new file mode 100644 index 000000000000..5b4fc5ee7006 --- /dev/null +++ b/src/test/rustdoc-ui/issue-61592-2.rs @@ -0,0 +1,10 @@ +// aux-build:issue-61592.rs + +extern crate foo; + +#[doc = "bar"] +#[doc(inline)] //~ ERROR +#[doc = "baz"] +pub use foo::Foo as _; + +fn main() {} diff --git a/src/test/rustdoc-ui/issue-61592-2.stderr b/src/test/rustdoc-ui/issue-61592-2.stderr new file mode 100644 index 000000000000..1b7f8bb552c0 --- /dev/null +++ b/src/test/rustdoc-ui/issue-61592-2.stderr @@ -0,0 +1,12 @@ +error[E0780]: anonymous imports cannot be inlined + --> $DIR/issue-61592-2.rs:6:7 + | +LL | #[doc(inline)] + | ^^^^^^ +LL | #[doc = "baz"] +LL | pub use foo::Foo as _; + | ---------------------- anonymous import + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0780`. diff --git a/src/test/rustdoc-ui/issue-61592.rs b/src/test/rustdoc-ui/issue-61592.rs new file mode 100644 index 000000000000..66772557f2c2 --- /dev/null +++ b/src/test/rustdoc-ui/issue-61592.rs @@ -0,0 +1,8 @@ +// aux-build:issue-61592.rs + +extern crate foo; + +#[doc(inline)] //~ ERROR +pub use foo::Foo as _; + +fn main() {} diff --git a/src/test/rustdoc-ui/issue-61592.stderr b/src/test/rustdoc-ui/issue-61592.stderr new file mode 100644 index 000000000000..9c9c9106f8ac --- /dev/null +++ b/src/test/rustdoc-ui/issue-61592.stderr @@ -0,0 +1,11 @@ +error[E0780]: anonymous imports cannot be inlined + --> $DIR/issue-61592.rs:5:7 + | +LL | #[doc(inline)] + | ^^^^^^ +LL | pub use foo::Foo as _; + | ---------------------- anonymous import + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0780`. diff --git a/src/test/rustdoc-ui/reference-link-has-one-warning.rs b/src/test/rustdoc-ui/reference-link-has-one-warning.rs deleted file mode 100644 index 21cb7eb9040b..000000000000 --- a/src/test/rustdoc-ui/reference-link-has-one-warning.rs +++ /dev/null @@ -1,6 +0,0 @@ -// ignore-test -// check-pass - -/// docs [label][with#anchor#error] -//~^ WARNING has an issue with the link anchor -pub struct S; diff --git a/src/test/rustdoc-ui/reference-link-has-one-warning.stderr b/src/test/rustdoc-ui/reference-link-has-one-warning.stderr deleted file mode 100644 index a1eeb60f1785..000000000000 --- a/src/test/rustdoc-ui/reference-link-has-one-warning.stderr +++ /dev/null @@ -1,10 +0,0 @@ -warning: `[with#anchor#error]` has an issue with the link anchor. - --> $DIR/reference-link-has-one-warning.rs:3:18 - | -LL | /// docs [label][with#anchor#error] - | ^^^^^^^^^^^^^^^^^ only one `#` is allowed in a link - | - = note: `#[warn(broken_intra_doc_links)]` on by default - -warning: 1 warning emitted - diff --git a/src/test/rustdoc-ui/reference-links.rs b/src/test/rustdoc-ui/reference-links.rs new file mode 100644 index 000000000000..7c1a79722c99 --- /dev/null +++ b/src/test/rustdoc-ui/reference-links.rs @@ -0,0 +1,7 @@ +// Test that errors point to the reference, not to the title text. +#![deny(broken_intra_doc_links)] +//! Links to [a] [link][a] +//! +//! [a]: std::process::Comman +//~^ ERROR unresolved +//~| ERROR unresolved diff --git a/src/test/rustdoc-ui/reference-links.stderr b/src/test/rustdoc-ui/reference-links.stderr new file mode 100644 index 000000000000..6ba73fbdb006 --- /dev/null +++ b/src/test/rustdoc-ui/reference-links.stderr @@ -0,0 +1,20 @@ +error: unresolved link to `std::process::Comman` + --> $DIR/reference-links.rs:5:10 + | +LL | //! [a]: std::process::Comman + | ^^^^^^^^^^^^^^^^^^^^ no item named `Comman` in module `process` + | +note: the lint level is defined here + --> $DIR/reference-links.rs:2:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `std::process::Comman` + --> $DIR/reference-links.rs:5:10 + | +LL | //! [a]: std::process::Comman + | ^^^^^^^^^^^^^^^^^^^^ no item named `Comman` in module `process` + +error: aborting due to 2 previous errors + diff --git a/src/test/rustdoc/async-fn.rs b/src/test/rustdoc/async-fn.rs index e7a7d1831f73..aa4ad261c80f 100644 --- a/src/test/rustdoc/async-fn.rs +++ b/src/test/rustdoc/async-fn.rs @@ -1,6 +1,5 @@ +// ignore-tidy-linelength // edition:2018 -#![feature(min_const_generics)] - // @has async_fn/fn.foo.html '//pre[@class="rust fn"]' 'pub async fn foo() -> Option' pub async fn foo() -> Option { None @@ -48,7 +47,50 @@ impl Foo { pub async fn mut_self(mut self, mut first: usize) {} } +pub trait Pattern<'a> {} + pub trait Trait {} // @has async_fn/fn.const_generics.html // @has - '//pre[@class="rust fn"]' 'pub async fn const_generics(_: impl Trait)' pub async fn const_generics(_: impl Trait) {} + +// test that elided lifetimes are properly elided and not displayed as `'_` +// regression test for #63037 +// @has async_fn/fn.elided.html +// @has - '//pre[@class="rust fn"]' 'pub async fn elided(foo: &str) -> &str' +pub async fn elided(foo: &str) -> &str {} +// This should really be shown as written, but for implementation reasons it's difficult. +// See `impl Clean for TyKind::Rptr`. +// @has async_fn/fn.user_elided.html +// @has - '//pre[@class="rust fn"]' 'pub async fn user_elided(foo: &str) -> &str' +pub async fn user_elided(foo: &'_ str) -> &str {} +// @has async_fn/fn.static_trait.html +// @has - '//pre[@class="rust fn"]' 'pub async fn static_trait(foo: &str) -> Box' +pub async fn static_trait(foo: &str) -> Box {} +// @has async_fn/fn.lifetime_for_trait.html +// @has - '//pre[@class="rust fn"]' "pub async fn lifetime_for_trait(foo: &str) -> Box" +pub async fn lifetime_for_trait(foo: &str) -> Box {} +// @has async_fn/fn.elided_in_input_trait.html +// @has - '//pre[@class="rust fn"]' "pub async fn elided_in_input_trait(t: impl Pattern<'_>)" +pub async fn elided_in_input_trait(t: impl Pattern<'_>) {} + +struct AsyncFdReadyGuard<'a, T> { x: &'a T } + +impl Foo { + // @has async_fn/struct.Foo.html + // @has - '//h4[@class="method"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar) -> impl Iterator' + pub async fn complicated_lifetimes(&self, context: &impl Bar) -> impl Iterator {} + // taken from `tokio` as an example of a method that was particularly bad before + // @has - '//h4[@class="method"]' "pub async fn readable(&self) -> Result, ()>" + pub async fn readable(&self) -> Result, ()> {} + // @has - '//h4[@class="method"]' "pub async fn mut_self(&mut self)" + pub async fn mut_self(&mut self) {} +} + +// test named lifetimes, just in case +// @has async_fn/fn.named.html +// @has - '//pre[@class="rust fn"]' "pub async fn named<'a, 'b>(foo: &'a str) -> &'b str" +pub async fn named<'a, 'b>(foo: &'a str) -> &'b str {} +// @has async_fn/fn.named_trait.html +// @has - '//pre[@class="rust fn"]' "pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b>" +pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b> {} diff --git a/src/test/rustdoc/auxiliary/issue-61592.rs b/src/test/rustdoc/auxiliary/issue-61592.rs new file mode 100644 index 000000000000..6e16a4caf594 --- /dev/null +++ b/src/test/rustdoc/auxiliary/issue-61592.rs @@ -0,0 +1,4 @@ +#![crate_name = "foo"] + +pub trait FooTrait {} +pub struct FooStruct; diff --git a/src/test/rustdoc/codeblock-title.rs b/src/test/rustdoc/codeblock-title.rs index b59b21111b00..140c5b3a6720 100644 --- a/src/test/rustdoc/codeblock-title.rs +++ b/src/test/rustdoc/codeblock-title.rs @@ -1,10 +1,9 @@ #![crate_name = "foo"] -// ignore-tidy-linelength - -// @has foo/fn.bar.html '//*[@class="tooltip compile_fail"]/span' "This example deliberately fails to compile" -// @has foo/fn.bar.html '//*[@class="tooltip ignore"]/span' "This example is not tested" -// @has foo/fn.bar.html '//*[@class="tooltip should_panic"]/span' "This example panics" +// @has foo/fn.bar.html '//*[@class="tooltip compile_fail"]' "ⓘ" +// @has foo/fn.bar.html '//*[@class="tooltip ignore"]' "ⓘ" +// @has foo/fn.bar.html '//*[@class="tooltip should_panic"]' "ⓘ" +// @has foo/fn.bar.html '//*[@data-edition="2018"]' "ⓘ" /// foo /// @@ -20,7 +19,7 @@ /// hoo(); /// ``` /// -/// ``` +/// ```edition2018 /// let x = 0; /// ``` pub fn bar() -> usize { 2 } diff --git a/src/test/rustdoc/const-generics/auxiliary/extern_crate.rs b/src/test/rustdoc/const-generics/auxiliary/extern_crate.rs index b8bd040f7a4b..55b632a48f2e 100644 --- a/src/test/rustdoc/const-generics/auxiliary/extern_crate.rs +++ b/src/test/rustdoc/const-generics/auxiliary/extern_crate.rs @@ -1,6 +1,4 @@ // edition:2018 -#![feature(min_const_generics)] - pub fn extern_fn() -> impl Iterator { [[0; N]; N].iter().copied() } diff --git a/src/test/rustdoc/const-generics/const-generics-docs.rs b/src/test/rustdoc/const-generics/const-generics-docs.rs index 9c68e067c6f8..21bf216c3044 100644 --- a/src/test/rustdoc/const-generics/const-generics-docs.rs +++ b/src/test/rustdoc/const-generics/const-generics-docs.rs @@ -1,6 +1,5 @@ // edition:2018 // aux-build: extern_crate.rs -#![feature(min_const_generics)] #![crate_name = "foo"] extern crate extern_crate; diff --git a/src/test/rustdoc/const-generics/type-alias.rs b/src/test/rustdoc/const-generics/type-alias.rs index 3064d0701e30..85160dc07a7a 100644 --- a/src/test/rustdoc/const-generics/type-alias.rs +++ b/src/test/rustdoc/const-generics/type-alias.rs @@ -1,5 +1,4 @@ // ignore-tidy-linelength -#![feature(min_const_generics)] #![crate_name = "foo"] // @has foo/type.CellIndex.html '//pre[@class="rust typedef"]' 'type CellIndex = [i64; D];' diff --git a/src/test/rustdoc/doc-cfg.rs b/src/test/rustdoc/doc-cfg.rs index f86541cd1181..51a583782990 100644 --- a/src/test/rustdoc/doc-cfg.rs +++ b/src/test/rustdoc/doc-cfg.rs @@ -25,12 +25,13 @@ pub mod unix_only { // @has doc_cfg/unix_only/trait.ArmOnly.html \ // '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \ // 'This is supported on Unix and ARM only.' - // @count - '//*[@class="stab portability"]' 2 + // @count - '//*[@class="stab portability"]' 1 #[doc(cfg(target_arch = "arm"))] pub trait ArmOnly { fn unix_and_arm_only_function(); } + #[doc(cfg(target_arch = "arm"))] impl ArmOnly for super::Portable { fn unix_and_arm_only_function() {} } diff --git a/src/test/rustdoc/intra-doc/non-path-primitives.rs b/src/test/rustdoc/intra-doc/non-path-primitives.rs new file mode 100644 index 000000000000..ad4f6ddd9de6 --- /dev/null +++ b/src/test/rustdoc/intra-doc/non-path-primitives.rs @@ -0,0 +1,39 @@ +// ignore-tidy-linelength +#![crate_name = "foo"] +#![deny(broken_intra_doc_links)] + +// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.rotate_left"]' 'slice::rotate_left' +//! [slice::rotate_left] + +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.array.html#method.map"]' 'array::map' +//! [array::map] + +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' 'pointer::is_null' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' '*const::is_null' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' '*mut::is_null' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' '*::is_null' +//! [pointer::is_null] +//! [*const::is_null] +//! [*mut::is_null] +//! [*::is_null] + +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.unit.html"]' 'unit' +//! [unit] + +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.tuple.html"]' 'tuple' +//! [tuple] + +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.reference.html"]' 'reference' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.reference.html"]' '&' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.reference.html"]' '&mut' +//! [reference] +//! [&] +//! [&mut] + +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.fn.html"]' 'fn' +//! [fn] + +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.never.html"]' 'never' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.never.html"]' '!' +//! [never] +//! [!] diff --git a/src/test/rustdoc/intra-link-self-cache.rs b/src/test/rustdoc/intra-link-self-cache.rs new file mode 100644 index 000000000000..add1530a5a67 --- /dev/null +++ b/src/test/rustdoc/intra-link-self-cache.rs @@ -0,0 +1,14 @@ +#![crate_name = "foo"] +// @has foo/enum.E1.html '//a/@href' '../foo/enum.E1.html#variant.A' + +/// [Self::A::b] +pub enum E1 { + A { b: usize } +} + +// @has foo/enum.E2.html '//a/@href' '../foo/enum.E2.html#variant.A' + +/// [Self::A::b] +pub enum E2 { + A { b: usize } +} diff --git a/src/test/rustdoc/issue-61592.rs b/src/test/rustdoc/issue-61592.rs new file mode 100644 index 000000000000..aef038c07d89 --- /dev/null +++ b/src/test/rustdoc/issue-61592.rs @@ -0,0 +1,15 @@ +// aux-build:issue-61592.rs + +extern crate foo; + +// @has issue_61592/index.html +// @has - '//a[@href="#reexports"]' 'Re-exports' +// @has - '//code' 'pub use foo::FooTrait as _;' +// @!has - '//a[@href="trait._.html"]' +pub use foo::FooTrait as _; + +// @has issue_61592/index.html +// @has - '//a[@href="#reexports"]' 'Re-exports' +// @has - '//code' 'pub use foo::FooStruct as _;' +// @!has - '//a[@href="struct._.html"]' +pub use foo::FooStruct as _; diff --git a/src/test/rustdoc/issue-79201.rs b/src/test/rustdoc/issue-79201.rs new file mode 100644 index 000000000000..f95d79cd493e --- /dev/null +++ b/src/test/rustdoc/issue-79201.rs @@ -0,0 +1,41 @@ +#![feature(doc_cfg)] + +// @has 'issue_79201/trait.Foo.html' +// @count - '//*[@class="stab portability"]' 6 +// @matches - '//*[@class="stab portability"]' 'crate feature foo-root' +// @matches - '//*[@class="stab portability"]' 'crate feature foo-public-mod' +// @matches - '//*[@class="stab portability"]' 'crate feature foo-private-mod' +// @matches - '//*[@class="stab portability"]' 'crate feature foo-fn' +// @matches - '//*[@class="stab portability"]' 'crate feature foo-method' + +pub trait Foo {} + +#[doc(cfg(feature = "foo-root"))] +impl crate::Foo for usize {} + +#[doc(cfg(feature = "foo-public-mod"))] +pub mod public { + impl crate::Foo for u8 {} +} + +#[doc(cfg(feature = "foo-private-mod"))] +mod private { + impl crate::Foo for u16 {} +} + +#[doc(cfg(feature = "foo-const"))] +const _: () = { + impl crate::Foo for u32 {} +}; + +#[doc(cfg(feature = "foo-fn"))] +fn __() { + impl crate::Foo for u64 {} +} + +#[doc(cfg(feature = "foo-method"))] +impl dyn Foo { + fn __() { + impl crate::Foo for u128 {} + } +} diff --git a/src/test/rustdoc/pub-restricted.rs b/src/test/rustdoc/pub-restricted.rs deleted file mode 100644 index 6720d848ac3b..000000000000 --- a/src/test/rustdoc/pub-restricted.rs +++ /dev/null @@ -1,32 +0,0 @@ -// compile-flags: --document-private-items - -#![feature(crate_visibility_modifier)] - -#![crate_name = "foo"] - -// @has 'foo/struct.FooPublic.html' '//pre' 'pub struct FooPublic' -pub struct FooPublic; -// @has 'foo/struct.FooJustCrate.html' '//pre' 'pub(crate) struct FooJustCrate' -crate struct FooJustCrate; -// @has 'foo/struct.FooPubCrate.html' '//pre' 'pub(crate) struct FooPubCrate' -pub(crate) struct FooPubCrate; -// @has 'foo/struct.FooSelf.html' '//pre' 'pub(crate) struct FooSelf' -pub(self) struct FooSelf; -// @has 'foo/struct.FooInSelf.html' '//pre' 'pub(crate) struct FooInSelf' -pub(in self) struct FooInSelf; -mod a { - // @has 'foo/a/struct.FooSuper.html' '//pre' 'pub(crate) struct FooSuper' - pub(super) struct FooSuper; - // @has 'foo/a/struct.FooInSuper.html' '//pre' 'pub(crate) struct FooInSuper' - pub(in super) struct FooInSuper; - // @has 'foo/a/struct.FooInA.html' '//pre' 'pub(in a) struct FooInA' - pub(in a) struct FooInA; - mod b { - // @has 'foo/a/b/struct.FooInSelfSuperB.html' '//pre' 'pub(in a::b) struct FooInSelfSuperB' - pub(in a::b) struct FooInSelfSuperB; - // @has 'foo/a/b/struct.FooInSuperSuper.html' '//pre' 'pub(crate) struct FooInSuperSuper' - pub(in super::super) struct FooInSuperSuper; - // @has 'foo/a/b/struct.FooInAB.html' '//pre' 'pub(in a::b) struct FooInAB' - pub(in a::b) struct FooInAB; - } -} diff --git a/src/test/rustdoc/rustc_deprecated-future.rs b/src/test/rustdoc/rustc_deprecated-future.rs index 3133775706b8..95a767a8329a 100644 --- a/src/test/rustdoc/rustc_deprecated-future.rs +++ b/src/test/rustdoc/rustc_deprecated-future.rs @@ -4,8 +4,16 @@ // @has rustc_deprecated_future/index.html '//*[@class="stab deprecated"]' \ // 'Deprecation planned' -// @has rustc_deprecated_future/struct.S.html '//*[@class="stab deprecated"]' \ +// @has rustc_deprecated_future/struct.S1.html '//*[@class="stab deprecated"]' \ // 'Deprecating in 99.99.99: effectively never' #[rustc_deprecated(since = "99.99.99", reason = "effectively never")] #[stable(feature = "rustc_deprecated-future-test", since = "1.0.0")] -pub struct S; +pub struct S1; + +// @has rustc_deprecated_future/index.html '//*[@class="stab deprecated"]' \ +// 'Deprecation planned' +// @has rustc_deprecated_future/struct.S2.html '//*[@class="stab deprecated"]' \ +// 'Deprecating in a future Rust version: literally never' +#[rustc_deprecated(since = "TBD", reason = "literally never")] +#[stable(feature = "rustc_deprecated-future-test", since = "1.0.0")] +pub struct S2; diff --git a/src/test/rustdoc/visibility.rs b/src/test/rustdoc/visibility.rs new file mode 100644 index 000000000000..59427693c5a5 --- /dev/null +++ b/src/test/rustdoc/visibility.rs @@ -0,0 +1,44 @@ +// compile-flags: --document-private-items + +#![feature(crate_visibility_modifier)] + +#![crate_name = "foo"] + +// @has 'foo/struct.FooPublic.html' '//pre' 'pub struct FooPublic' +pub struct FooPublic; +// @has 'foo/struct.FooJustCrate.html' '//pre' 'pub(crate) struct FooJustCrate' +crate struct FooJustCrate; +// @has 'foo/struct.FooPubCrate.html' '//pre' 'pub(crate) struct FooPubCrate' +pub(crate) struct FooPubCrate; +// @has 'foo/struct.FooSelf.html' '//pre' 'pub(crate) struct FooSelf' +pub(self) struct FooSelf; +// @has 'foo/struct.FooInSelf.html' '//pre' 'pub(crate) struct FooInSelf' +pub(in self) struct FooInSelf; +// @has 'foo/struct.FooPriv.html' '//pre' 'pub(crate) struct FooPriv' +struct FooPriv; + +mod a { + // @has 'foo/a/struct.FooASuper.html' '//pre' 'pub(crate) struct FooASuper' + pub(super) struct FooASuper; + // @has 'foo/a/struct.FooAInSuper.html' '//pre' 'pub(crate) struct FooAInSuper' + pub(in super) struct FooAInSuper; + // @has 'foo/a/struct.FooAInA.html' '//pre' 'struct FooAInA' + // @!has 'foo/a/struct.FooAInA.html' '//pre' 'pub' + pub(in a) struct FooAInA; + // @has 'foo/a/struct.FooAPriv.html' '//pre' 'struct FooAPriv' + // @!has 'foo/a/struct.FooAPriv.html' '//pre' 'pub' + struct FooAPriv; + + mod b { + // @has 'foo/a/b/struct.FooBSuper.html' '//pre' 'pub(super) struct FooBSuper' + pub(super) struct FooBSuper; + // @has 'foo/a/b/struct.FooBInSuperSuper.html' '//pre' 'pub(crate) struct FooBInSuperSuper' + pub(in super::super) struct FooBInSuperSuper; + // @has 'foo/a/b/struct.FooBInAB.html' '//pre' 'struct FooBInAB' + // @!has 'foo/a/b/struct.FooBInAB.html' '//pre' 'pub' + pub(in a::b) struct FooBInAB; + // @has 'foo/a/b/struct.FooBPriv.html' '//pre' 'struct FooBPriv' + // @!has 'foo/a/b/struct.FooBPriv.html' '//pre' 'pub' + struct FooBPriv; + } +} diff --git a/src/test/ui-fulldeps/dropck-tarena-cycle-checked.rs b/src/test/ui-fulldeps/dropck-tarena-cycle-checked.rs index fabcd727482d..cc97971a0ddb 100644 --- a/src/test/ui-fulldeps/dropck-tarena-cycle-checked.rs +++ b/src/test/ui-fulldeps/dropck-tarena-cycle-checked.rs @@ -1,8 +1,8 @@ // Reject mixing cyclic structure and Drop when using TypedArena. // -// (Compare against compile-fail/dropck_vec_cycle_checked.rs) +// (Compare against dropck-vec-cycle-checked.rs) // -// (Also compare against compile-fail/dropck_tarena_unsound_drop.rs, +// (Also compare against ui-fulldeps/dropck-tarena-unsound-drop.rs, // which is a reduction of this code to more directly show the reason // for the error message we see here.) diff --git a/src/test/ui-fulldeps/dropck_tarena_sound_drop.rs b/src/test/ui-fulldeps/dropck_tarena_sound_drop.rs index c5b9efee8e73..187f9a24a907 100644 --- a/src/test/ui-fulldeps/dropck_tarena_sound_drop.rs +++ b/src/test/ui-fulldeps/dropck_tarena_sound_drop.rs @@ -5,7 +5,7 @@ // methods might access borrowed data, as long as the borrowed data // has lifetime that strictly outlives the arena itself. // -// Compare against compile-fail/dropck_tarena_unsound_drop.rs, which +// Compare against ui-fulldeps/dropck-tarena-unsound-drop.rs, which // shows a similar setup, but restricts `f` so that the struct `C<'a>` // is force-fed a lifetime equal to that of the borrowed arena. diff --git a/src/test/ui/array-slice-vec/box-of-array-of-drop-1.rs b/src/test/ui/array-slice-vec/box-of-array-of-drop-1.rs index d48589328156..c8559d247282 100644 --- a/src/test/ui/array-slice-vec/box-of-array-of-drop-1.rs +++ b/src/test/ui/array-slice-vec/box-of-array-of-drop-1.rs @@ -17,7 +17,12 @@ impl Drop for D { fn drop(&mut self) { println!("Dropping {}", self.0); let old = LOG.load(Ordering::SeqCst); - LOG.compare_and_swap(old, old << 4 | self.0 as usize, Ordering::SeqCst); + let _ = LOG.compare_exchange( + old, + old << 4 | self.0 as usize, + Ordering::SeqCst, + Ordering::SeqCst + ); } } diff --git a/src/test/ui/array-slice-vec/box-of-array-of-drop-2.rs b/src/test/ui/array-slice-vec/box-of-array-of-drop-2.rs index e8a5b00a55b9..e75051caabcc 100644 --- a/src/test/ui/array-slice-vec/box-of-array-of-drop-2.rs +++ b/src/test/ui/array-slice-vec/box-of-array-of-drop-2.rs @@ -17,7 +17,12 @@ impl Drop for D { fn drop(&mut self) { println!("Dropping {}", self.0); let old = LOG.load(Ordering::SeqCst); - LOG.compare_and_swap(old, old << 4 | self.0 as usize, Ordering::SeqCst); + let _ = LOG.compare_exchange( + old, + old << 4 | self.0 as usize, + Ordering::SeqCst, + Ordering::SeqCst + ); } } diff --git a/src/test/ui/array-slice-vec/copy-out-of-array-1.rs b/src/test/ui/array-slice-vec/copy-out-of-array-1.rs index e64985ae3f69..c6d311148d07 100644 --- a/src/test/ui/array-slice-vec/copy-out-of-array-1.rs +++ b/src/test/ui/array-slice-vec/copy-out-of-array-1.rs @@ -2,7 +2,7 @@ // Ensure that we can copy out of a fixed-size array. // -// (Compare with compile-fail/move-out-of-array-1.rs) +// (Compare with ui/moves/move-out-of-array-1.rs) #[derive(Copy, Clone)] struct C { _x: u8 } diff --git a/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr b/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr index 7c1a92c79d97..0ad05b3adeb8 100644 --- a/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr +++ b/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error[E0308]: mismatched types --> $DIR/match_arr_unknown_len.rs:6:9 diff --git a/src/test/ui/array-slice-vec/nested-vec-3.rs b/src/test/ui/array-slice-vec/nested-vec-3.rs index 52b892dbcdfa..96497a53d308 100644 --- a/src/test/ui/array-slice-vec/nested-vec-3.rs +++ b/src/test/ui/array-slice-vec/nested-vec-3.rs @@ -18,7 +18,12 @@ impl Drop for D { fn drop(&mut self) { println!("Dropping {}", self.0); let old = LOG.load(Ordering::SeqCst); - LOG.compare_and_swap(old, old << 4 | self.0 as usize, Ordering::SeqCst); + let _ = LOG.compare_exchange( + old, + old << 4 | self.0 as usize, + Ordering::SeqCst, + Ordering::SeqCst, + ); } } diff --git a/src/test/ui/associated-consts/associated-const-type-parameter-arrays.rs b/src/test/ui/associated-consts/associated-const-type-parameter-arrays.rs index d51821059fc1..5152d784047a 100644 --- a/src/test/ui/associated-consts/associated-const-type-parameter-arrays.rs +++ b/src/test/ui/associated-consts/associated-const-type-parameter-arrays.rs @@ -14,8 +14,7 @@ impl Foo for Def { pub fn test() { let _array: [u32;
::Y]; - //~^ ERROR the trait bound `A: Foo` is not satisfied [E0277] + //~^ ERROR generic parameters may not be used } -fn main() { -} +fn main() {} diff --git a/src/test/ui/associated-consts/associated-const-type-parameter-arrays.stderr b/src/test/ui/associated-consts/associated-const-type-parameter-arrays.stderr index ac40e390cfbb..d3a1cd30e2be 100644 --- a/src/test/ui/associated-consts/associated-const-type-parameter-arrays.stderr +++ b/src/test/ui/associated-consts/associated-const-type-parameter-arrays.stderr @@ -1,17 +1,11 @@ -error[E0277]: the trait bound `A: Foo` is not satisfied - --> $DIR/associated-const-type-parameter-arrays.rs:16:23 +error: generic parameters may not be used in const operations + --> $DIR/associated-const-type-parameter-arrays.rs:16:24 | -LL | const Y: usize; - | --------------- required by `Foo::Y` -... LL | let _array: [u32; ::Y]; - | ^^^^^^^^^^^^^ the trait `Foo` is not implemented for `A` + | ^ cannot perform const operation using `A` | -help: consider further restricting this bound - | -LL | pub fn test() { - | ^^^^^ + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-item/associated-item-duplicate-bounds.rs b/src/test/ui/associated-item/associated-item-duplicate-bounds.rs index bec922b0721b..242a02353a10 100644 --- a/src/test/ui/associated-item/associated-item-duplicate-bounds.rs +++ b/src/test/ui/associated-item/associated-item-duplicate-bounds.rs @@ -5,7 +5,7 @@ trait Adapter { struct Foo { adapter: A, links: [u32; A::LINKS], // Shouldn't suggest bounds already there. - //~^ ERROR: no associated item named `LINKS` found + //~^ ERROR generic parameters may not be used in const operations } fn main() {} diff --git a/src/test/ui/associated-item/associated-item-duplicate-bounds.stderr b/src/test/ui/associated-item/associated-item-duplicate-bounds.stderr index ff1ad4c006e7..0d84dca5b809 100644 --- a/src/test/ui/associated-item/associated-item-duplicate-bounds.stderr +++ b/src/test/ui/associated-item/associated-item-duplicate-bounds.stderr @@ -1,11 +1,11 @@ -error[E0599]: no associated item named `LINKS` found for type parameter `A` in the current scope - --> $DIR/associated-item-duplicate-bounds.rs:7:21 +error: generic parameters may not be used in const operations + --> $DIR/associated-item-duplicate-bounds.rs:7:18 | LL | links: [u32; A::LINKS], // Shouldn't suggest bounds already there. - | ^^^^^ associated item not found in `A` + | ^^^^^^^^ cannot perform const operation using `A` | - = help: items from traits can only be used if the type parameter is bounded by the trait + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: aborting due to previous error -For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed index 760d2b433c87..bca69a976778 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed @@ -11,7 +11,7 @@ pub trait Foo { fn foo2 Foo<&'x isize>>( x: >::A) - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters { // This case is illegal because we have to instantiate `'x`, and // we don't know what region to instantiate it with. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs index 6eb584ea645a..1e23dd8890b9 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs @@ -11,7 +11,7 @@ pub trait Foo { fn foo2 Foo<&'x isize>>( x: I::A) - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters { // This case is illegal because we have to instantiate `'x`, and // we don't know what region to instantiate it with. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr index f2137f68665d..989624bdd93e 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr @@ -1,4 +1,4 @@ -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-fn.rs:13:8 | LL | x: I::A) @@ -6,3 +6,4 @@ LL | x: I::A) error: aborting due to previous error +For more information about this error, try `rustc --explain E0212`. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs index 58f186d7775e..ed30d86cb5b4 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs @@ -9,14 +9,14 @@ pub trait Foo { struct SomeStruct Foo<&'x isize>> { field: I::A - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters } enum SomeEnum<'b, I: for<'a> Foo<&'a isize>> { TupleVariant(I::A), - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters StructVariant { field: I::A }, - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters OkVariant(&'b usize), } @@ -33,7 +33,7 @@ struct YetAnotherStruct<'a, I: for<'x> Foo<&'x isize>> { struct Why<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, 'y, 'z, 'aa, I: for<'l, 'm> Foo<&'l &'m isize>> { field: I::A, - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters } pub fn main() {} diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr index e3fd2860ebcf..cadc3e9eab1c 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr @@ -1,4 +1,4 @@ -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-struct.rs:11:12 | LL | field: I::A @@ -10,7 +10,7 @@ LL | struct SomeStruct<'a, I: for<'x> Foo<&'x isize>> { LL | field: >::A | -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-struct.rs:16:18 | LL | TupleVariant(I::A), @@ -22,7 +22,7 @@ LL | enum SomeEnum<'c, 'b, I: for<'a> Foo<&'a isize>> { LL | TupleVariant(>::A), | -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-struct.rs:18:28 | LL | StructVariant { field: I::A }, @@ -36,7 +36,7 @@ LL | LL | StructVariant { field: >::A }, | -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-struct.rs:35:12 | LL | field: I::A, @@ -51,3 +51,4 @@ LL | field: >::A, error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0212`. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed index acf32bccbecf..66d8613f184a 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed @@ -11,7 +11,7 @@ pub trait Foo { trait SomeTrait Foo<&'x isize>> { fn some_method(&self, arg: >::A); - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters } trait AnotherTrait Foo<&'x isize>> { @@ -30,7 +30,7 @@ struct Peach(std::marker::PhantomData); impl Banana<'a>> Peach { fn mango(&self) -> >::Assoc { - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters Default::default() } } diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs index a249f89685e3..0a1b29de19e3 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs @@ -11,7 +11,7 @@ pub trait Foo { trait SomeTrait Foo<&'x isize>> { fn some_method(&self, arg: I::A); - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters } trait AnotherTrait Foo<&'x isize>> { @@ -30,7 +30,7 @@ struct Peach(std::marker::PhantomData); impl Banana<'a>> Peach { fn mango(&self) -> X::Assoc { - //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters Default::default() } } diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr index a37fec244933..d457f9f8468b 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr @@ -1,10 +1,10 @@ -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:13:32 | LL | fn some_method(&self, arg: I::A); | ^^^^ help: use a fully qualified path with inferred lifetimes: `>::A` -error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:32:24 | LL | fn mango(&self) -> X::Assoc { @@ -12,3 +12,4 @@ LL | fn mango(&self) -> X::Assoc { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0212`. diff --git a/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait.rs b/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait.rs index 5f06a829600a..3b8c8c019e50 100644 --- a/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait.rs +++ b/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait.rs @@ -3,7 +3,7 @@ // the trait definition if there is no default method and for every impl, // `Self` does implement `Get`. // -// See also compile-fail tests associated-types-no-suitable-supertrait +// See also tests associated-types-no-suitable-supertrait // and associated-types-no-suitable-supertrait-2, which show how small // variants of the code below can fail. diff --git a/src/test/ui/associated-types/defaults-cyclic-fail-1.rs b/src/test/ui/associated-types/defaults-cyclic-fail-1.rs index afb2b3df716e..61ef013236e8 100644 --- a/src/test/ui/associated-types/defaults-cyclic-fail-1.rs +++ b/src/test/ui/associated-types/defaults-cyclic-fail-1.rs @@ -24,13 +24,13 @@ impl Tr for u32 { // ...but not in an impl that redefines one of the types. impl Tr for bool { type A = Box; - //~^ ERROR type mismatch resolving `::B == _` + //~^ ERROR overflow evaluating the requirement `::B == _` } // (the error is shown twice for some reason) impl Tr for usize { type B = &'static Self::A; - //~^ ERROR type mismatch resolving `::A == _` + //~^ ERROR overflow evaluating the requirement `::A == _` } fn main() { diff --git a/src/test/ui/associated-types/defaults-cyclic-fail-1.stderr b/src/test/ui/associated-types/defaults-cyclic-fail-1.stderr index ae7150d47ca9..5e98520b4118 100644 --- a/src/test/ui/associated-types/defaults-cyclic-fail-1.stderr +++ b/src/test/ui/associated-types/defaults-cyclic-fail-1.stderr @@ -1,15 +1,15 @@ -error[E0271]: type mismatch resolving `::B == _` +error[E0275]: overflow evaluating the requirement `::B == _` --> $DIR/defaults-cyclic-fail-1.rs:26:5 | LL | type A = Box; - | ^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size + | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0271]: type mismatch resolving `::A == _` +error[E0275]: overflow evaluating the requirement `::A == _` --> $DIR/defaults-cyclic-fail-1.rs:32:5 | LL | type B = &'static Self::A; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/associated-types/defaults-cyclic-fail-2.rs b/src/test/ui/associated-types/defaults-cyclic-fail-2.rs index ba4bb0d5a296..e91c9f2d29a8 100644 --- a/src/test/ui/associated-types/defaults-cyclic-fail-2.rs +++ b/src/test/ui/associated-types/defaults-cyclic-fail-2.rs @@ -25,13 +25,13 @@ impl Tr for u32 { impl Tr for bool { type A = Box; - //~^ ERROR type mismatch resolving `::B == _` + //~^ ERROR overflow evaluating the requirement `::B == _` } // (the error is shown twice for some reason) impl Tr for usize { type B = &'static Self::A; - //~^ ERROR type mismatch resolving `::A == _` + //~^ ERROR overflow evaluating the requirement `::A == _` } fn main() { diff --git a/src/test/ui/associated-types/defaults-cyclic-fail-2.stderr b/src/test/ui/associated-types/defaults-cyclic-fail-2.stderr index 0dfbac2dec5d..c538805f8582 100644 --- a/src/test/ui/associated-types/defaults-cyclic-fail-2.stderr +++ b/src/test/ui/associated-types/defaults-cyclic-fail-2.stderr @@ -1,15 +1,15 @@ -error[E0271]: type mismatch resolving `::B == _` +error[E0275]: overflow evaluating the requirement `::B == _` --> $DIR/defaults-cyclic-fail-2.rs:27:5 | LL | type A = Box; - | ^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size + | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0271]: type mismatch resolving `::A == _` +error[E0275]: overflow evaluating the requirement `::A == _` --> $DIR/defaults-cyclic-fail-2.rs:33:5 | LL | type B = &'static Self::A; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/associated-types/defaults-wf.stderr b/src/test/ui/associated-types/defaults-wf.stderr index 4c43e6a182dc..d4fa5be742ff 100644 --- a/src/test/ui/associated-types/defaults-wf.stderr +++ b/src/test/ui/associated-types/defaults-wf.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | type Ty = Vec<[u8]>; | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/alloc/src/vec.rs:LL:COL + ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | LL | pub struct Vec { | - required by this bound in `Vec` diff --git a/src/test/ui/associated-types/impl-wf-cycle-1.rs b/src/test/ui/associated-types/impl-wf-cycle-1.rs new file mode 100644 index 000000000000..ba074210a2b5 --- /dev/null +++ b/src/test/ui/associated-types/impl-wf-cycle-1.rs @@ -0,0 +1,29 @@ +// Regression test for #79714 + +trait Baz {} +impl Baz for () {} +impl Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for (T,) +where + Self::A: Baz, + Self::B: Fiz, +{ + type A = (); + //~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` + type B = bool; + //~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +} +//~^^^^^^^^^^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` + +fn main() { + let x: <(_,) as Grault>::A = (); +} diff --git a/src/test/ui/associated-types/impl-wf-cycle-1.stderr b/src/test/ui/associated-types/impl-wf-cycle-1.stderr new file mode 100644 index 000000000000..82328048c99a --- /dev/null +++ b/src/test/ui/associated-types/impl-wf-cycle-1.stderr @@ -0,0 +1,39 @@ +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-1.rs:15:1 + | +LL | / impl Grault for (T,) +LL | | where +LL | | Self::A: Baz, +LL | | Self::B: Fiz, +... | +LL | | +LL | | } + | |_^ + | + = note: required because of the requirements on the impl of `Grault` for `(T,)` + = note: 1 redundant requirements hidden + = note: required because of the requirements on the impl of `Grault` for `(T,)` + +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-1.rs:20:5 + | +LL | type A = (); + | ^^^^^^^^^^^^ + | + = note: required because of the requirements on the impl of `Grault` for `(T,)` + = note: 1 redundant requirements hidden + = note: required because of the requirements on the impl of `Grault` for `(T,)` + +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-1.rs:22:5 + | +LL | type B = bool; + | ^^^^^^^^^^^^^^ + | + = note: required because of the requirements on the impl of `Grault` for `(T,)` + = note: 1 redundant requirements hidden + = note: required because of the requirements on the impl of `Grault` for `(T,)` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/associated-types/impl-wf-cycle-2.rs b/src/test/ui/associated-types/impl-wf-cycle-2.rs new file mode 100644 index 000000000000..6fccc54f229a --- /dev/null +++ b/src/test/ui/associated-types/impl-wf-cycle-2.rs @@ -0,0 +1,16 @@ +// Regression test for #79714 + +trait Grault { + type A; +} + +impl Grault for (T,) +where + Self::A: Copy, +{ + type A = (); + //~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +} +//~^^^^^^^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` + +fn main() {} diff --git a/src/test/ui/associated-types/impl-wf-cycle-2.stderr b/src/test/ui/associated-types/impl-wf-cycle-2.stderr new file mode 100644 index 000000000000..5cd18a33adf3 --- /dev/null +++ b/src/test/ui/associated-types/impl-wf-cycle-2.stderr @@ -0,0 +1,25 @@ +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-2.rs:7:1 + | +LL | / impl Grault for (T,) +LL | | where +LL | | Self::A: Copy, +LL | | { +LL | | type A = (); +LL | | +LL | | } + | |_^ + | + = note: required because of the requirements on the impl of `Grault` for `(T,)` + +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-2.rs:11:5 + | +LL | type A = (); + | ^^^^^^^^^^^^ + | + = note: required because of the requirements on the impl of `Grault` for `(T,)` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/compile-fail/issue-23595-1.rs b/src/test/ui/associated-types/issue-23595-1.rs similarity index 100% rename from src/test/compile-fail/issue-23595-1.rs rename to src/test/ui/associated-types/issue-23595-1.rs diff --git a/src/test/ui/associated-types/issue-23595-1.stderr b/src/test/ui/associated-types/issue-23595-1.stderr new file mode 100644 index 000000000000..bb455684ee3e --- /dev/null +++ b/src/test/ui/associated-types/issue-23595-1.stderr @@ -0,0 +1,16 @@ +error[E0191]: the value of the associated types `ChildKey` (from trait `Hierarchy`), `Children` (from trait `Hierarchy`), `Value` (from trait `Hierarchy`) must be specified + --> $DIR/issue-23595-1.rs:8:58 + | +LL | type Value; + | ----------- `Value` defined here +LL | type ChildKey; + | -------------- `ChildKey` defined here +LL | type Children = dyn Index; + | -----------------------------------------------------^^^^^^^^^-- + | | | + | | help: specify the associated types: `Hierarchy` + | `Children` defined here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0191`. diff --git a/src/test/compile-fail/issue-27675-unchecked-bounds.rs b/src/test/ui/associated-types/issue-27675-unchecked-bounds.rs similarity index 100% rename from src/test/compile-fail/issue-27675-unchecked-bounds.rs rename to src/test/ui/associated-types/issue-27675-unchecked-bounds.rs diff --git a/src/test/ui/associated-types/issue-27675-unchecked-bounds.stderr b/src/test/ui/associated-types/issue-27675-unchecked-bounds.stderr new file mode 100644 index 000000000000..02396bd00a59 --- /dev/null +++ b/src/test/ui/associated-types/issue-27675-unchecked-bounds.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/issue-27675-unchecked-bounds.rs:15:31 + | +LL | fn copy(from: &U::From) -> U::From { + | ----- required by this bound in `copy` +... +LL | copy::>(t) + | ^ the trait `Copy` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | pub fn copy_any(t: &T) -> T { + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issue-74072-lifetime-name-annotations.stderr b/src/test/ui/async-await/issue-74072-lifetime-name-annotations.stderr index 123c3192cffb..b96cab9f0f51 100644 --- a/src/test/ui/async-await/issue-74072-lifetime-name-annotations.stderr +++ b/src/test/ui/async-await/issue-74072-lifetime-name-annotations.stderr @@ -2,7 +2,7 @@ error[E0506]: cannot assign to `*x` because it is borrowed --> $DIR/issue-74072-lifetime-name-annotations.rs:9:5 | LL | pub async fn async_fn(x: &mut i32) -> &i32 { - | - let's call the lifetime of this reference `'1` + | - let's call the lifetime of this reference `'1` LL | let y = &*x; | --- borrow of `*x` occurs here LL | *x += 1; diff --git a/src/test/ui/async-await/issue-75785-confusing-named-region.rs b/src/test/ui/async-await/issue-75785-confusing-named-region.rs new file mode 100644 index 000000000000..452614087be9 --- /dev/null +++ b/src/test/ui/async-await/issue-75785-confusing-named-region.rs @@ -0,0 +1,13 @@ +// edition:2018 +// +// Regression test for issue #75785 +// Tests that we don't point to a confusing named +// region when emitting a diagnostic + +pub async fn async_fn(x: &mut i32) -> (&i32, &i32) { + let y = &*x; + *x += 1; //~ ERROR cannot assign to + (&32, y) +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-75785-confusing-named-region.stderr b/src/test/ui/async-await/issue-75785-confusing-named-region.stderr new file mode 100644 index 000000000000..3b731d9c60a6 --- /dev/null +++ b/src/test/ui/async-await/issue-75785-confusing-named-region.stderr @@ -0,0 +1,15 @@ +error[E0506]: cannot assign to `*x` because it is borrowed + --> $DIR/issue-75785-confusing-named-region.rs:9:5 + | +LL | pub async fn async_fn(x: &mut i32) -> (&i32, &i32) { + | - let's call the lifetime of this reference `'1` +LL | let y = &*x; + | --- borrow of `*x` occurs here +LL | *x += 1; + | ^^^^^^^ assignment to borrowed `*x` occurs here +LL | (&32, y) + | -------- returning this value requires that `*x` is borrowed for `'1` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/async-await/issues/issue-78654.full.stderr b/src/test/ui/async-await/issues/issue-78654.full.stderr index ff0260523db3..a9208ade7406 100644 --- a/src/test/ui/async-await/issues/issue-78654.full.stderr +++ b/src/test/ui/async-await/issues/issue-78654.full.stderr @@ -1,11 +1,11 @@ error[E0573]: expected type, found built-in attribute `feature` - --> $DIR/issue-78654.rs:10:15 + --> $DIR/issue-78654.rs:9:15 | LL | impl Foo { | ^^^^^^^ not a type error[E0207]: the const parameter `H` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-78654.rs:10:12 + --> $DIR/issue-78654.rs:9:12 | LL | impl Foo { | ^ unconstrained const parameter diff --git a/src/test/ui/async-await/issues/issue-78654.min.stderr b/src/test/ui/async-await/issues/issue-78654.min.stderr index ff0260523db3..a9208ade7406 100644 --- a/src/test/ui/async-await/issues/issue-78654.min.stderr +++ b/src/test/ui/async-await/issues/issue-78654.min.stderr @@ -1,11 +1,11 @@ error[E0573]: expected type, found built-in attribute `feature` - --> $DIR/issue-78654.rs:10:15 + --> $DIR/issue-78654.rs:9:15 | LL | impl Foo { | ^^^^^^^ not a type error[E0207]: the const parameter `H` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-78654.rs:10:12 + --> $DIR/issue-78654.rs:9:12 | LL | impl Foo { | ^ unconstrained const parameter diff --git a/src/test/ui/async-await/issues/issue-78654.rs b/src/test/ui/async-await/issues/issue-78654.rs index b57ed35f8e36..37ebb4ecac8b 100644 --- a/src/test/ui/async-await/issues/issue-78654.rs +++ b/src/test/ui/async-await/issues/issue-78654.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo; diff --git a/src/test/ui/bad/bad-sized.stderr b/src/test/ui/bad/bad-sized.stderr index 60a5bb9f7866..260d78b543a4 100644 --- a/src/test/ui/bad/bad-sized.stderr +++ b/src/test/ui/bad/bad-sized.stderr @@ -15,7 +15,7 @@ error[E0277]: the size for values of type `dyn Trait` cannot be known at compila LL | let x: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/alloc/src/vec.rs:LL:COL + ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | LL | pub struct Vec { | - required by this bound in `Vec` diff --git a/src/test/ui/binding/const-param.full.stderr b/src/test/ui/binding/const-param.full.stderr new file mode 100644 index 000000000000..0200c6def246 --- /dev/null +++ b/src/test/ui/binding/const-param.full.stderr @@ -0,0 +1,9 @@ +error[E0158]: const parameters cannot be referenced in patterns + --> $DIR/const-param.rs:8:9 + | +LL | N => {} + | ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0158`. diff --git a/src/test/ui/binding/const-param.min.stderr b/src/test/ui/binding/const-param.min.stderr new file mode 100644 index 000000000000..0200c6def246 --- /dev/null +++ b/src/test/ui/binding/const-param.min.stderr @@ -0,0 +1,9 @@ +error[E0158]: const parameters cannot be referenced in patterns + --> $DIR/const-param.rs:8:9 + | +LL | N => {} + | ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0158`. diff --git a/src/test/ui/binding/const-param.rs b/src/test/ui/binding/const-param.rs index 3c7f4d071f69..4aec801cb155 100644 --- a/src/test/ui/binding/const-param.rs +++ b/src/test/ui/binding/const-param.rs @@ -1,6 +1,7 @@ // Identifier pattern referring to a const generic parameter is an error (issue #68853). - -#![feature(const_generics)] //~ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] fn check() { match 1 { diff --git a/src/test/ui/binding/const-param.stderr b/src/test/ui/binding/const-param.stderr deleted file mode 100644 index d3d06a2d834f..000000000000 --- a/src/test/ui/binding/const-param.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-param.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete - -error[E0158]: const parameters cannot be referenced in patterns - --> $DIR/const-param.rs:7:9 - | -LL | N => {} - | ^ - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0158`. diff --git a/src/test/ui/binop/issue-77910-1.rs b/src/test/ui/binop/issue-77910-1.rs new file mode 100644 index 000000000000..d786e3358598 --- /dev/null +++ b/src/test/ui/binop/issue-77910-1.rs @@ -0,0 +1,11 @@ +fn foo(s: &i32) -> &i32 { + let xs; + xs +} +fn main() { + let y; + // we shouldn't ice with the bound var here. + assert_eq!(foo, y); + //~^ ERROR binary operation `==` cannot be applied to type + //~| ERROR `for<'r> fn(&'r i32) -> &'r i32 {foo}` doesn't implement `Debug` +} diff --git a/src/test/ui/binop/issue-77910-1.stderr b/src/test/ui/binop/issue-77910-1.stderr new file mode 100644 index 000000000000..e48d3e19996f --- /dev/null +++ b/src/test/ui/binop/issue-77910-1.stderr @@ -0,0 +1,26 @@ +error[E0369]: binary operation `==` cannot be applied to type `for<'r> fn(&'r i32) -> &'r i32 {foo}` + --> $DIR/issue-77910-1.rs:8:5 + | +LL | assert_eq!(foo, y); + | ^^^^^^^^^^^^^^^^^^^ + | | + | for<'r> fn(&'r i32) -> &'r i32 {foo} + | _ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `for<'r> fn(&'r i32) -> &'r i32 {foo}` doesn't implement `Debug` + --> $DIR/issue-77910-1.rs:8:5 + | +LL | assert_eq!(foo, y); + | ^^^^^^^^^^^^^^^^^^^ `for<'r> fn(&'r i32) -> &'r i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = help: the trait `Debug` is not implemented for `for<'r> fn(&'r i32) -> &'r i32 {foo}` + = note: required because of the requirements on the impl of `Debug` for `&for<'r> fn(&'r i32) -> &'r i32 {foo}` + = note: required by `std::fmt::Debug::fmt` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0369. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/binop/issue-77910-2.rs b/src/test/ui/binop/issue-77910-2.rs new file mode 100644 index 000000000000..2bb48d365761 --- /dev/null +++ b/src/test/ui/binop/issue-77910-2.rs @@ -0,0 +1,9 @@ +fn foo(s: &i32) -> &i32 { + let xs; + xs +} +fn main() { + let y; + if foo == y {} + //~^ ERROR binary operation `==` cannot be applied to type +} diff --git a/src/test/ui/binop/issue-77910-2.stderr b/src/test/ui/binop/issue-77910-2.stderr new file mode 100644 index 000000000000..5477a5762a8f --- /dev/null +++ b/src/test/ui/binop/issue-77910-2.stderr @@ -0,0 +1,11 @@ +error[E0369]: binary operation `==` cannot be applied to type `for<'r> fn(&'r i32) -> &'r i32 {foo}` + --> $DIR/issue-77910-2.rs:7:12 + | +LL | if foo == y {} + | --- ^^ - _ + | | + | for<'r> fn(&'r i32) -> &'r i32 {foo} + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr b/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr index ca1496a6c8d9..a40907779390 100644 --- a/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr +++ b/src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr @@ -23,7 +23,7 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time --> $DIR/borrowck-mut-borrow-linear-errors.rs:12:30 | LL | _ => { addr.push(&mut x); } - | ^^^^^^ mutable borrow starts here in previous iteration of loop + | ^^^^^^ `x` was mutably borrowed here in the previous iteration of the loop error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/mut-borrow-in-loop.stderr b/src/test/ui/borrowck/mut-borrow-in-loop.stderr index 260b9673d74b..b621694a548c 100644 --- a/src/test/ui/borrowck/mut-borrow-in-loop.stderr +++ b/src/test/ui/borrowck/mut-borrow-in-loop.stderr @@ -15,7 +15,7 @@ LL | impl<'a, T : 'a> FuncWrapper<'a, T> { LL | (self.func)(arg) | ------------^^^- | | | - | | mutable borrow starts here in previous iteration of loop + | | `*arg` was mutably borrowed here in the previous iteration of the loop | argument requires that `*arg` is borrowed for `'a` error[E0499]: cannot borrow `*arg` as mutable more than once at a time @@ -27,7 +27,7 @@ LL | impl<'a, T : 'a> FuncWrapper<'a, T> { LL | (self.func)(arg) | ------------^^^- | | | - | | mutable borrow starts here in previous iteration of loop + | | `*arg` was mutably borrowed here in the previous iteration of the loop | argument requires that `*arg` is borrowed for `'a` error[E0499]: cannot borrow `*arg` as mutable more than once at a time @@ -39,7 +39,7 @@ LL | impl<'a, T : 'a> FuncWrapper<'a, T> { LL | (self.func)(arg) | ------------^^^- | | | - | | mutable borrow starts here in previous iteration of loop + | | `*arg` was mutably borrowed here in the previous iteration of the loop | argument requires that `*arg` is borrowed for `'a` error: aborting due to 3 previous errors; 1 warning emitted diff --git a/src/test/ui/borrowck/two-phase-across-loop.stderr b/src/test/ui/borrowck/two-phase-across-loop.stderr index 38993a50bf6b..d4e515d12bbb 100644 --- a/src/test/ui/borrowck/two-phase-across-loop.stderr +++ b/src/test/ui/borrowck/two-phase-across-loop.stderr @@ -2,7 +2,7 @@ error[E0499]: cannot borrow `foo` as mutable more than once at a time --> $DIR/two-phase-across-loop.rs:17:22 | LL | strings.push(foo.get_string()); - | ^^^ mutable borrow starts here in previous iteration of loop + | ^^^ `foo` was mutably borrowed here in the previous iteration of the loop error: aborting due to previous error diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs new file mode 100644 index 000000000000..0b94317fd713 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs @@ -0,0 +1,86 @@ +// Test that arrays are completely captured by closures by relying on the borrow check diagnostics + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +fn arrays_1() { + let mut arr = [1, 2, 3, 4, 5]; + + let mut c = || { + arr[0] += 10; + }; + + // c will capture `arr` completely, therefore another index into the + // array can't be modified here + arr[1] += 10; + //~^ ERROR: cannot use `arr` because it was mutably borrowed + //~| ERROR: cannot use `arr[_]` because it was mutably borrowed + c(); +} + +fn arrays_2() { + let mut arr = [1, 2, 3, 4, 5]; + + let c = || { + println!("{:#?}", &arr[3..4]); + }; + + // c will capture `arr` completely, therefore another index into the + // array can't be modified here + arr[1] += 10; + //~^ ERROR: cannot assign to `arr[_]` because it is borrowed + c(); +} + +fn arrays_3() { + let mut arr = [1, 2, 3, 4, 5]; + + let c = || { + println!("{}", arr[3]); + }; + + // c will capture `arr` completely, therefore another index into the + // array can't be modified here + arr[1] += 10; + //~^ ERROR: cannot assign to `arr[_]` because it is borrowed + c(); +} + +fn arrays_4() { + let mut arr = [1, 2, 3, 4, 5]; + + let mut c = || { + arr[1] += 10; + }; + + // c will capture `arr` completely, therefore we cannot borrow another index + // into the array. + println!("{}", arr[3]); + //~^ ERROR: cannot use `arr` because it was mutably borrowed + //~| ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable + + c(); +} + +fn arrays_5() { + let mut arr = [1, 2, 3, 4, 5]; + + let mut c = || { + arr[1] += 10; + }; + + // c will capture `arr` completely, therefore we cannot borrow other indecies + // into the array. + println!("{:#?}", &arr[3..2]); + //~^ ERROR: cannot borrow `arr` as immutable because it is also borrowed as mutable + + c(); +} + +fn main() { + arrays_1(); + arrays_2(); + arrays_3(); + arrays_4(); + arrays_5(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr new file mode 100644 index 000000000000..77e3e71bc612 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr @@ -0,0 +1,111 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/arrays.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0503]: cannot use `arr` because it was mutably borrowed + --> $DIR/arrays.rs:15:5 + | +LL | let mut c = || { + | -- borrow of `arr` occurs here +LL | arr[0] += 10; + | --- borrow occurs due to use of `arr` in closure +... +LL | arr[1] += 10; + | ^^^^^^ use of borrowed `arr` +... +LL | c(); + | - borrow later used here + +error[E0503]: cannot use `arr[_]` because it was mutably borrowed + --> $DIR/arrays.rs:15:5 + | +LL | let mut c = || { + | -- borrow of `arr` occurs here +LL | arr[0] += 10; + | --- borrow occurs due to use of `arr` in closure +... +LL | arr[1] += 10; + | ^^^^^^^^^^^^ use of borrowed `arr` +... +LL | c(); + | - borrow later used here + +error[E0506]: cannot assign to `arr[_]` because it is borrowed + --> $DIR/arrays.rs:30:5 + | +LL | let c = || { + | -- borrow of `arr[_]` occurs here +LL | println!("{:#?}", &arr[3..4]); + | --- borrow occurs due to use in closure +... +LL | arr[1] += 10; + | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here +LL | +LL | c(); + | - borrow later used here + +error[E0506]: cannot assign to `arr[_]` because it is borrowed + --> $DIR/arrays.rs:44:5 + | +LL | let c = || { + | -- borrow of `arr[_]` occurs here +LL | println!("{}", arr[3]); + | --- borrow occurs due to use in closure +... +LL | arr[1] += 10; + | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here +LL | +LL | c(); + | - borrow later used here + +error[E0503]: cannot use `arr` because it was mutably borrowed + --> $DIR/arrays.rs:58:20 + | +LL | let mut c = || { + | -- borrow of `arr` occurs here +LL | arr[1] += 10; + | --- borrow occurs due to use of `arr` in closure +... +LL | println!("{}", arr[3]); + | ^^^^^^ use of borrowed `arr` +... +LL | c(); + | - borrow later used here + +error[E0502]: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable + --> $DIR/arrays.rs:58:20 + | +LL | let mut c = || { + | -- mutable borrow occurs here +LL | arr[1] += 10; + | --- first borrow occurs due to use of `arr` in closure +... +LL | println!("{}", arr[3]); + | ^^^^^^ immutable borrow occurs here +... +LL | c(); + | - mutable borrow later used here + +error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable + --> $DIR/arrays.rs:74:24 + | +LL | let mut c = || { + | -- mutable borrow occurs here +LL | arr[1] += 10; + | --- first borrow occurs due to use of `arr` in closure +... +LL | println!("{:#?}", &arr[3..2]); + | ^^^ immutable borrow occurs here +... +LL | c(); + | - mutable borrow later used here + +error: aborting due to 7 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0502, E0503, E0506. +For more information about an error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs new file mode 100644 index 000000000000..15be1d8c7220 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs @@ -0,0 +1,65 @@ +// Test borrow checker when we precise capture when using boxes + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +struct MetaData { x: String, name: String } +struct Data { m: MetaData } +struct BoxedData(Box); +struct EvenMoreBoxedData(Box); + +// Check diagnostics when the same path is mutated both inside and outside the closure +fn box_1() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let mut c = || { + e.0.0.m.x = format!("not-x"); + }; + + e.0.0.m.x = format!("not-x"); + //~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed + c(); +} + +// Check diagnostics when a path is mutated inside a closure while attempting to read it outside +// the closure. +fn box_2() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let mut c = || { + e.0.0.m.x = format!("not-x"); + }; + + println!("{}", e.0.0.m.x); + //~^ ERROR: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable + c(); +} + +// Check diagnostics when a path is read inside a closure while attempting to mutate it outside +// the closure. +fn box_3() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let c = || { + println!("{}", e.0.0.m.x); + }; + + e.0.0.m.x = format!("not-x"); + //~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed + c(); +} + +fn main() { + box_1(); + box_2(); + box_3(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr new file mode 100644 index 000000000000..17a9332fb3e6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr @@ -0,0 +1,55 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/box.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed + --> $DIR/box.rs:22:5 + | +LL | let mut c = || { + | -- borrow of `e.0.0.m.x` occurs here +LL | e.0.0.m.x = format!("not-x"); + | - borrow occurs due to use in closure +... +LL | e.0.0.m.x = format!("not-x"); + | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here +LL | +LL | c(); + | - borrow later used here + +error[E0502]: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable + --> $DIR/box.rs:39:20 + | +LL | let mut c = || { + | -- mutable borrow occurs here +LL | e.0.0.m.x = format!("not-x"); + | - first borrow occurs due to use of `e.0.0.m.x` in closure +... +LL | println!("{}", e.0.0.m.x); + | ^^^^^^^^^ immutable borrow occurs here +LL | +LL | c(); + | - mutable borrow later used here + +error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed + --> $DIR/box.rs:56:5 + | +LL | let c = || { + | -- borrow of `e.0.0.m.x` occurs here +LL | println!("{}", e.0.0.m.x); + | - borrow occurs due to use in closure +... +LL | e.0.0.m.x = format!("not-x"); + | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here +LL | +LL | c(); + | - borrow later used here + +error: aborting due to 3 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0502, E0506. +For more information about an error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs new file mode 100644 index 000000000000..39b04c833e38 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs @@ -0,0 +1,28 @@ +// Test that when a borrow checker diagnostics are emitted, it's as precise +// as the capture by the closure. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let mut c = || { + w.p.x += 20; + }; + + let py = &mut w.p.x; + //~^ ERROR: cannot borrow `w.p.x` as mutable more than once at a time + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr new file mode 100644 index 000000000000..e5a396c4e98a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr @@ -0,0 +1,26 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path.rs:4:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0499]: cannot borrow `w.p.x` as mutable more than once at a time + --> $DIR/multilevel-path.rs:23:14 + | +LL | let mut c = || { + | -- first mutable borrow occurs here +LL | w.p.x += 20; + | - first borrow occurs due to use of `w.p.x` in closure +... +LL | let py = &mut w.p.x; + | ^^^^^^^^^^ second mutable borrow occurs here +LL | +LL | c(); + | - first borrow later used here + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs new file mode 100644 index 000000000000..e78d8715e489 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs @@ -0,0 +1,26 @@ +// Test that borrow checker error is accurate and that min capture pass of the +// closure analysis is working as expected. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 20 }; + + // `p` is captured via mutable borrow. + let mut c = || { + p.x += 10; + println!("{:?}", p); + }; + + + println!("{:?}", p); + //~^ ERROR: cannot borrow `p` as immutable because it is also borrowed as mutable + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr new file mode 100644 index 000000000000..45a61cd98b10 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr @@ -0,0 +1,26 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/simple-struct-min-capture.rs:4:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0502]: cannot borrow `p` as immutable because it is also borrowed as mutable + --> $DIR/simple-struct-min-capture.rs:23:22 + | +LL | let mut c = || { + | -- mutable borrow occurs here +LL | p.x += 10; + | - first borrow occurs due to use of `p` in closure +... +LL | println!("{:?}", p); + | ^ immutable borrow occurs here +LL | +LL | c(); + | - mutable borrow later used here + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs new file mode 100644 index 000000000000..3a66399d0289 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs @@ -0,0 +1,97 @@ +// run-pass + +// Test precise capture when using boxes + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 + + +struct MetaData { x: String, name: String } +struct Data { m: MetaData } +struct BoxedData(Box); +struct EvenMoreBoxedData(Box); + +// Mutate disjoint paths, one inside one outside the closure +fn box_1() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let mut c = || { + e.0.0.m.x = format!("not-x"); + }; + + e.0.0.m.name = format!("not-name"); + c(); +} + +// Mutate a path inside the closure and read a disjoint path outside the closure +fn box_2() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let mut c = || { + e.0.0.m.x = format!("not-x"); + }; + + println!("{}", e.0.0.m.name); + c(); +} + +// Read a path inside the closure and mutate a disjoint path outside the closure +fn box_3() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let c = || { + println!("{}", e.0.0.m.name); + }; + + e.0.0.m.x = format!("not-x"); + c(); +} + +// Read disjoint paths, one inside the closure and one outside the closure. +fn box_4() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let e = EvenMoreBoxedData(Box::new(b)); + + let c = || { + println!("{}", e.0.0.m.name); + }; + + println!("{}", e.0.0.m.x); + c(); +} + +// Read the same path, once inside the closure and once outside the closure. +fn box_5() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let e = EvenMoreBoxedData(Box::new(b)); + + let c = || { + println!("{}", e.0.0.m.name); + }; + + println!("{}", e.0.0.m.name); + c(); +} + +fn main() { + box_1(); + box_2(); + box_3(); + box_4(); + box_5(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/box.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/box.stderr new file mode 100644 index 000000000000..9883c01b946c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/box.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/box.rs:5:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs new file mode 100644 index 000000000000..2c359519b769 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs @@ -0,0 +1,28 @@ +// run-pass + +// Test that we can immutably borrow field of an instance of a structure from within a closure, +// while having a mutable borrow to another field of the same instance outside the closure. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 10 }; + + let c = || { + println!("{}", p.x); + }; + + // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. + let py = &mut p.y; + + c(); + *py = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.stderr new file mode 100644 index 000000000000..9b0dea770fba --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-struct.rs:6:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs new file mode 100644 index 000000000000..2c6679feabe4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs @@ -0,0 +1,23 @@ +// run-pass + +// Test that we can mutate an element of a tuple from within a closure +// while immutably borrowing another element of the same tuple outside the closure. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +fn main() { + let mut t = (10, 10); + + let mut c = || { + let t1 = &mut t.1; + *t1 = 20; + }; + + // Test that `c` only captures t.1, therefore reading t.0 is allowed. + println!("{}", t.0); + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.stderr new file mode 100644 index 000000000000..28d091539527 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-tuple-mut.rs:6:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs new file mode 100644 index 000000000000..52f5cef9f01c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs @@ -0,0 +1,24 @@ +// run-pass + +// Test that we can immutably borrow an element of a tuple from within a closure, +// while having a mutable borrow to another element of the same tuple outside the closure. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +fn main() { + let mut t = (10, 10); + + let c = || { + println!("{}", t.0); + }; + + // `c` only captures t.0, therefore mutating t.1 is allowed. + let t1 = &mut t.1; + + c(); + *t1 = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.stderr new file mode 100644 index 000000000000..4fb37f85f88b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-tuple.rs:6:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs new file mode 100644 index 000000000000..3f8e197b7837 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs @@ -0,0 +1,27 @@ +// run-pass + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 + +// Tests that if a closure uses indivual fields of the same object +// then that case is handled properly. + +#![allow(unused)] + +struct Struct { + x: i32, + y: i32, + s: String, +} + +fn main() { + let mut s = Struct { x: 10, y: 10, s: String::new() }; + + let mut c = { + s.x += 10; + s.y += 42; + s.s = String::from("new"); + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.stderr new file mode 100644 index 000000000000..bba90f8917ac --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/disjoint-capture-in-same-closure.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs new file mode 100644 index 000000000000..8c12593430ef --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs @@ -0,0 +1,41 @@ +// run-pass + +// Test disjoint capture within an impl block + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 + +struct Filter { + div: i32, +} +impl Filter { + fn allowed(&self, x: i32) -> bool { + x % self.div == 1 + } +} + +struct Data { + filter: Filter, + list: Vec, +} +impl Data { + fn update(&mut self) { + // The closure passed to filter only captures self.filter, + // therefore mutating self.list is allowed. + self.list.retain( + |v| self.filter.allowed(*v), + ); + } +} + +fn main() { + let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() }; + + for i in 1..10 { + d.list.push(i); + } + + d.update(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.stderr new file mode 100644 index 000000000000..6930e18992ae --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/filter-on-struct-member.rs:5:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs new file mode 100644 index 000000000000..142c156bd56e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs @@ -0,0 +1,36 @@ +// run-pass + +// Test that closures can catpure paths that are more precise than just one level +// from the root variable. +// +// If the closures can handle such precison we should be able to mutate one path in the closure +// while being able to mutate another path outside the closure, where the two paths are disjoint +// after applying two projections on the root variable. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let mut c = || { + w.p.x += 20; + }; + + // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.stderr new file mode 100644 index 000000000000..94b877522f4a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-1.rs:10:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs new file mode 100644 index 000000000000..d8f7d55d5aa7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs @@ -0,0 +1,34 @@ +// run-pass + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![allow(unused)] + +// If the closures can handle such precison we should be able to read one path in the closure +// while being able mutate another path outside the closure, where the two paths are disjoint +// after applying two projections on the root variable. + + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let c = || { + println!("{}", w.p.x); + }; + + // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.stderr new file mode 100644 index 000000000000..100a0e167c58 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-2.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs new file mode 100644 index 000000000000..fc3d48ec4581 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs @@ -0,0 +1,31 @@ +// run-pass + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![allow(unused)] + +// Test that when `capture_disjoint_fields` is enabled we can read a path +// both inside and outside the closure at the same time. + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let c = || { + println!("{}", w.p.x); + }; + + let px = &w.p.x; + c(); + + println!("{}", px); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.stderr new file mode 100644 index 000000000000..cf5be6a00e9a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-3.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs new file mode 100644 index 000000000000..238580929ef1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs @@ -0,0 +1,40 @@ +// run-pass + +// Test whether if we can do precise capture when using nested clsoure. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 5, y: 20 }; + + // c1 should capture `p.x` via immutable borrow and + // `p.y` via mutable borrow. + let mut c1 = || { + println!("{}", p.x); + + let incr = 10; + + let mut c2 = || p.y += incr; + c2(); + + println!("{}", p.y); + }; + + c1(); + + // This should not throw an error because `p.x` is borrowed via Immutable borrow, + // and multiple immutable borrow of the same place are allowed. + let px = &p.x; + + println!("{}", px); + + c1(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.stderr new file mode 100644 index 000000000000..293aa82ce9fe --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/nested-closure.rs:5:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs similarity index 100% rename from src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs rename to src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs diff --git a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr new file mode 100644 index 000000000000..a1fb1c02e462 --- /dev/null +++ b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr @@ -0,0 +1,11 @@ +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:2:31 + | +LL | let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/compile-fail/coerce-unsafe-to-closure.rs b/src/test/ui/closures/coerce-unsafe-to-closure.rs similarity index 100% rename from src/test/compile-fail/coerce-unsafe-to-closure.rs rename to src/test/ui/closures/coerce-unsafe-to-closure.rs diff --git a/src/test/ui/closures/coerce-unsafe-to-closure.stderr b/src/test/ui/closures/coerce-unsafe-to-closure.stderr new file mode 100644 index 000000000000..ab035d03b056 --- /dev/null +++ b/src/test/ui/closures/coerce-unsafe-to-closure.stderr @@ -0,0 +1,11 @@ +error[E0277]: expected a `FnOnce<(&str,)>` closure, found `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` + --> $DIR/coerce-unsafe-to-closure.rs:2:44 + | +LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); + | ^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(&str,)>` closure, found `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` + | + = help: the trait `FnOnce<(&str,)>` is not implemented for `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/apit-with-const-param.rs b/src/test/ui/const-generics/apit-with-const-param.rs index facc0bcf5130..3bc62141927a 100644 --- a/src/test/ui/const-generics/apit-with-const-param.rs +++ b/src/test/ui/const-generics/apit-with-const-param.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Trait {} diff --git a/src/test/ui/const-generics/arg-in-pat-1.rs b/src/test/ui/const-generics/arg-in-pat-1.rs new file mode 100644 index 000000000000..82555084e418 --- /dev/null +++ b/src/test/ui/const-generics/arg-in-pat-1.rs @@ -0,0 +1,23 @@ +// check-pass +enum ConstGenericEnum { + Foo([i32; N]), + Bar, +} + +fn foo(val: &ConstGenericEnum) { + if let ConstGenericEnum::::Foo(field, ..) = val {} +} + +fn bar(val: &ConstGenericEnum) { + match val { + ConstGenericEnum::::Foo(field, ..) => (), + ConstGenericEnum::::Bar => (), + } +} + +fn main() { + match ConstGenericEnum::Bar { + ConstGenericEnum::<3>::Foo(field, ..) => (), + ConstGenericEnum::<3>::Bar => (), + } +} diff --git a/src/test/ui/const-generics/arg-in-pat-2.rs b/src/test/ui/const-generics/arg-in-pat-2.rs new file mode 100644 index 000000000000..dc9e722eda84 --- /dev/null +++ b/src/test/ui/const-generics/arg-in-pat-2.rs @@ -0,0 +1,10 @@ +// check-pass +enum Generic { + Variant, +} + +fn main() { + match todo!() { + Generic::<0usize>::Variant => todo!() + } +} diff --git a/src/test/ui/const-generics/arg-in-pat-3.rs b/src/test/ui/const-generics/arg-in-pat-3.rs new file mode 100644 index 000000000000..24626a3b68ae --- /dev/null +++ b/src/test/ui/const-generics/arg-in-pat-3.rs @@ -0,0 +1,43 @@ +// check-pass +struct Foo; + +fn bindingp() { + match Foo { + mut x @ Foo::<3> => { + let ref mut _x @ Foo::<3> = x; + } + } +} + +struct Bar { + field: Foo, +} + +fn structp() { + match todo!() { + Bar::<3> { + field: Foo::<3>, + } => (), + } +} + +struct Baz(Foo); + +fn tuplestructp() { + match Baz(Foo) { + Baz::<3>(Foo::<3>) => (), + } +} + +impl Baz { + const ASSOC: usize = 3; +} + +fn pathp() { + match 3 { + Baz::<3>::ASSOC => (), + _ => (), + } +} + +fn main() {} diff --git a/src/test/ui/const-generics/argument_order.full.stderr b/src/test/ui/const-generics/argument_order.full.stderr index b52e50507033..e533d4f7fb83 100644 --- a/src/test/ui/const-generics/argument_order.full.stderr +++ b/src/test/ui/const-generics/argument_order.full.stderr @@ -1,11 +1,11 @@ error: lifetime parameters must be declared prior to const parameters - --> $DIR/argument_order.rs:12:32 + --> $DIR/argument_order.rs:11:32 | LL | struct AlsoBad { | -----------------^^-----^^-------------------- help: reorder the parameters: lifetimes, then consts and types: `<'a, 'b, const N: usize, T, const M: usize, U>` error[E0747]: lifetime provided when a type was expected - --> $DIR/argument_order.rs:20:23 + --> $DIR/argument_order.rs:19:23 | LL | let _: AlsoBad<7, 'static, u32, 'static, 17, u16>; | ^^^^^^^ diff --git a/src/test/ui/const-generics/argument_order.min.stderr b/src/test/ui/const-generics/argument_order.min.stderr index 728ae69b41f1..f23bc6d6a2bf 100644 --- a/src/test/ui/const-generics/argument_order.min.stderr +++ b/src/test/ui/const-generics/argument_order.min.stderr @@ -1,23 +1,23 @@ error: type parameters must be declared prior to const parameters - --> $DIR/argument_order.rs:6:28 + --> $DIR/argument_order.rs:5:28 | LL | struct Bad { | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `` error: lifetime parameters must be declared prior to const parameters - --> $DIR/argument_order.rs:12:32 + --> $DIR/argument_order.rs:11:32 | LL | struct AlsoBad { | -----------------^^-----^^-------------------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T, U, const N: usize, const M: usize>` error: type parameters must be declared prior to const parameters - --> $DIR/argument_order.rs:12:36 + --> $DIR/argument_order.rs:11:36 | LL | struct AlsoBad { | ---------------------^----------------------^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T, U, const N: usize, const M: usize>` error[E0747]: lifetime provided when a type was expected - --> $DIR/argument_order.rs:20:23 + --> $DIR/argument_order.rs:19:23 | LL | let _: AlsoBad<7, 'static, u32, 'static, 17, u16>; | ^^^^^^^ diff --git a/src/test/ui/const-generics/argument_order.rs b/src/test/ui/const-generics/argument_order.rs index 507baf5fd755..95eaeea58184 100644 --- a/src/test/ui/const-generics/argument_order.rs +++ b/src/test/ui/const-generics/argument_order.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Bad { //[min]~^ ERROR type parameters must be declared prior to const parameters diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.full.stderr b/src/test/ui/const-generics/array-size-in-generic-struct-param.full.stderr index cf4487b5829c..0fb23e41b013 100644 --- a/src/test/ui/const-generics/array-size-in-generic-struct-param.full.stderr +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/array-size-in-generic-struct-param.rs:9:38 + --> $DIR/array-size-in-generic-struct-param.rs:8:38 | LL | struct ArithArrayLen([u32; 0 + N]); | ^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | struct ArithArrayLen([u32; 0 + N]); = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/array-size-in-generic-struct-param.rs:20:10 + --> $DIR/array-size-in-generic-struct-param.rs:19:10 | LL | arr: [u8; CFG.arr_size], | ^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr b/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr index 73c9ea59c955..7af23103ce70 100644 --- a/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/array-size-in-generic-struct-param.rs:9:48 + --> $DIR/array-size-in-generic-struct-param.rs:8:48 | LL | struct ArithArrayLen([u32; 0 + N]); | ^ cannot perform const operation using `N` @@ -8,7 +8,7 @@ LL | struct ArithArrayLen([u32; 0 + N]); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/array-size-in-generic-struct-param.rs:20:15 + --> $DIR/array-size-in-generic-struct-param.rs:19:15 | LL | arr: [u8; CFG.arr_size], | ^^^ cannot perform const operation using `CFG` @@ -17,7 +17,7 @@ LL | arr: [u8; CFG.arr_size], = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: `Config` is forbidden as the type of a const generic parameter - --> $DIR/array-size-in-generic-struct-param.rs:18:21 + --> $DIR/array-size-in-generic-struct-param.rs:17:21 | LL | struct B { | ^^^^^^ diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.rs b/src/test/ui/const-generics/array-size-in-generic-struct-param.rs index 768180d0813a..cd0a9742cd1a 100644 --- a/src/test/ui/const-generics/array-size-in-generic-struct-param.rs +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #[allow(dead_code)] struct ArithArrayLen([u32; 0 + N]); diff --git a/src/test/ui/const-generics/array-wrapper-struct-ctor.rs b/src/test/ui/const-generics/array-wrapper-struct-ctor.rs index 390b6cc2049e..732a18714566 100644 --- a/src/test/ui/const-generics/array-wrapper-struct-ctor.rs +++ b/src/test/ui/const-generics/array-wrapper-struct-ctor.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![allow(dead_code)] diff --git a/src/test/ui/const-generics/associated-type-bound-fail.full.stderr b/src/test/ui/const-generics/associated-type-bound-fail.full.stderr index 8ccbe5dee0e4..6644e74f97a0 100644 --- a/src/test/ui/const-generics/associated-type-bound-fail.full.stderr +++ b/src/test/ui/const-generics/associated-type-bound-fail.full.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `u16: Bar` is not satisfied - --> $DIR/associated-type-bound-fail.rs:14:5 + --> $DIR/associated-type-bound-fail.rs:13:5 | LL | type Assoc: Bar; | ------ required by this bound in `Foo::Assoc` diff --git a/src/test/ui/const-generics/associated-type-bound-fail.min.stderr b/src/test/ui/const-generics/associated-type-bound-fail.min.stderr index 8ccbe5dee0e4..6644e74f97a0 100644 --- a/src/test/ui/const-generics/associated-type-bound-fail.min.stderr +++ b/src/test/ui/const-generics/associated-type-bound-fail.min.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `u16: Bar` is not satisfied - --> $DIR/associated-type-bound-fail.rs:14:5 + --> $DIR/associated-type-bound-fail.rs:13:5 | LL | type Assoc: Bar; | ------ required by this bound in `Foo::Assoc` diff --git a/src/test/ui/const-generics/associated-type-bound-fail.rs b/src/test/ui/const-generics/associated-type-bound-fail.rs index 3440b1356c24..83b267008057 100644 --- a/src/test/ui/const-generics/associated-type-bound-fail.rs +++ b/src/test/ui/const-generics/associated-type-bound-fail.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] trait Bar {} diff --git a/src/test/ui/const-generics/associated-type-bound.rs b/src/test/ui/const-generics/associated-type-bound.rs index 374a49194b17..02f77396c0b6 100644 --- a/src/test/ui/const-generics/associated-type-bound.rs +++ b/src/test/ui/const-generics/associated-type-bound.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] trait Bar {} diff --git a/src/test/ui/const-generics/auxiliary/const_generic_lib.rs b/src/test/ui/const-generics/auxiliary/const_generic_lib.rs index 899a5a1836c3..8d4cd9c0d6b7 100644 --- a/src/test/ui/const-generics/auxiliary/const_generic_lib.rs +++ b/src/test/ui/const-generics/auxiliary/const_generic_lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct Struct(pub [u8; N]); diff --git a/src/test/ui/const-generics/auxiliary/crayte.rs b/src/test/ui/const-generics/auxiliary/crayte.rs index 725005971e1e..d9baab956c9f 100644 --- a/src/test/ui/const-generics/auxiliary/crayte.rs +++ b/src/test/ui/const-generics/auxiliary/crayte.rs @@ -1,7 +1,6 @@ // edition:2018 #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub trait Foo {} struct Local; diff --git a/src/test/ui/const-generics/auxiliary/impl-const.rs b/src/test/ui/const-generics/auxiliary/impl-const.rs index 2e25dadf119c..4a6b57842217 100644 --- a/src/test/ui/const-generics/auxiliary/impl-const.rs +++ b/src/test/ui/const-generics/auxiliary/impl-const.rs @@ -1,6 +1,5 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct Num; diff --git a/src/test/ui/const-generics/broken-mir-1.rs b/src/test/ui/const-generics/broken-mir-1.rs index d13ae12c03bc..34255fa9f588 100644 --- a/src/test/ui/const-generics/broken-mir-1.rs +++ b/src/test/ui/const-generics/broken-mir-1.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub trait Foo { fn foo(&self); diff --git a/src/test/ui/const-generics/broken-mir-2.rs b/src/test/ui/const-generics/broken-mir-2.rs index 2f9afe0b4643..ac358b01672b 100644 --- a/src/test/ui/const-generics/broken-mir-2.rs +++ b/src/test/ui/const-generics/broken-mir-2.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::fmt::Debug; diff --git a/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs b/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs index 931f6ade7f15..44aef859f2de 100644 --- a/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs +++ b/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] // This test confirms that the types can be inferred correctly for this example with const // generics. Previously this would ICE, and more recently error. diff --git a/src/test/ui/const-generics/closing-args-token.full.stderr b/src/test/ui/const-generics/closing-args-token.full.stderr index 1c3ddd345a53..7737705440eb 100644 --- a/src/test/ui/const-generics/closing-args-token.full.stderr +++ b/src/test/ui/const-generics/closing-args-token.full.stderr @@ -1,5 +1,5 @@ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/closing-args-token.rs:11:9 + --> $DIR/closing-args-token.rs:10:9 | LL | S::<5 + 2 >> 7>; | ^^^^^ @@ -10,7 +10,7 @@ LL | S::<{ 5 + 2 } >> 7>; | ^ ^ error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:11:16 + --> $DIR/closing-args-token.rs:10:16 | LL | S::<5 + 2 >> 7>; | ^ ^ @@ -21,7 +21,7 @@ LL | S::<5 + 2 >> 7 && 7>; | ^^^^ error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:17:20 + --> $DIR/closing-args-token.rs:16:20 | LL | S::<{ 5 + 2 } >> 7>; | ^ ^ @@ -32,13 +32,13 @@ LL | S::<{ 5 + 2 } >> 7 && 7>; | ^^^^ error: expected expression, found `;` - --> $DIR/closing-args-token.rs:22:16 + --> $DIR/closing-args-token.rs:21:16 | LL | T::<0 >= 3>; | ^ expected expression error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:28:12 + --> $DIR/closing-args-token.rs:27:12 | LL | T::>= 2 > 0>; | ^^ ^ diff --git a/src/test/ui/const-generics/closing-args-token.min.stderr b/src/test/ui/const-generics/closing-args-token.min.stderr index 1c3ddd345a53..7737705440eb 100644 --- a/src/test/ui/const-generics/closing-args-token.min.stderr +++ b/src/test/ui/const-generics/closing-args-token.min.stderr @@ -1,5 +1,5 @@ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/closing-args-token.rs:11:9 + --> $DIR/closing-args-token.rs:10:9 | LL | S::<5 + 2 >> 7>; | ^^^^^ @@ -10,7 +10,7 @@ LL | S::<{ 5 + 2 } >> 7>; | ^ ^ error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:11:16 + --> $DIR/closing-args-token.rs:10:16 | LL | S::<5 + 2 >> 7>; | ^ ^ @@ -21,7 +21,7 @@ LL | S::<5 + 2 >> 7 && 7>; | ^^^^ error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:17:20 + --> $DIR/closing-args-token.rs:16:20 | LL | S::<{ 5 + 2 } >> 7>; | ^ ^ @@ -32,13 +32,13 @@ LL | S::<{ 5 + 2 } >> 7 && 7>; | ^^^^ error: expected expression, found `;` - --> $DIR/closing-args-token.rs:22:16 + --> $DIR/closing-args-token.rs:21:16 | LL | T::<0 >= 3>; | ^ expected expression error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:28:12 + --> $DIR/closing-args-token.rs:27:12 | LL | T::>= 2 > 0>; | ^^ ^ diff --git a/src/test/ui/const-generics/closing-args-token.rs b/src/test/ui/const-generics/closing-args-token.rs index 8699637c54e9..a9b552ebed7a 100644 --- a/src/test/ui/const-generics/closing-args-token.rs +++ b/src/test/ui/const-generics/closing-args-token.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct S; struct T; diff --git a/src/test/ui/const-generics/coerce_unsized_array.rs b/src/test/ui/const-generics/coerce_unsized_array.rs index a3c295f73c7e..8e20df281039 100644 --- a/src/test/ui/const-generics/coerce_unsized_array.rs +++ b/src/test/ui/const-generics/coerce_unsized_array.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn foo(v: &[u8; N]) -> &[u8] { v diff --git a/src/test/ui/const-generics/concrete-const-as-fn-arg.rs b/src/test/ui/const-generics/concrete-const-as-fn-arg.rs index 7771bf336016..8c31c8651a2b 100644 --- a/src/test/ui/const-generics/concrete-const-as-fn-arg.rs +++ b/src/test/ui/const-generics/concrete-const-as-fn-arg.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct A; // ok diff --git a/src/test/ui/const-generics/concrete-const-impl-method.rs b/src/test/ui/const-generics/concrete-const-impl-method.rs index edb403ce8fd6..3d3bd2664c8b 100644 --- a/src/test/ui/const-generics/concrete-const-impl-method.rs +++ b/src/test/ui/const-generics/concrete-const-impl-method.rs @@ -5,7 +5,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct A; diff --git a/src/test/ui/const-generics/condition-in-trait-const-arg.rs b/src/test/ui/const-generics/condition-in-trait-const-arg.rs index 77b68052fc0b..ad40b48afe5e 100644 --- a/src/test/ui/const-generics/condition-in-trait-const-arg.rs +++ b/src/test/ui/const-generics/condition-in-trait-const-arg.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait IsZeroTrait{} diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr b/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr index 042fa9ad958b..bfa4ba306862 100644 --- a/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr +++ b/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:14:23 + --> $DIR/const-arg-in-const-arg.rs:13:23 | LL | let _: [u8; foo::()]; | ^ cannot perform const operation using `T` @@ -8,7 +8,7 @@ LL | let _: [u8; foo::()]; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:15:23 + --> $DIR/const-arg-in-const-arg.rs:14:23 | LL | let _: [u8; bar::()]; | ^ cannot perform const operation using `N` @@ -17,7 +17,7 @@ LL | let _: [u8; bar::()]; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:25:23 + --> $DIR/const-arg-in-const-arg.rs:24:23 | LL | let _ = [0; bar::()]; | ^ cannot perform const operation using `N` @@ -26,7 +26,7 @@ LL | let _ = [0; bar::()]; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:30:24 + --> $DIR/const-arg-in-const-arg.rs:29:24 | LL | let _: Foo<{ foo::() }>; | ^ cannot perform const operation using `T` @@ -35,7 +35,7 @@ LL | let _: Foo<{ foo::() }>; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:31:24 + --> $DIR/const-arg-in-const-arg.rs:30:24 | LL | let _: Foo<{ bar::() }>; | ^ cannot perform const operation using `N` @@ -44,7 +44,7 @@ LL | let _: Foo<{ bar::() }>; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:36:27 + --> $DIR/const-arg-in-const-arg.rs:35:27 | LL | let _ = Foo::<{ foo::() }>; | ^ cannot perform const operation using `T` @@ -53,7 +53,7 @@ LL | let _ = Foo::<{ foo::() }>; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:37:27 + --> $DIR/const-arg-in-const-arg.rs:36:27 | LL | let _ = Foo::<{ bar::() }>; | ^ cannot perform const operation using `N` @@ -62,7 +62,7 @@ LL | let _ = Foo::<{ bar::() }>; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:16:23 + --> $DIR/const-arg-in-const-arg.rs:15:23 | LL | let _: [u8; faz::<'a>(&())]; | ^^ @@ -71,7 +71,7 @@ LL | let _: [u8; faz::<'a>(&())]; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:17:23 + --> $DIR/const-arg-in-const-arg.rs:16:23 | LL | let _: [u8; baz::<'a>(&())]; | ^^ @@ -80,7 +80,7 @@ LL | let _: [u8; baz::<'a>(&())]; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:18:23 + --> $DIR/const-arg-in-const-arg.rs:17:23 | LL | let _: [u8; faz::<'b>(&())]; | ^^ @@ -89,7 +89,7 @@ LL | let _: [u8; faz::<'b>(&())]; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:19:23 + --> $DIR/const-arg-in-const-arg.rs:18:23 | LL | let _: [u8; baz::<'b>(&())]; | ^^ @@ -98,7 +98,7 @@ LL | let _: [u8; baz::<'b>(&())]; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:26:23 + --> $DIR/const-arg-in-const-arg.rs:25:23 | LL | let _ = [0; faz::<'a>(&())]; | ^^ @@ -107,7 +107,7 @@ LL | let _ = [0; faz::<'a>(&())]; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:27:23 + --> $DIR/const-arg-in-const-arg.rs:26:23 | LL | let _ = [0; baz::<'a>(&())]; | ^^ @@ -116,7 +116,7 @@ LL | let _ = [0; baz::<'a>(&())]; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:28:23 + --> $DIR/const-arg-in-const-arg.rs:27:23 | LL | let _ = [0; faz::<'b>(&())]; | ^^ @@ -125,7 +125,7 @@ LL | let _ = [0; faz::<'b>(&())]; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:29:23 + --> $DIR/const-arg-in-const-arg.rs:28:23 | LL | let _ = [0; baz::<'b>(&())]; | ^^ @@ -134,7 +134,7 @@ LL | let _ = [0; baz::<'b>(&())]; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:32:24 + --> $DIR/const-arg-in-const-arg.rs:31:24 | LL | let _: Foo<{ faz::<'a>(&()) }>; | ^^ @@ -143,7 +143,7 @@ LL | let _: Foo<{ faz::<'a>(&()) }>; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:33:24 + --> $DIR/const-arg-in-const-arg.rs:32:24 | LL | let _: Foo<{ baz::<'a>(&()) }>; | ^^ @@ -152,7 +152,7 @@ LL | let _: Foo<{ baz::<'a>(&()) }>; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:34:24 + --> $DIR/const-arg-in-const-arg.rs:33:24 | LL | let _: Foo<{ faz::<'b>(&()) }>; | ^^ @@ -161,7 +161,7 @@ LL | let _: Foo<{ faz::<'b>(&()) }>; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:35:24 + --> $DIR/const-arg-in-const-arg.rs:34:24 | LL | let _: Foo<{ baz::<'b>(&()) }>; | ^^ @@ -170,7 +170,7 @@ LL | let _: Foo<{ baz::<'b>(&()) }>; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:38:27 + --> $DIR/const-arg-in-const-arg.rs:37:27 | LL | let _ = Foo::<{ faz::<'a>(&()) }>; | ^^ @@ -179,7 +179,7 @@ LL | let _ = Foo::<{ faz::<'a>(&()) }>; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:39:27 + --> $DIR/const-arg-in-const-arg.rs:38:27 | LL | let _ = Foo::<{ baz::<'a>(&()) }>; | ^^ @@ -188,7 +188,7 @@ LL | let _ = Foo::<{ baz::<'a>(&()) }>; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:40:27 + --> $DIR/const-arg-in-const-arg.rs:39:27 | LL | let _ = Foo::<{ faz::<'b>(&()) }>; | ^^ @@ -197,7 +197,7 @@ LL | let _ = Foo::<{ faz::<'b>(&()) }>; = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:41:27 + --> $DIR/const-arg-in-const-arg.rs:40:27 | LL | let _ = Foo::<{ baz::<'b>(&()) }>; | ^^ diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.rs b/src/test/ui/const-generics/const-arg-in-const-arg.rs index 9927538ef50c..8279f4a3f61e 100644 --- a/src/test/ui/const-generics/const-arg-in-const-arg.rs +++ b/src/test/ui/const-generics/const-arg-in-const-arg.rs @@ -2,7 +2,6 @@ // FIXME(const_generics): This test currently causes an ICE because // we don't yet correctly deal with lifetimes, reenable this test once // this is fixed. -#![cfg_attr(min, feature(min_const_generics))] const fn foo() -> usize { std::mem::size_of::() } const fn bar() -> usize { N } diff --git a/src/test/ui/const-generics/const-arg-in-fn.rs b/src/test/ui/const-generics/const-arg-in-fn.rs index 5c438efd82a0..43ed12efb895 100644 --- a/src/test/ui/const-generics/const-arg-in-fn.rs +++ b/src/test/ui/const-generics/const-arg-in-fn.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn const_u32_identity() -> u32 { X diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.full.stderr b/src/test/ui/const-generics/const-arg-type-arg-misordered.full.stderr index 3827002ff4bc..d0ea51ea4173 100644 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.full.stderr +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.full.stderr @@ -1,5 +1,5 @@ error[E0747]: constant provided when a type was expected - --> $DIR/const-arg-type-arg-misordered.rs:8:35 + --> $DIR/const-arg-type-arg-misordered.rs:7:35 | LL | fn foo() -> Array { | ^ diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.min.stderr b/src/test/ui/const-generics/const-arg-type-arg-misordered.min.stderr index 2c5fc8dcc01f..d7b7df0eb55b 100644 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.min.stderr +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.min.stderr @@ -1,5 +1,5 @@ error[E0747]: constant provided when a type was expected - --> $DIR/const-arg-type-arg-misordered.rs:8:35 + --> $DIR/const-arg-type-arg-misordered.rs:7:35 | LL | fn foo() -> Array { | ^ diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs index 6680f772fa3f..5415791d21bb 100644 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] type Array = [T; N]; diff --git a/src/test/ui/const-generics/const-argument-if-length.full.stderr b/src/test/ui/const-generics/const-argument-if-length.full.stderr index 4d627f05adc0..5dca01f0dc07 100644 --- a/src/test/ui/const-generics/const-argument-if-length.full.stderr +++ b/src/test/ui/const-generics/const-argument-if-length.full.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/const-argument-if-length.rs:8:28 + --> $DIR/const-argument-if-length.rs:7:28 | LL | pub const fn is_zst() -> usize { | - this type parameter needs to be `Sized` @@ -12,7 +12,7 @@ LL | pub const fn size_of() -> usize { | - required by this bound in `std::mem::size_of` error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/const-argument-if-length.rs:17:12 + --> $DIR/const-argument-if-length.rs:16:12 | LL | pub struct AtLeastByte { | - this type parameter needs to be `Sized` diff --git a/src/test/ui/const-generics/const-argument-if-length.min.stderr b/src/test/ui/const-generics/const-argument-if-length.min.stderr index 8a1074392a5c..ea177c197461 100644 --- a/src/test/ui/const-generics/const-argument-if-length.min.stderr +++ b/src/test/ui/const-generics/const-argument-if-length.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/const-argument-if-length.rs:19:24 + --> $DIR/const-argument-if-length.rs:18:24 | LL | pad: [u8; is_zst::()], | ^ cannot perform const operation using `T` @@ -8,7 +8,7 @@ LL | pad: [u8; is_zst::()], = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/const-argument-if-length.rs:17:12 + --> $DIR/const-argument-if-length.rs:16:12 | LL | pub struct AtLeastByte { | - this type parameter needs to be `Sized` diff --git a/src/test/ui/const-generics/const-argument-if-length.rs b/src/test/ui/const-generics/const-argument-if-length.rs index 809073831241..67ed85f96afd 100644 --- a/src/test/ui/const-generics/const-argument-if-length.rs +++ b/src/test/ui/const-generics/const-argument-if-length.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] pub const fn is_zst() -> usize { if std::mem::size_of::() == 0 { diff --git a/src/test/ui/const-generics/const-expression-parameter.full.stderr b/src/test/ui/const-generics/const-expression-parameter.full.stderr index 0615a4c206d8..93c5173554f3 100644 --- a/src/test/ui/const-generics/const-expression-parameter.full.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.full.stderr @@ -1,5 +1,5 @@ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/const-expression-parameter.rs:16:20 + --> $DIR/const-expression-parameter.rs:15:20 | LL | i32_identity::<1 + 2>(); | ^^^^^ diff --git a/src/test/ui/const-generics/const-expression-parameter.min.stderr b/src/test/ui/const-generics/const-expression-parameter.min.stderr index 0615a4c206d8..93c5173554f3 100644 --- a/src/test/ui/const-generics/const-expression-parameter.min.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.min.stderr @@ -1,5 +1,5 @@ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/const-expression-parameter.rs:16:20 + --> $DIR/const-expression-parameter.rs:15:20 | LL | i32_identity::<1 + 2>(); | ^^^^^ diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index 3ef7c8b32e03..cb609a564161 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn i32_identity() -> i32 { 5 diff --git a/src/test/ui/const-generics/const-fn-with-const-param.rs b/src/test/ui/const-generics/const-fn-with-const-param.rs index add1290b1d97..5c1ee4e0d5a9 100644 --- a/src/test/ui/const-generics/const-fn-with-const-param.rs +++ b/src/test/ui/const-generics/const-fn-with-const-param.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] const fn const_u32_identity() -> u32 { X diff --git a/src/test/ui/const-generics/const-generic-array-wrapper.rs b/src/test/ui/const-generics/const-generic-array-wrapper.rs index 34edd0b4a8e8..224fc794e327 100644 --- a/src/test/ui/const-generics/const-generic-array-wrapper.rs +++ b/src/test/ui/const-generics/const-generic-array-wrapper.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo([T; N]); diff --git a/src/test/ui/const-generics/const-generic-type_name.rs b/src/test/ui/const-generics/const-generic-type_name.rs index a954c0263521..95632f798969 100644 --- a/src/test/ui/const-generics/const-generic-type_name.rs +++ b/src/test/ui/const-generics/const-generic-type_name.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #[derive(Debug)] struct S; diff --git a/src/test/ui/const-generics/const-param-after-const-literal-arg.rs b/src/test/ui/const-generics/const-param-after-const-literal-arg.rs index 3982f7a7f125..6c2b14f2770d 100644 --- a/src/test/ui/const-generics/const-param-after-const-literal-arg.rs +++ b/src/test/ui/const-generics/const-param-after-const-literal-arg.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo; diff --git a/src/test/ui/const-generics/const-param-before-other-params.full.stderr b/src/test/ui/const-generics/const-param-before-other-params.full.stderr index c2acaabbd883..09a4f66de39c 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.full.stderr +++ b/src/test/ui/const-generics/const-param-before-other-params.full.stderr @@ -1,5 +1,5 @@ error: lifetime parameters must be declared prior to const parameters - --> $DIR/const-param-before-other-params.rs:6:21 + --> $DIR/const-param-before-other-params.rs:5:21 | LL | fn bar(_: &'a ()) { | --------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const X: ()>` diff --git a/src/test/ui/const-generics/const-param-before-other-params.min.stderr b/src/test/ui/const-generics/const-param-before-other-params.min.stderr index 354c6d0615f1..1e49588f1b4e 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.min.stderr +++ b/src/test/ui/const-generics/const-param-before-other-params.min.stderr @@ -1,17 +1,17 @@ error: lifetime parameters must be declared prior to const parameters - --> $DIR/const-param-before-other-params.rs:6:21 + --> $DIR/const-param-before-other-params.rs:5:21 | LL | fn bar(_: &'a ()) { | --------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const X: ()>` error: type parameters must be declared prior to const parameters - --> $DIR/const-param-before-other-params.rs:11:21 + --> $DIR/const-param-before-other-params.rs:10:21 | LL | fn foo(_: &T) {} | --------------^- help: reorder the parameters: lifetimes, then types, then consts: `` error: `()` is forbidden as the type of a const generic parameter - --> $DIR/const-param-before-other-params.rs:6:17 + --> $DIR/const-param-before-other-params.rs:5:17 | LL | fn bar(_: &'a ()) { | ^^ @@ -20,7 +20,7 @@ LL | fn bar(_: &'a ()) { = help: more complex types are supported with `#[feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter - --> $DIR/const-param-before-other-params.rs:11:17 + --> $DIR/const-param-before-other-params.rs:10:17 | LL | fn foo(_: &T) {} | ^^ diff --git a/src/test/ui/const-generics/const-param-before-other-params.rs b/src/test/ui/const-generics/const-param-before-other-params.rs index f1be90cf2e41..508bb3e6a689 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.rs +++ b/src/test/ui/const-generics/const-param-before-other-params.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn bar(_: &'a ()) { //~^ ERROR lifetime parameters must be declared prior to const parameters diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.full.stderr b/src/test/ui/const-generics/const-param-elided-lifetime.full.stderr index aa29d61d917b..119f932745b3 100644 --- a/src/test/ui/const-generics/const-param-elided-lifetime.full.stderr +++ b/src/test/ui/const-generics/const-param-elided-lifetime.full.stderr @@ -1,29 +1,29 @@ error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:11:19 + --> $DIR/const-param-elided-lifetime.rs:10:19 | LL | struct A; | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:16:15 + --> $DIR/const-param-elided-lifetime.rs:15:15 | LL | impl A { | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:19:21 + --> $DIR/const-param-elided-lifetime.rs:18:21 | LL | fn foo(&self) {} | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:24:15 + --> $DIR/const-param-elided-lifetime.rs:23:15 | LL | impl B for A {} | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:28:17 + --> $DIR/const-param-elided-lifetime.rs:27:17 | LL | fn bar() {} | ^ explicit lifetime name needed here diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr b/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr index ed30182690a7..5d3c8f7b2e61 100644 --- a/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr +++ b/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr @@ -1,35 +1,35 @@ error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:11:19 + --> $DIR/const-param-elided-lifetime.rs:10:19 | LL | struct A; | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:16:15 + --> $DIR/const-param-elided-lifetime.rs:15:15 | LL | impl A { | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:19:21 + --> $DIR/const-param-elided-lifetime.rs:18:21 | LL | fn foo(&self) {} | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:24:15 + --> $DIR/const-param-elided-lifetime.rs:23:15 | LL | impl B for A {} | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:28:17 + --> $DIR/const-param-elided-lifetime.rs:27:17 | LL | fn bar() {} | ^ explicit lifetime name needed here error: `&'static u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:11:19 + --> $DIR/const-param-elided-lifetime.rs:10:19 | LL | struct A; | ^^^ @@ -38,7 +38,7 @@ LL | struct A; = help: more complex types are supported with `#[feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:16:15 + --> $DIR/const-param-elided-lifetime.rs:15:15 | LL | impl A { | ^^^ @@ -47,7 +47,7 @@ LL | impl A { = help: more complex types are supported with `#[feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:24:15 + --> $DIR/const-param-elided-lifetime.rs:23:15 | LL | impl B for A {} | ^^^ @@ -56,7 +56,7 @@ LL | impl B for A {} = help: more complex types are supported with `#[feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:28:17 + --> $DIR/const-param-elided-lifetime.rs:27:17 | LL | fn bar() {} | ^^^ @@ -65,7 +65,7 @@ LL | fn bar() {} = help: more complex types are supported with `#[feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:19:21 + --> $DIR/const-param-elided-lifetime.rs:18:21 | LL | fn foo(&self) {} | ^^^ diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.rs b/src/test/ui/const-generics/const-param-elided-lifetime.rs index 633e876f1d7d..89715a7b8e9d 100644 --- a/src/test/ui/const-generics/const-param-elided-lifetime.rs +++ b/src/test/ui/const-generics/const-param-elided-lifetime.rs @@ -6,7 +6,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct A; //~^ ERROR `&` without an explicit lifetime name cannot be used here diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.full.stderr b/src/test/ui/const-generics/const-param-from-outer-fn.full.stderr index 5a126f5c3c69..c2ec7359c9f7 100644 --- a/src/test/ui/const-generics/const-param-from-outer-fn.full.stderr +++ b/src/test/ui/const-generics/const-param-from-outer-fn.full.stderr @@ -1,5 +1,5 @@ error[E0401]: can't use generic parameters from outer function - --> $DIR/const-param-from-outer-fn.rs:9:9 + --> $DIR/const-param-from-outer-fn.rs:8:9 | LL | fn foo() { | - const parameter from outer function diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.min.stderr b/src/test/ui/const-generics/const-param-from-outer-fn.min.stderr index 5a126f5c3c69..c2ec7359c9f7 100644 --- a/src/test/ui/const-generics/const-param-from-outer-fn.min.stderr +++ b/src/test/ui/const-generics/const-param-from-outer-fn.min.stderr @@ -1,5 +1,5 @@ error[E0401]: can't use generic parameters from outer function - --> $DIR/const-param-from-outer-fn.rs:9:9 + --> $DIR/const-param-from-outer-fn.rs:8:9 | LL | fn foo() { | - const parameter from outer function diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.rs b/src/test/ui/const-generics/const-param-from-outer-fn.rs index e1376c6e108b..27b9ca9c291e 100644 --- a/src/test/ui/const-generics/const-param-from-outer-fn.rs +++ b/src/test/ui/const-generics/const-param-from-outer-fn.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn foo() { fn bar() -> u32 { diff --git a/src/test/ui/const-generics/const-param-hygiene.rs b/src/test/ui/const-generics/const-param-hygiene.rs index c8cefc36732b..9cafb05fbcb6 100644 --- a/src/test/ui/const-generics/const-param-hygiene.rs +++ b/src/test/ui/const-generics/const-param-hygiene.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] macro_rules! bar { ($($t:tt)*) => { impl $($t)* }; diff --git a/src/test/ui/const-generics/const-param-in-async.rs b/src/test/ui/const-generics/const-param-in-async.rs index e8601985287b..9dc9c80241d5 100644 --- a/src/test/ui/const-generics/const-param-in-async.rs +++ b/src/test/ui/const-generics/const-param-in-async.rs @@ -3,7 +3,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] async fn foo(arg: [u8; N]) -> usize { arg.len() } diff --git a/src/test/ui/const-generics/const-param-in-trait-ungated.rs b/src/test/ui/const-generics/const-param-in-trait-ungated.rs deleted file mode 100644 index 8a81bcc1a80c..000000000000 --- a/src/test/ui/const-generics/const-param-in-trait-ungated.rs +++ /dev/null @@ -1,3 +0,0 @@ -trait Trait {} //~ ERROR const generics are unstable - -fn main() {} diff --git a/src/test/ui/const-generics/const-param-in-trait-ungated.stderr b/src/test/ui/const-generics/const-param-in-trait-ungated.stderr deleted file mode 100644 index d53a4ac2d4c2..000000000000 --- a/src/test/ui/const-generics/const-param-in-trait-ungated.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: const generics are unstable - --> $DIR/const-param-in-trait-ungated.rs:1:19 - | -LL | trait Trait {} - | ^ - | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/const-param-in-trait.rs b/src/test/ui/const-generics/const-param-in-trait.rs index 9d31162c1c6f..79b3ae2037ed 100644 --- a/src/test/ui/const-generics/const-param-in-trait.rs +++ b/src/test/ui/const-generics/const-param-in-trait.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Trait {} diff --git a/src/test/ui/const-generics/const-param-shadowing.rs b/src/test/ui/const-generics/const-param-shadowing.rs index 8440e47968e9..ddd15dbc41bc 100644 --- a/src/test/ui/const-generics/const-param-shadowing.rs +++ b/src/test/ui/const-generics/const-param-shadowing.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - type N = u32; struct Foo; fn test() -> Foo { //~ ERROR type provided when diff --git a/src/test/ui/const-generics/const-param-shadowing.stderr b/src/test/ui/const-generics/const-param-shadowing.stderr index df1702780267..7447ca3ff363 100644 --- a/src/test/ui/const-generics/const-param-shadowing.stderr +++ b/src/test/ui/const-generics/const-param-shadowing.stderr @@ -1,5 +1,5 @@ error[E0747]: type provided when a constant was expected - --> $DIR/const-param-shadowing.rs:5:34 + --> $DIR/const-param-shadowing.rs:3:34 | LL | fn test() -> Foo { | ^ diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.full.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.full.stderr index f7ad579dbca0..f639e276f466 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.full.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.full.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-const-param.rs:12:52 + --> $DIR/const-param-type-depends-on-const-param.rs:11:52 | LL | pub struct Dependent([(); N]); | ^ the type must not depend on the parameter `N` error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-const-param.rs:16:40 + --> $DIR/const-param-type-depends-on-const-param.rs:15:40 | LL | pub struct SelfDependent; | ^ the type must not depend on the parameter `N` diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr index 6b7a218ada5d..d63bc2363209 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr @@ -1,17 +1,17 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-const-param.rs:12:52 + --> $DIR/const-param-type-depends-on-const-param.rs:11:52 | LL | pub struct Dependent([(); N]); | ^ the type must not depend on the parameter `N` error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-const-param.rs:16:40 + --> $DIR/const-param-type-depends-on-const-param.rs:15:40 | LL | pub struct SelfDependent; | ^ the type must not depend on the parameter `N` error: `[u8; _]` is forbidden as the type of a const generic parameter - --> $DIR/const-param-type-depends-on-const-param.rs:12:47 + --> $DIR/const-param-type-depends-on-const-param.rs:11:47 | LL | pub struct Dependent([(); N]); | ^^^^^^^ @@ -20,7 +20,7 @@ LL | pub struct Dependent([(); N]); = help: more complex types are supported with `#[feature(const_generics)]` error: `[u8; _]` is forbidden as the type of a const generic parameter - --> $DIR/const-param-type-depends-on-const-param.rs:16:35 + --> $DIR/const-param-type-depends-on-const-param.rs:15:35 | LL | pub struct SelfDependent; | ^^^^^^^ diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs index 29371eeb21d1..62b146e016a1 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] // Currently, const parameters cannot depend on other generic parameters, // as our current implementation can't really support this. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs index ea75a3d04035..781f50e61734 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; -struct B(PhantomData<[T; N]>); //~ ERROR const generics are unstable +struct B(PhantomData<[T; N]>); //~^ ERROR the type of const parameters must not depend on other generic parameters fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr index 5d379ff083ca..8e14defd65d9 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr @@ -4,16 +4,6 @@ error[E0770]: the type of const parameters must not depend on other generic para LL | struct B(PhantomData<[T; N]>); | ^ the type must not depend on the parameter `T` -error[E0658]: const generics are unstable - --> $DIR/const-param-type-depends-on-type-param-ungated.rs:6:19 - | -LL | struct B(PhantomData<[T; N]>); - | ^ - | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable +error: aborting due to previous error -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0658, E0770. -For more information about an error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.full.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.full.stderr index f860788e778a..a83ee627187b 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.full.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.full.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-type-param.rs:12:34 + --> $DIR/const-param-type-depends-on-type-param.rs:11:34 | LL | pub struct Dependent([(); X]); | ^ the type must not depend on the parameter `T` error[E0392]: parameter `T` is never used - --> $DIR/const-param-type-depends-on-type-param.rs:12:22 + --> $DIR/const-param-type-depends-on-type-param.rs:11:22 | LL | pub struct Dependent([(); X]); | ^ unused parameter diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.min.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.min.stderr index f860788e778a..a83ee627187b 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.min.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.min.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-type-param.rs:12:34 + --> $DIR/const-param-type-depends-on-type-param.rs:11:34 | LL | pub struct Dependent([(); X]); | ^ the type must not depend on the parameter `T` error[E0392]: parameter `T` is never used - --> $DIR/const-param-type-depends-on-type-param.rs:12:22 + --> $DIR/const-param-type-depends-on-type-param.rs:11:22 | LL | pub struct Dependent([(); X]); | ^ unused parameter diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs index 93ae11175123..910a96435022 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] // Currently, const parameters cannot depend on other generic parameters, // as our current implementation can't really support this. diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.full.stderr b/src/test/ui/const-generics/const-parameter-uppercase-lint.full.stderr index 0f4f007f9d24..923964a4070a 100644 --- a/src/test/ui/const-generics/const-parameter-uppercase-lint.full.stderr +++ b/src/test/ui/const-generics/const-parameter-uppercase-lint.full.stderr @@ -1,11 +1,11 @@ error: const parameter `x` should have an upper case name - --> $DIR/const-parameter-uppercase-lint.rs:9:15 + --> $DIR/const-parameter-uppercase-lint.rs:8:15 | LL | fn noop() { | ^ help: convert the identifier to upper case (notice the capitalization): `X` | note: the lint level is defined here - --> $DIR/const-parameter-uppercase-lint.rs:7:9 + --> $DIR/const-parameter-uppercase-lint.rs:6:9 | LL | #![deny(non_upper_case_globals)] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.min.stderr b/src/test/ui/const-generics/const-parameter-uppercase-lint.min.stderr index 0f4f007f9d24..923964a4070a 100644 --- a/src/test/ui/const-generics/const-parameter-uppercase-lint.min.stderr +++ b/src/test/ui/const-generics/const-parameter-uppercase-lint.min.stderr @@ -1,11 +1,11 @@ error: const parameter `x` should have an upper case name - --> $DIR/const-parameter-uppercase-lint.rs:9:15 + --> $DIR/const-parameter-uppercase-lint.rs:8:15 | LL | fn noop() { | ^ help: convert the identifier to upper case (notice the capitalization): `X` | note: the lint level is defined here - --> $DIR/const-parameter-uppercase-lint.rs:7:9 + --> $DIR/const-parameter-uppercase-lint.rs:6:9 | LL | #![deny(non_upper_case_globals)] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.rs b/src/test/ui/const-generics/const-parameter-uppercase-lint.rs index b9bd6666af39..5d97907c2e7f 100644 --- a/src/test/ui/const-generics/const-parameter-uppercase-lint.rs +++ b/src/test/ui/const-generics/const-parameter-uppercase-lint.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![deny(non_upper_case_globals)] diff --git a/src/test/ui/const-generics/const-types.rs b/src/test/ui/const-generics/const-types.rs index cd34cfc0478c..fb150f892edc 100644 --- a/src/test/ui/const-generics/const-types.rs +++ b/src/test/ui/const-generics/const-types.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![allow(dead_code, unused_variables)] diff --git a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.full.stderr b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.full.stderr index b2816367ea10..d6a54ead1316 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.full.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/feature-gate-const_evaluatable_checked.rs:9:30 + --> $DIR/feature-gate-const_evaluatable_checked.rs:8:30 | LL | fn test() -> Arr where Arr: Default { | ^^^^^^ diff --git a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.min.stderr b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.min.stderr index 573bc66b7c7e..7de4bfcdd058 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.min.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/feature-gate-const_evaluatable_checked.rs:6:33 + --> $DIR/feature-gate-const_evaluatable_checked.rs:5:33 | LL | type Arr = [u8; N - 1]; | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs index 9746adab29bf..f49ca0251aa9 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs +++ b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] type Arr = [u8; N - 1]; //[min]~^ ERROR generic parameters may not be used in const operations diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple.min.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple.min.stderr index d476a7eb6455..9f3d94bbd8ab 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/simple.min.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/simple.rs:8:53 + --> $DIR/simple.rs:7:53 | LL | fn test() -> [u8; N - 1] where [u8; N - 1]: Default { | ^ cannot perform const operation using `N` @@ -8,7 +8,7 @@ LL | fn test() -> [u8; N - 1] where [u8; N - 1]: Default { = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/simple.rs:8:35 + --> $DIR/simple.rs:7:35 | LL | fn test() -> [u8; N - 1] where [u8; N - 1]: Default { | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple.rs b/src/test/ui/const-generics/const_evaluatable_checked/simple.rs index dcf0071cb29b..94ad71b6c1ac 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/simple.rs +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple.rs @@ -1,7 +1,6 @@ // [full] run-pass // revisions: full min #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] #![feature(const_evaluatable_checked)] #![allow(incomplete_features)] diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr index f95d6d2d5709..c8549f101daf 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/simple_fail.rs:7:33 + --> $DIR/simple_fail.rs:6:33 | LL | type Arr = [u8; N - 1]; | ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.min.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.min.stderr index bd81e0bc5a8c..df54b4cbca5b 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.min.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/simple_fail.rs:7:33 + --> $DIR/simple_fail.rs:6:33 | LL | type Arr = [u8; N - 1]; | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs index 5e2c080927f8..3cbc077f4f14 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs @@ -1,6 +1,5 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] #![feature(const_evaluatable_checked)] #![allow(incomplete_features)] diff --git a/src/test/ui/const-generics/core-types.rs b/src/test/ui/const-generics/core-types.rs index c4351e059dec..b6fa478f48df 100644 --- a/src/test/ui/const-generics/core-types.rs +++ b/src/test/ui/const-generics/core-types.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct A; struct B; diff --git a/src/test/ui/const-generics/cross_crate_complex.rs b/src/test/ui/const-generics/cross_crate_complex.rs index 30749b8bc6d7..1d495c9562de 100644 --- a/src/test/ui/const-generics/cross_crate_complex.rs +++ b/src/test/ui/const-generics/cross_crate_complex.rs @@ -5,7 +5,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] extern crate crayte; use crayte::*; diff --git a/src/test/ui/const-generics/defaults/complex-unord-param.min.stderr b/src/test/ui/const-generics/defaults/complex-unord-param.min.stderr index 0574ddfb2557..8e8d26a00043 100644 --- a/src/test/ui/const-generics/defaults/complex-unord-param.min.stderr +++ b/src/test/ui/const-generics/defaults/complex-unord-param.min.stderr @@ -1,8 +1,8 @@ error: type parameters must be declared prior to const parameters - --> $DIR/complex-unord-param.rs:9:41 + --> $DIR/complex-unord-param.rs:8:41 | LL | struct NestedArrays<'a, const N: usize, A: 'a, const M: usize, T:'a =u32> { - | ---------------------^----------------------^--------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, A: 'a, T: 'a, const N: usize, const M: usize>` + | ---------------------^----------------------^--------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, A: 'a, T: 'a = u32, const N: usize, const M: usize>` error: aborting due to previous error diff --git a/src/test/ui/const-generics/defaults/complex-unord-param.rs b/src/test/ui/const-generics/defaults/complex-unord-param.rs index e83a96388c19..82b3627d22ff 100644 --- a/src/test/ui/const-generics/defaults/complex-unord-param.rs +++ b/src/test/ui/const-generics/defaults/complex-unord-param.rs @@ -3,7 +3,6 @@ // Checks a complicated usage of unordered params #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![allow(dead_code)] struct NestedArrays<'a, const N: usize, A: 'a, const M: usize, T:'a =u32> { diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr b/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr index 9cc3e9c0da66..c4a666a829d8 100644 --- a/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr @@ -1,14 +1,14 @@ error: lifetime parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:7:28 + --> $DIR/intermixed-lifetime.rs:6:28 | LL | struct Foo(&'a (), T); - | -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>` + | -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T = u32>` error: lifetime parameters must be declared prior to type parameters - --> $DIR/intermixed-lifetime.rs:11:37 + --> $DIR/intermixed-lifetime.rs:10:37 | LL | struct Bar(&'a (), T); - | --------------------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>` + | --------------------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T = u32>` error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr b/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr index 4d80fdb5bcbc..69a490978d1d 100644 --- a/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr @@ -1,26 +1,26 @@ error: lifetime parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:7:28 + --> $DIR/intermixed-lifetime.rs:6:28 | LL | struct Foo(&'a (), T); - | -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + | -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` error: type parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:7:32 + --> $DIR/intermixed-lifetime.rs:6:32 | LL | struct Foo(&'a (), T); - | ---------------------^------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + | ---------------------^------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` error: lifetime parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:11:37 + --> $DIR/intermixed-lifetime.rs:10:37 | LL | struct Bar(&'a (), T); - | --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + | --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` error: type parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:11:28 + --> $DIR/intermixed-lifetime.rs:10:28 | LL | struct Bar(&'a (), T); - | -----------------^----------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + | -----------------^----------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.rs b/src/test/ui/const-generics/defaults/intermixed-lifetime.rs index cc0d1c6c0c97..9e83bf92a59b 100644 --- a/src/test/ui/const-generics/defaults/intermixed-lifetime.rs +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.rs @@ -2,7 +2,6 @@ // Checks that lifetimes cannot be interspersed between consts and types. #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo(&'a (), T); //~^ Error lifetime parameters must be declared prior to const parameters diff --git a/src/test/ui/const-generics/defaults/needs-feature.min.stderr b/src/test/ui/const-generics/defaults/needs-feature.min.stderr index 7058327fdce1..a4006203e4a2 100644 --- a/src/test/ui/const-generics/defaults/needs-feature.min.stderr +++ b/src/test/ui/const-generics/defaults/needs-feature.min.stderr @@ -1,8 +1,8 @@ error: type parameters must be declared prior to const parameters - --> $DIR/needs-feature.rs:10:26 + --> $DIR/needs-feature.rs:9:26 | LL | struct A(T); - | -----------------^----- help: reorder the parameters: lifetimes, then types, then consts: `` + | -----------------^----- help: reorder the parameters: lifetimes, then types, then consts: `` error: aborting due to previous error diff --git a/src/test/ui/const-generics/defaults/needs-feature.none.stderr b/src/test/ui/const-generics/defaults/needs-feature.none.stderr index 3b6f63a8efec..a4006203e4a2 100644 --- a/src/test/ui/const-generics/defaults/needs-feature.none.stderr +++ b/src/test/ui/const-generics/defaults/needs-feature.none.stderr @@ -1,18 +1,8 @@ error: type parameters must be declared prior to const parameters - --> $DIR/needs-feature.rs:10:26 + --> $DIR/needs-feature.rs:9:26 | LL | struct A(T); - | -----------------^----- help: reorder the parameters: lifetimes, then types: `` + | -----------------^----- help: reorder the parameters: lifetimes, then types, then consts: `` -error[E0658]: const generics are unstable - --> $DIR/needs-feature.rs:10:16 - | -LL | struct A(T); - | ^ - | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable +error: aborting due to previous error -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/defaults/needs-feature.rs b/src/test/ui/const-generics/defaults/needs-feature.rs index ec02dbf407d6..7eb7764a6444 100644 --- a/src/test/ui/const-generics/defaults/needs-feature.rs +++ b/src/test/ui/const-generics/defaults/needs-feature.rs @@ -1,16 +1,13 @@ //[full] run-pass // Verifies that having generic parameters after constants is not permitted without the // `const_generics` feature. -// revisions: none min full +// revisions: min full #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct A(T); -//[none]~^ ERROR type parameters must be declared prior -//[none]~| ERROR const generics are unstable -//[min]~^^^ ERROR type parameters must be declared prior +//[min]~^ ERROR type parameters must be declared prior fn main() { let _: A<3> = A(0); diff --git a/src/test/ui/const-generics/defaults/simple-defaults.min.stderr b/src/test/ui/const-generics/defaults/simple-defaults.min.stderr index 59cc6f28af85..0746c64ac8cf 100644 --- a/src/test/ui/const-generics/defaults/simple-defaults.min.stderr +++ b/src/test/ui/const-generics/defaults/simple-defaults.min.stderr @@ -1,8 +1,8 @@ error: type parameters must be declared prior to const parameters - --> $DIR/simple-defaults.rs:9:40 + --> $DIR/simple-defaults.rs:8:40 | LL | struct FixedOutput<'a, const N: usize, T=u32> { - | ---------------------^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + | ---------------------^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` error: aborting due to previous error diff --git a/src/test/ui/const-generics/defaults/simple-defaults.rs b/src/test/ui/const-generics/defaults/simple-defaults.rs index 78abe3519985..1f1b6c2260db 100644 --- a/src/test/ui/const-generics/defaults/simple-defaults.rs +++ b/src/test/ui/const-generics/defaults/simple-defaults.rs @@ -3,7 +3,6 @@ // Checks some basic test cases for defaults. #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![allow(dead_code)] struct FixedOutput<'a, const N: usize, T=u32> { diff --git a/src/test/ui/const-generics/defaults/wrong-order.full.stderr b/src/test/ui/const-generics/defaults/wrong-order.full.stderr index 99f46309bf6f..96deb4a8b5ab 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.full.stderr +++ b/src/test/ui/const-generics/defaults/wrong-order.full.stderr @@ -1,5 +1,5 @@ error: type parameters with a default must be trailing - --> $DIR/wrong-order.rs:5:10 + --> $DIR/wrong-order.rs:4:10 | LL | struct A { | ^ @@ -14,7 +14,6 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/defaults/wrong-order.min.stderr b/src/test/ui/const-generics/defaults/wrong-order.min.stderr index 29a46367004d..b19da76f415d 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.min.stderr +++ b/src/test/ui/const-generics/defaults/wrong-order.min.stderr @@ -1,5 +1,5 @@ error: type parameters with a default must be trailing - --> $DIR/wrong-order.rs:5:10 + --> $DIR/wrong-order.rs:4:10 | LL | struct A { | ^ diff --git a/src/test/ui/const-generics/defaults/wrong-order.rs b/src/test/ui/const-generics/defaults/wrong-order.rs index cb36d456f388..4f1c05011b0b 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.rs +++ b/src/test/ui/const-generics/defaults/wrong-order.rs @@ -1,6 +1,5 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] struct A { //~^ ERROR type parameters with a default must be trailing diff --git a/src/test/ui/const-generics/derive-debug-array-wrapper.rs b/src/test/ui/const-generics/derive-debug-array-wrapper.rs index 13fd87f1e3e5..ce1481d97e9b 100644 --- a/src/test/ui/const-generics/derive-debug-array-wrapper.rs +++ b/src/test/ui/const-generics/derive-debug-array-wrapper.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #[derive(Debug)] struct X { diff --git a/src/test/ui/const-generics/different_byref.full.stderr b/src/test/ui/const-generics/different_byref.full.stderr index 4463ed7fcdd2..d6b32323e2d0 100644 --- a/src/test/ui/const-generics/different_byref.full.stderr +++ b/src/test/ui/const-generics/different_byref.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/different_byref.rs:13:9 + --> $DIR/different_byref.rs:12:9 | LL | x = Const::<{ [4] }> {}; | ^^^^^^^^^^^^^^^^^^^ expected `3_usize`, found `4_usize` diff --git a/src/test/ui/const-generics/different_byref.min.stderr b/src/test/ui/const-generics/different_byref.min.stderr index e5b393ffe99e..05720d15404f 100644 --- a/src/test/ui/const-generics/different_byref.min.stderr +++ b/src/test/ui/const-generics/different_byref.min.stderr @@ -1,5 +1,5 @@ error: `[usize; 1]` is forbidden as the type of a const generic parameter - --> $DIR/different_byref.rs:8:23 + --> $DIR/different_byref.rs:7:23 | LL | struct Const {} | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/different_byref.rs b/src/test/ui/const-generics/different_byref.rs index cd3960eeb8e0..7977560ecbcf 100644 --- a/src/test/ui/const-generics/different_byref.rs +++ b/src/test/ui/const-generics/different_byref.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Const {} //[min]~^ ERROR `[usize; 1]` is forbidden diff --git a/src/test/ui/const-generics/different_byref_simple.full.stderr b/src/test/ui/const-generics/different_byref_simple.full.stderr index b6729c852abc..027e282c398d 100644 --- a/src/test/ui/const-generics/different_byref_simple.full.stderr +++ b/src/test/ui/const-generics/different_byref_simple.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/different_byref_simple.rs:12:9 + --> $DIR/different_byref_simple.rs:11:9 | LL | u = ConstUsize::<4> {}; | ^^^^^^^^^^^^^^^^^^ expected `3_usize`, found `4_usize` diff --git a/src/test/ui/const-generics/different_byref_simple.min.stderr b/src/test/ui/const-generics/different_byref_simple.min.stderr index b6729c852abc..027e282c398d 100644 --- a/src/test/ui/const-generics/different_byref_simple.min.stderr +++ b/src/test/ui/const-generics/different_byref_simple.min.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/different_byref_simple.rs:12:9 + --> $DIR/different_byref_simple.rs:11:9 | LL | u = ConstUsize::<4> {}; | ^^^^^^^^^^^^^^^^^^ expected `3_usize`, found `4_usize` diff --git a/src/test/ui/const-generics/different_byref_simple.rs b/src/test/ui/const-generics/different_byref_simple.rs index 93289f933317..b48189fc2cba 100644 --- a/src/test/ui/const-generics/different_byref_simple.rs +++ b/src/test/ui/const-generics/different_byref_simple.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct ConstUsize {} diff --git a/src/test/ui/const-generics/dyn-supertraits.rs b/src/test/ui/const-generics/dyn-supertraits.rs index 0295255d8099..73ed23521c30 100644 --- a/src/test/ui/const-generics/dyn-supertraits.rs +++ b/src/test/ui/const-generics/dyn-supertraits.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Foo { fn myfun(&self) -> usize; diff --git a/src/test/ui/const-generics/exhaustive-value.full.stderr b/src/test/ui/const-generics/exhaustive-value.full.stderr index fdea1fb0c3ea..e0e1423ba010 100644 --- a/src/test/ui/const-generics/exhaustive-value.full.stderr +++ b/src/test/ui/const-generics/exhaustive-value.full.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/exhaustive-value.rs:267:5 + --> $DIR/exhaustive-value.rs:266:5 | LL | fn test() {} | --------- required by `Foo::test` diff --git a/src/test/ui/const-generics/exhaustive-value.min.stderr b/src/test/ui/const-generics/exhaustive-value.min.stderr index fdea1fb0c3ea..e0e1423ba010 100644 --- a/src/test/ui/const-generics/exhaustive-value.min.stderr +++ b/src/test/ui/const-generics/exhaustive-value.min.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/exhaustive-value.rs:267:5 + --> $DIR/exhaustive-value.rs:266:5 | LL | fn test() {} | --------- required by `Foo::test` diff --git a/src/test/ui/const-generics/exhaustive-value.rs b/src/test/ui/const-generics/exhaustive-value.rs index fce036b0da62..921f9a467078 100644 --- a/src/test/ui/const-generics/exhaustive-value.rs +++ b/src/test/ui/const-generics/exhaustive-value.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Foo { fn test() {} diff --git a/src/test/ui/const-generics/fn-const-param-call.full.stderr b/src/test/ui/const-generics/fn-const-param-call.full.stderr index f1bd8def9ff1..d984449e6ca6 100644 --- a/src/test/ui/const-generics/fn-const-param-call.full.stderr +++ b/src/test/ui/const-generics/fn-const-param-call.full.stderr @@ -1,11 +1,11 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-call.rs:12:25 + --> $DIR/fn-const-param-call.rs:11:25 | LL | struct Wrapper u32>; | ^^^^^^^^^^^ error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-call.rs:14:15 + --> $DIR/fn-const-param-call.rs:13:15 | LL | impl u32> Wrapper { | ^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/fn-const-param-call.min.stderr b/src/test/ui/const-generics/fn-const-param-call.min.stderr index f1bd8def9ff1..d984449e6ca6 100644 --- a/src/test/ui/const-generics/fn-const-param-call.min.stderr +++ b/src/test/ui/const-generics/fn-const-param-call.min.stderr @@ -1,11 +1,11 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-call.rs:12:25 + --> $DIR/fn-const-param-call.rs:11:25 | LL | struct Wrapper u32>; | ^^^^^^^^^^^ error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-call.rs:14:15 + --> $DIR/fn-const-param-call.rs:13:15 | LL | impl u32> Wrapper { | ^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/fn-const-param-call.rs b/src/test/ui/const-generics/fn-const-param-call.rs index bba6c1f7a16f..70a104e22227 100644 --- a/src/test/ui/const-generics/fn-const-param-call.rs +++ b/src/test/ui/const-generics/fn-const-param-call.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn function() -> u32 { 17 diff --git a/src/test/ui/const-generics/fn-const-param-infer.full.stderr b/src/test/ui/const-generics/fn-const-param-infer.full.stderr index 4bdc9b89af60..f0767a10994a 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.full.stderr +++ b/src/test/ui/const-generics/fn-const-param-infer.full.stderr @@ -1,5 +1,5 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-infer.rs:7:25 + --> $DIR/fn-const-param-infer.rs:6:25 | LL | struct Checked bool>; | ^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/fn-const-param-infer.min.stderr b/src/test/ui/const-generics/fn-const-param-infer.min.stderr index 4bdc9b89af60..f0767a10994a 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.min.stderr +++ b/src/test/ui/const-generics/fn-const-param-infer.min.stderr @@ -1,5 +1,5 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-infer.rs:7:25 + --> $DIR/fn-const-param-infer.rs:6:25 | LL | struct Checked bool>; | ^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/fn-const-param-infer.rs b/src/test/ui/const-generics/fn-const-param-infer.rs index 3ed75e7b00dd..d090479d4c30 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.rs +++ b/src/test/ui/const-generics/fn-const-param-infer.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Checked bool>; //~^ ERROR: using function pointers as const generic parameters diff --git a/src/test/ui/const-generics/fn-taking-const-generic-array.rs b/src/test/ui/const-generics/fn-taking-const-generic-array.rs index 950684aaa8dc..58c1b95893e7 100644 --- a/src/test/ui/const-generics/fn-taking-const-generic-array.rs +++ b/src/test/ui/const-generics/fn-taking-const-generic-array.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::fmt::Display; diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.full.stderr b/src/test/ui/const-generics/forbid-non-structural_match-types.full.stderr index adcaa7599632..5c0f17537fa5 100644 --- a/src/test/ui/const-generics/forbid-non-structural_match-types.full.stderr +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.full.stderr @@ -1,5 +1,5 @@ error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/forbid-non-structural_match-types.rs:15:19 + --> $DIR/forbid-non-structural_match-types.rs:14:19 | LL | struct D; | ^ `C` doesn't derive both `PartialEq` and `Eq` diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr b/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr index 014200178b9c..3912cf577512 100644 --- a/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr @@ -1,5 +1,5 @@ error: `A` is forbidden as the type of a const generic parameter - --> $DIR/forbid-non-structural_match-types.rs:10:19 + --> $DIR/forbid-non-structural_match-types.rs:9:19 | LL | struct B; // ok | ^ @@ -8,7 +8,7 @@ LL | struct B; // ok = help: more complex types are supported with `#[feature(const_generics)]` error: `C` is forbidden as the type of a const generic parameter - --> $DIR/forbid-non-structural_match-types.rs:15:19 + --> $DIR/forbid-non-structural_match-types.rs:14:19 | LL | struct D; | ^ @@ -17,7 +17,7 @@ LL | struct D; = help: more complex types are supported with `#[feature(const_generics)]` error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/forbid-non-structural_match-types.rs:15:19 + --> $DIR/forbid-non-structural_match-types.rs:14:19 | LL | struct D; | ^ `C` doesn't derive both `PartialEq` and `Eq` diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.rs b/src/test/ui/const-generics/forbid-non-structural_match-types.rs index e7356d485dbf..0fdb3ed4a5a7 100644 --- a/src/test/ui/const-generics/forbid-non-structural_match-types.rs +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #[derive(PartialEq, Eq)] struct A; diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.full.stderr b/src/test/ui/const-generics/foreign-item-const-parameter.full.stderr index 0ac51e8c9e61..b827e482977b 100644 --- a/src/test/ui/const-generics/foreign-item-const-parameter.full.stderr +++ b/src/test/ui/const-generics/foreign-item-const-parameter.full.stderr @@ -1,5 +1,5 @@ error[E0044]: foreign items may not have const parameters - --> $DIR/foreign-item-const-parameter.rs:8:5 + --> $DIR/foreign-item-const-parameter.rs:7:5 | LL | fn foo(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't have const parameters @@ -7,7 +7,7 @@ LL | fn foo(); = help: replace the const parameters with concrete consts error[E0044]: foreign items may not have type or const parameters - --> $DIR/foreign-item-const-parameter.rs:10:5 + --> $DIR/foreign-item-const-parameter.rs:9:5 | LL | fn bar(_: T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't have type or const parameters diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.min.stderr b/src/test/ui/const-generics/foreign-item-const-parameter.min.stderr index 0ac51e8c9e61..b827e482977b 100644 --- a/src/test/ui/const-generics/foreign-item-const-parameter.min.stderr +++ b/src/test/ui/const-generics/foreign-item-const-parameter.min.stderr @@ -1,5 +1,5 @@ error[E0044]: foreign items may not have const parameters - --> $DIR/foreign-item-const-parameter.rs:8:5 + --> $DIR/foreign-item-const-parameter.rs:7:5 | LL | fn foo(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't have const parameters @@ -7,7 +7,7 @@ LL | fn foo(); = help: replace the const parameters with concrete consts error[E0044]: foreign items may not have type or const parameters - --> $DIR/foreign-item-const-parameter.rs:10:5 + --> $DIR/foreign-item-const-parameter.rs:9:5 | LL | fn bar(_: T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't have type or const parameters diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.rs b/src/test/ui/const-generics/foreign-item-const-parameter.rs index 44b6d0332c3f..83caa89f0330 100644 --- a/src/test/ui/const-generics/foreign-item-const-parameter.rs +++ b/src/test/ui/const-generics/foreign-item-const-parameter.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] extern "C" { fn foo(); //~ ERROR foreign items may not have const parameters diff --git a/src/test/ui/const-generics/generic-function-call-in-array-length.full.stderr b/src/test/ui/const-generics/generic-function-call-in-array-length.full.stderr index 43b42d82d0c4..2d19a58a1457 100644 --- a/src/test/ui/const-generics/generic-function-call-in-array-length.full.stderr +++ b/src/test/ui/const-generics/generic-function-call-in-array-length.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/generic-function-call-in-array-length.rs:9:29 + --> $DIR/generic-function-call-in-array-length.rs:8:29 | LL | fn bar() -> [u32; foo(N)] { | ^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/generic-function-call-in-array-length.min.stderr b/src/test/ui/const-generics/generic-function-call-in-array-length.min.stderr index 526f98fe8cd4..d7a3f04a8da8 100644 --- a/src/test/ui/const-generics/generic-function-call-in-array-length.min.stderr +++ b/src/test/ui/const-generics/generic-function-call-in-array-length.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/generic-function-call-in-array-length.rs:9:39 + --> $DIR/generic-function-call-in-array-length.rs:8:39 | LL | fn bar() -> [u32; foo(N)] { | ^ cannot perform const operation using `N` @@ -8,7 +8,7 @@ LL | fn bar() -> [u32; foo(N)] { = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/generic-function-call-in-array-length.rs:12:13 + --> $DIR/generic-function-call-in-array-length.rs:11:13 | LL | [0; foo(N)] | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/generic-function-call-in-array-length.rs b/src/test/ui/const-generics/generic-function-call-in-array-length.rs index c838070dc95a..a6d2bbd17eaa 100644 --- a/src/test/ui/const-generics/generic-function-call-in-array-length.rs +++ b/src/test/ui/const-generics/generic-function-call-in-array-length.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] const fn foo(n: usize) -> usize { n * 2 } diff --git a/src/test/ui/const-generics/generic-param-mismatch.full.stderr b/src/test/ui/const-generics/generic-param-mismatch.full.stderr index 6befa9d1f699..aff8780fb0d1 100644 --- a/src/test/ui/const-generics/generic-param-mismatch.full.stderr +++ b/src/test/ui/const-generics/generic-param-mismatch.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/generic-param-mismatch.rs:7:5 + --> $DIR/generic-param-mismatch.rs:6:5 | LL | fn test() -> [u8; M] { | ------- expected `[u8; M]` because of return type diff --git a/src/test/ui/const-generics/generic-param-mismatch.min.stderr b/src/test/ui/const-generics/generic-param-mismatch.min.stderr index 6befa9d1f699..aff8780fb0d1 100644 --- a/src/test/ui/const-generics/generic-param-mismatch.min.stderr +++ b/src/test/ui/const-generics/generic-param-mismatch.min.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/generic-param-mismatch.rs:7:5 + --> $DIR/generic-param-mismatch.rs:6:5 | LL | fn test() -> [u8; M] { | ------- expected `[u8; M]` because of return type diff --git a/src/test/ui/const-generics/generic-param-mismatch.rs b/src/test/ui/const-generics/generic-param-mismatch.rs index e409094eb734..22fffe47dcc2 100644 --- a/src/test/ui/const-generics/generic-param-mismatch.rs +++ b/src/test/ui/const-generics/generic-param-mismatch.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] fn test() -> [u8; M] { [0; N] //~ ERROR mismatched types diff --git a/src/test/ui/const-generics/generic-sum-in-array-length.full.stderr b/src/test/ui/const-generics/generic-sum-in-array-length.full.stderr index d311e1c0bae0..c13882e7fe11 100644 --- a/src/test/ui/const-generics/generic-sum-in-array-length.full.stderr +++ b/src/test/ui/const-generics/generic-sum-in-array-length.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/generic-sum-in-array-length.rs:7:45 + --> $DIR/generic-sum-in-array-length.rs:6:45 | LL | fn foo(bar: [usize; A + B]) {} | ^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/generic-sum-in-array-length.min.stderr b/src/test/ui/const-generics/generic-sum-in-array-length.min.stderr index e531b612b560..cff5a62193c3 100644 --- a/src/test/ui/const-generics/generic-sum-in-array-length.min.stderr +++ b/src/test/ui/const-generics/generic-sum-in-array-length.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/generic-sum-in-array-length.rs:7:53 + --> $DIR/generic-sum-in-array-length.rs:6:53 | LL | fn foo(bar: [usize; A + B]) {} | ^ cannot perform const operation using `A` @@ -8,7 +8,7 @@ LL | fn foo(bar: [usize; A + B]) {} = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/generic-sum-in-array-length.rs:7:57 + --> $DIR/generic-sum-in-array-length.rs:6:57 | LL | fn foo(bar: [usize; A + B]) {} | ^ cannot perform const operation using `B` diff --git a/src/test/ui/const-generics/generic-sum-in-array-length.rs b/src/test/ui/const-generics/generic-sum-in-array-length.rs index 84ddfe055dc3..7ee0394ba14c 100644 --- a/src/test/ui/const-generics/generic-sum-in-array-length.rs +++ b/src/test/ui/const-generics/generic-sum-in-array-length.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] fn foo(bar: [usize; A + B]) {} //[min]~^ ERROR generic parameters may not be used in const operations diff --git a/src/test/ui/const-generics/impl-const-generic-struct.rs b/src/test/ui/const-generics/impl-const-generic-struct.rs index 05cabc46baa3..1aa22698b640 100644 --- a/src/test/ui/const-generics/impl-const-generic-struct.rs +++ b/src/test/ui/const-generics/impl-const-generic-struct.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct S; diff --git a/src/test/ui/const-generics/impl-trait-with-const-arguments.full.stderr b/src/test/ui/const-generics/impl-trait-with-const-arguments.full.stderr index a587cb618731..26b965901a4c 100644 --- a/src/test/ui/const-generics/impl-trait-with-const-arguments.full.stderr +++ b/src/test/ui/const-generics/impl-trait-with-const-arguments.full.stderr @@ -1,5 +1,5 @@ error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position - --> $DIR/impl-trait-with-const-arguments.rs:24:20 + --> $DIR/impl-trait-with-const-arguments.rs:23:20 | LL | assert_eq!(f::<4usize>(Usizable), 20usize); | ^^^^^^ explicit generic argument not allowed diff --git a/src/test/ui/const-generics/impl-trait-with-const-arguments.min.stderr b/src/test/ui/const-generics/impl-trait-with-const-arguments.min.stderr index a587cb618731..26b965901a4c 100644 --- a/src/test/ui/const-generics/impl-trait-with-const-arguments.min.stderr +++ b/src/test/ui/const-generics/impl-trait-with-const-arguments.min.stderr @@ -1,5 +1,5 @@ error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position - --> $DIR/impl-trait-with-const-arguments.rs:24:20 + --> $DIR/impl-trait-with-const-arguments.rs:23:20 | LL | assert_eq!(f::<4usize>(Usizable), 20usize); | ^^^^^^ explicit generic argument not allowed diff --git a/src/test/ui/const-generics/impl-trait-with-const-arguments.rs b/src/test/ui/const-generics/impl-trait-with-const-arguments.rs index a4c75792ee35..2e6e49b9c0aa 100644 --- a/src/test/ui/const-generics/impl-trait-with-const-arguments.rs +++ b/src/test/ui/const-generics/impl-trait-with-const-arguments.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] trait Usizer { fn m(self) -> usize; diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.full.stderr b/src/test/ui/const-generics/incorrect-number-of-const-args.full.stderr index 6b902e2d6585..9c8359b08a5c 100644 --- a/src/test/ui/const-generics/incorrect-number-of-const-args.full.stderr +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.full.stderr @@ -1,11 +1,11 @@ error[E0107]: wrong number of const arguments: expected 2, found 1 - --> $DIR/incorrect-number-of-const-args.rs:12:5 + --> $DIR/incorrect-number-of-const-args.rs:11:5 | LL | foo::<0>(); | ^^^^^^^^ expected 2 const arguments error[E0107]: wrong number of const arguments: expected 2, found 3 - --> $DIR/incorrect-number-of-const-args.rs:13:17 + --> $DIR/incorrect-number-of-const-args.rs:12:17 | LL | foo::<0, 0, 0>(); | ^ unexpected const argument diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.min.stderr b/src/test/ui/const-generics/incorrect-number-of-const-args.min.stderr index 6b902e2d6585..9c8359b08a5c 100644 --- a/src/test/ui/const-generics/incorrect-number-of-const-args.min.stderr +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.min.stderr @@ -1,11 +1,11 @@ error[E0107]: wrong number of const arguments: expected 2, found 1 - --> $DIR/incorrect-number-of-const-args.rs:12:5 + --> $DIR/incorrect-number-of-const-args.rs:11:5 | LL | foo::<0>(); | ^^^^^^^^ expected 2 const arguments error[E0107]: wrong number of const arguments: expected 2, found 3 - --> $DIR/incorrect-number-of-const-args.rs:13:17 + --> $DIR/incorrect-number-of-const-args.rs:12:17 | LL | foo::<0, 0, 0>(); | ^ unexpected const argument diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.rs b/src/test/ui/const-generics/incorrect-number-of-const-args.rs index f7bdf761f7d1..3c4290df0564 100644 --- a/src/test/ui/const-generics/incorrect-number-of-const-args.rs +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn foo() -> usize { 0 diff --git a/src/test/ui/const-generics/infer/cannot-infer-const-args.full.stderr b/src/test/ui/const-generics/infer/cannot-infer-const-args.full.stderr index 05bf67a5ff7c..e85bf8829aee 100644 --- a/src/test/ui/const-generics/infer/cannot-infer-const-args.full.stderr +++ b/src/test/ui/const-generics/infer/cannot-infer-const-args.full.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/cannot-infer-const-args.rs:12:5 + --> $DIR/cannot-infer-const-args.rs:11:5 | LL | foo(); | ^^^ cannot infer the value of const parameter `X` declared on the function `foo` diff --git a/src/test/ui/const-generics/infer/cannot-infer-const-args.min.stderr b/src/test/ui/const-generics/infer/cannot-infer-const-args.min.stderr index 05bf67a5ff7c..e85bf8829aee 100644 --- a/src/test/ui/const-generics/infer/cannot-infer-const-args.min.stderr +++ b/src/test/ui/const-generics/infer/cannot-infer-const-args.min.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/cannot-infer-const-args.rs:12:5 + --> $DIR/cannot-infer-const-args.rs:11:5 | LL | foo(); | ^^^ cannot infer the value of const parameter `X` declared on the function `foo` diff --git a/src/test/ui/const-generics/infer/cannot-infer-const-args.rs b/src/test/ui/const-generics/infer/cannot-infer-const-args.rs index 2d74b4788bf4..cc52892bd04b 100644 --- a/src/test/ui/const-generics/infer/cannot-infer-const-args.rs +++ b/src/test/ui/const-generics/infer/cannot-infer-const-args.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn foo() -> usize { 0 diff --git a/src/test/ui/const-generics/infer/issue-77092.rs b/src/test/ui/const-generics/infer/issue-77092.rs index 9a1dd1a82589..fcf7d3282b43 100644 --- a/src/test/ui/const-generics/infer/issue-77092.rs +++ b/src/test/ui/const-generics/infer/issue-77092.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - use std::convert::TryInto; fn take_array_from_mut(data: &mut [T], start: usize) -> &mut [T; N] { diff --git a/src/test/ui/const-generics/infer/issue-77092.stderr b/src/test/ui/const-generics/infer/issue-77092.stderr index 99894173bc8f..5857a4211985 100644 --- a/src/test/ui/const-generics/infer/issue-77092.stderr +++ b/src/test/ui/const-generics/infer/issue-77092.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/issue-77092.rs:13:26 + --> $DIR/issue-77092.rs:11:26 | LL | println!("{:?}", take_array_from_mut(&mut arr, i)); | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `take_array_from_mut` diff --git a/src/test/ui/const-generics/infer/method-chain.full.stderr b/src/test/ui/const-generics/infer/method-chain.full.stderr index 7aa3bd44df84..f6d9c4a26453 100644 --- a/src/test/ui/const-generics/infer/method-chain.full.stderr +++ b/src/test/ui/const-generics/infer/method-chain.full.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/method-chain.rs:21:33 + --> $DIR/method-chain.rs:20:33 | LL | Foo.bar().bar().bar().bar().baz(); | ^^^ cannot infer the value of const parameter `N` declared on the associated function `baz` diff --git a/src/test/ui/const-generics/infer/method-chain.min.stderr b/src/test/ui/const-generics/infer/method-chain.min.stderr index 7aa3bd44df84..f6d9c4a26453 100644 --- a/src/test/ui/const-generics/infer/method-chain.min.stderr +++ b/src/test/ui/const-generics/infer/method-chain.min.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/method-chain.rs:21:33 + --> $DIR/method-chain.rs:20:33 | LL | Foo.bar().bar().bar().bar().baz(); | ^^^ cannot infer the value of const parameter `N` declared on the associated function `baz` diff --git a/src/test/ui/const-generics/infer/method-chain.rs b/src/test/ui/const-generics/infer/method-chain.rs index 9389ca20d106..8ac6a7d6267b 100644 --- a/src/test/ui/const-generics/infer/method-chain.rs +++ b/src/test/ui/const-generics/infer/method-chain.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo; diff --git a/src/test/ui/const-generics/infer/uninferred-consts.full.stderr b/src/test/ui/const-generics/infer/uninferred-consts.full.stderr index 4be625ba9093..254a28f70e21 100644 --- a/src/test/ui/const-generics/infer/uninferred-consts.full.stderr +++ b/src/test/ui/const-generics/infer/uninferred-consts.full.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/uninferred-consts.rs:14:9 + --> $DIR/uninferred-consts.rs:13:9 | LL | Foo.foo(); | ^^^ cannot infer the value of const parameter `A` declared on the associated function `foo` diff --git a/src/test/ui/const-generics/infer/uninferred-consts.min.stderr b/src/test/ui/const-generics/infer/uninferred-consts.min.stderr index 4be625ba9093..254a28f70e21 100644 --- a/src/test/ui/const-generics/infer/uninferred-consts.min.stderr +++ b/src/test/ui/const-generics/infer/uninferred-consts.min.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/uninferred-consts.rs:14:9 + --> $DIR/uninferred-consts.rs:13:9 | LL | Foo.foo(); | ^^^ cannot infer the value of const parameter `A` declared on the associated function `foo` diff --git a/src/test/ui/const-generics/infer/uninferred-consts.rs b/src/test/ui/const-generics/infer/uninferred-consts.rs index 00fb6eac9920..bcd9aadb78af 100644 --- a/src/test/ui/const-generics/infer/uninferred-consts.rs +++ b/src/test/ui/const-generics/infer/uninferred-consts.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] // taken from https://github.com/rust-lang/rust/issues/70507#issuecomment-615268893 struct Foo; diff --git a/src/test/ui/const-generics/infer_arg_from_pat.rs b/src/test/ui/const-generics/infer_arg_from_pat.rs index 609fdb35cf18..5e2a3eaff543 100644 --- a/src/test/ui/const-generics/infer_arg_from_pat.rs +++ b/src/test/ui/const-generics/infer_arg_from_pat.rs @@ -5,7 +5,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct A { arr: [u8; N], diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.rs b/src/test/ui/const-generics/infer_arr_len_from_pat.rs index cbf48e3d2499..0273383856fd 100644 --- a/src/test/ui/const-generics/infer_arr_len_from_pat.rs +++ b/src/test/ui/const-generics/infer_arr_len_from_pat.rs @@ -5,7 +5,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn as_chunks() -> [u8; N] { loop {} diff --git a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs index bdbf338295cc..96e5976e44b3 100644 --- a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs +++ b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn takes_closure_of_array_3(f: F) where F: Fn([i32; 3]) { f([1, 2, 3]); diff --git a/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.full.stderr b/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.full.stderr index c09d16d0ab05..3e90dbeece95 100644 --- a/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.full.stderr +++ b/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/intrinsics-type_name-as-const-argument.rs:15:8 + --> $DIR/intrinsics-type_name-as-const-argument.rs:14:8 | LL | T: Trait<{std::intrinsics::type_name::()}> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr b/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr index 02467df193c3..4c2aaef34939 100644 --- a/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr +++ b/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/intrinsics-type_name-as-const-argument.rs:15:44 + --> $DIR/intrinsics-type_name-as-const-argument.rs:14:44 | LL | T: Trait<{std::intrinsics::type_name::()}> | ^ cannot perform const operation using `T` @@ -8,7 +8,7 @@ LL | T: Trait<{std::intrinsics::type_name::()}> = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: `&'static str` is forbidden as the type of a const generic parameter - --> $DIR/intrinsics-type_name-as-const-argument.rs:10:22 + --> $DIR/intrinsics-type_name-as-const-argument.rs:9:22 | LL | trait Trait {} | ^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.rs b/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.rs index 8971c00ed5a2..f24dd42eb2da 100644 --- a/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.rs +++ b/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] #![feature(core_intrinsics)] #![feature(const_type_name)] diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.full.stderr b/src/test/ui/const-generics/issue-61522-array-len-succ.full.stderr index 8855f187e970..56deec16548f 100644 --- a/src/test/ui/const-generics/issue-61522-array-len-succ.full.stderr +++ b/src/test/ui/const-generics/issue-61522-array-len-succ.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-61522-array-len-succ.rs:7:40 + --> $DIR/issue-61522-array-len-succ.rs:6:40 | LL | pub struct MyArray([u8; COUNT + 1]); | ^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | pub struct MyArray([u8; COUNT + 1]); = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/issue-61522-array-len-succ.rs:12:24 + --> $DIR/issue-61522-array-len-succ.rs:11:24 | LL | fn inner(&self) -> &[u8; COUNT + 1] { | ^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.min.stderr b/src/test/ui/const-generics/issue-61522-array-len-succ.min.stderr index 2eaef95c2321..36a0a37ae9c3 100644 --- a/src/test/ui/const-generics/issue-61522-array-len-succ.min.stderr +++ b/src/test/ui/const-generics/issue-61522-array-len-succ.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-61522-array-len-succ.rs:7:45 + --> $DIR/issue-61522-array-len-succ.rs:6:45 | LL | pub struct MyArray([u8; COUNT + 1]); | ^^^^^ cannot perform const operation using `COUNT` @@ -8,7 +8,7 @@ LL | pub struct MyArray([u8; COUNT + 1]); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-61522-array-len-succ.rs:12:30 + --> $DIR/issue-61522-array-len-succ.rs:11:30 | LL | fn inner(&self) -> &[u8; COUNT + 1] { | ^^^^^ cannot perform const operation using `COUNT` diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.rs b/src/test/ui/const-generics/issue-61522-array-len-succ.rs index 8c0a3a037746..d4a948b92597 100644 --- a/src/test/ui/const-generics/issue-61522-array-len-succ.rs +++ b/src/test/ui/const-generics/issue-61522-array-len-succ.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct MyArray([u8; COUNT + 1]); //[full]~^ ERROR constant expression depends on a generic parameter diff --git a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr index 1c2e7e069a18..b6c6e6fe3740 100644 --- a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr +++ b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr @@ -1,5 +1,5 @@ error: `&'static str` is forbidden as the type of a const generic parameter - --> $DIR/issue-66596-impl-trait-for-str-const-arg.rs:9:25 + --> $DIR/issue-66596-impl-trait-for-str-const-arg.rs:8:25 | LL | trait Trait { | ^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs index 11d4bf4c3e6a..2a741ba87a98 100644 --- a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs +++ b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Trait { diff --git a/src/test/ui/const-generics/issue-67375.full.stderr b/src/test/ui/const-generics/issue-67375.full.stderr index e15d65f197e2..2f004f75de56 100644 --- a/src/test/ui/const-generics/issue-67375.full.stderr +++ b/src/test/ui/const-generics/issue-67375.full.stderr @@ -1,5 +1,5 @@ warning: cannot use constants which depend on generic parameters in types - --> $DIR/issue-67375.rs:9:12 + --> $DIR/issue-67375.rs:8:12 | LL | inner: [(); { [|_: &T| {}; 0].len() }], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | inner: [(); { [|_: &T| {}; 0].len() }], = note: for more information, see issue #76200 error[E0392]: parameter `T` is never used - --> $DIR/issue-67375.rs:7:12 + --> $DIR/issue-67375.rs:6:12 | LL | struct Bug { | ^ unused parameter diff --git a/src/test/ui/const-generics/issue-67375.min.stderr b/src/test/ui/const-generics/issue-67375.min.stderr index da96b5374a57..337e7bc14099 100644 --- a/src/test/ui/const-generics/issue-67375.min.stderr +++ b/src/test/ui/const-generics/issue-67375.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-67375.rs:9:25 + --> $DIR/issue-67375.rs:8:25 | LL | inner: [(); { [|_: &T| {}; 0].len() }], | ^ cannot perform const operation using `T` @@ -8,7 +8,7 @@ LL | inner: [(); { [|_: &T| {}; 0].len() }], = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0392]: parameter `T` is never used - --> $DIR/issue-67375.rs:7:12 + --> $DIR/issue-67375.rs:6:12 | LL | struct Bug { | ^ unused parameter diff --git a/src/test/ui/const-generics/issue-67375.rs b/src/test/ui/const-generics/issue-67375.rs index ecc76bcae06c..a8875b8b6bfc 100644 --- a/src/test/ui/const-generics/issue-67375.rs +++ b/src/test/ui/const-generics/issue-67375.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] struct Bug { //~^ ERROR parameter `T` is never used diff --git a/src/test/ui/const-generics/issue-67945-1.full.stderr b/src/test/ui/const-generics/issue-67945-1.full.stderr index e79c4f5374e1..5cdcefe35015 100644 --- a/src/test/ui/const-generics/issue-67945-1.full.stderr +++ b/src/test/ui/const-generics/issue-67945-1.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-67945-1.rs:14:20 + --> $DIR/issue-67945-1.rs:13:20 | LL | struct Bug { | - this type parameter @@ -13,7 +13,7 @@ LL | let x: S = MaybeUninit::uninit(); found union `MaybeUninit<_>` error[E0392]: parameter `S` is never used - --> $DIR/issue-67945-1.rs:11:12 + --> $DIR/issue-67945-1.rs:10:12 | LL | struct Bug { | ^ unused parameter diff --git a/src/test/ui/const-generics/issue-67945-1.min.stderr b/src/test/ui/const-generics/issue-67945-1.min.stderr index 8fea130baa57..a3e086ea9548 100644 --- a/src/test/ui/const-generics/issue-67945-1.min.stderr +++ b/src/test/ui/const-generics/issue-67945-1.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-67945-1.rs:14:16 + --> $DIR/issue-67945-1.rs:13:16 | LL | let x: S = MaybeUninit::uninit(); | ^ cannot perform const operation using `S` @@ -8,7 +8,7 @@ LL | let x: S = MaybeUninit::uninit(); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-67945-1.rs:17:45 + --> $DIR/issue-67945-1.rs:16:45 | LL | let b = &*(&x as *const _ as *const S); | ^ cannot perform const operation using `S` @@ -17,7 +17,7 @@ LL | let b = &*(&x as *const _ as *const S); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0392]: parameter `S` is never used - --> $DIR/issue-67945-1.rs:11:12 + --> $DIR/issue-67945-1.rs:10:12 | LL | struct Bug { | ^ unused parameter diff --git a/src/test/ui/const-generics/issue-67945-1.rs b/src/test/ui/const-generics/issue-67945-1.rs index 6771603f2594..84737e4e9857 100644 --- a/src/test/ui/const-generics/issue-67945-1.rs +++ b/src/test/ui/const-generics/issue-67945-1.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] use std::marker::PhantomData; diff --git a/src/test/ui/const-generics/issue-67945-2.full.stderr b/src/test/ui/const-generics/issue-67945-2.full.stderr index 2f54b802df8a..4d96058b395b 100644 --- a/src/test/ui/const-generics/issue-67945-2.full.stderr +++ b/src/test/ui/const-generics/issue-67945-2.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-67945-2.rs:12:20 + --> $DIR/issue-67945-2.rs:11:20 | LL | struct Bug { | - this type parameter @@ -13,7 +13,7 @@ LL | let x: S = MaybeUninit::uninit(); found union `MaybeUninit<_>` error[E0392]: parameter `S` is never used - --> $DIR/issue-67945-2.rs:9:12 + --> $DIR/issue-67945-2.rs:8:12 | LL | struct Bug { | ^ unused parameter diff --git a/src/test/ui/const-generics/issue-67945-2.min.stderr b/src/test/ui/const-generics/issue-67945-2.min.stderr index 50633772b75a..860be4a9b6ac 100644 --- a/src/test/ui/const-generics/issue-67945-2.min.stderr +++ b/src/test/ui/const-generics/issue-67945-2.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-67945-2.rs:12:16 + --> $DIR/issue-67945-2.rs:11:16 | LL | let x: S = MaybeUninit::uninit(); | ^ cannot perform const operation using `S` @@ -8,7 +8,7 @@ LL | let x: S = MaybeUninit::uninit(); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-67945-2.rs:15:45 + --> $DIR/issue-67945-2.rs:14:45 | LL | let b = &*(&x as *const _ as *const S); | ^ cannot perform const operation using `S` @@ -17,7 +17,7 @@ LL | let b = &*(&x as *const _ as *const S); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0392]: parameter `S` is never used - --> $DIR/issue-67945-2.rs:9:12 + --> $DIR/issue-67945-2.rs:8:12 | LL | struct Bug { | ^ unused parameter diff --git a/src/test/ui/const-generics/issue-67945-2.rs b/src/test/ui/const-generics/issue-67945-2.rs index 72dbb674e66b..4a46786e9a9b 100644 --- a/src/test/ui/const-generics/issue-67945-2.rs +++ b/src/test/ui/const-generics/issue-67945-2.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] use std::mem::MaybeUninit; diff --git a/src/test/ui/const-generics/issue-67945-3.full.stderr b/src/test/ui/const-generics/issue-67945-3.full.stderr index c33b88588c0d..fa66252bd694 100644 --- a/src/test/ui/const-generics/issue-67945-3.full.stderr +++ b/src/test/ui/const-generics/issue-67945-3.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-67945-3.rs:8:8 + --> $DIR/issue-67945-3.rs:7:8 | LL | A: [(); { | ________^ diff --git a/src/test/ui/const-generics/issue-67945-3.min.stderr b/src/test/ui/const-generics/issue-67945-3.min.stderr index 9c6e101ece86..5c30429c8958 100644 --- a/src/test/ui/const-generics/issue-67945-3.min.stderr +++ b/src/test/ui/const-generics/issue-67945-3.min.stderr @@ -1,5 +1,5 @@ error: generic `Self` types are currently not permitted in anonymous constants - --> $DIR/issue-67945-3.rs:10:27 + --> $DIR/issue-67945-3.rs:9:27 | LL | let x: Option> = None; | ^^^^ diff --git a/src/test/ui/const-generics/issue-67945-3.rs b/src/test/ui/const-generics/issue-67945-3.rs index bca079101e2b..5bad61cfc763 100644 --- a/src/test/ui/const-generics/issue-67945-3.rs +++ b/src/test/ui/const-generics/issue-67945-3.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] struct Bug { A: [(); { diff --git a/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs b/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs index eab63d3a6e6a..43c3999133c6 100644 --- a/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs +++ b/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] extern crate impl_const; diff --git a/src/test/ui/const-generics/issue-70180-1-stalled_on.rs b/src/test/ui/const-generics/issue-70180-1-stalled_on.rs index 9cfa57006d5c..f0554823273a 100644 --- a/src/test/ui/const-generics/issue-70180-1-stalled_on.rs +++ b/src/test/ui/const-generics/issue-70180-1-stalled_on.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub fn works() { let array/*: [_; _]*/ = default_array(); diff --git a/src/test/ui/const-generics/issue-70180-2-stalled_on.rs b/src/test/ui/const-generics/issue-70180-2-stalled_on.rs index bbde404966cc..21cefc09c253 100644 --- a/src/test/ui/const-generics/issue-70180-2-stalled_on.rs +++ b/src/test/ui/const-generics/issue-70180-2-stalled_on.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn works() { let array/*: [u8; _]*/ = default_byte_array(); diff --git a/src/test/ui/const-generics/issue-71986.rs b/src/test/ui/const-generics/issue-71986.rs index d4c962452d18..6bfdba5711ef 100644 --- a/src/test/ui/const-generics/issue-71986.rs +++ b/src/test/ui/const-generics/issue-71986.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub trait Foo {} pub fn bar>() {} diff --git a/src/test/ui/const-generics/issue-74906.rs b/src/test/ui/const-generics/issue-74906.rs index 9162d1142b64..dc3c33736dab 100644 --- a/src/test/ui/const-generics/issue-74906.rs +++ b/src/test/ui/const-generics/issue-74906.rs @@ -3,7 +3,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] const SIZE: usize = 16; diff --git a/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs b/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs index 7ea8d936d614..f59eb60cb38f 100644 --- a/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs +++ b/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] // All of these three items must be in `lib2` to reproduce the error diff --git a/src/test/ui/const-generics/issues/issue-56445.full.stderr b/src/test/ui/const-generics/issues/issue-56445.full.stderr index 50e914185511..61fba92c1962 100644 --- a/src/test/ui/const-generics/issues/issue-56445.full.stderr +++ b/src/test/ui/const-generics/issues/issue-56445.full.stderr @@ -6,10 +6,9 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error[E0771]: use of non-static lifetime `'a` in const generic - --> $DIR/issue-56445.rs:9:26 + --> $DIR/issue-56445.rs:8:26 | LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); | ^^ diff --git a/src/test/ui/const-generics/issues/issue-56445.min.stderr b/src/test/ui/const-generics/issues/issue-56445.min.stderr index bcb27d8d1e19..80702dd4bc33 100644 --- a/src/test/ui/const-generics/issues/issue-56445.min.stderr +++ b/src/test/ui/const-generics/issues/issue-56445.min.stderr @@ -1,5 +1,5 @@ error[E0771]: use of non-static lifetime `'a` in const generic - --> $DIR/issue-56445.rs:9:26 + --> $DIR/issue-56445.rs:8:26 | LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); | ^^ diff --git a/src/test/ui/const-generics/issues/issue-56445.rs b/src/test/ui/const-generics/issues/issue-56445.rs index 0bcde348b05d..bc9e1dee853e 100644 --- a/src/test/ui/const-generics/issues/issue-56445.rs +++ b/src/test/ui/const-generics/issues/issue-56445.rs @@ -1,7 +1,6 @@ // Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-518402995. // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] #![crate_type = "lib"] use std::marker::PhantomData; diff --git a/src/test/ui/const-generics/issues/issue-60263.rs b/src/test/ui/const-generics/issues/issue-60263.rs deleted file mode 100644 index 70cbc242c41a..000000000000 --- a/src/test/ui/const-generics/issues/issue-60263.rs +++ /dev/null @@ -1,9 +0,0 @@ -struct B; //~ ERROR const generics are unstable - -impl B<0> { - fn bug() -> Self { - panic!() - } -} - -fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-60263.stderr b/src/test/ui/const-generics/issues/issue-60263.stderr deleted file mode 100644 index aeef296f3852..000000000000 --- a/src/test/ui/const-generics/issues/issue-60263.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: const generics are unstable - --> $DIR/issue-60263.rs:1:16 - | -LL | struct B; - | ^ - | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr index cc014ea429d5..c03b7252a3c8 100644 --- a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr +++ b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr @@ -6,7 +6,6 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs index ae2b0520fb1c..6e64c78cd8c9 100644 --- a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs +++ b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs @@ -1,7 +1,6 @@ // check-pass // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] struct Generic; diff --git a/src/test/ui/const-generics/issues/issue-61336-1.full.stderr b/src/test/ui/const-generics/issues/issue-61336-1.full.stderr index 3a9f819a6262..f18728eabbb4 100644 --- a/src/test/ui/const-generics/issues/issue-61336-1.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61336-1.full.stderr @@ -6,7 +6,6 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61336-1.rs b/src/test/ui/const-generics/issues/issue-61336-1.rs index 201c0d039d98..c93b296dbb55 100644 --- a/src/test/ui/const-generics/issues/issue-61336-1.rs +++ b/src/test/ui/const-generics/issues/issue-61336-1.rs @@ -1,7 +1,6 @@ // build-pass // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] fn f(x: T) -> [T; N] { [x; N] diff --git a/src/test/ui/const-generics/issues/issue-61336-2.full.stderr b/src/test/ui/const-generics/issues/issue-61336-2.full.stderr index 883ebbef3e80..9f8e68d211de 100644 --- a/src/test/ui/const-generics/issues/issue-61336-2.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61336-2.full.stderr @@ -6,10 +6,9 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/issue-61336-2.rs:10:5 + --> $DIR/issue-61336-2.rs:9:5 | LL | [x; { N }] | ^^^^^^^^^^ the trait `Copy` is not implemented for `T` diff --git a/src/test/ui/const-generics/issues/issue-61336-2.min.stderr b/src/test/ui/const-generics/issues/issue-61336-2.min.stderr index 40863a4f7186..82d17a87e0af 100644 --- a/src/test/ui/const-generics/issues/issue-61336-2.min.stderr +++ b/src/test/ui/const-generics/issues/issue-61336-2.min.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/issue-61336-2.rs:10:5 + --> $DIR/issue-61336-2.rs:9:5 | LL | [x; { N }] | ^^^^^^^^^^ the trait `Copy` is not implemented for `T` diff --git a/src/test/ui/const-generics/issues/issue-61336-2.rs b/src/test/ui/const-generics/issues/issue-61336-2.rs index 44995157cc91..a1cf641ff749 100644 --- a/src/test/ui/const-generics/issues/issue-61336-2.rs +++ b/src/test/ui/const-generics/issues/issue-61336-2.rs @@ -1,6 +1,5 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] fn f(x: T) -> [T; N] { [x; { N }] diff --git a/src/test/ui/const-generics/issues/issue-61336.full.stderr b/src/test/ui/const-generics/issues/issue-61336.full.stderr index 3863da8da054..974e2af6fd2c 100644 --- a/src/test/ui/const-generics/issues/issue-61336.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61336.full.stderr @@ -6,10 +6,9 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/issue-61336.rs:10:5 + --> $DIR/issue-61336.rs:9:5 | LL | [x; N] | ^^^^^^ the trait `Copy` is not implemented for `T` diff --git a/src/test/ui/const-generics/issues/issue-61336.min.stderr b/src/test/ui/const-generics/issues/issue-61336.min.stderr index 6c57f9ccbf51..19c7153582cb 100644 --- a/src/test/ui/const-generics/issues/issue-61336.min.stderr +++ b/src/test/ui/const-generics/issues/issue-61336.min.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/issue-61336.rs:10:5 + --> $DIR/issue-61336.rs:9:5 | LL | [x; N] | ^^^^^^ the trait `Copy` is not implemented for `T` diff --git a/src/test/ui/const-generics/issues/issue-61336.rs b/src/test/ui/const-generics/issues/issue-61336.rs index 7c34250e6b2d..c0106ee38c20 100644 --- a/src/test/ui/const-generics/issues/issue-61336.rs +++ b/src/test/ui/const-generics/issues/issue-61336.rs @@ -1,6 +1,5 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] fn f(x: T) -> [T; N] { [x; N] diff --git a/src/test/ui/const-generics/issues/issue-61422.full.stderr b/src/test/ui/const-generics/issues/issue-61422.full.stderr index 294378a66901..ac6c378295d3 100644 --- a/src/test/ui/const-generics/issues/issue-61422.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61422.full.stderr @@ -6,7 +6,6 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61422.rs b/src/test/ui/const-generics/issues/issue-61422.rs index 649f8b4255b1..421f696f3fd8 100644 --- a/src/test/ui/const-generics/issues/issue-61422.rs +++ b/src/test/ui/const-generics/issues/issue-61422.rs @@ -1,7 +1,6 @@ // check-pass // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] use std::mem; diff --git a/src/test/ui/const-generics/issues/issue-61432.full.stderr b/src/test/ui/const-generics/issues/issue-61432.full.stderr index eec1b20254ed..82b36de45a2a 100644 --- a/src/test/ui/const-generics/issues/issue-61432.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61432.full.stderr @@ -6,7 +6,6 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete warning: 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61432.rs b/src/test/ui/const-generics/issues/issue-61432.rs index 91a4794099c0..0e228126d778 100644 --- a/src/test/ui/const-generics/issues/issue-61432.rs +++ b/src/test/ui/const-generics/issues/issue-61432.rs @@ -1,7 +1,6 @@ // run-pass // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] fn promote() { // works: diff --git a/src/test/ui/const-generics/issues/issue-61747.full.stderr b/src/test/ui/const-generics/issues/issue-61747.full.stderr index 3a266c8e9742..b7f66345c4aa 100644 --- a/src/test/ui/const-generics/issues/issue-61747.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61747.full.stderr @@ -6,10 +6,9 @@ LL | #![cfg_attr(full, feature(const_generics))] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error: constant expression depends on a generic parameter - --> $DIR/issue-61747.rs:8:23 + --> $DIR/issue-61747.rs:7:23 | LL | fn successor() -> Const<{C + 1}> { | ^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-61747.min.stderr b/src/test/ui/const-generics/issues/issue-61747.min.stderr index 1de9e71b6eb0..b85533ccb46f 100644 --- a/src/test/ui/const-generics/issues/issue-61747.min.stderr +++ b/src/test/ui/const-generics/issues/issue-61747.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-61747.rs:8:30 + --> $DIR/issue-61747.rs:7:30 | LL | fn successor() -> Const<{C + 1}> { | ^ cannot perform const operation using `C` diff --git a/src/test/ui/const-generics/issues/issue-61747.rs b/src/test/ui/const-generics/issues/issue-61747.rs index 3a4dd1cdd181..3aa2e6a5c31d 100644 --- a/src/test/ui/const-generics/issues/issue-61747.rs +++ b/src/test/ui/const-generics/issues/issue-61747.rs @@ -1,6 +1,5 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete -#![cfg_attr(min, feature(min_const_generics))] struct Const; diff --git a/src/test/ui/const-generics/issues/issue-61935.full.stderr b/src/test/ui/const-generics/issues/issue-61935.full.stderr index b805bc0db7e5..b970f4e4c8e3 100644 --- a/src/test/ui/const-generics/issues/issue-61935.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61935.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-61935.rs:10:14 + --> $DIR/issue-61935.rs:9:14 | LL | Self:FooImpl<{N==0}> | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-61935.min.stderr b/src/test/ui/const-generics/issues/issue-61935.min.stderr index b1d92056a544..9382dca31530 100644 --- a/src/test/ui/const-generics/issues/issue-61935.min.stderr +++ b/src/test/ui/const-generics/issues/issue-61935.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-61935.rs:10:23 + --> $DIR/issue-61935.rs:9:23 | LL | Self:FooImpl<{N==0}> | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/issues/issue-61935.rs b/src/test/ui/const-generics/issues/issue-61935.rs index 9fa02329a712..ed861c63bf1e 100644 --- a/src/test/ui/const-generics/issues/issue-61935.rs +++ b/src/test/ui/const-generics/issues/issue-61935.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Foo {} diff --git a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs index a8fa37803566..1a0e46e599d9 100644 --- a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs +++ b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs @@ -3,7 +3,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub trait BitLen: Sized { const BIT_LEN: usize; diff --git a/src/test/ui/const-generics/issues/issue-62220.full.stderr b/src/test/ui/const-generics/issues/issue-62220.full.stderr index 120aa8e4af5d..373360c7ced6 100644 --- a/src/test/ui/const-generics/issues/issue-62220.full.stderr +++ b/src/test/ui/const-generics/issues/issue-62220.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-62220.rs:13:27 + --> $DIR/issue-62220.rs:12:27 | LL | pub fn trunc(self) -> (TruncatedVector, T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-62220.min.stderr b/src/test/ui/const-generics/issues/issue-62220.min.stderr index b338cdb87e1e..72311d030cf0 100644 --- a/src/test/ui/const-generics/issues/issue-62220.min.stderr +++ b/src/test/ui/const-generics/issues/issue-62220.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-62220.rs:8:59 + --> $DIR/issue-62220.rs:7:59 | LL | pub type TruncatedVector = Vector; | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/issues/issue-62220.rs b/src/test/ui/const-generics/issues/issue-62220.rs index 2017473fa9e2..c26784c9813c 100644 --- a/src/test/ui/const-generics/issues/issue-62220.rs +++ b/src/test/ui/const-generics/issues/issue-62220.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct Vector([T; N]); diff --git a/src/test/ui/const-generics/issues/issue-62456.full.stderr b/src/test/ui/const-generics/issues/issue-62456.full.stderr index a8d44074db9d..833e70ca6d38 100644 --- a/src/test/ui/const-generics/issues/issue-62456.full.stderr +++ b/src/test/ui/const-generics/issues/issue-62456.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-62456.rs:7:20 + --> $DIR/issue-62456.rs:6:20 | LL | let _ = [0u64; N + 1]; | ^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-62456.min.stderr b/src/test/ui/const-generics/issues/issue-62456.min.stderr index a4b501a7bb10..920318fa0ac3 100644 --- a/src/test/ui/const-generics/issues/issue-62456.min.stderr +++ b/src/test/ui/const-generics/issues/issue-62456.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-62456.rs:7:20 + --> $DIR/issue-62456.rs:6:20 | LL | let _ = [0u64; N + 1]; | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/issues/issue-62456.rs b/src/test/ui/const-generics/issues/issue-62456.rs index cbb2a11a931a..e24cf36c8ce4 100644 --- a/src/test/ui/const-generics/issues/issue-62456.rs +++ b/src/test/ui/const-generics/issues/issue-62456.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn foo() { let _ = [0u64; N + 1]; diff --git a/src/test/ui/const-generics/issues/issue-62504.full.stderr b/src/test/ui/const-generics/issues/issue-62504.full.stderr index 9c84f06ce9f7..f09af76325e9 100644 --- a/src/test/ui/const-generics/issues/issue-62504.full.stderr +++ b/src/test/ui/const-generics/issues/issue-62504.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-62504.rs:19:25 + --> $DIR/issue-62504.rs:18:25 | LL | ArrayHolder([0; Self::SIZE]) | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-62504.min.stderr b/src/test/ui/const-generics/issues/issue-62504.min.stderr index 865eaf749326..5d45e302888d 100644 --- a/src/test/ui/const-generics/issues/issue-62504.min.stderr +++ b/src/test/ui/const-generics/issues/issue-62504.min.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-62504.rs:19:21 + --> $DIR/issue-62504.rs:18:21 | LL | ArrayHolder([0; Self::SIZE]) | ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE` @@ -8,7 +8,7 @@ LL | ArrayHolder([0; Self::SIZE]) found array `[u32; _]` error: constant expression depends on a generic parameter - --> $DIR/issue-62504.rs:19:25 + --> $DIR/issue-62504.rs:18:25 | LL | ArrayHolder([0; Self::SIZE]) | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-62504.rs b/src/test/ui/const-generics/issues/issue-62504.rs index 5630962ff537..0b95754cab45 100644 --- a/src/test/ui/const-generics/issues/issue-62504.rs +++ b/src/test/ui/const-generics/issues/issue-62504.rs @@ -2,7 +2,6 @@ #![allow(incomplete_features)] #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait HasSize { const SIZE: usize; diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr b/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr index 5117e20d6266..a0aee4821c69 100644 --- a/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr +++ b/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr @@ -1,5 +1,5 @@ error: `NoMatch` is forbidden as the type of a const generic parameter - --> $DIR/issue-62579-no-match.rs:10:17 + --> $DIR/issue-62579-no-match.rs:9:17 | LL | fn foo() -> bool { | ^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.rs b/src/test/ui/const-generics/issues/issue-62579-no-match.rs index c9853aa9162e..46813f5256e5 100644 --- a/src/test/ui/const-generics/issues/issue-62579-no-match.rs +++ b/src/test/ui/const-generics/issues/issue-62579-no-match.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #[derive(PartialEq, Eq)] struct NoMatch; diff --git a/src/test/ui/const-generics/issues/issue-62878.full.stderr b/src/test/ui/const-generics/issues/issue-62878.full.stderr index dce2e27c71a1..6e6aa1966330 100644 --- a/src/test/ui/const-generics/issues/issue-62878.full.stderr +++ b/src/test/ui/const-generics/issues/issue-62878.full.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-62878.rs:6:38 + --> $DIR/issue-62878.rs:5:38 | LL | fn foo() {} | ^ the type must not depend on the parameter `N` error[E0747]: type provided when a constant was expected - --> $DIR/issue-62878.rs:11:11 + --> $DIR/issue-62878.rs:10:11 | LL | foo::<_, {[1]}>(); | ^ @@ -13,7 +13,7 @@ LL | foo::<_, {[1]}>(); = help: const arguments cannot yet be inferred with `_` error[E0308]: mismatched types - --> $DIR/issue-62878.rs:11:15 + --> $DIR/issue-62878.rs:10:15 | LL | foo::<_, {[1]}>(); | ^^^ expected `usize`, found array `[{integer}; 1]` diff --git a/src/test/ui/const-generics/issues/issue-62878.min.stderr b/src/test/ui/const-generics/issues/issue-62878.min.stderr index 9f95e5d88623..920d7e43b9b7 100644 --- a/src/test/ui/const-generics/issues/issue-62878.min.stderr +++ b/src/test/ui/const-generics/issues/issue-62878.min.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-62878.rs:6:38 + --> $DIR/issue-62878.rs:5:38 | LL | fn foo() {} | ^ the type must not depend on the parameter `N` error: `[u8; _]` is forbidden as the type of a const generic parameter - --> $DIR/issue-62878.rs:6:33 + --> $DIR/issue-62878.rs:5:33 | LL | fn foo() {} | ^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-62878.rs b/src/test/ui/const-generics/issues/issue-62878.rs index c087711e5f9a..a70606c4a7d3 100644 --- a/src/test/ui/const-generics/issues/issue-62878.rs +++ b/src/test/ui/const-generics/issues/issue-62878.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn foo() {} //~^ ERROR the type of const parameters must not diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr index a20c7264acfd..e1c20e6ae781 100644 --- a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr @@ -1,5 +1,5 @@ error[E0741]: `&'static (dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/issue-63322-forbid-dyn.rs:10:18 + --> $DIR/issue-63322-forbid-dyn.rs:9:18 | LL | fn test() { | ^^^^^^^^^^^^^^ `&'static (dyn A + 'static)` doesn't derive both `PartialEq` and `Eq` diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr index 5dbfdc6d652b..543e4b29a162 100644 --- a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr @@ -1,5 +1,5 @@ error: `&'static (dyn A + 'static)` is forbidden as the type of a const generic parameter - --> $DIR/issue-63322-forbid-dyn.rs:10:18 + --> $DIR/issue-63322-forbid-dyn.rs:9:18 | LL | fn test() { | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn test() { = help: more complex types are supported with `#[feature(const_generics)]` error[E0741]: `&'static (dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/issue-63322-forbid-dyn.rs:10:18 + --> $DIR/issue-63322-forbid-dyn.rs:9:18 | LL | fn test() { | ^^^^^^^^^^^^^^ `&'static (dyn A + 'static)` doesn't derive both `PartialEq` and `Eq` diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs index 2194eb97a410..334e2aac02a4 100644 --- a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait A {} struct B; diff --git a/src/test/ui/const-generics/issues/issue-64494.full.stderr b/src/test/ui/const-generics/issues/issue-64494.full.stderr index a97ec9308f81..abb26d6cf175 100644 --- a/src/test/ui/const-generics/issues/issue-64494.full.stderr +++ b/src/test/ui/const-generics/issues/issue-64494.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-64494.rs:16:53 + --> $DIR/issue-64494.rs:15:53 | LL | impl MyTrait for T where Is<{T::VAL == 5}>: True {} | ^^^^ @@ -7,7 +7,7 @@ LL | impl MyTrait for T where Is<{T::VAL == 5}>: True {} = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/issue-64494.rs:19:53 + --> $DIR/issue-64494.rs:18:53 | LL | impl MyTrait for T where Is<{T::VAL == 6}>: True {} | ^^^^ diff --git a/src/test/ui/const-generics/issues/issue-64494.min.stderr b/src/test/ui/const-generics/issues/issue-64494.min.stderr index 681166b1d2b9..936ab7f6e7e3 100644 --- a/src/test/ui/const-generics/issues/issue-64494.min.stderr +++ b/src/test/ui/const-generics/issues/issue-64494.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-64494.rs:16:38 + --> $DIR/issue-64494.rs:15:38 | LL | impl MyTrait for T where Is<{T::VAL == 5}>: True {} | ^^^^^^ cannot perform const operation using `T` @@ -8,7 +8,7 @@ LL | impl MyTrait for T where Is<{T::VAL == 5}>: True {} = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-64494.rs:19:38 + --> $DIR/issue-64494.rs:18:38 | LL | impl MyTrait for T where Is<{T::VAL == 6}>: True {} | ^^^^^^ cannot perform const operation using `T` @@ -17,7 +17,7 @@ LL | impl MyTrait for T where Is<{T::VAL == 6}>: True {} = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0119]: conflicting implementations of trait `MyTrait`: - --> $DIR/issue-64494.rs:19:1 + --> $DIR/issue-64494.rs:18:1 | LL | impl MyTrait for T where Is<{T::VAL == 5}>: True {} | ------------------------------------ first implementation here diff --git a/src/test/ui/const-generics/issues/issue-64494.rs b/src/test/ui/const-generics/issues/issue-64494.rs index 014742be03db..96d19203109a 100644 --- a/src/test/ui/const-generics/issues/issue-64494.rs +++ b/src/test/ui/const-generics/issues/issue-64494.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Foo { const VAL: usize; diff --git a/src/test/ui/const-generics/issues/issue-64519.rs b/src/test/ui/const-generics/issues/issue-64519.rs index 1ca709d09755..8c603b74b907 100644 --- a/src/test/ui/const-generics/issues/issue-64519.rs +++ b/src/test/ui/const-generics/issues/issue-64519.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo { state: Option<[u8; D]>, diff --git a/src/test/ui/const-generics/issues/issue-66205.full.stderr b/src/test/ui/const-generics/issues/issue-66205.full.stderr index a1520912e4e2..7e150f5f6db5 100644 --- a/src/test/ui/const-generics/issues/issue-66205.full.stderr +++ b/src/test/ui/const-generics/issues/issue-66205.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-66205.rs:8:12 + --> $DIR/issue-66205.rs:7:12 | LL | fact::<{ N - 1 }>(); | ^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-66205.min.stderr b/src/test/ui/const-generics/issues/issue-66205.min.stderr index ecd96ac37e40..b41793b62d2d 100644 --- a/src/test/ui/const-generics/issues/issue-66205.min.stderr +++ b/src/test/ui/const-generics/issues/issue-66205.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-66205.rs:8:14 + --> $DIR/issue-66205.rs:7:14 | LL | fact::<{ N - 1 }>(); | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/issues/issue-66205.rs b/src/test/ui/const-generics/issues/issue-66205.rs index 4e37c247d008..14249b62ceed 100644 --- a/src/test/ui/const-generics/issues/issue-66205.rs +++ b/src/test/ui/const-generics/issues/issue-66205.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![allow(dead_code, unconditional_recursion)] fn fact() { diff --git a/src/test/ui/const-generics/issues/issue-66906.rs b/src/test/ui/const-generics/issues/issue-66906.rs index 3e048593c9b2..a871b118dcc5 100644 --- a/src/test/ui/const-generics/issues/issue-66906.rs +++ b/src/test/ui/const-generics/issues/issue-66906.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct Tuple; diff --git a/src/test/ui/const-generics/issues/issue-67185-1.rs b/src/test/ui/const-generics/issues/issue-67185-1.rs index 09d88ef89a30..ed35a5f7c0a8 100644 --- a/src/test/ui/const-generics/issues/issue-67185-1.rs +++ b/src/test/ui/const-generics/issues/issue-67185-1.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Baz { type Quaks; diff --git a/src/test/ui/const-generics/issues/issue-67185-2.full.stderr b/src/test/ui/const-generics/issues/issue-67185-2.full.stderr index 78c7ebff0598..fa9c680d4b8d 100644 --- a/src/test/ui/const-generics/issues/issue-67185-2.full.stderr +++ b/src/test/ui/const-generics/issues/issue-67185-2.full.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:17:1 + --> $DIR/issue-67185-2.rs:16:1 | LL | / trait Foo LL | | @@ -17,7 +17,7 @@ LL | | } = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:17:1 + --> $DIR/issue-67185-2.rs:16:1 | LL | / trait Foo LL | | @@ -35,7 +35,7 @@ LL | | } = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:27:6 + --> $DIR/issue-67185-2.rs:26:6 | LL | trait Foo | --- required by a bound in this @@ -51,7 +51,7 @@ LL | impl Foo for FooImpl {} <[u16; 4] as Bar> error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:27:6 + --> $DIR/issue-67185-2.rs:26:6 | LL | trait Foo | --- required by a bound in this @@ -67,7 +67,7 @@ LL | impl Foo for FooImpl {} <[u16; 4] as Bar> error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:31:14 + --> $DIR/issue-67185-2.rs:30:14 | LL | trait Foo | --- required by a bound in this @@ -83,7 +83,7 @@ LL | fn f(_: impl Foo) {} <[u16; 4] as Bar> error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:31:14 + --> $DIR/issue-67185-2.rs:30:14 | LL | trait Foo | --- required by a bound in this diff --git a/src/test/ui/const-generics/issues/issue-67185-2.min.stderr b/src/test/ui/const-generics/issues/issue-67185-2.min.stderr index 78c7ebff0598..fa9c680d4b8d 100644 --- a/src/test/ui/const-generics/issues/issue-67185-2.min.stderr +++ b/src/test/ui/const-generics/issues/issue-67185-2.min.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:17:1 + --> $DIR/issue-67185-2.rs:16:1 | LL | / trait Foo LL | | @@ -17,7 +17,7 @@ LL | | } = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:17:1 + --> $DIR/issue-67185-2.rs:16:1 | LL | / trait Foo LL | | @@ -35,7 +35,7 @@ LL | | } = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:27:6 + --> $DIR/issue-67185-2.rs:26:6 | LL | trait Foo | --- required by a bound in this @@ -51,7 +51,7 @@ LL | impl Foo for FooImpl {} <[u16; 4] as Bar> error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:27:6 + --> $DIR/issue-67185-2.rs:26:6 | LL | trait Foo | --- required by a bound in this @@ -67,7 +67,7 @@ LL | impl Foo for FooImpl {} <[u16; 4] as Bar> error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:31:14 + --> $DIR/issue-67185-2.rs:30:14 | LL | trait Foo | --- required by a bound in this @@ -83,7 +83,7 @@ LL | fn f(_: impl Foo) {} <[u16; 4] as Bar> error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:31:14 + --> $DIR/issue-67185-2.rs:30:14 | LL | trait Foo | --- required by a bound in this diff --git a/src/test/ui/const-generics/issues/issue-67185-2.rs b/src/test/ui/const-generics/issues/issue-67185-2.rs index 1176d0c69040..94a713d7cf95 100644 --- a/src/test/ui/const-generics/issues/issue-67185-2.rs +++ b/src/test/ui/const-generics/issues/issue-67185-2.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Baz { type Quaks; diff --git a/src/test/ui/const-generics/issues/issue-67739.full.stderr b/src/test/ui/const-generics/issues/issue-67739.full.stderr index 27a56b8eb02b..dcbe5b94a628 100644 --- a/src/test/ui/const-generics/issues/issue-67739.full.stderr +++ b/src/test/ui/const-generics/issues/issue-67739.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-67739.rs:12:15 + --> $DIR/issue-67739.rs:11:15 | LL | [0u8; mem::size_of::()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-67739.min.stderr b/src/test/ui/const-generics/issues/issue-67739.min.stderr index 27a56b8eb02b..dcbe5b94a628 100644 --- a/src/test/ui/const-generics/issues/issue-67739.min.stderr +++ b/src/test/ui/const-generics/issues/issue-67739.min.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-67739.rs:12:15 + --> $DIR/issue-67739.rs:11:15 | LL | [0u8; mem::size_of::()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-67739.rs b/src/test/ui/const-generics/issues/issue-67739.rs index 0f5860f22fdd..e4960e56c9e4 100644 --- a/src/test/ui/const-generics/issues/issue-67739.rs +++ b/src/test/ui/const-generics/issues/issue-67739.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::mem; diff --git a/src/test/ui/const-generics/issues/issue-68366.full.stderr b/src/test/ui/const-generics/issues/issue-68366.full.stderr index ac774f50c749..4015fb090b93 100644 --- a/src/test/ui/const-generics/issues/issue-68366.full.stderr +++ b/src/test/ui/const-generics/issues/issue-68366.full.stderr @@ -1,5 +1,5 @@ error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-68366.rs:12:13 + --> $DIR/issue-68366.rs:11:13 | LL | impl Collatz<{Some(N)}> {} | ^ unconstrained const parameter @@ -8,7 +8,7 @@ LL | impl Collatz<{Some(N)}> {} = note: proving the result of expressions other than the parameter are unique is not supported error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-68366.rs:18:12 + --> $DIR/issue-68366.rs:17:12 | LL | impl Foo {} | ^ unconstrained const parameter diff --git a/src/test/ui/const-generics/issues/issue-68366.min.stderr b/src/test/ui/const-generics/issues/issue-68366.min.stderr index acaf4a33ee0a..da4cbd3081f8 100644 --- a/src/test/ui/const-generics/issues/issue-68366.min.stderr +++ b/src/test/ui/const-generics/issues/issue-68366.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-68366.rs:12:37 + --> $DIR/issue-68366.rs:11:37 | LL | impl Collatz<{Some(N)}> {} | ^ cannot perform const operation using `N` @@ -8,7 +8,7 @@ LL | impl Collatz<{Some(N)}> {} = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-68366.rs:12:13 + --> $DIR/issue-68366.rs:11:13 | LL | impl Collatz<{Some(N)}> {} | ^ unconstrained const parameter @@ -17,7 +17,7 @@ LL | impl Collatz<{Some(N)}> {} = note: proving the result of expressions other than the parameter are unique is not supported error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-68366.rs:18:12 + --> $DIR/issue-68366.rs:17:12 | LL | impl Foo {} | ^ unconstrained const parameter diff --git a/src/test/ui/const-generics/issues/issue-68366.rs b/src/test/ui/const-generics/issues/issue-68366.rs index 474cdb7258d9..37afed62327d 100644 --- a/src/test/ui/const-generics/issues/issue-68366.rs +++ b/src/test/ui/const-generics/issues/issue-68366.rs @@ -5,7 +5,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Collatz>; diff --git a/src/test/ui/const-generics/issues/issue-68596.rs b/src/test/ui/const-generics/issues/issue-68596.rs index 3b27d4d68c5d..0bb23be1eb4e 100644 --- a/src/test/ui/const-generics/issues/issue-68596.rs +++ b/src/test/ui/const-generics/issues/issue-68596.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct S(u8); diff --git a/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr b/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr index 59653114a6b6..2de8ada27661 100644 --- a/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr +++ b/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr @@ -1,5 +1,5 @@ error: `[usize; 0]` is forbidden as the type of a const generic parameter - --> $DIR/issue-68615-adt.rs:7:23 + --> $DIR/issue-68615-adt.rs:6:23 | LL | struct Const {} | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-68615-adt.rs b/src/test/ui/const-generics/issues/issue-68615-adt.rs index d616f3ab95a8..ddea3e8ab658 100644 --- a/src/test/ui/const-generics/issues/issue-68615-adt.rs +++ b/src/test/ui/const-generics/issues/issue-68615-adt.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Const {} //[min]~^ ERROR `[usize; 0]` is forbidden as the type of a const generic parameter diff --git a/src/test/ui/const-generics/issues/issue-68615-array.min.stderr b/src/test/ui/const-generics/issues/issue-68615-array.min.stderr index 1ee881b96ec6..0d17b04a5cdd 100644 --- a/src/test/ui/const-generics/issues/issue-68615-array.min.stderr +++ b/src/test/ui/const-generics/issues/issue-68615-array.min.stderr @@ -1,5 +1,5 @@ error: `[usize; 0]` is forbidden as the type of a const generic parameter - --> $DIR/issue-68615-array.rs:7:21 + --> $DIR/issue-68615-array.rs:6:21 | LL | struct Foo {} | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-68615-array.rs b/src/test/ui/const-generics/issues/issue-68615-array.rs index 24c9a59a1855..56afd9b2a154 100644 --- a/src/test/ui/const-generics/issues/issue-68615-array.rs +++ b/src/test/ui/const-generics/issues/issue-68615-array.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo {} //[min]~^ ERROR `[usize; 0]` is forbidden as the type of a const generic parameter diff --git a/src/test/ui/const-generics/issues/issue-68977.full.stderr b/src/test/ui/const-generics/issues/issue-68977.full.stderr index 3690bac3eb34..25dcd88a4afc 100644 --- a/src/test/ui/const-generics/issues/issue-68977.full.stderr +++ b/src/test/ui/const-generics/issues/issue-68977.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-68977.rs:35:44 + --> $DIR/issue-68977.rs:34:44 | LL | FxpStorageHelper: FxpStorage, | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-68977.min.stderr b/src/test/ui/const-generics/issues/issue-68977.min.stderr index ea91df1e0bf4..0b3d5b9a760f 100644 --- a/src/test/ui/const-generics/issues/issue-68977.min.stderr +++ b/src/test/ui/const-generics/issues/issue-68977.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-68977.rs:29:17 + --> $DIR/issue-68977.rs:28:17 | LL | PhantomU8<{(INT_BITS + FRAC_BITS + 7) / 8}>; | ^^^^^^^^ cannot perform const operation using `INT_BITS` @@ -8,7 +8,7 @@ LL | PhantomU8<{(INT_BITS + FRAC_BITS + 7) / 8}>; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-68977.rs:29:28 + --> $DIR/issue-68977.rs:28:28 | LL | PhantomU8<{(INT_BITS + FRAC_BITS + 7) / 8}>; | ^^^^^^^^^ cannot perform const operation using `FRAC_BITS` diff --git a/src/test/ui/const-generics/issues/issue-68977.rs b/src/test/ui/const-generics/issues/issue-68977.rs index 4fea94cb4652..a0ffcc84c7a3 100644 --- a/src/test/ui/const-generics/issues/issue-68977.rs +++ b/src/test/ui/const-generics/issues/issue-68977.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct PhantomU8; diff --git a/src/test/ui/const-generics/issues/issue-70125-1.rs b/src/test/ui/const-generics/issues/issue-70125-1.rs index 04175089dc06..5c118d245a1e 100644 --- a/src/test/ui/const-generics/issues/issue-70125-1.rs +++ b/src/test/ui/const-generics/issues/issue-70125-1.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] const L: usize = 4; diff --git a/src/test/ui/const-generics/issues/issue-70125-2.rs b/src/test/ui/const-generics/issues/issue-70125-2.rs index ceefc2dcb32c..f82131262d6e 100644 --- a/src/test/ui/const-generics/issues/issue-70125-2.rs +++ b/src/test/ui/const-generics/issues/issue-70125-2.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn main() { <()>::foo(); diff --git a/src/test/ui/const-generics/issues/issue-70167.rs b/src/test/ui/const-generics/issues/issue-70167.rs index 04c76a4dcaff..9e912b691773 100644 --- a/src/test/ui/const-generics/issues/issue-70167.rs +++ b/src/test/ui/const-generics/issues/issue-70167.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub trait Trait: From<>::Item> { type Item; diff --git a/src/test/ui/const-generics/issues/issue-71169.full.stderr b/src/test/ui/const-generics/issues/issue-71169.full.stderr index b87825d20ce3..7b1a2f98dfeb 100644 --- a/src/test/ui/const-generics/issues/issue-71169.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71169.full.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71169.rs:6:43 + --> $DIR/issue-71169.rs:5:43 | LL | fn foo() {} | ^^^ the type must not depend on the parameter `LEN` error: constant expression depends on a generic parameter - --> $DIR/issue-71169.rs:11:14 + --> $DIR/issue-71169.rs:10:14 | LL | foo::<4, DATA>(); | ^^^^ diff --git a/src/test/ui/const-generics/issues/issue-71169.min.stderr b/src/test/ui/const-generics/issues/issue-71169.min.stderr index 9b0a2946ca6c..68ac47460b35 100644 --- a/src/test/ui/const-generics/issues/issue-71169.min.stderr +++ b/src/test/ui/const-generics/issues/issue-71169.min.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71169.rs:6:43 + --> $DIR/issue-71169.rs:5:43 | LL | fn foo() {} | ^^^ the type must not depend on the parameter `LEN` error: `[u8; _]` is forbidden as the type of a const generic parameter - --> $DIR/issue-71169.rs:6:38 + --> $DIR/issue-71169.rs:5:38 | LL | fn foo() {} | ^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-71169.rs b/src/test/ui/const-generics/issues/issue-71169.rs index 7007ec222caa..a574da4b6b31 100644 --- a/src/test/ui/const-generics/issues/issue-71169.rs +++ b/src/test/ui/const-generics/issues/issue-71169.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn foo() {} //~^ ERROR the type of const parameters must not diff --git a/src/test/ui/const-generics/issues/issue-71381.full.stderr b/src/test/ui/const-generics/issues/issue-71381.full.stderr index 453ef00e6dc1..3950317b3705 100644 --- a/src/test/ui/const-generics/issues/issue-71381.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71381.full.stderr @@ -1,23 +1,23 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71381.rs:15:82 + --> $DIR/issue-71381.rs:14:82 | LL | pub fn call_me(&self) { | ^^^^ the type must not depend on the parameter `Args` error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71381.rs:24:40 + --> $DIR/issue-71381.rs:23:40 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^ the type must not depend on the parameter `Args` error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:15:61 + --> $DIR/issue-71381.rs:14:61 | LL | pub fn call_me(&self) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:24:19 + --> $DIR/issue-71381.rs:23:19 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-71381.min.stderr b/src/test/ui/const-generics/issues/issue-71381.min.stderr index 453ef00e6dc1..3950317b3705 100644 --- a/src/test/ui/const-generics/issues/issue-71381.min.stderr +++ b/src/test/ui/const-generics/issues/issue-71381.min.stderr @@ -1,23 +1,23 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71381.rs:15:82 + --> $DIR/issue-71381.rs:14:82 | LL | pub fn call_me(&self) { | ^^^^ the type must not depend on the parameter `Args` error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71381.rs:24:40 + --> $DIR/issue-71381.rs:23:40 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^ the type must not depend on the parameter `Args` error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:15:61 + --> $DIR/issue-71381.rs:14:61 | LL | pub fn call_me(&self) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:24:19 + --> $DIR/issue-71381.rs:23:19 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-71381.rs b/src/test/ui/const-generics/issues/issue-71381.rs index 65d88e553b9e..f015d6946954 100644 --- a/src/test/ui/const-generics/issues/issue-71381.rs +++ b/src/test/ui/const-generics/issues/issue-71381.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Test(*const usize); diff --git a/src/test/ui/const-generics/issues/issue-71382.full.stderr b/src/test/ui/const-generics/issues/issue-71382.full.stderr index 3da85ee040de..715037bd5f1e 100644 --- a/src/test/ui/const-generics/issues/issue-71382.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71382.full.stderr @@ -1,5 +1,5 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71382.rs:17:23 + --> $DIR/issue-71382.rs:16:23 | LL | fn test(&self) { | ^^^^ diff --git a/src/test/ui/const-generics/issues/issue-71382.min.stderr b/src/test/ui/const-generics/issues/issue-71382.min.stderr index 3da85ee040de..715037bd5f1e 100644 --- a/src/test/ui/const-generics/issues/issue-71382.min.stderr +++ b/src/test/ui/const-generics/issues/issue-71382.min.stderr @@ -1,5 +1,5 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71382.rs:17:23 + --> $DIR/issue-71382.rs:16:23 | LL | fn test(&self) { | ^^^^ diff --git a/src/test/ui/const-generics/issues/issue-71382.rs b/src/test/ui/const-generics/issues/issue-71382.rs index 12a7d08382a3..3a56db937de0 100644 --- a/src/test/ui/const-generics/issues/issue-71382.rs +++ b/src/test/ui/const-generics/issues/issue-71382.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Test(); diff --git a/src/test/ui/const-generics/issues/issue-71611.full.stderr b/src/test/ui/const-generics/issues/issue-71611.full.stderr index 48d4bb361a18..01a85b745ce3 100644 --- a/src/test/ui/const-generics/issues/issue-71611.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71611.full.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71611.rs:6:31 + --> $DIR/issue-71611.rs:5:31 | LL | fn func(outer: A) { | ^ the type must not depend on the parameter `A` error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71611.rs:6:21 + --> $DIR/issue-71611.rs:5:21 | LL | fn func(outer: A) { | ^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-71611.min.stderr b/src/test/ui/const-generics/issues/issue-71611.min.stderr index 48d4bb361a18..01a85b745ce3 100644 --- a/src/test/ui/const-generics/issues/issue-71611.min.stderr +++ b/src/test/ui/const-generics/issues/issue-71611.min.stderr @@ -1,11 +1,11 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71611.rs:6:31 + --> $DIR/issue-71611.rs:5:31 | LL | fn func(outer: A) { | ^ the type must not depend on the parameter `A` error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71611.rs:6:21 + --> $DIR/issue-71611.rs:5:21 | LL | fn func(outer: A) { | ^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-71611.rs b/src/test/ui/const-generics/issues/issue-71611.rs index 9b8e8be6bc6f..6468d0b6bdae 100644 --- a/src/test/ui/const-generics/issues/issue-71611.rs +++ b/src/test/ui/const-generics/issues/issue-71611.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn func(outer: A) { //~^ ERROR: using function pointers as const generic parameters is forbidden diff --git a/src/test/ui/const-generics/issues/issue-72352.full.stderr b/src/test/ui/const-generics/issues/issue-72352.full.stderr index 51f946784674..eedd73c4dcc0 100644 --- a/src/test/ui/const-generics/issues/issue-72352.full.stderr +++ b/src/test/ui/const-generics/issues/issue-72352.full.stderr @@ -1,5 +1,5 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-72352.rs:8:42 + --> $DIR/issue-72352.rs:7:42 | LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8) -> usize { | ^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-72352.min.stderr b/src/test/ui/const-generics/issues/issue-72352.min.stderr index 51f946784674..eedd73c4dcc0 100644 --- a/src/test/ui/const-generics/issues/issue-72352.min.stderr +++ b/src/test/ui/const-generics/issues/issue-72352.min.stderr @@ -1,5 +1,5 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-72352.rs:8:42 + --> $DIR/issue-72352.rs:7:42 | LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8) -> usize { | ^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-72352.rs b/src/test/ui/const-generics/issues/issue-72352.rs index 1517f3dae4ff..9cd95c11026d 100644 --- a/src/test/ui/const-generics/issues/issue-72352.rs +++ b/src/test/ui/const-generics/issues/issue-72352.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::ffi::{CStr, CString}; diff --git a/src/test/ui/const-generics/issues/issue-72787.full.stderr b/src/test/ui/const-generics/issues/issue-72787.full.stderr index b4c79d4171b7..fbb7ae59bef7 100644 --- a/src/test/ui/const-generics/issues/issue-72787.full.stderr +++ b/src/test/ui/const-generics/issues/issue-72787.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-72787.rs:11:32 + --> $DIR/issue-72787.rs:10:32 | LL | Condition<{ LHS <= RHS }>: True | ^^^^ @@ -7,7 +7,7 @@ LL | Condition<{ LHS <= RHS }>: True = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/issue-72787.rs:26:42 + --> $DIR/issue-72787.rs:25:42 | LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, | ^^^^ @@ -15,7 +15,7 @@ LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/issue-72787.rs:26:42 + --> $DIR/issue-72787.rs:25:42 | LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, | ^^^^ @@ -23,7 +23,7 @@ LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/issue-72787.rs:26:42 + --> $DIR/issue-72787.rs:25:42 | LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, | ^^^^ @@ -31,7 +31,7 @@ LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/issue-72787.rs:26:42 + --> $DIR/issue-72787.rs:25:42 | LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, | ^^^^ diff --git a/src/test/ui/const-generics/issues/issue-72787.min.stderr b/src/test/ui/const-generics/issues/issue-72787.min.stderr index 27bbc28011f2..aadf19ba6b6f 100644 --- a/src/test/ui/const-generics/issues/issue-72787.min.stderr +++ b/src/test/ui/const-generics/issues/issue-72787.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-72787.rs:11:17 + --> $DIR/issue-72787.rs:10:17 | LL | Condition<{ LHS <= RHS }>: True | ^^^ cannot perform const operation using `LHS` @@ -8,7 +8,7 @@ LL | Condition<{ LHS <= RHS }>: True = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-72787.rs:11:24 + --> $DIR/issue-72787.rs:10:24 | LL | Condition<{ LHS <= RHS }>: True | ^^^ cannot perform const operation using `RHS` @@ -17,7 +17,7 @@ LL | Condition<{ LHS <= RHS }>: True = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-72787.rs:26:25 + --> $DIR/issue-72787.rs:25:25 | LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, | ^ cannot perform const operation using `I` @@ -26,7 +26,7 @@ LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-72787.rs:26:36 + --> $DIR/issue-72787.rs:25:36 | LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, | ^ cannot perform const operation using `J` @@ -35,7 +35,7 @@ LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0283]: type annotations needed - --> $DIR/issue-72787.rs:22:26 + --> $DIR/issue-72787.rs:21:26 | LL | pub trait True {} | -------------- required by this bound in `True` @@ -46,7 +46,7 @@ LL | IsLessOrEqual: True, = note: cannot satisfy `IsLessOrEqual: True` error[E0283]: type annotations needed - --> $DIR/issue-72787.rs:22:26 + --> $DIR/issue-72787.rs:21:26 | LL | pub trait True {} | -------------- required by this bound in `True` diff --git a/src/test/ui/const-generics/issues/issue-72787.rs b/src/test/ui/const-generics/issues/issue-72787.rs index 57572e23aa4d..16bc9470470f 100644 --- a/src/test/ui/const-generics/issues/issue-72787.rs +++ b/src/test/ui/const-generics/issues/issue-72787.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct IsLessOrEqual; pub struct Condition; diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.full.stderr b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.full.stderr index b49940047215..82f9b9d346dd 100644 --- a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.full.stderr +++ b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-72819-generic-in-const-eval.rs:9:39 + --> $DIR/issue-72819-generic-in-const-eval.rs:8:39 | LL | where Assert::<{N < usize::MAX / 2}>: IsTrue, | ^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.min.stderr b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.min.stderr index 8df3c85ec1f7..6646be47b31e 100644 --- a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.min.stderr +++ b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-72819-generic-in-const-eval.rs:9:17 + --> $DIR/issue-72819-generic-in-const-eval.rs:8:17 | LL | where Assert::<{N < usize::MAX / 2}>: IsTrue, | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs index 4c0004795f0d..f612d8bd3f6b 100644 --- a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs +++ b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs @@ -3,7 +3,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Arr where Assert::<{N < usize::MAX / 2}>: IsTrue, diff --git a/src/test/ui/const-generics/issues/issue-73491.min.stderr b/src/test/ui/const-generics/issues/issue-73491.min.stderr index 3ff0563acc77..aeab9e267728 100644 --- a/src/test/ui/const-generics/issues/issue-73491.min.stderr +++ b/src/test/ui/const-generics/issues/issue-73491.min.stderr @@ -1,5 +1,5 @@ error: `[u32; _]` is forbidden as the type of a const generic parameter - --> $DIR/issue-73491.rs:9:19 + --> $DIR/issue-73491.rs:8:19 | LL | fn hoge() {} | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-73491.rs b/src/test/ui/const-generics/issues/issue-73491.rs index 4f6c44ad2cdb..c7cb92baf30a 100644 --- a/src/test/ui/const-generics/issues/issue-73491.rs +++ b/src/test/ui/const-generics/issues/issue-73491.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] const LEN: usize = 1024; diff --git a/src/test/ui/const-generics/issues/issue-73508.full.stderr b/src/test/ui/const-generics/issues/issue-73508.full.stderr index 0816bad35b2d..81691a14ef67 100644 --- a/src/test/ui/const-generics/issues/issue-73508.full.stderr +++ b/src/test/ui/const-generics/issues/issue-73508.full.stderr @@ -1,5 +1,5 @@ error: using raw pointers as const generic parameters is forbidden - --> $DIR/issue-73508.rs:6:33 + --> $DIR/issue-73508.rs:5:33 | LL | pub const fn func_name() {} | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-73508.min.stderr b/src/test/ui/const-generics/issues/issue-73508.min.stderr index 0816bad35b2d..81691a14ef67 100644 --- a/src/test/ui/const-generics/issues/issue-73508.min.stderr +++ b/src/test/ui/const-generics/issues/issue-73508.min.stderr @@ -1,5 +1,5 @@ error: using raw pointers as const generic parameters is forbidden - --> $DIR/issue-73508.rs:6:33 + --> $DIR/issue-73508.rs:5:33 | LL | pub const fn func_name() {} | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-73508.rs b/src/test/ui/const-generics/issues/issue-73508.rs index 21b87f7f9014..f02c4161dc10 100644 --- a/src/test/ui/const-generics/issues/issue-73508.rs +++ b/src/test/ui/const-generics/issues/issue-73508.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub const fn func_name() {} //~^ ERROR using raw pointers diff --git a/src/test/ui/const-generics/issues/issue-74101.min.stderr b/src/test/ui/const-generics/issues/issue-74101.min.stderr index 1351246667e9..6561183f7cad 100644 --- a/src/test/ui/const-generics/issues/issue-74101.min.stderr +++ b/src/test/ui/const-generics/issues/issue-74101.min.stderr @@ -1,5 +1,5 @@ error: `[u8; _]` is forbidden as the type of a const generic parameter - --> $DIR/issue-74101.rs:7:18 + --> $DIR/issue-74101.rs:6:18 | LL | fn test() {} | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn test() {} = help: more complex types are supported with `#[feature(const_generics)]` error: `[u8; _]` is forbidden as the type of a const generic parameter - --> $DIR/issue-74101.rs:10:21 + --> $DIR/issue-74101.rs:9:21 | LL | struct Foo; | ^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-74101.rs b/src/test/ui/const-generics/issues/issue-74101.rs index 2a7d31ac8dd6..d4fd72eb6daa 100644 --- a/src/test/ui/const-generics/issues/issue-74101.rs +++ b/src/test/ui/const-generics/issues/issue-74101.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn test() {} //[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter diff --git a/src/test/ui/const-generics/issues/issue-74255.min.stderr b/src/test/ui/const-generics/issues/issue-74255.min.stderr index e3e8502ae634..2b6aa7dad97e 100644 --- a/src/test/ui/const-generics/issues/issue-74255.min.stderr +++ b/src/test/ui/const-generics/issues/issue-74255.min.stderr @@ -1,5 +1,5 @@ error: `IceEnum` is forbidden as the type of a const generic parameter - --> $DIR/issue-74255.rs:15:31 + --> $DIR/issue-74255.rs:14:31 | LL | fn ice_struct_fn() {} | ^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-74255.rs b/src/test/ui/const-generics/issues/issue-74255.rs index b277c273461c..75a876c27e59 100644 --- a/src/test/ui/const-generics/issues/issue-74255.rs +++ b/src/test/ui/const-generics/issues/issue-74255.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #[derive(PartialEq, Eq)] enum IceEnum { diff --git a/src/test/ui/const-generics/issues/issue-74950.min.stderr b/src/test/ui/const-generics/issues/issue-74950.min.stderr index 12947a2ab375..27393d38c6b1 100644 --- a/src/test/ui/const-generics/issues/issue-74950.min.stderr +++ b/src/test/ui/const-generics/issues/issue-74950.min.stderr @@ -1,5 +1,5 @@ error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:18:23 + --> $DIR/issue-74950.rs:17:23 | LL | struct Outer; | ^^^^^ @@ -8,7 +8,7 @@ LL | struct Outer; = help: more complex types are supported with `#[feature(const_generics)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:18:23 + --> $DIR/issue-74950.rs:17:23 | LL | struct Outer; | ^^^^^ @@ -17,7 +17,7 @@ LL | struct Outer; = help: more complex types are supported with `#[feature(const_generics)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:18:23 + --> $DIR/issue-74950.rs:17:23 | LL | struct Outer; | ^^^^^ @@ -26,7 +26,7 @@ LL | struct Outer; = help: more complex types are supported with `#[feature(const_generics)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:18:23 + --> $DIR/issue-74950.rs:17:23 | LL | struct Outer; | ^^^^^ @@ -35,7 +35,7 @@ LL | struct Outer; = help: more complex types are supported with `#[feature(const_generics)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:18:23 + --> $DIR/issue-74950.rs:17:23 | LL | struct Outer; | ^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-74950.rs b/src/test/ui/const-generics/issues/issue-74950.rs index 39f91f2b83df..91e5cc776fac 100644 --- a/src/test/ui/const-generics/issues/issue-74950.rs +++ b/src/test/ui/const-generics/issues/issue-74950.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #[derive(PartialEq, Eq)] diff --git a/src/test/ui/const-generics/issues/issue-75047.min.stderr b/src/test/ui/const-generics/issues/issue-75047.min.stderr index b87bb18a5a68..4ab90dd1ec66 100644 --- a/src/test/ui/const-generics/issues/issue-75047.min.stderr +++ b/src/test/ui/const-generics/issues/issue-75047.min.stderr @@ -1,5 +1,5 @@ error: `[u8; _]` is forbidden as the type of a const generic parameter - --> $DIR/issue-75047.rs:15:21 + --> $DIR/issue-75047.rs:14:21 | LL | struct Foo::value()]>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-75047.rs b/src/test/ui/const-generics/issues/issue-75047.rs index 7bab7cdd0989..97437748177e 100644 --- a/src/test/ui/const-generics/issues/issue-75047.rs +++ b/src/test/ui/const-generics/issues/issue-75047.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Bar(T); diff --git a/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.full.stderr b/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.full.stderr index 089937e66ca0..88b8ff89ffe1 100644 --- a/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.full.stderr +++ b/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/issue-76701-ty-param-in-const.rs:6:21 + --> $DIR/issue-76701-ty-param-in-const.rs:5:21 | LL | fn ty_param() -> [u8; std::mem::size_of::()] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | fn ty_param() -> [u8; std::mem::size_of::()] { = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/issue-76701-ty-param-in-const.rs:12:37 + --> $DIR/issue-76701-ty-param-in-const.rs:11:37 | LL | fn const_param() -> [u8; N + 1] { | ^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.min.stderr b/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.min.stderr index 551b8e43e1d4..32f70fa30072 100644 --- a/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.min.stderr +++ b/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/issue-76701-ty-param-in-const.rs:6:46 + --> $DIR/issue-76701-ty-param-in-const.rs:5:46 | LL | fn ty_param() -> [u8; std::mem::size_of::()] { | ^ cannot perform const operation using `T` @@ -8,7 +8,7 @@ LL | fn ty_param() -> [u8; std::mem::size_of::()] { = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/issue-76701-ty-param-in-const.rs:12:42 + --> $DIR/issue-76701-ty-param-in-const.rs:11:42 | LL | fn const_param() -> [u8; N + 1] { | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.rs b/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.rs index 9051c36fe81f..994898265635 100644 --- a/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.rs +++ b/src/test/ui/const-generics/issues/issue-76701-ty-param-in-const.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn ty_param() -> [u8; std::mem::size_of::()] { //[full]~^ ERROR constant expression depends on a generic parameter diff --git a/src/test/ui/const-generics/issues/issue-80062.rs b/src/test/ui/const-generics/issues/issue-80062.rs new file mode 100644 index 000000000000..56dc53298fb3 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-80062.rs @@ -0,0 +1,10 @@ +// Regression test for issue #80062 (fixed by `min_const_generics`) + +fn sof() -> T { unimplemented!() } + +fn test() { + let _: [u8; sof::()]; + //~^ ERROR generic parameters may not be used in const operations +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-80062.stderr b/src/test/ui/const-generics/issues/issue-80062.stderr new file mode 100644 index 000000000000..aad8907bda2d --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-80062.stderr @@ -0,0 +1,11 @@ +error: generic parameters may not be used in const operations + --> $DIR/issue-80062.rs:6:23 + | +LL | let _: [u8; sof::()]; + | ^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-80375.rs b/src/test/ui/const-generics/issues/issue-80375.rs new file mode 100644 index 000000000000..c906bb2c4d9b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-80375.rs @@ -0,0 +1,4 @@ +struct MyArray([u8; COUNT + 1]); +//~^ ERROR generic parameters may not be used in const operations + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-80375.stderr b/src/test/ui/const-generics/issues/issue-80375.stderr new file mode 100644 index 000000000000..9765a639a48d --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-80375.stderr @@ -0,0 +1,11 @@ +error: generic parameters may not be used in const operations + --> $DIR/issue-80375.rs:1:41 + | +LL | struct MyArray([u8; COUNT + 1]); + | ^^^^^ cannot perform const operation using `COUNT` + | + = help: const parameters may only be used as standalone arguments, i.e. `COUNT` + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs index 28f80702dcf0..189a32570f76 100644 --- a/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs +++ b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait T { fn f(); diff --git a/src/test/ui/const-generics/macro_rules-braces.full.stderr b/src/test/ui/const-generics/macro_rules-braces.full.stderr index 3c9d4c9b4700..1883f454e602 100644 --- a/src/test/ui/const-generics/macro_rules-braces.full.stderr +++ b/src/test/ui/const-generics/macro_rules-braces.full.stderr @@ -1,5 +1,5 @@ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/macro_rules-braces.rs:49:17 + --> $DIR/macro_rules-braces.rs:48:17 | LL | let _: baz!(m::P); | ^^^^ @@ -10,7 +10,7 @@ LL | let _: baz!({ m::P }); | ^ ^ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/macro_rules-braces.rs:69:17 + --> $DIR/macro_rules-braces.rs:68:17 | LL | let _: baz!(10 + 7); | ^^^^^^ @@ -21,7 +21,7 @@ LL | let _: baz!({ 10 + 7 }); | ^ ^ error: constant expression depends on a generic parameter - --> $DIR/macro_rules-braces.rs:16:13 + --> $DIR/macro_rules-braces.rs:15:13 | LL | [u8; $x] | ^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _: foo!({{ N }}); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: constant expression depends on a generic parameter - --> $DIR/macro_rules-braces.rs:21:13 + --> $DIR/macro_rules-braces.rs:20:13 | LL | [u8; { $x }] | ^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | let _: bar!({ N }); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: constant expression depends on a generic parameter - --> $DIR/macro_rules-braces.rs:26:13 + --> $DIR/macro_rules-braces.rs:25:13 | LL | Foo<$x> | ^^^^^^^ @@ -57,7 +57,7 @@ LL | let _: baz!({{ N }}); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: constant expression depends on a generic parameter - --> $DIR/macro_rules-braces.rs:31:13 + --> $DIR/macro_rules-braces.rs:30:13 | LL | Foo<{ $x }> | ^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/macro_rules-braces.min.stderr b/src/test/ui/const-generics/macro_rules-braces.min.stderr index c400e2c814dd..60583d43c016 100644 --- a/src/test/ui/const-generics/macro_rules-braces.min.stderr +++ b/src/test/ui/const-generics/macro_rules-braces.min.stderr @@ -1,5 +1,5 @@ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/macro_rules-braces.rs:49:17 + --> $DIR/macro_rules-braces.rs:48:17 | LL | let _: baz!(m::P); | ^^^^ @@ -10,7 +10,7 @@ LL | let _: baz!({ m::P }); | ^ ^ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/macro_rules-braces.rs:69:17 + --> $DIR/macro_rules-braces.rs:68:17 | LL | let _: baz!(10 + 7); | ^^^^^^ @@ -21,7 +21,7 @@ LL | let _: baz!({ 10 + 7 }); | ^ ^ error: generic parameters may not be used in const operations - --> $DIR/macro_rules-braces.rs:37:20 + --> $DIR/macro_rules-braces.rs:36:20 | LL | let _: foo!({{ N }}); | ^ cannot perform const operation using `N` @@ -30,7 +30,7 @@ LL | let _: foo!({{ N }}); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/macro_rules-braces.rs:41:19 + --> $DIR/macro_rules-braces.rs:40:19 | LL | let _: bar!({ N }); | ^ cannot perform const operation using `N` @@ -39,7 +39,7 @@ LL | let _: bar!({ N }); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/macro_rules-braces.rs:46:20 + --> $DIR/macro_rules-braces.rs:45:20 | LL | let _: baz!({{ N }}); | ^ cannot perform const operation using `N` @@ -48,7 +48,7 @@ LL | let _: baz!({{ N }}); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/macro_rules-braces.rs:51:19 + --> $DIR/macro_rules-braces.rs:50:19 | LL | let _: biz!({ N }); | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/macro_rules-braces.rs b/src/test/ui/const-generics/macro_rules-braces.rs index c6b43bec243f..605a10880bbb 100644 --- a/src/test/ui/const-generics/macro_rules-braces.rs +++ b/src/test/ui/const-generics/macro_rules-braces.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, allow(incomplete_features))] #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] mod m { pub const P: usize = 0; diff --git a/src/test/ui/const-generics/min-and-full-same-time.rs b/src/test/ui/const-generics/min-and-full-same-time.rs deleted file mode 100644 index 2365adc3a861..000000000000 --- a/src/test/ui/const-generics/min-and-full-same-time.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![feature(const_generics)] -//~^ ERROR features `const_generics` and `min_const_generics` are incompatible -#![allow(incomplete_features)] -#![feature(min_const_generics)] - - -fn main() {} diff --git a/src/test/ui/const-generics/min-and-full-same-time.stderr b/src/test/ui/const-generics/min-and-full-same-time.stderr deleted file mode 100644 index 907fec9bbe17..000000000000 --- a/src/test/ui/const-generics/min-and-full-same-time.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: features `const_generics` and `min_const_generics` are incompatible, using them at the same time is not allowed - --> $DIR/min-and-full-same-time.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ -... -LL | #![feature(min_const_generics)] - | ^^^^^^^^^^^^^^^^^^ - | - = help: remove one of these features - -error: aborting due to previous error - diff --git a/src/test/ui/const-generics/min_const_generics/assoc_const.rs b/src/test/ui/const-generics/min_const_generics/assoc_const.rs index fa75613d9ddc..27e971b5b6f9 100644 --- a/src/test/ui/const-generics/min_const_generics/assoc_const.rs +++ b/src/test/ui/const-generics/min_const_generics/assoc_const.rs @@ -1,6 +1,4 @@ // check-pass -#![feature(min_const_generics)] - struct Foo; impl Foo { diff --git a/src/test/ui/const-generics/min_const_generics/complex-expression.rs b/src/test/ui/const-generics/min_const_generics/complex-expression.rs index 686ce98fcdff..7840989cb081 100644 --- a/src/test/ui/const-generics/min_const_generics/complex-expression.rs +++ b/src/test/ui/const-generics/min_const_generics/complex-expression.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - use std::mem::size_of; fn test() {} diff --git a/src/test/ui/const-generics/min_const_generics/complex-expression.stderr b/src/test/ui/const-generics/min_const_generics/complex-expression.stderr index 2ea66279d460..176692448491 100644 --- a/src/test/ui/const-generics/min_const_generics/complex-expression.stderr +++ b/src/test/ui/const-generics/min_const_generics/complex-expression.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/complex-expression.rs:11:38 + --> $DIR/complex-expression.rs:9:38 | LL | struct Break0([u8; { N + 1 }]); | ^ cannot perform const operation using `N` @@ -8,7 +8,7 @@ LL | struct Break0([u8; { N + 1 }]); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/complex-expression.rs:14:40 + --> $DIR/complex-expression.rs:12:40 | LL | struct Break1([u8; { { N } }]); | ^ cannot perform const operation using `N` @@ -17,7 +17,7 @@ LL | struct Break1([u8; { { N } }]); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/complex-expression.rs:18:17 + --> $DIR/complex-expression.rs:16:17 | LL | let _: [u8; N + 1]; | ^ cannot perform const operation using `N` @@ -26,7 +26,7 @@ LL | let _: [u8; N + 1]; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/complex-expression.rs:23:17 + --> $DIR/complex-expression.rs:21:17 | LL | let _ = [0; N + 1]; | ^ cannot perform const operation using `N` @@ -35,7 +35,7 @@ LL | let _ = [0; N + 1]; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/complex-expression.rs:27:45 + --> $DIR/complex-expression.rs:25:45 | LL | struct BreakTy0(T, [u8; { size_of::<*mut T>() }]); | ^ cannot perform const operation using `T` @@ -44,7 +44,7 @@ LL | struct BreakTy0(T, [u8; { size_of::<*mut T>() }]); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/complex-expression.rs:30:47 + --> $DIR/complex-expression.rs:28:47 | LL | struct BreakTy1(T, [u8; { { size_of::<*mut T>() } }]); | ^ cannot perform const operation using `T` @@ -53,7 +53,7 @@ LL | struct BreakTy1(T, [u8; { { size_of::<*mut T>() } }]); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/complex-expression.rs:34:32 + --> $DIR/complex-expression.rs:32:32 | LL | let _: [u8; size_of::<*mut T>() + 1]; | ^ cannot perform const operation using `T` @@ -62,7 +62,7 @@ LL | let _: [u8; size_of::<*mut T>() + 1]; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions warning: cannot use constants which depend on generic parameters in types - --> $DIR/complex-expression.rs:39:17 + --> $DIR/complex-expression.rs:37:17 | LL | let _ = [0; size_of::<*mut T>() + 1]; | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.rs b/src/test/ui/const-generics/min_const_generics/complex-types.rs index 2aaf2c398755..057bd5af89ef 100644 --- a/src/test/ui/const-generics/min_const_generics/complex-types.rs +++ b/src/test/ui/const-generics/min_const_generics/complex-types.rs @@ -1,4 +1,3 @@ -#![feature(min_const_generics)] #![feature(never_type)] struct Foo; diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.stderr b/src/test/ui/const-generics/min_const_generics/complex-types.stderr index 5d473f1f8769..20d498f9c93b 100644 --- a/src/test/ui/const-generics/min_const_generics/complex-types.stderr +++ b/src/test/ui/const-generics/min_const_generics/complex-types.stderr @@ -1,5 +1,5 @@ error: `[u8; 0]` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:4:21 + --> $DIR/complex-types.rs:3:21 | LL | struct Foo; | ^^^^^^^ @@ -8,7 +8,7 @@ LL | struct Foo; = help: more complex types are supported with `#[feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:7:21 + --> $DIR/complex-types.rs:6:21 | LL | struct Bar; | ^^ @@ -17,7 +17,7 @@ LL | struct Bar; = help: more complex types are supported with `#[feature(const_generics)]` error: `No` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:12:21 + --> $DIR/complex-types.rs:11:21 | LL | struct Fez; | ^^ @@ -26,7 +26,7 @@ LL | struct Fez; = help: more complex types are supported with `#[feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:15:21 + --> $DIR/complex-types.rs:14:21 | LL | struct Faz; | ^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | struct Faz; = help: more complex types are supported with `#[feature(const_generics)]` error: `!` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:18:21 + --> $DIR/complex-types.rs:17:21 | LL | struct Fiz; | ^ @@ -44,7 +44,7 @@ LL | struct Fiz; = help: more complex types are supported with `#[feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:21:19 + --> $DIR/complex-types.rs:20:19 | LL | enum Goo { A, B } | ^^ @@ -53,7 +53,7 @@ LL | enum Goo { A, B } = help: more complex types are supported with `#[feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter - --> $DIR/complex-types.rs:24:20 + --> $DIR/complex-types.rs:23:20 | LL | union Boo { a: () } | ^^ diff --git a/src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.rs b/src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.rs index dd82be33a8e8..71d13ca61c9b 100644 --- a/src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.rs +++ b/src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.rs @@ -1,5 +1,4 @@ // check-pass -#![feature(min_const_generics)] #![allow(dead_code)] fn foo() { diff --git a/src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.stderr b/src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.stderr index 4d0cab012f99..f9f6660f6b82 100644 --- a/src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.stderr +++ b/src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.stderr @@ -1,5 +1,5 @@ warning: cannot use constants which depend on generic parameters in types - --> $DIR/const-evaluatable-unchecked.rs:6:9 + --> $DIR/const-evaluatable-unchecked.rs:5:9 | LL | [0; std::mem::size_of::<*mut T>()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | [0; std::mem::size_of::<*mut T>()]; = note: for more information, see issue #76200 warning: cannot use constants which depend on generic parameters in types - --> $DIR/const-evaluatable-unchecked.rs:17:21 + --> $DIR/const-evaluatable-unchecked.rs:16:21 | LL | let _ = [0; Self::ASSOC]; | ^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | let _ = [0; Self::ASSOC]; = note: for more information, see issue #76200 warning: cannot use constants which depend on generic parameters in types - --> $DIR/const-evaluatable-unchecked.rs:29:21 + --> $DIR/const-evaluatable-unchecked.rs:28:21 | LL | let _ = [0; Self::ASSOC]; | ^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.rs b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.rs index b9afd2264307..fac3777cf217 100644 --- a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.rs +++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - fn foo() {} const BAR: usize = 42; diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr index 13742238a201..beea0acac600 100644 --- a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr +++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr @@ -1,5 +1,5 @@ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:8:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:6:8 | LL | foo(); | ^ ^ @@ -10,7 +10,7 @@ LL | foo::(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:11:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:9:8 | LL | foo(); | ^ ^ @@ -21,7 +21,7 @@ LL | foo::(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:14:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:12:8 | LL | foo<3 + 3>(); | ^ ^ @@ -32,7 +32,7 @@ LL | foo::<3 + 3>(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:17:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:15:8 | LL | foo(); | ^ ^ @@ -43,7 +43,7 @@ LL | foo::(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:20:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:18:8 | LL | foo(); | ^ ^ @@ -54,7 +54,7 @@ LL | foo::(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:23:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:21:8 | LL | foo<100 - BAR>(); | ^ ^ @@ -65,7 +65,7 @@ LL | foo::<100 - BAR>(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:26:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:24:8 | LL | foo()>(); | ^ ^ @@ -76,13 +76,13 @@ LL | foo::()>(); | ^^ error: expected one of `;` or `}`, found `>` - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:26:19 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:24:19 | LL | foo()>(); | ^ expected one of `;` or `}` error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:30:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:28:8 | LL | foo()>(); | ^ ^ @@ -93,7 +93,7 @@ LL | foo::()>(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:33:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:31:8 | LL | foo() + BAR>(); | ^ ^ @@ -104,7 +104,7 @@ LL | foo::() + BAR>(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:36:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:34:8 | LL | foo() - BAR>(); | ^ ^ @@ -115,7 +115,7 @@ LL | foo::() - BAR>(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:39:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:37:8 | LL | foo()>(); | ^ ^ @@ -126,7 +126,7 @@ LL | foo::()>(); | ^^ error: comparison operators cannot be chained - --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:42:8 + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:40:8 | LL | foo()>(); | ^ ^ diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.rs b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.rs index b96d5c561ff5..f8b9d7adbfef 100644 --- a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.rs +++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - fn foo() {} const BAR: usize = 42; diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.stderr b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.stderr index 6adcf6a3e36d..ad451fcf65df 100644 --- a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.stderr +++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.stderr @@ -1,5 +1,5 @@ error: expected one of `,` or `>`, found `3` - --> $DIR/const-expression-suggest-missing-braces.rs:8:17 + --> $DIR/const-expression-suggest-missing-braces.rs:6:17 | LL | foo::(); | ^ expected one of `,` or `>` @@ -10,7 +10,7 @@ LL | foo::<{ BAR + 3 }>(); | ^ ^ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/const-expression-suggest-missing-braces.rs:20:11 + --> $DIR/const-expression-suggest-missing-braces.rs:18:11 | LL | foo::<3 + 3>(); | ^^^^^ @@ -21,7 +21,7 @@ LL | foo::<{ 3 + 3 }>(); | ^ ^ error: expected one of `,` or `>`, found `-` - --> $DIR/const-expression-suggest-missing-braces.rs:23:15 + --> $DIR/const-expression-suggest-missing-braces.rs:21:15 | LL | foo::(); | ^ expected one of `,` or `>` @@ -32,7 +32,7 @@ LL | foo::<{ BAR - 3 }>(); | ^ ^ error: expected one of `,` or `>`, found `-` - --> $DIR/const-expression-suggest-missing-braces.rs:26:15 + --> $DIR/const-expression-suggest-missing-braces.rs:24:15 | LL | foo::(); | ^ expected one of `,` or `>` @@ -43,7 +43,7 @@ LL | foo::<{ BAR - BAR }>(); | ^ ^ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/const-expression-suggest-missing-braces.rs:29:11 + --> $DIR/const-expression-suggest-missing-braces.rs:27:11 | LL | foo::<100 - BAR>(); | ^^^^^^^^^ @@ -54,7 +54,7 @@ LL | foo::<{ 100 - BAR }>(); | ^ ^ error: expected one of `,` or `>`, found `(` - --> $DIR/const-expression-suggest-missing-braces.rs:32:19 + --> $DIR/const-expression-suggest-missing-braces.rs:30:19 | LL | foo::()>(); | ^ expected one of `,` or `>` @@ -65,7 +65,7 @@ LL | foo::<{ bar() }>(); | ^ ^ error: expected one of `,` or `>`, found `(` - --> $DIR/const-expression-suggest-missing-braces.rs:35:21 + --> $DIR/const-expression-suggest-missing-braces.rs:33:21 | LL | foo::()>(); | ^ expected one of `,` or `>` @@ -76,7 +76,7 @@ LL | foo::<{ bar::() }>(); | ^ ^ error: expected one of `,` or `>`, found `(` - --> $DIR/const-expression-suggest-missing-braces.rs:38:21 + --> $DIR/const-expression-suggest-missing-braces.rs:36:21 | LL | foo::() + BAR>(); | ^ expected one of `,` or `>` @@ -87,7 +87,7 @@ LL | foo::<{ bar::() + BAR }>(); | ^ ^ error: expected one of `,` or `>`, found `(` - --> $DIR/const-expression-suggest-missing-braces.rs:41:21 + --> $DIR/const-expression-suggest-missing-braces.rs:39:21 | LL | foo::() - BAR>(); | ^ expected one of `,` or `>` @@ -98,7 +98,7 @@ LL | foo::<{ bar::() - BAR }>(); | ^ ^ error: expected one of `,` or `>`, found `-` - --> $DIR/const-expression-suggest-missing-braces.rs:44:15 + --> $DIR/const-expression-suggest-missing-braces.rs:42:15 | LL | foo::()>(); | ^ expected one of `,` or `>` @@ -109,7 +109,7 @@ LL | foo::<{ BAR - bar::() }>(); | ^ ^ error: expected one of `,` or `>`, found `-` - --> $DIR/const-expression-suggest-missing-braces.rs:47:15 + --> $DIR/const-expression-suggest-missing-braces.rs:45:15 | LL | foo::()>(); | ^ expected one of `,` or `>` @@ -120,19 +120,19 @@ LL | foo::<{ BAR - bar::() }>(); | ^ ^ error[E0404]: expected trait, found constant `BAR` - --> $DIR/const-expression-suggest-missing-braces.rs:13:11 + --> $DIR/const-expression-suggest-missing-braces.rs:11:11 | LL | foo::(); | ^^^ not a trait error[E0404]: expected trait, found constant `BAR` - --> $DIR/const-expression-suggest-missing-braces.rs:13:17 + --> $DIR/const-expression-suggest-missing-braces.rs:11:17 | LL | foo::(); | ^^^ not a trait warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/const-expression-suggest-missing-braces.rs:13:11 + --> $DIR/const-expression-suggest-missing-braces.rs:11:11 | LL | foo::(); | ^^^^^^^^^ help: use `dyn`: `dyn BAR + BAR` @@ -140,7 +140,7 @@ LL | foo::(); = note: `#[warn(bare_trait_objects)]` on by default error[E0747]: type provided when a constant was expected - --> $DIR/const-expression-suggest-missing-braces.rs:13:11 + --> $DIR/const-expression-suggest-missing-braces.rs:11:11 | LL | foo::(); | ^^^^^^^^^ diff --git a/src/test/ui/const-generics/min_const_generics/const_fn_in_generics.rs b/src/test/ui/const-generics/min_const_generics/const_fn_in_generics.rs index 3370666cc5cd..0c10af6c43f5 100644 --- a/src/test/ui/const-generics/min_const_generics/const_fn_in_generics.rs +++ b/src/test/ui/const-generics/min_const_generics/const_fn_in_generics.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(min_const_generics)] - const fn identity() -> u32 { T } #[derive(Eq, PartialEq, Debug)] diff --git a/src/test/ui/const-generics/min_const_generics/default_function_param.rs b/src/test/ui/const-generics/min_const_generics/default_function_param.rs index 7e0c1c2ed9fa..d7918a73ab8b 100644 --- a/src/test/ui/const-generics/min_const_generics/default_function_param.rs +++ b/src/test/ui/const-generics/min_const_generics/default_function_param.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - fn foo() {} //~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=` diff --git a/src/test/ui/const-generics/min_const_generics/default_function_param.stderr b/src/test/ui/const-generics/min_const_generics/default_function_param.stderr index ed1a83b6a4dd..8eb796d9bb7a 100644 --- a/src/test/ui/const-generics/min_const_generics/default_function_param.stderr +++ b/src/test/ui/const-generics/min_const_generics/default_function_param.stderr @@ -1,5 +1,5 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=` - --> $DIR/default_function_param.rs:3:26 + --> $DIR/default_function_param.rs:1:26 | LL | fn foo() {} | ^ expected one of 7 possible tokens diff --git a/src/test/ui/const-generics/min_const_generics/default_trait_param.rs b/src/test/ui/const-generics/min_const_generics/default_trait_param.rs index 322ddccbf189..c8003ad5d44d 100644 --- a/src/test/ui/const-generics/min_const_generics/default_trait_param.rs +++ b/src/test/ui/const-generics/min_const_generics/default_trait_param.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - trait Foo {} //~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=` diff --git a/src/test/ui/const-generics/min_const_generics/default_trait_param.stderr b/src/test/ui/const-generics/min_const_generics/default_trait_param.stderr index 49c3ac86744a..6d112ef1de03 100644 --- a/src/test/ui/const-generics/min_const_generics/default_trait_param.stderr +++ b/src/test/ui/const-generics/min_const_generics/default_trait_param.stderr @@ -1,5 +1,5 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=` - --> $DIR/default_trait_param.rs:3:28 + --> $DIR/default_trait_param.rs:1:28 | LL | trait Foo {} | ^ expected one of 7 possible tokens diff --git a/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs deleted file mode 100644 index 423deae46008..000000000000 --- a/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn test() {} -//~^ ERROR const generics are unstable - -fn main() {} diff --git a/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr deleted file mode 100644 index 7f82a960da25..000000000000 --- a/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: const generics are unstable - --> $DIR/feature-gate-min_const_generics.rs:1:15 - | -LL | fn test() {} - | ^ - | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/min_const_generics/forbid-non-static-lifetimes.rs b/src/test/ui/const-generics/min_const_generics/forbid-non-static-lifetimes.rs index 02944e2bff2f..881f8b98aad0 100644 --- a/src/test/ui/const-generics/min_const_generics/forbid-non-static-lifetimes.rs +++ b/src/test/ui/const-generics/min_const_generics/forbid-non-static-lifetimes.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - // This test checks that non-static lifetimes are prohibited under `min_const_generics`. It // currently emits an error with `min_const_generics`. This will ICE under `const_generics`. diff --git a/src/test/ui/const-generics/min_const_generics/forbid-non-static-lifetimes.stderr b/src/test/ui/const-generics/min_const_generics/forbid-non-static-lifetimes.stderr index cdfd491e3954..5def54ca26d9 100644 --- a/src/test/ui/const-generics/min_const_generics/forbid-non-static-lifetimes.stderr +++ b/src/test/ui/const-generics/min_const_generics/forbid-non-static-lifetimes.stderr @@ -1,5 +1,5 @@ error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/forbid-non-static-lifetimes.rs:9:22 + --> $DIR/forbid-non-static-lifetimes.rs:7:22 | LL | test::<{ let _: &'a (); 3 },>(); | ^^ @@ -8,7 +8,7 @@ LL | test::<{ let _: &'a (); 3 },>(); = help: add `#![feature(const_generics)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/forbid-non-static-lifetimes.rs:23:16 + --> $DIR/forbid-non-static-lifetimes.rs:21:16 | LL | [(); (|_: &'a u8| (), 0).1]; | ^^ diff --git a/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs b/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs index e59b97922bea..a120eee67ee4 100644 --- a/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs +++ b/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs @@ -1,4 +1,3 @@ -#![feature(min_const_generics)] use std::mem::transmute; fn get_flag() -> Option { diff --git a/src/test/ui/const-generics/min_const_generics/invalid-patterns.stderr b/src/test/ui/const-generics/min_const_generics/invalid-patterns.stderr index a3157c6b5644..04f716fa7335 100644 --- a/src/test/ui/const-generics/min_const_generics/invalid-patterns.stderr +++ b/src/test/ui/const-generics/min_const_generics/invalid-patterns.stderr @@ -1,29 +1,29 @@ error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:29:21 + --> $DIR/invalid-patterns.rs:28:21 | LL | get_flag::(); | ^^^^ expected `char`, found `u8` error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:31:14 + --> $DIR/invalid-patterns.rs:30:14 | LL | get_flag::<7, 'c'>(); | ^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:14 + --> $DIR/invalid-patterns.rs:32:14 | LL | get_flag::<42, 0x5ad>(); | ^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:18 + --> $DIR/invalid-patterns.rs:32:18 | LL | get_flag::<42, 0x5ad>(); | ^^^^^ expected `char`, found `u8` error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:38:21 + --> $DIR/invalid-patterns.rs:37:21 | LL | get_flag::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) @@ -31,7 +31,7 @@ LL | get_flag::(); = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:40:14 + --> $DIR/invalid-patterns.rs:39:14 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean @@ -39,7 +39,7 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:42:14 + --> $DIR/invalid-patterns.rs:41:14 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean @@ -47,7 +47,7 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:42:47 + --> $DIR/invalid-patterns.rs:41:47 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) diff --git a/src/test/ui/const-generics/min_const_generics/macro-fail.rs b/src/test/ui/const-generics/min_const_generics/macro-fail.rs index 1bd0c46f55e2..f83518fc9d47 100644 --- a/src/test/ui/const-generics/min_const_generics/macro-fail.rs +++ b/src/test/ui/const-generics/min_const_generics/macro-fail.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - struct Example; macro_rules! external_macro { diff --git a/src/test/ui/const-generics/min_const_generics/macro-fail.stderr b/src/test/ui/const-generics/min_const_generics/macro-fail.stderr index a5dedf6fe205..22930a352a86 100644 --- a/src/test/ui/const-generics/min_const_generics/macro-fail.stderr +++ b/src/test/ui/const-generics/min_const_generics/macro-fail.stderr @@ -1,5 +1,5 @@ error: expected type, found `{` - --> $DIR/macro-fail.rs:31:27 + --> $DIR/macro-fail.rs:29:27 | LL | fn make_marker() -> impl Marker { | ---------------------- @@ -13,7 +13,7 @@ LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }} = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: expected type, found `{` - --> $DIR/macro-fail.rs:31:27 + --> $DIR/macro-fail.rs:29:27 | LL | Example:: | ---------------------- @@ -27,7 +27,7 @@ LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }} = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: expected type, found `{` - --> $DIR/macro-fail.rs:6:10 + --> $DIR/macro-fail.rs:4:10 | LL | () => {{ | __________^ @@ -46,7 +46,7 @@ LL | let _fail = Example::; = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unexpected end of macro invocation - --> $DIR/macro-fail.rs:41:25 + --> $DIR/macro-fail.rs:39:25 | LL | macro_rules! gimme_a_const { | -------------------------- when calling this macro @@ -55,25 +55,25 @@ LL | let _fail = Example::; | ^^^^^^^^^^^^^^^^ missing tokens in macro arguments error[E0747]: type provided when a constant was expected - --> $DIR/macro-fail.rs:16:33 + --> $DIR/macro-fail.rs:14:33 | LL | fn make_marker() -> impl Marker { | ^^^^^^^^^^^^^^^^^^^^^^ error[E0747]: type provided when a constant was expected - --> $DIR/macro-fail.rs:18:13 + --> $DIR/macro-fail.rs:16:13 | LL | Example:: | ^^^^^^^^^^^^^^^^^^^^^^ error[E0747]: type provided when a constant was expected - --> $DIR/macro-fail.rs:38:25 + --> $DIR/macro-fail.rs:36:25 | LL | let _fail = Example::; | ^^^^^^^^^^^^^^^^^ error[E0747]: type provided when a constant was expected - --> $DIR/macro-fail.rs:41:25 + --> $DIR/macro-fail.rs:39:25 | LL | let _fail = Example::; | ^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/min_const_generics/macro.rs b/src/test/ui/const-generics/min_const_generics/macro.rs index 575fbd33572f..9b63f76987a0 100644 --- a/src/test/ui/const-generics/min_const_generics/macro.rs +++ b/src/test/ui/const-generics/min_const_generics/macro.rs @@ -1,6 +1,4 @@ // run-pass -#![feature(min_const_generics)] - struct Example; macro_rules! external_macro { diff --git a/src/test/ui/const-generics/min_const_generics/self-ty-in-const-1.rs b/src/test/ui/const-generics/min_const_generics/self-ty-in-const-1.rs index 0973b373c122..9ef619365a08 100644 --- a/src/test/ui/const-generics/min_const_generics/self-ty-in-const-1.rs +++ b/src/test/ui/const-generics/min_const_generics/self-ty-in-const-1.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - trait Foo { fn t1() -> [u8; std::mem::size_of::()]; //~ERROR generic parameters } diff --git a/src/test/ui/const-generics/min_const_generics/self-ty-in-const-1.stderr b/src/test/ui/const-generics/min_const_generics/self-ty-in-const-1.stderr index 40c73f0b9513..4fdfb5fbcb1a 100644 --- a/src/test/ui/const-generics/min_const_generics/self-ty-in-const-1.stderr +++ b/src/test/ui/const-generics/min_const_generics/self-ty-in-const-1.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/self-ty-in-const-1.rs:4:41 + --> $DIR/self-ty-in-const-1.rs:2:41 | LL | fn t1() -> [u8; std::mem::size_of::()]; | ^^^^ cannot perform const operation using `Self` @@ -8,13 +8,13 @@ LL | fn t1() -> [u8; std::mem::size_of::()]; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic `Self` types are currently not permitted in anonymous constants - --> $DIR/self-ty-in-const-1.rs:14:41 + --> $DIR/self-ty-in-const-1.rs:12:41 | LL | fn t3() -> [u8; std::mem::size_of::()] {} | ^^^^ | note: not a concrete type - --> $DIR/self-ty-in-const-1.rs:13:9 + --> $DIR/self-ty-in-const-1.rs:11:9 | LL | impl Bar { | ^^^^^^ diff --git a/src/test/ui/const-generics/min_const_generics/self-ty-in-const-2.rs b/src/test/ui/const-generics/min_const_generics/self-ty-in-const-2.rs index e7f80d50082b..286ec2d24508 100644 --- a/src/test/ui/const-generics/min_const_generics/self-ty-in-const-2.rs +++ b/src/test/ui/const-generics/min_const_generics/self-ty-in-const-2.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - struct Bar(T); trait Baz { diff --git a/src/test/ui/const-generics/min_const_generics/self-ty-in-const-2.stderr b/src/test/ui/const-generics/min_const_generics/self-ty-in-const-2.stderr index 9ac6410a290a..41546292c47d 100644 --- a/src/test/ui/const-generics/min_const_generics/self-ty-in-const-2.stderr +++ b/src/test/ui/const-generics/min_const_generics/self-ty-in-const-2.stderr @@ -1,11 +1,11 @@ error: generic `Self` types are currently not permitted in anonymous constants - --> $DIR/self-ty-in-const-2.rs:17:41 + --> $DIR/self-ty-in-const-2.rs:15:41 | LL | let _: [u8; std::mem::size_of::()]; | ^^^^ | note: not a concrete type - --> $DIR/self-ty-in-const-2.rs:15:17 + --> $DIR/self-ty-in-const-2.rs:13:17 | LL | impl Baz for Bar { | ^^^^^^ diff --git a/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.rs b/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.rs index 0ef17109bed4..7518dc59e599 100644 --- a/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.rs +++ b/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - fn a() {} //~^ ERROR `&'static [u32]` is forbidden as the type of a const generic parameter diff --git a/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr b/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr index 6c39f6b4c1dc..05939b05bba3 100644 --- a/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr +++ b/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr @@ -1,5 +1,5 @@ error: `&'static [u32]` is forbidden as the type of a const generic parameter - --> $DIR/static-reference-array-const-param.rs:3:15 + --> $DIR/static-reference-array-const-param.rs:1:15 | LL | fn a() {} | ^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.rs b/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.rs index dfa1ece2f365..560795a51f58 100644 --- a/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.rs +++ b/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.rs @@ -1,5 +1,3 @@ -#![feature(min_const_generics)] - struct Const; //~^ ERROR `&'static ()` is forbidden as the type of a const generic parameter diff --git a/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr b/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr index 6b90329b72ce..8724c7e33b1c 100644 --- a/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr +++ b/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr @@ -1,5 +1,5 @@ error: `&'static ()` is forbidden as the type of a const generic parameter - --> $DIR/transmute-const-param-static-reference.rs:3:23 + --> $DIR/transmute-const-param-static-reference.rs:1:23 | LL | struct Const; | ^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/mut-ref-const-param-array.rs b/src/test/ui/const-generics/mut-ref-const-param-array.rs index cf24cbe7e82b..6a5739db3aef 100644 --- a/src/test/ui/const-generics/mut-ref-const-param-array.rs +++ b/src/test/ui/const-generics/mut-ref-const-param-array.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::ops::AddAssign; diff --git a/src/test/ui/const-generics/nested-type.full.stderr b/src/test/ui/const-generics/nested-type.full.stderr index 06ab9a6ff291..9d7ca36545c9 100644 --- a/src/test/ui/const-generics/nested-type.full.stderr +++ b/src/test/ui/const-generics/nested-type.full.stderr @@ -1,5 +1,5 @@ error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants - --> $DIR/nested-type.rs:16:5 + --> $DIR/nested-type.rs:15:5 | LL | Foo::<17>::value() | ^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/nested-type.min.stderr b/src/test/ui/const-generics/nested-type.min.stderr index 369e387508e9..dabb3f245f5b 100644 --- a/src/test/ui/const-generics/nested-type.min.stderr +++ b/src/test/ui/const-generics/nested-type.min.stderr @@ -1,5 +1,5 @@ error: `[u8; _]` is forbidden as the type of a const generic parameter - --> $DIR/nested-type.rs:7:21 + --> $DIR/nested-type.rs:6:21 | LL | struct Foo; = help: more complex types are supported with `#[feature(const_generics)]` error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants - --> $DIR/nested-type.rs:16:5 + --> $DIR/nested-type.rs:15:5 | LL | Foo::<17>::value() | ^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/nested-type.rs b/src/test/ui/const-generics/nested-type.rs index c5660983985c..be8ebb7f401f 100644 --- a/src/test/ui/const-generics/nested-type.rs +++ b/src/test/ui/const-generics/nested-type.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo; diff --git a/src/test/ui/const-generics/occurs-check/unify-fixpoint.stderr b/src/test/ui/const-generics/occurs-check/unify-fixpoint.stderr index 8a1462c59e81..671f1103dcca 100644 --- a/src/test/ui/const-generics/occurs-check/unify-fixpoint.stderr +++ b/src/test/ui/const-generics/occurs-check/unify-fixpoint.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error: constant expression depends on a generic parameter --> $DIR/unify-fixpoint.rs:9:32 diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-5.rs b/src/test/ui/const-generics/occurs-check/unused-substs-5.rs new file mode 100644 index 000000000000..e5d487d89b9f --- /dev/null +++ b/src/test/ui/const-generics/occurs-check/unused-substs-5.rs @@ -0,0 +1,20 @@ +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +// `N + 1` also depends on `T` here even if it doesn't use it. +fn q(_: T) -> [u8; N + 1] { + todo!() +} + +fn supplier() -> T { + todo!() +} + +fn catch_me() where [u8; N + 1]: Default { + let mut x = supplier(); + x = q::<_, N>(x); //~ ERROR mismatched types +} + +fn main() { + catch_me::<3>(); +} diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-5.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-5.stderr new file mode 100644 index 000000000000..239569dab096 --- /dev/null +++ b/src/test/ui/const-generics/occurs-check/unused-substs-5.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/unused-substs-5.rs:15:9 + | +LL | x = q::<_, N>(x); + | ^^^^^^^^^^^^ + | | + | cyclic type of infinite size + | help: try using a conversion method: `q::<_, N>(x).to_vec()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr index 3dccfd73dccc..debb272da360 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr @@ -1,5 +1,5 @@ error: type parameters with a default must be trailing - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:12:12 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12 | LL | struct Bar(T); | ^ @@ -7,13 +7,13 @@ LL | struct Bar(T); = note: using type defaults and const parameters in the same parameter list is currently not permitted error: constant values inside of type parameter defaults must not depend on generic parameters - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:7:44 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44 | LL | struct Foo()]>(T, U); | ^ the anonymous constant must not depend on the parameter `T` error: constant values inside of type parameter defaults must not depend on generic parameters - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:12:21 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21 | LL | struct Bar(T); | ^ the anonymous constant must not depend on the parameter `N` diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr index 9e0837a0a620..171efca19383 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr @@ -1,5 +1,5 @@ error: type parameters with a default must be trailing - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:12:12 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12 | LL | struct Bar(T); | ^ @@ -7,7 +7,7 @@ LL | struct Bar(T); = note: using type defaults and const parameters in the same parameter list is currently not permitted error: generic parameters may not be used in const operations - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:7:44 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44 | LL | struct Foo()]>(T, U); | ^ cannot perform const operation using `T` @@ -16,7 +16,7 @@ LL | struct Foo()]>(T, U); = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: constant values inside of type parameter defaults must not depend on generic parameters - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:12:21 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21 | LL | struct Bar(T); | ^ the anonymous constant must not depend on the parameter `N` diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs index 51f0cff3f215..845c6111b596 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo()]>(T, U); //[full]~^ ERROR constant values inside of type parameter defaults diff --git a/src/test/ui/const-generics/promotion.rs b/src/test/ui/const-generics/promotion.rs index ac568bb75f00..ce9a1a0feb42 100644 --- a/src/test/ui/const-generics/promotion.rs +++ b/src/test/ui/const-generics/promotion.rs @@ -1,7 +1,5 @@ // run-pass // tests that promoting expressions containing const parameters is allowed. -#![feature(min_const_generics)] - fn promotion_test() -> &'static usize { &(3 + N) } diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr index ffaab51f766d..04bc46cb4ab1 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr @@ -1,11 +1,11 @@ error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param-deref.rs:10:23 + --> $DIR/raw-ptr-const-param-deref.rs:9:23 | LL | struct Const; | ^^^^^^^^^^ error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param-deref.rs:12:15 + --> $DIR/raw-ptr-const-param-deref.rs:11:15 | LL | impl Const

{ | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.min.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.min.stderr index ffaab51f766d..04bc46cb4ab1 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.min.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.min.stderr @@ -1,11 +1,11 @@ error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param-deref.rs:10:23 + --> $DIR/raw-ptr-const-param-deref.rs:9:23 | LL | struct Const; | ^^^^^^^^^^ error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param-deref.rs:12:15 + --> $DIR/raw-ptr-const-param-deref.rs:11:15 | LL | impl Const

+ ng-repeat="lint in data | filter:byLevels | filter:byGroups | filter:bySearch | orderBy:'id' track by lint.id">

@@ -215,6 +215,46 @@ return $scope.groups[lint.group]; }; + $scope.bySearch = function (lint, index, array) { + let search_str = $scope.search; + // It can be `null` I haven't missed this value + if (search_str == null || search_str.length == 0) { + return true; + } + search_str = search_str.toLowerCase(); + + // Search by id + let id_search = search_str.trim().replace(/(\-| )/g, "_"); + if (lint.id.includes(id_search)) { + return true; + } + + // Search the description + // The use of `for`-loops instead of `foreach` enables us to return early + let search_lint = (lint, therm) => { + for (const field in lint.docs) { + // Continue if it's not a property + if (!lint.docs.hasOwnProperty(field)) { + continue; + } + + // Return if not found + if (lint.docs[field].toLowerCase().includes(therm)) { + return true; + } + } + return false; + }; + let therms = search_str.split(" "); + for (index = 0; index < therms.length; index++) { + if (!search_lint(lint, therms[index])) { + return false; + } + } + + return true; + } + // Get data $scope.open = {}; $scope.loading = true; diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index eba02333c8cb..43dbaeb46555 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -10,8 +10,6 @@ use test::ColorConfig; #[derive(Clone, Copy, PartialEq, Debug)] pub enum Mode { - CompileFail, - RunFail, RunPassValgrind, Pretty, DebugInfo, @@ -42,8 +40,6 @@ impl FromStr for Mode { type Err = (); fn from_str(s: &str) -> Result { match s { - "compile-fail" => Ok(CompileFail), - "run-fail" => Ok(RunFail), "run-pass-valgrind" => Ok(RunPassValgrind), "pretty" => Ok(Pretty), "debuginfo" => Ok(DebugInfo), @@ -65,8 +61,6 @@ impl FromStr for Mode { impl fmt::Display for Mode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match *self { - CompileFail => "compile-fail", - RunFail => "run-fail", RunPassValgrind => "run-pass-valgrind", Pretty => "pretty", DebugInfo => "debuginfo", @@ -127,6 +121,8 @@ pub enum CompareMode { Nll, Polonius, Chalk, + SplitDwarf, + SplitDwarfSingle, } impl CompareMode { @@ -135,6 +131,8 @@ impl CompareMode { CompareMode::Nll => "nll", CompareMode::Polonius => "polonius", CompareMode::Chalk => "chalk", + CompareMode::SplitDwarf => "split-dwarf", + CompareMode::SplitDwarfSingle => "split-dwarf-single", } } @@ -143,6 +141,8 @@ impl CompareMode { "nll" => CompareMode::Nll, "polonius" => CompareMode::Polonius, "chalk" => CompareMode::Chalk, + "split-dwarf" => CompareMode::SplitDwarf, + "split-dwarf-single" => CompareMode::SplitDwarfSingle, x => panic!("unknown --compare-mode option: {}", x), } } @@ -224,7 +224,7 @@ pub struct Config { /// The name of the stage being built (stage1, etc) pub stage_id: String, - /// The test mode, compile-fail, run-fail, ui + /// The test mode, e.g. ui or debuginfo. pub mode: Mode, /// The test suite (essentially which directory is running, but without the @@ -327,6 +327,9 @@ pub struct Config { /// created in `//rustfix_missing_coverage.txt` pub rustfix_coverage: bool, + /// whether to run `tidy` when a rustdoc test fails + pub has_tidy: bool, + // Configuration for various run-make tests frobbing things like C compilers // or querying about various LLVM component information. pub cc: String, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 59f64e7df0f4..2eba91fd1f4c 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -542,10 +542,7 @@ impl TestProps { } if self.failure_status == -1 { - self.failure_status = match config.mode { - Mode::RunFail => 101, - _ => 1, - }; + self.failure_status = 1; } if self.should_ice { self.failure_status = 101; @@ -852,6 +849,8 @@ impl Config { Some(CompareMode::Nll) => name == "compare-mode-nll", Some(CompareMode::Polonius) => name == "compare-mode-polonius", Some(CompareMode::Chalk) => name == "compare-mode-chalk", + Some(CompareMode::SplitDwarf) => name == "compare-mode-split-dwarf", + Some(CompareMode::SplitDwarfSingle) => name == "compare-mode-split-dwarf-single", None => false, } || (cfg!(debug_assertions) && name == "debug") || diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 5177dae8a660..aefcfe222e50 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -14,7 +14,7 @@ use std::ffi::OsString; use std::fs; use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; -use std::process::Command; +use std::process::{Command, Stdio}; use std::time::SystemTime; use test::ColorConfig; use tracing::*; @@ -43,6 +43,10 @@ fn main() { panic!("Can't find Valgrind to run Valgrind tests"); } + if !config.has_tidy && config.mode == Mode::Rustdoc { + eprintln!("warning: `tidy` is not installed; generated diffs will be harder to read"); + } + log_config(&config); run_tests(config); } @@ -67,7 +71,7 @@ pub fn parse_config(args: Vec) -> Config { "", "mode", "which sort of compile tests to run", - "compile-fail | run-fail | run-pass-valgrind | pretty | debug-info | codegen | rustdoc \ + "run-pass-valgrind | pretty | debug-info | codegen | rustdoc \ | rustdoc-json | codegen-units | incremental | run-make | ui | js-doc-test | mir-opt | assembly", ) .reqopt( @@ -189,6 +193,11 @@ pub fn parse_config(args: Vec) -> Config { let src_base = opt_path(matches, "src-base"); let run_ignored = matches.opt_present("ignored"); + let has_tidy = Command::new("tidy") + .arg("--version") + .stdout(Stdio::null()) + .status() + .map_or(false, |status| status.success()); Config { bless: matches.opt_present("bless"), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), @@ -244,6 +253,7 @@ pub fn parse_config(args: Vec) -> Config { remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from), compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse), rustfix_coverage: matches.opt_present("rustfix-coverage"), + has_tidy, cc: matches.opt_str("cc").unwrap(), cxx: matches.opt_str("cxx").unwrap(), diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index e9089b4b15ba..9f31b3ae1b1a 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -5,8 +5,8 @@ use crate::common::{output_base_dir, output_base_name, output_testname_unique}; use crate::common::{Assembly, Incremental, JsDocTest, MirOpt, RunMake, RustdocJson, Ui}; use crate::common::{Codegen, CodegenUnits, DebugInfo, Debugger, Rustdoc}; use crate::common::{CompareMode, FailMode, PassMode}; -use crate::common::{CompileFail, Pretty, RunFail, RunPassValgrind}; use crate::common::{Config, TestPaths}; +use crate::common::{Pretty, RunPassValgrind}; use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT}; use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; @@ -330,13 +330,11 @@ impl<'test> TestCx<'test> { /// revisions, exactly once, with revision == None). fn run_revision(&self) { if self.props.should_ice { - if self.config.mode != CompileFail && self.config.mode != Incremental { + if self.config.mode != Incremental { self.fatal("cannot use should-ice in a test that is not cfail"); } } match self.config.mode { - CompileFail => self.run_cfail_test(), - RunFail => self.run_rfail_test(), RunPassValgrind => self.run_valgrind_test(), Pretty => self.run_pretty_test(), DebugInfo => self.run_debuginfo_test(), @@ -377,7 +375,6 @@ impl<'test> TestCx<'test> { fn should_compile_successfully(&self, pm: Option) -> bool { match self.config.mode { - CompileFail => false, JsDocTest => true, Ui => pm.is_some() || self.props.fail_mode > Some(FailMode::Build), Incremental => { @@ -1537,8 +1534,8 @@ impl<'test> TestCx<'test> { }; let allow_unused = match self.config.mode { - CompileFail | Ui => { - // compile-fail and ui tests tend to have tons of unused code as + Ui => { + // UI tests tend to have tons of unused code as // it's just testing various pieces of the compile, but we don't // want to actually assert warnings about all this code. Instead // let's just ignore unused code warnings by defaults and tests @@ -1940,7 +1937,7 @@ impl<'test> TestCx<'test> { } match self.config.mode { - CompileFail | Incremental => { + Incremental => { // If we are extracting and matching errors in the new // fashion, then you want JSON mode. Old-skool error // patterns still match the raw compiler output. @@ -1975,8 +1972,8 @@ impl<'test> TestCx<'test> { rustc.arg(dir_opt); } - RunFail | RunPassValgrind | Pretty | DebugInfo | Codegen | Rustdoc | RustdocJson - | RunMake | CodegenUnits | JsDocTest | Assembly => { + RunPassValgrind | Pretty | DebugInfo | Codegen | Rustdoc | RustdocJson | RunMake + | CodegenUnits | JsDocTest | Assembly => { // do not use JSON output } } @@ -2017,6 +2014,12 @@ impl<'test> TestCx<'test> { Some(CompareMode::Chalk) => { rustc.args(&["-Zchalk"]); } + Some(CompareMode::SplitDwarf) => { + rustc.args(&["-Zsplit-dwarf=split"]); + } + Some(CompareMode::SplitDwarfSingle) => { + rustc.args(&["-Zsplit-dwarf=single"]); + } None => {} } @@ -2394,7 +2397,8 @@ impl<'test> TestCx<'test> { let proc_res = new_rustdoc.document(&compare_dir); if !proc_res.status.success() { - proc_res.fatal(Some("failed to run nightly rustdoc"), || ()); + eprintln!("failed to run nightly rustdoc"); + return; } #[rustfmt::skip] @@ -2408,28 +2412,22 @@ impl<'test> TestCx<'test> { "-modify", ]; let tidy_dir = |dir| { - let tidy = |file: &_| { - Command::new("tidy") - .args(&tidy_args) - .arg(file) - .spawn() - .unwrap_or_else(|err| { - self.fatal(&format!("failed to run tidy - is it installed? - {}", err)) - }) - .wait() - .unwrap() - }; for entry in walkdir::WalkDir::new(dir) { let entry = entry.expect("failed to read file"); if entry.file_type().is_file() && entry.path().extension().and_then(|p| p.to_str()) == Some("html".into()) { - tidy(entry.path()); + let status = + Command::new("tidy").args(&tidy_args).arg(entry.path()).status().unwrap(); + // `tidy` returns 1 if it modified the file. + assert!(status.success() || status.code() == Some(1)); } } }; - tidy_dir(out_dir); - tidy_dir(&compare_dir); + if self.config.has_tidy { + tidy_dir(out_dir); + tidy_dir(&compare_dir); + } let pager = { let output = Command::new("git").args(&["config", "--get", "core.pager"]).output().ok(); @@ -2442,7 +2440,8 @@ impl<'test> TestCx<'test> { }) }; let mut diff = Command::new("diff"); - diff.args(&["-u", "-r"]).args(&[out_dir, &compare_dir]); + // diff recursively, showing context, and excluding .css files + diff.args(&["-u", "-r", "-x", "*.css"]).args(&[&compare_dir, out_dir]); let output = if let Some(pager) = pager { let diff_pid = diff.stdout(Stdio::piped()).spawn().expect("failed to run `diff`"); diff --git a/src/tools/expand-yaml-anchors/src/main.rs b/src/tools/expand-yaml-anchors/src/main.rs index f7ff64036a1a..f8cf18a9309e 100644 --- a/src/tools/expand-yaml-anchors/src/main.rs +++ b/src/tools/expand-yaml-anchors/src/main.rs @@ -87,7 +87,8 @@ impl App { let content = std::fs::read_to_string(source) .with_context(|| format!("failed to read {}", self.path(source)))?; - let mut buf = HEADER_MESSAGE.replace("{source}", &self.path(source).to_string()); + let mut buf = + HEADER_MESSAGE.replace("{source}", &self.path(source).to_string().replace("\\", "/")); let documents = YamlLoader::load_from_str(&content) .with_context(|| format!("failed to parse {}", self.path(source)))?; diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index f213944e0ab6..dcfe1bb803fb 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -14,6 +14,8 @@ //! A few exceptions are allowed as there's known bugs in rustdoc, but this //! should catch the majority of "broken link" cases. +#![feature(str_split_once)] + use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::env; @@ -232,11 +234,12 @@ fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Opti { return; } - let mut parts = url.splitn(2, '#'); - let url = parts.next().unwrap(); - let fragment = parts.next(); - let mut parts = url.splitn(2, '?'); - let url = parts.next().unwrap(); + let (url, fragment) = match url.split_once('#') { + None => (url, None), + Some((url, fragment)) => (url, Some(fragment)), + }; + // NB: the `splitn` always succeeds, even if the delimiter is not present. + let url = url.splitn(2, '?').next().unwrap(); // Once we've plucked out the URL, parse it using our base url and // then try to extract a file path. diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs index 0a69b18a3325..e8fd19a63812 100644 --- a/src/tools/lint-docs/src/groups.rs +++ b/src/tools/lint-docs/src/groups.rs @@ -116,13 +116,23 @@ impl<'a> LintExtractor<'a> { result.push('\n'); result.push_str("[warn-by-default]: listing/warn-by-default.md\n"); for lint_name in to_link { - let lint_def = - lints.iter().find(|l| l.name == lint_name.replace("-", "_")).ok_or_else(|| { - format!( - "`rustc -W help` defined lint `{}` but that lint does not appear to exist", + let lint_def = match lints.iter().find(|l| l.name == lint_name.replace("-", "_")) { + Some(def) => def, + None => { + let msg = format!( + "`rustc -W help` defined lint `{}` but that lint does not \ + appear to exist\n\ + Check that the lint definition includes the appropriate doc comments.", lint_name - ) - })?; + ); + if self.validate { + return Err(msg.into()); + } else { + eprintln!("warning: {}", msg); + continue; + } + } + }; write!( result, "[{}]: listing/{}#{}\n", diff --git a/src/tools/miri b/src/tools/miri index e54c5db4f0ed..2065b52dfef3 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit e54c5db4f0edbe51db42d2c3e63e9821537ed4f4 +Subproject commit 2065b52dfef3cd5a5216e65c21a056a69574bddc diff --git a/src/tools/rust-installer b/src/tools/rust-installer index d66f476b4d5e..5254dbfd25d5 160000 --- a/src/tools/rust-installer +++ b/src/tools/rust-installer @@ -1 +1 @@ -Subproject commit d66f476b4d5e7fdf1ec215c9ac16c923dc292324 +Subproject commit 5254dbfd25d5284728ab624dca1969d61427a0db diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index 11b175f9e80e..1cde0e25cedc 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -65,6 +65,8 @@ byteorder = { version = "1", features = ['default', 'std'] } curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true } crossbeam-utils = { version = "0.7.2", features = ["nightly"] } libc = { version = "0.2.79", features = ["align"] } +# Ensure default features of libz-sys, which are disabled in some scenarios. +libz-sys = { version = "1.1.2" } proc-macro2 = { version = "1", features = ["default"] } quote = { version = "1", features = ["default"] } serde = { version = "1.0.82", features = ['derive'] } diff --git a/src/tools/rustfmt b/src/tools/rustfmt index 70ce18255f42..acd94866fd0f 160000 --- a/src/tools/rustfmt +++ b/src/tools/rustfmt @@ -1 +1 @@ -Subproject commit 70ce18255f429caf0d75ecfed8c1464535ee779b +Subproject commit acd94866fd0ff5eacb7e184ae21c19e5440fc5fb diff --git a/src/tools/tidy/src/cargo.rs b/src/tools/tidy/src/cargo.rs index 7bdd78a91e7d..e06616a59f38 100644 --- a/src/tools/tidy/src/cargo.rs +++ b/src/tools/tidy/src/cargo.rs @@ -59,11 +59,10 @@ fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) { break; } - let mut parts = line.splitn(2, '='); - let krate = parts.next().unwrap().trim(); - if parts.next().is_none() { - continue; - } + let krate = match line.split_once('=') { + None => continue, + Some((krate, _)) => krate.trim(), + }; // Don't worry about depending on core/std while not writing `extern crate // core/std` -- that's intentional. diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 057b0884e287..4b521985ca1a 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -104,6 +104,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "getopts", "getrandom", "gimli", + "gsgdt", "hashbrown", "hermit-abi", "humantime", @@ -214,12 +215,12 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) { for (name, license) in EXCEPTIONS { // Check that the package actually exists. if !metadata.packages.iter().any(|p| p.name == *name) { - println!( + tidy_error!( + bad, "could not find exception package `{}`\n\ Remove from EXCEPTIONS list if it is no longer used.", name ); - *bad = true; } // Check that the license hasn't changed. for pkg in metadata.packages.iter().filter(|p| p.name == *name) { @@ -232,11 +233,11 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) { } match &pkg.license { None => { - println!( + tidy_error!( + bad, "dependency exception `{}` does not declare a license expression", pkg.id ); - *bad = true; } Some(pkg_license) => { if pkg_license.as_str() != *license { @@ -273,8 +274,7 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) { let license = match &pkg.license { Some(license) => license, None => { - println!("dependency `{}` does not define a license expression", pkg.id,); - *bad = true; + tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id); continue; } }; @@ -286,8 +286,7 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) { // general, these should never be added. continue; } - println!("invalid license `{}` in `{}`", license, pkg.id); - *bad = true; + tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id); } } } @@ -300,12 +299,12 @@ fn check_dependencies(metadata: &Metadata, bad: &mut bool) { // Check that the PERMITTED_DEPENDENCIES does not have unused entries. for name in PERMITTED_DEPENDENCIES { if !metadata.packages.iter().any(|p| p.name == *name) { - println!( + tidy_error!( + bad, "could not find allowed package `{}`\n\ Remove from PERMITTED_DEPENDENCIES list if it is no longer used.", name ); - *bad = true; } } // Get the list in a convenient form. @@ -322,11 +321,10 @@ fn check_dependencies(metadata: &Metadata, bad: &mut bool) { } if !unapproved.is_empty() { - println!("Dependencies not explicitly permitted:"); + tidy_error!(bad, "Dependencies not explicitly permitted:"); for dep in unapproved { println!("* {}", dep); } - *bad = true; } } @@ -381,16 +379,17 @@ fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) { let matches: Vec<_> = metadata.packages.iter().filter(|pkg| pkg.name == name).collect(); match matches.len() { 0 => { - println!( + tidy_error!( + bad, "crate `{}` is missing, update `check_crate_duplicate` \ if it is no longer used", name ); - *bad = true; } 1 => {} _ => { - println!( + tidy_error!( + bad, "crate `{}` is duplicated in `Cargo.lock`, \ it is too expensive to build multiple times, \ so make sure only one version appears across all dependencies", @@ -399,7 +398,6 @@ fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) { for pkg in matches { println!(" * {}", pkg.id); } - *bad = true; } } } diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs index 82a5234ac5b2..a7199fdfce66 100644 --- a/src/tools/tidy/src/error_codes_check.rs +++ b/src/tools/tidy/src/error_codes_check.rs @@ -85,47 +85,61 @@ fn extract_error_codes( for line in f.lines() { let s = line.trim(); if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") { - if let Some(err_code) = s.splitn(2, ':').next() { - let err_code = err_code.to_owned(); - if !error_codes.contains_key(&err_code) { - error_codes.insert(err_code.clone(), false); + let err_code = s + .split_once(':') + .expect( + format!( + "Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} without a `:` delimiter", + s, + ).as_str() + ) + .0 + .to_owned(); + if !error_codes.contains_key(&err_code) { + error_codes.insert(err_code.clone(), false); + } + // Now we extract the tests from the markdown file! + let md_file_name = match s.split_once("include_str!(\"") { + None => continue, + Some((_, md)) => match md.split_once("\")") { + None => continue, + Some((file_name, _)) => file_name, + }, + }; + let path = some_or_continue!(path.parent()) + .join(md_file_name) + .canonicalize() + .expect("failed to canonicalize error explanation file path"); + match read_to_string(&path) { + Ok(content) => { + if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) + && !check_if_error_code_is_test_in_explanation(&content, &err_code) + { + errors.push(format!( + "`{}` doesn't use its own error code in compile_fail example", + path.display(), + )); + } + if check_error_code_explanation(&content, error_codes, err_code) { + errors.push(format!( + "`{}` uses invalid tag `compile-fail` instead of `compile_fail`", + path.display(), + )); + } } - // Now we extract the tests from the markdown file! - let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1)); - let md_file_name = some_or_continue!(md.splitn(2, "\")").next()); - let path = some_or_continue!(path.parent()) - .join(md_file_name) - .canonicalize() - .expect("failed to canonicalize error explanation file path"); - match read_to_string(&path) { - Ok(content) => { - if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) - && !check_if_error_code_is_test_in_explanation(&content, &err_code) - { - errors.push(format!( - "`{}` doesn't use its own error code in compile_fail example", - path.display(), - )); - } - if check_error_code_explanation(&content, error_codes, err_code) { - errors.push(format!( - "`{}` uses invalid tag `compile-fail` instead of `compile_fail`", - path.display(), - )); - } - } - Err(e) => { - eprintln!("Couldn't read `{}`: {}", path.display(), e); - } + Err(e) => { + eprintln!("Couldn't read `{}`: {}", path.display(), e); } } } else if reached_no_explanation && s.starts_with('E') { - if let Some(err_code) = s.splitn(2, ',').next() { - let err_code = err_code.to_owned(); - if !error_codes.contains_key(&err_code) { - // this check should *never* fail! - error_codes.insert(err_code, false); - } + let err_code = match s.split_once(',') { + None => s, + Some((err_code, _)) => err_code, + } + .to_string(); + if !error_codes.contains_key(&err_code) { + // this check should *never* fail! + error_codes.insert(err_code, false); } } else if s == ";" { reached_no_explanation = true; @@ -137,12 +151,15 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap continue, + Some((err_code, _)) => match err_code.split_once('[') { + None => continue, + Some((_, err_code)) => err_code, + }, + }; + let nb = error_codes.entry(err_code.to_owned()).or_insert(false); + *nb = true; } } } diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 1cf0d24e26ff..aad57cacbb41 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -23,12 +23,11 @@ pub fn check(root: &Path, bad: &mut bool) { } // Extract source value. - let source = line.splitn(2, '=').nth(1).unwrap().trim(); + let source = line.split_once('=').unwrap().1.trim(); // Ensure source is allowed. if !ALLOWED_SOURCES.contains(&&*source) { - println!("invalid source: {}", source); - *bad = true; + tidy_error!(bad, "invalid source: {}", source); } } } diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index d8029ea04f0b..384a291a777c 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -85,11 +85,7 @@ pub fn check( assert!(!lib_features.is_empty()); super::walk_many( - &[ - &src_path.join("test/ui"), - &src_path.join("test/ui-fulldeps"), - &src_path.join("test/compile-fail"), - ], + &[&src_path.join("test/ui"), &src_path.join("test/ui-fulldeps")], &mut |path| super::filter_dirs(path), &mut |entry, contents| { let file = entry.path(); @@ -112,6 +108,7 @@ pub fn check( let gate_test_str = "gate-test-"; let feature_name = match line.find(gate_test_str) { + // NB: the `splitn` always succeeds, even if the delimiter is not present. Some(i) => line[i + gate_test_str.len()..].splitn(2, ' ').next().unwrap(), None => continue, }; @@ -329,7 +326,6 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features let issue_str = parts.next().unwrap().trim(); let tracking_issue = if issue_str.starts_with("None") { if level == Status::Unstable && !next_feature_omits_tracking_issue { - *bad = true; tidy_error!( bad, "{}:{}: no tracking issue for feature {}", diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index cc4c43f0468a..d282d240d823 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -3,6 +3,8 @@ //! This library contains the tidy lints and exposes it //! to be used by tools. +#![feature(str_split_once)] + use std::fs::File; use std::io::Read; use walkdir::{DirEntry, WalkDir}; @@ -26,6 +28,10 @@ macro_rules! t { } macro_rules! tidy_error { + ($bad:expr, $fmt:expr) => ({ + *$bad = true; + eprintln!("tidy error: {}", $fmt); + }); ($bad:expr, $fmt:expr, $($arg:tt)*) => ({ *$bad = true; eprint!("tidy error: "); diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index e1525f8e1bf2..080e16316242 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -35,9 +35,12 @@ fn main() { // Checks that only make sense for the std libs. pal::check(&library_path, &mut bad); - unit_tests::check(&library_path, &mut bad); // Checks that need to be done for both the compiler and std libraries. + unit_tests::check(&src_path, &mut bad); + unit_tests::check(&compiler_path, &mut bad); + unit_tests::check(&library_path, &mut bad); + bins::check(&src_path, &output_directory, &mut bad); bins::check(&compiler_path, &output_directory, &mut bad); bins::check(&library_path, &output_directory, &mut bad); diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 47b328dae47f..d8d2b449fee8 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -1,9 +1,51 @@ -//! Tidy check to ensure that there are no stray `.stderr` files in UI test directories. +//! Tidy check to ensure below in UI test directories: +//! - the number of entries in each directory must be less than `ENTRY_LIMIT` +//! - there are no stray `.stderr` files use std::fs; use std::path::Path; +const ENTRY_LIMIT: usize = 1000; +// FIXME: The following limits should be reduced eventually. +const ROOT_ENTRY_LIMIT: usize = 1580; +const ISSUES_ENTRY_LIMIT: usize = 2830; + +fn check_entries(path: &Path, bad: &mut bool) { + let dirs = walkdir::WalkDir::new(&path.join("test/ui")) + .into_iter() + .filter_entry(|e| e.file_type().is_dir()); + for dir in dirs { + if let Ok(dir) = dir { + let dir_path = dir.path(); + + // Use special values for these dirs. + let is_root = path.join("test/ui") == dir_path; + let is_issues_dir = path.join("test/ui/issues") == dir_path; + let limit = if is_root { + ROOT_ENTRY_LIMIT + } else if is_issues_dir { + ISSUES_ENTRY_LIMIT + } else { + ENTRY_LIMIT + }; + + let count = std::fs::read_dir(dir_path).unwrap().count(); + if count >= limit { + tidy_error!( + bad, + "following path contains more than {} entries, \ + you should move the test to some relevant subdirectory (current: {}): {}", + limit, + count, + dir_path.display() + ); + } + } + } +} + pub fn check(path: &Path, bad: &mut bool) { + check_entries(&path, bad); for path in &[&path.join("test/ui"), &path.join("test/ui-fulldeps")] { super::walk_no_read(path, &mut |_| false, &mut |entry| { let file_path = entry.path(); @@ -19,23 +61,18 @@ pub fn check(path: &Path, bad: &mut bool) { // // For now, just make sure that there is a corresponding // `$testname.rs` file. - let testname = file_path - .file_name() - .unwrap() - .to_str() - .unwrap() - .splitn(2, '.') - .next() - .unwrap(); + // + // NB: We do not use file_stem() as some file names have multiple `.`s and we + // must strip all of them. + let testname = + file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0; if !file_path.with_file_name(testname).with_extension("rs").exists() { - println!("Stray file with UI testing output: {:?}", file_path); - *bad = true; + tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path); } if let Ok(metadata) = fs::metadata(file_path) { if metadata.len() == 0 { - println!("Empty file with UI testing output: {:?}", file_path); - *bad = true; + tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path); } } } diff --git a/src/tools/x/README.md b/src/tools/x/README.md index 3b3cf2847c20..80bf02e8a0ef 100644 --- a/src/tools/x/README.md +++ b/src/tools/x/README.md @@ -1,3 +1,10 @@ # x `x` invokes `x.py` from any subdirectory. + +To install, run the following commands: + +``` +$ cd rust/src/tools/x/ +$ cargo install --path . +``` diff --git a/src/version b/src/version index 5a5c7211dc68..ba0a719118ce 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.50.0 +1.51.0 diff --git a/triagebot.toml b/triagebot.toml index fc733b9e45f1..c0cf50e51670 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -90,7 +90,7 @@ exclude_labels = [ [notify-zulip."I-prioritize"] zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts -topic = "I-prioritize #{number} {title}" +topic = "#{number} {title}" message_on_add = """\ @*WG-prioritization/alerts* issue #{number} has been requested for prioritization.

{ | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.rs b/src/test/ui/const-generics/raw-ptr-const-param-deref.rs index 20cc62ebc17c..ca7d33c0eb98 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.rs +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] const A: u32 = 3; diff --git a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr index d317aa0f585c..310422aafcd3 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr @@ -1,5 +1,5 @@ error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param.rs:7:23 + --> $DIR/raw-ptr-const-param.rs:6:23 | LL | struct Const; | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/raw-ptr-const-param.min.stderr b/src/test/ui/const-generics/raw-ptr-const-param.min.stderr index d317aa0f585c..310422aafcd3 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.min.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param.min.stderr @@ -1,5 +1,5 @@ error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param.rs:7:23 + --> $DIR/raw-ptr-const-param.rs:6:23 | LL | struct Const; | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/raw-ptr-const-param.rs b/src/test/ui/const-generics/raw-ptr-const-param.rs index 36e593aa2102..a04c6d5e64e1 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.rs +++ b/src/test/ui/const-generics/raw-ptr-const-param.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Const; //~ ERROR: using raw pointers as const generic parameters diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.full.stderr b/src/test/ui/const-generics/slice-const-param-mismatch.full.stderr index d06da2ef0630..80dd1be33c24 100644 --- a/src/test/ui/const-generics/slice-const-param-mismatch.full.stderr +++ b/src/test/ui/const-generics/slice-const-param-mismatch.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/slice-const-param-mismatch.rs:15:35 + --> $DIR/slice-const-param-mismatch.rs:14:35 | LL | let _: ConstString<"Hello"> = ConstString::<"World">; | -------------------- ^^^^^^^^^^^^^^^^^^^^^^ expected `"Hello"`, found `"World"` @@ -10,7 +10,7 @@ LL | let _: ConstString<"Hello"> = ConstString::<"World">; found struct `ConstString<"World">` error[E0308]: mismatched types - --> $DIR/slice-const-param-mismatch.rs:17:33 + --> $DIR/slice-const-param-mismatch.rs:16:33 | LL | let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">; | ------------------- ^^^^^^^^^^^^^^^^^^^^^ expected `"ℇ㇈↦"`, found `"ℇ㇈↥"` @@ -21,7 +21,7 @@ LL | let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">; found struct `ConstString<"ℇ㇈↥">` error[E0308]: mismatched types - --> $DIR/slice-const-param-mismatch.rs:19:33 + --> $DIR/slice-const-param-mismatch.rs:18:33 | LL | let _: ConstBytes = ConstBytes::; | ------------------ ^^^^^^^^^^^^^^^^^^^^ expected `b"AAA"`, found `b"BBB"` diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr b/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr index 46997fed770a..13d0b217ed29 100644 --- a/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr +++ b/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr @@ -1,5 +1,5 @@ error: `&'static str` is forbidden as the type of a const generic parameter - --> $DIR/slice-const-param-mismatch.rs:8:29 + --> $DIR/slice-const-param-mismatch.rs:7:29 | LL | struct ConstString; | ^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | struct ConstString; = help: more complex types are supported with `#[feature(const_generics)]` error: `&'static [u8]` is forbidden as the type of a const generic parameter - --> $DIR/slice-const-param-mismatch.rs:10:28 + --> $DIR/slice-const-param-mismatch.rs:9:28 | LL | struct ConstBytes; | ^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.rs b/src/test/ui/const-generics/slice-const-param-mismatch.rs index 0f8ae9bac4a3..f020e2bf66fb 100644 --- a/src/test/ui/const-generics/slice-const-param-mismatch.rs +++ b/src/test/ui/const-generics/slice-const-param-mismatch.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct ConstString; diff --git a/src/test/ui/const-generics/slice-const-param.min.stderr b/src/test/ui/const-generics/slice-const-param.min.stderr index 7a9f65233e79..821c6e3995aa 100644 --- a/src/test/ui/const-generics/slice-const-param.min.stderr +++ b/src/test/ui/const-generics/slice-const-param.min.stderr @@ -1,5 +1,5 @@ error: `&'static str` is forbidden as the type of a const generic parameter - --> $DIR/slice-const-param.rs:8:40 + --> $DIR/slice-const-param.rs:7:40 | LL | pub fn function_with_str() -> &'static str { | ^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn function_with_str() -> &'static str { = help: more complex types are supported with `#[feature(const_generics)]` error: `&'static [u8]` is forbidden as the type of a const generic parameter - --> $DIR/slice-const-param.rs:13:41 + --> $DIR/slice-const-param.rs:12:41 | LL | pub fn function_with_bytes() -> &'static [u8] { | ^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/slice-const-param.rs b/src/test/ui/const-generics/slice-const-param.rs index f76e948c4af2..bf1bf8af9222 100644 --- a/src/test/ui/const-generics/slice-const-param.rs +++ b/src/test/ui/const-generics/slice-const-param.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub fn function_with_str() -> &'static str { //[min]~^ ERROR `&'static str` is forbidden diff --git a/src/test/ui/const-generics/std/const-generics-range.min.stderr b/src/test/ui/const-generics/std/const-generics-range.min.stderr index 9274ccd2b921..d7d2a8447e90 100644 --- a/src/test/ui/const-generics/std/const-generics-range.min.stderr +++ b/src/test/ui/const-generics/std/const-generics-range.min.stderr @@ -1,5 +1,5 @@ error: `std::ops::Range` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:8:24 + --> $DIR/const-generics-range.rs:7:24 | LL | struct _Range>; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | struct _Range>; = help: more complex types are supported with `#[feature(const_generics)]` error: `RangeFrom` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:13:28 + --> $DIR/const-generics-range.rs:12:28 | LL | struct _RangeFrom>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | struct _RangeFrom>; = help: more complex types are supported with `#[feature(const_generics)]` error: `RangeFull` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:18:28 + --> $DIR/const-generics-range.rs:17:28 | LL | struct _RangeFull; | ^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | struct _RangeFull; = help: more complex types are supported with `#[feature(const_generics)]` error: `RangeInclusive` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:24:33 + --> $DIR/const-generics-range.rs:23:33 | LL | struct _RangeInclusive>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | struct _RangeInclusive>; = help: more complex types are supported with `#[feature(const_generics)]` error: `RangeTo` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:29:26 + --> $DIR/const-generics-range.rs:28:26 | LL | struct _RangeTo>; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | struct _RangeTo>; = help: more complex types are supported with `#[feature(const_generics)]` error: `RangeToInclusive` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:34:35 + --> $DIR/const-generics-range.rs:33:35 | LL | struct _RangeToInclusive>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/std/const-generics-range.rs b/src/test/ui/const-generics/std/const-generics-range.rs index 136ac3528902..deaab830e91d 100644 --- a/src/test/ui/const-generics/std/const-generics-range.rs +++ b/src/test/ui/const-generics/std/const-generics-range.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] // `Range` should be usable within const generics: struct _Range>; diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.full.stderr b/src/test/ui/const-generics/struct-with-invalid-const-param.full.stderr index e73a297c878f..db998033c0a2 100644 --- a/src/test/ui/const-generics/struct-with-invalid-const-param.full.stderr +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.full.stderr @@ -1,5 +1,5 @@ error[E0573]: expected type, found const parameter `C` - --> $DIR/struct-with-invalid-const-param.rs:8:23 + --> $DIR/struct-with-invalid-const-param.rs:7:23 | LL | struct S(C); | ^ not a type diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.min.stderr b/src/test/ui/const-generics/struct-with-invalid-const-param.min.stderr index e73a297c878f..db998033c0a2 100644 --- a/src/test/ui/const-generics/struct-with-invalid-const-param.min.stderr +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.min.stderr @@ -1,5 +1,5 @@ error[E0573]: expected type, found const parameter `C` - --> $DIR/struct-with-invalid-const-param.rs:8:23 + --> $DIR/struct-with-invalid-const-param.rs:7:23 | LL | struct S(C); | ^ not a type diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.rs b/src/test/ui/const-generics/struct-with-invalid-const-param.rs index f0122ace3aec..32970ccaa5db 100644 --- a/src/test/ui/const-generics/struct-with-invalid-const-param.rs +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct S(C); //~ ERROR expected type, found const parameter diff --git a/src/test/ui/const-generics/trait-const-args.rs b/src/test/ui/const-generics/trait-const-args.rs index b66d79845f97..30d05c708e14 100644 --- a/src/test/ui/const-generics/trait-const-args.rs +++ b/src/test/ui/const-generics/trait-const-args.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Const; trait Foo {} diff --git a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs index e041e9709d0e..bf855d4dcaac 100644 --- a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs +++ b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::mem::MaybeUninit; diff --git a/src/test/ui/const-generics/type-after-const-ok.min.stderr b/src/test/ui/const-generics/type-after-const-ok.min.stderr index 67a44d2c5b4a..ad38754c7412 100644 --- a/src/test/ui/const-generics/type-after-const-ok.min.stderr +++ b/src/test/ui/const-generics/type-after-const-ok.min.stderr @@ -1,5 +1,5 @@ error: type parameters must be declared prior to const parameters - --> $DIR/type-after-const-ok.rs:9:26 + --> $DIR/type-after-const-ok.rs:8:26 | LL | struct A(T); | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `` diff --git a/src/test/ui/const-generics/type-after-const-ok.rs b/src/test/ui/const-generics/type-after-const-ok.rs index 69227cdf19c3..920c067dc1a7 100644 --- a/src/test/ui/const-generics/type-after-const-ok.rs +++ b/src/test/ui/const-generics/type-after-const-ok.rs @@ -3,7 +3,6 @@ // Verifies that having generic parameters after constants is permitted #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #[allow(dead_code)] struct A(T); diff --git a/src/test/ui/const-generics/type-dependent/auxiliary/type_dependent_lib.rs b/src/test/ui/const-generics/type-dependent/auxiliary/type_dependent_lib.rs index aa85376bf0d7..cd9c3ae7bbc0 100644 --- a/src/test/ui/const-generics/type-dependent/auxiliary/type_dependent_lib.rs +++ b/src/test/ui/const-generics/type-dependent/auxiliary/type_dependent_lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub struct Struct(()); diff --git a/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs b/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs index 3ccdd4726137..4997d493bbb4 100644 --- a/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs +++ b/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs @@ -1,7 +1,6 @@ // run-pass // revisions: full min #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(min, feature(min_const_generics))] #![allow(incomplete_features)] struct Foo; diff --git a/src/test/ui/const-generics/type-dependent/issue-61936.rs b/src/test/ui/const-generics/type-dependent/issue-61936.rs index f3b19109a7c8..417fe2501ae3 100644 --- a/src/test/ui/const-generics/type-dependent/issue-61936.rs +++ b/src/test/ui/const-generics/type-dependent/issue-61936.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait SliceExt { fn array_windows_example<'a, const N: usize>(&'a self) -> ArrayWindowsExample<'a, T, N>; diff --git a/src/test/ui/const-generics/type-dependent/issue-63695.rs b/src/test/ui/const-generics/type-dependent/issue-63695.rs index 465b66b09ce2..2ece25bb41b2 100644 --- a/src/test/ui/const-generics/type-dependent/issue-63695.rs +++ b/src/test/ui/const-generics/type-dependent/issue-63695.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait T { fn test(&self) -> i32 { A } diff --git a/src/test/ui/const-generics/type-dependent/issue-67144-1.rs b/src/test/ui/const-generics/type-dependent/issue-67144-1.rs index 3d4910e9e4b4..4a2c303095e5 100644 --- a/src/test/ui/const-generics/type-dependent/issue-67144-1.rs +++ b/src/test/ui/const-generics/type-dependent/issue-67144-1.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct X; diff --git a/src/test/ui/const-generics/type-dependent/issue-67144-2.rs b/src/test/ui/const-generics/type-dependent/issue-67144-2.rs index 0868d309b337..a1163fca8d4e 100644 --- a/src/test/ui/const-generics/type-dependent/issue-67144-2.rs +++ b/src/test/ui/const-generics/type-dependent/issue-67144-2.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct A; diff --git a/src/test/ui/const-generics/type-dependent/issue-69816.rs b/src/test/ui/const-generics/type-dependent/issue-69816.rs index 4a374dc1db60..75ddd839f664 100644 --- a/src/test/ui/const-generics/type-dependent/issue-69816.rs +++ b/src/test/ui/const-generics/type-dependent/issue-69816.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait IterExt: Sized + Iterator { fn default_for_size(self) -> [Self::Item; N] diff --git a/src/test/ui/const-generics/type-dependent/issue-70217.rs b/src/test/ui/const-generics/type-dependent/issue-70217.rs index ba5a42e47e92..b3585d5fc107 100644 --- a/src/test/ui/const-generics/type-dependent/issue-70217.rs +++ b/src/test/ui/const-generics/type-dependent/issue-70217.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Struct; diff --git a/src/test/ui/const-generics/type-dependent/issue-70507.rs b/src/test/ui/const-generics/type-dependent/issue-70507.rs index 234c09e04ae6..df7c277f605b 100644 --- a/src/test/ui/const-generics/type-dependent/issue-70507.rs +++ b/src/test/ui/const-generics/type-dependent/issue-70507.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait ConstChunksExactTrait { fn const_chunks_exact(&self) -> ConstChunksExact<'_, T, {N}>; diff --git a/src/test/ui/const-generics/type-dependent/issue-70586.rs b/src/test/ui/const-generics/type-dependent/issue-70586.rs index fd52373cee21..5fb571f2394d 100644 --- a/src/test/ui/const-generics/type-dependent/issue-70586.rs +++ b/src/test/ui/const-generics/type-dependent/issue-70586.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::marker::PhantomData; diff --git a/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr b/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr index 8f240f0d930a..92f5d815a0fe 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr +++ b/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr @@ -1,5 +1,5 @@ error: `&'static str` is forbidden as the type of a const generic parameter - --> $DIR/issue-71348.rs:11:24 + --> $DIR/issue-71348.rs:10:24 | LL | trait Get<'a, const N: &'static str> { | ^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | trait Get<'a, const N: &'static str> { = help: more complex types are supported with `#[feature(const_generics)]` error: `&'static str` is forbidden as the type of a const generic parameter - --> $DIR/issue-71348.rs:19:25 + --> $DIR/issue-71348.rs:18:25 | LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target | ^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/type-dependent/issue-71348.rs b/src/test/ui/const-generics/type-dependent/issue-71348.rs index 772e179746dd..33735ef87c5a 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71348.rs +++ b/src/test/ui/const-generics/type-dependent/issue-71348.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Foo { i: i32, diff --git a/src/test/ui/const-generics/type-dependent/issue-71382.full.stderr b/src/test/ui/const-generics/type-dependent/issue-71382.full.stderr index da1d3270b7cc..8ac9bab63208 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71382.full.stderr +++ b/src/test/ui/const-generics/type-dependent/issue-71382.full.stderr @@ -1,5 +1,5 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71382.rs:17:23 + --> $DIR/issue-71382.rs:16:23 | LL | fn test u8>(&self) -> u8 { | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/type-dependent/issue-71382.min.stderr b/src/test/ui/const-generics/type-dependent/issue-71382.min.stderr index da1d3270b7cc..8ac9bab63208 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71382.min.stderr +++ b/src/test/ui/const-generics/type-dependent/issue-71382.min.stderr @@ -1,5 +1,5 @@ error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71382.rs:17:23 + --> $DIR/issue-71382.rs:16:23 | LL | fn test u8>(&self) -> u8 { | ^^^^^^^^^^ diff --git a/src/test/ui/const-generics/type-dependent/issue-71382.rs b/src/test/ui/const-generics/type-dependent/issue-71382.rs index 497fd1381de7..b3677613dbc8 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71382.rs +++ b/src/test/ui/const-generics/type-dependent/issue-71382.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct Test; diff --git a/src/test/ui/const-generics/type-dependent/issue-71805.rs b/src/test/ui/const-generics/type-dependent/issue-71805.rs index 2aaf12cea4f8..3701e14eadcf 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71805.rs +++ b/src/test/ui/const-generics/type-dependent/issue-71805.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::mem::MaybeUninit; diff --git a/src/test/ui/const-generics/type-dependent/issue-73730.rs b/src/test/ui/const-generics/type-dependent/issue-73730.rs index 3e53190ee486..5d7dcb9c458a 100644 --- a/src/test/ui/const-generics/type-dependent/issue-73730.rs +++ b/src/test/ui/const-generics/type-dependent/issue-73730.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Foo<'a, A>: Iterator { fn bar(&mut self) -> *const [A; N]; diff --git a/src/test/ui/const-generics/type-dependent/non-local.rs b/src/test/ui/const-generics/type-dependent/non-local.rs index 747664a09629..9e4afba31140 100644 --- a/src/test/ui/const-generics/type-dependent/non-local.rs +++ b/src/test/ui/const-generics/type-dependent/non-local.rs @@ -3,7 +3,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] extern crate type_dependent_lib; diff --git a/src/test/ui/const-generics/type-dependent/qpath.rs b/src/test/ui/const-generics/type-dependent/qpath.rs index ec23ff1d2212..b61e970cfb37 100644 --- a/src/test/ui/const-generics/type-dependent/qpath.rs +++ b/src/test/ui/const-generics/type-dependent/qpath.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct A; impl A { diff --git a/src/test/ui/const-generics/type-dependent/simple.rs b/src/test/ui/const-generics/type-dependent/simple.rs index 70af65509231..a4776a43b211 100644 --- a/src/test/ui/const-generics/type-dependent/simple.rs +++ b/src/test/ui/const-generics/type-dependent/simple.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct R; diff --git a/src/test/ui/const-generics/type-dependent/type-mismatch.full.stderr b/src/test/ui/const-generics/type-dependent/type-mismatch.full.stderr index a530e63449d4..b942c397a8d2 100644 --- a/src/test/ui/const-generics/type-dependent/type-mismatch.full.stderr +++ b/src/test/ui/const-generics/type-dependent/type-mismatch.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/type-mismatch.rs:12:27 + --> $DIR/type-mismatch.rs:11:27 | LL | assert_eq!(R.method::<1u16>(), 1); | ^^^^ expected `u8`, found `u16` diff --git a/src/test/ui/const-generics/type-dependent/type-mismatch.min.stderr b/src/test/ui/const-generics/type-dependent/type-mismatch.min.stderr index a530e63449d4..b942c397a8d2 100644 --- a/src/test/ui/const-generics/type-dependent/type-mismatch.min.stderr +++ b/src/test/ui/const-generics/type-dependent/type-mismatch.min.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/type-mismatch.rs:12:27 + --> $DIR/type-mismatch.rs:11:27 | LL | assert_eq!(R.method::<1u16>(), 1); | ^^^^ expected `u8`, found `u16` diff --git a/src/test/ui/const-generics/type-dependent/type-mismatch.rs b/src/test/ui/const-generics/type-dependent/type-mismatch.rs index 67d80973f039..7fba1afe9189 100644 --- a/src/test/ui/const-generics/type-dependent/type-mismatch.rs +++ b/src/test/ui/const-generics/type-dependent/type-mismatch.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct R; diff --git a/src/test/ui/const-generics/type_of_anon_const.rs b/src/test/ui/const-generics/type_of_anon_const.rs index f424fd03341f..9a2e9f09319d 100644 --- a/src/test/ui/const-generics/type_of_anon_const.rs +++ b/src/test/ui/const-generics/type_of_anon_const.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait T { fn l() -> usize; diff --git a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr index 265e9ee618be..480ecdb38734 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr +++ b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/types-mismatch-const-args.rs:15:41 + --> $DIR/types-mismatch-const-args.rs:14:41 | LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2_u32`, found `4_u32` @@ -8,7 +8,7 @@ LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data found type `4_u32` error[E0308]: mismatched types - --> $DIR/types-mismatch-const-args.rs:17:41 + --> $DIR/types-mismatch-const-args.rs:16:41 | LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u16`, found `u32` diff --git a/src/test/ui/const-generics/types-mismatch-const-args.min.stderr b/src/test/ui/const-generics/types-mismatch-const-args.min.stderr index 27277f0c0bef..c19c8db737a1 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.min.stderr +++ b/src/test/ui/const-generics/types-mismatch-const-args.min.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/types-mismatch-const-args.rs:15:41 + --> $DIR/types-mismatch-const-args.rs:14:41 | LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData }; | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2_u32`, found `4_u32` @@ -10,7 +10,7 @@ LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data found struct `A<'_, _, 4_u32, _>` error[E0308]: mismatched types - --> $DIR/types-mismatch-const-args.rs:17:41 + --> $DIR/types-mismatch-const-args.rs:16:41 | LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u16`, found `u32` diff --git a/src/test/ui/const-generics/types-mismatch-const-args.rs b/src/test/ui/const-generics/types-mismatch-const-args.rs index 34b85304cc4d..14cef083d837 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.rs +++ b/src/test/ui/const-generics/types-mismatch-const-args.rs @@ -1,7 +1,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] // tests the diagnostic output of type mismatches for types that have const generics arguments. diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs index 45afbdc9ab10..9592f2662307 100644 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs +++ b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::fmt; diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs index 65ae05e11982..4bab2bb5a77f 100644 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs +++ b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] use std::fmt; diff --git a/src/test/ui/const-generics/unknown_adt.full.stderr b/src/test/ui/const-generics/unknown_adt.full.stderr index 94f3165eaec3..b8b2e90aa66c 100644 --- a/src/test/ui/const-generics/unknown_adt.full.stderr +++ b/src/test/ui/const-generics/unknown_adt.full.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `UnknownStruct` in this scope - --> $DIR/unknown_adt.rs:8:12 + --> $DIR/unknown_adt.rs:7:12 | LL | let _: UnknownStruct<7>; | ^^^^^^^^^^^^^ not found in this scope diff --git a/src/test/ui/const-generics/unknown_adt.min.stderr b/src/test/ui/const-generics/unknown_adt.min.stderr index 94f3165eaec3..b8b2e90aa66c 100644 --- a/src/test/ui/const-generics/unknown_adt.min.stderr +++ b/src/test/ui/const-generics/unknown_adt.min.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `UnknownStruct` in this scope - --> $DIR/unknown_adt.rs:8:12 + --> $DIR/unknown_adt.rs:7:12 | LL | let _: UnknownStruct<7>; | ^^^^^^^^^^^^^ not found in this scope diff --git a/src/test/ui/const-generics/unknown_adt.rs b/src/test/ui/const-generics/unknown_adt.rs index c6131402aeb6..977f90aad116 100644 --- a/src/test/ui/const-generics/unknown_adt.rs +++ b/src/test/ui/const-generics/unknown_adt.rs @@ -2,7 +2,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] fn main() { let _: UnknownStruct<7>; diff --git a/src/test/ui/const-generics/unused-const-param.rs b/src/test/ui/const-generics/unused-const-param.rs index 3c305167b4b6..2918e399dc8e 100644 --- a/src/test/ui/const-generics/unused-const-param.rs +++ b/src/test/ui/const-generics/unused-const-param.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] struct A; // ok diff --git a/src/test/ui/const-generics/unused_braces.full.fixed b/src/test/ui/const-generics/unused_braces.full.fixed index 1b075ade16a0..46d57e0dcfca 100644 --- a/src/test/ui/const-generics/unused_braces.full.fixed +++ b/src/test/ui/const-generics/unused_braces.full.fixed @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![warn(unused_braces)] diff --git a/src/test/ui/const-generics/unused_braces.full.stderr b/src/test/ui/const-generics/unused_braces.full.stderr index 1752779a60a3..8899139aa6bb 100644 --- a/src/test/ui/const-generics/unused_braces.full.stderr +++ b/src/test/ui/const-generics/unused_braces.full.stderr @@ -1,11 +1,11 @@ warning: unnecessary braces around const expression - --> $DIR/unused_braces.rs:15:14 + --> $DIR/unused_braces.rs:14:14 | LL | let _: A<{ 7 }>; | ^^^^^ help: remove these braces | note: the lint level is defined here - --> $DIR/unused_braces.rs:8:9 + --> $DIR/unused_braces.rs:7:9 | LL | #![warn(unused_braces)] | ^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/unused_braces.min.fixed b/src/test/ui/const-generics/unused_braces.min.fixed index 1b075ade16a0..46d57e0dcfca 100644 --- a/src/test/ui/const-generics/unused_braces.min.fixed +++ b/src/test/ui/const-generics/unused_braces.min.fixed @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![warn(unused_braces)] diff --git a/src/test/ui/const-generics/unused_braces.min.stderr b/src/test/ui/const-generics/unused_braces.min.stderr index 1752779a60a3..8899139aa6bb 100644 --- a/src/test/ui/const-generics/unused_braces.min.stderr +++ b/src/test/ui/const-generics/unused_braces.min.stderr @@ -1,11 +1,11 @@ warning: unnecessary braces around const expression - --> $DIR/unused_braces.rs:15:14 + --> $DIR/unused_braces.rs:14:14 | LL | let _: A<{ 7 }>; | ^^^^^ help: remove these braces | note: the lint level is defined here - --> $DIR/unused_braces.rs:8:9 + --> $DIR/unused_braces.rs:7:9 | LL | #![warn(unused_braces)] | ^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/unused_braces.rs b/src/test/ui/const-generics/unused_braces.rs index 31c4caf7ab85..0348bbacaabd 100644 --- a/src/test/ui/const-generics/unused_braces.rs +++ b/src/test/ui/const-generics/unused_braces.rs @@ -4,7 +4,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] #![warn(unused_braces)] diff --git a/src/test/ui/const-generics/wf-misc.full.stderr b/src/test/ui/const-generics/wf-misc.full.stderr index 4af48fa15909..dfb593a9507d 100644 --- a/src/test/ui/const-generics/wf-misc.full.stderr +++ b/src/test/ui/const-generics/wf-misc.full.stderr @@ -1,5 +1,5 @@ error: constant expression depends on a generic parameter - --> $DIR/wf-misc.rs:9:12 + --> $DIR/wf-misc.rs:8:12 | LL | let _: [u8; N + 1]; | ^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let _: [u8; N + 1]; = note: this may fail depending on what value the parameter takes error: constant expression depends on a generic parameter - --> $DIR/wf-misc.rs:17:12 + --> $DIR/wf-misc.rs:16:12 | LL | let _: Const::<{N + 1}>; | ^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/const-generics/wf-misc.min.stderr b/src/test/ui/const-generics/wf-misc.min.stderr index 99142cb6ce7a..9967a2218f6e 100644 --- a/src/test/ui/const-generics/wf-misc.min.stderr +++ b/src/test/ui/const-generics/wf-misc.min.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/wf-misc.rs:9:17 + --> $DIR/wf-misc.rs:8:17 | LL | let _: [u8; N + 1]; | ^ cannot perform const operation using `N` @@ -8,7 +8,7 @@ LL | let _: [u8; N + 1]; = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/wf-misc.rs:17:21 + --> $DIR/wf-misc.rs:16:21 | LL | let _: Const::<{N + 1}>; | ^ cannot perform const operation using `N` diff --git a/src/test/ui/const-generics/wf-misc.rs b/src/test/ui/const-generics/wf-misc.rs index 103c580f28fc..8a5b6ddfe266 100644 --- a/src/test/ui/const-generics/wf-misc.rs +++ b/src/test/ui/const-generics/wf-misc.rs @@ -3,7 +3,6 @@ #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] pub fn arr_len() { let _: [u8; N + 1]; diff --git a/src/test/ui/const-generics/where-clauses.rs b/src/test/ui/const-generics/where-clauses.rs index cdcaf2509424..dc09cad3180b 100644 --- a/src/test/ui/const-generics/where-clauses.rs +++ b/src/test/ui/const-generics/where-clauses.rs @@ -2,7 +2,6 @@ // revisions: full min #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] -#![cfg_attr(min, feature(min_const_generics))] trait Bar { fn bar() {} } trait Foo: Bar {} diff --git a/src/test/ui/const-ptr/out_of_bounds_read.rs b/src/test/ui/const-ptr/out_of_bounds_read.rs new file mode 100644 index 000000000000..183aa9e51228 --- /dev/null +++ b/src/test/ui/const-ptr/out_of_bounds_read.rs @@ -0,0 +1,16 @@ +// error-pattern: any use of this value will cause an error + +#![feature(const_ptr_read)] +#![feature(const_ptr_offset)] + +fn main() { + use std::ptr; + + const DATA: [u32; 1] = [42]; + + const PAST_END_PTR: *const u32 = unsafe { DATA.as_ptr().add(1) }; + + const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) }; + const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() }; + const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() }; +} diff --git a/src/test/ui/const-ptr/out_of_bounds_read.stderr b/src/test/ui/const-ptr/out_of_bounds_read.stderr new file mode 100644 index 000000000000..ca65a079947e --- /dev/null +++ b/src/test/ui/const-ptr/out_of_bounds_read.stderr @@ -0,0 +1,54 @@ +error: any use of this value will cause an error + --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + | +LL | unsafe { copy_nonoverlapping(src, dst, count) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL + | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | inside `_READ` at $DIR/out_of_bounds_read.rs:13:33 + | + ::: $DIR/out_of_bounds_read.rs:13:5 + | +LL | const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) }; + | ------------------------------------------------------ + | + = note: `#[deny(const_err)]` on by default + +error: any use of this value will cause an error + --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + | +LL | unsafe { copy_nonoverlapping(src, dst, count) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL + | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | inside `ptr::const_ptr::::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | inside `_CONST_READ` at $DIR/out_of_bounds_read.rs:14:39 + | + ::: $DIR/out_of_bounds_read.rs:14:5 + | +LL | const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() }; + | -------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + | +LL | unsafe { copy_nonoverlapping(src, dst, count) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL + | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | inside `ptr::mut_ptr::::read` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL + | inside `_MUT_READ` at $DIR/out_of_bounds_read.rs:15:37 + | + ::: $DIR/out_of_bounds_read.rs:15:5 + | +LL | const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() }; + | -------------------------------------------------------------------- + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/consts/assume-type-intrinsics.rs b/src/test/ui/consts/assume-type-intrinsics.rs new file mode 100644 index 000000000000..77370e1ccc59 --- /dev/null +++ b/src/test/ui/consts/assume-type-intrinsics.rs @@ -0,0 +1,13 @@ +// error-pattern: any use of this value will cause an error + +#![feature(never_type)] +#![feature(const_maybe_uninit_assume_init)] + +#[allow(invalid_value)] +fn main() { + use std::mem::MaybeUninit; + + const _BAD: () = unsafe { + MaybeUninit::::uninit().assume_init(); + }; +} diff --git a/src/test/ui/consts/assume-type-intrinsics.stderr b/src/test/ui/consts/assume-type-intrinsics.stderr new file mode 100644 index 000000000000..ed09f74e9b1f --- /dev/null +++ b/src/test/ui/consts/assume-type-intrinsics.stderr @@ -0,0 +1,21 @@ +error: any use of this value will cause an error + --> $SRC_DIR/core/src/mem/maybe_uninit.rs:LL:COL + | +LL | intrinsics::assert_inhabited::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | aborted execution: attempted to instantiate uninhabited type `!` + | inside `MaybeUninit::::assume_init` at $SRC_DIR/core/src/mem/maybe_uninit.rs:LL:COL + | inside `_BAD` at $DIR/assume-type-intrinsics.rs:11:9 + | + ::: $DIR/assume-type-intrinsics.rs:10:5 + | +LL | / const _BAD: () = unsafe { +LL | | MaybeUninit::::uninit().assume_init(); +LL | | }; + | |______- + | + = note: `#[deny(const_err)]` on by default + +error: aborting due to previous error + diff --git a/src/test/compile-fail/consts/const-fn-error.rs b/src/test/ui/consts/const-fn-error.rs similarity index 100% rename from src/test/compile-fail/consts/const-fn-error.rs rename to src/test/ui/consts/const-fn-error.rs diff --git a/src/test/ui/consts/const-fn-error.stderr b/src/test/ui/consts/const-fn-error.stderr new file mode 100644 index 000000000000..86b1eebcb2c8 --- /dev/null +++ b/src/test/ui/consts/const-fn-error.stderr @@ -0,0 +1,49 @@ +error[E0744]: `for` is not allowed in a `const fn` + --> $DIR/const-fn-error.rs:7:5 + | +LL | / for i in 0..x { +LL | | +LL | | +LL | | +... | +LL | | sum += i; +LL | | } + | |_____^ + +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/const-fn-error.rs:7:14 + | +LL | for i in 0..x { + | ^^^^ + +error[E0658]: mutable references are not allowed in constant functions + --> $DIR/const-fn-error.rs:7:14 + | +LL | for i in 0..x { + | ^^^^ + | + = note: see issue #57349 for more information + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/const-fn-error.rs:7:14 + | +LL | for i in 0..x { + | ^^^^ + +error[E0080]: evaluation of constant value failed + --> $DIR/const-fn-error.rs:7:14 + | +LL | for i in 0..x { + | ^^^^ + | | + | calling non-const function ` as IntoIterator>::into_iter` + | inside `f` at $DIR/const-fn-error.rs:7:14 +... +LL | let a : [i32; f(X)]; + | ---- inside `main::{constant#0}` at $DIR/const-fn-error.rs:20:19 + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0015, E0080, E0658, E0744. +For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/consts/const-size_of_val-align_of_val.rs b/src/test/ui/consts/const-size_of_val-align_of_val.rs index e8e6f1d39009..5c0d7d94d64f 100644 --- a/src/test/ui/consts/const-size_of_val-align_of_val.rs +++ b/src/test/ui/consts/const-size_of_val-align_of_val.rs @@ -1,6 +1,7 @@ // run-pass #![feature(const_size_of_val, const_align_of_val)] +#![feature(const_size_of_val_raw, const_align_of_val_raw, layout_for_ptr)] use std::mem; @@ -32,6 +33,9 @@ const ALIGN_OF_UGH: usize = mem::align_of_val(&UGH); const SIZE_OF_SLICE: usize = mem::size_of_val("foobar".as_bytes()); +const SIZE_OF_DANGLING: usize = unsafe { mem::size_of_val_raw(0x100 as *const i32) }; +const ALIGN_OF_DANGLING: usize = unsafe { mem::align_of_val_raw(0x100 as *const i16) }; + fn main() { assert_eq!(SIZE_OF_FOO, mem::size_of::()); assert_eq!(SIZE_OF_BAR, mem::size_of::()); @@ -41,5 +45,8 @@ fn main() { assert_eq!(ALIGN_OF_BAR, mem::align_of::()); assert_eq!(ALIGN_OF_UGH, mem::align_of::()); + assert_eq!(SIZE_OF_DANGLING, mem::size_of::()); + assert_eq!(ALIGN_OF_DANGLING, mem::align_of::()); + assert_eq!(SIZE_OF_SLICE, "foobar".len()); } diff --git a/src/test/compile-fail/issue-44415.rs b/src/test/ui/consts/issue-44415.rs similarity index 100% rename from src/test/compile-fail/issue-44415.rs rename to src/test/ui/consts/issue-44415.rs diff --git a/src/test/ui/consts/issue-44415.stderr b/src/test/ui/consts/issue-44415.stderr new file mode 100644 index 000000000000..38841e99a722 --- /dev/null +++ b/src/test/ui/consts/issue-44415.stderr @@ -0,0 +1,28 @@ +error[E0391]: cycle detected when simplifying constant for the type system `Foo::bytes::{constant#0}` + --> $DIR/issue-44415.rs:6:17 + | +LL | bytes: [u8; unsafe { intrinsics::size_of::() }], + | ^^^^^^ + | +note: ...which requires simplifying constant for the type system `Foo::bytes::{constant#0}`... + --> $DIR/issue-44415.rs:6:17 + | +LL | bytes: [u8; unsafe { intrinsics::size_of::() }], + | ^^^^^^ +note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`... + --> $DIR/issue-44415.rs:6:17 + | +LL | bytes: [u8; unsafe { intrinsics::size_of::() }], + | ^^^^^^ + = note: ...which requires computing layout of `Foo`... + = note: ...which requires normalizing `[u8; _]`... + = note: ...which again requires simplifying constant for the type system `Foo::bytes::{constant#0}`, completing the cycle +note: cycle used when checking that `Foo` is well-formed + --> $DIR/issue-44415.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/compile-fail/consts/issue-55878.rs b/src/test/ui/consts/issue-55878.rs similarity index 94% rename from src/test/compile-fail/consts/issue-55878.rs rename to src/test/ui/consts/issue-55878.rs index fee664caa178..c1c54646db87 100644 --- a/src/test/compile-fail/consts/issue-55878.rs +++ b/src/test/ui/consts/issue-55878.rs @@ -1,3 +1,4 @@ +// build-fail // normalize-stderr-64bit "18446744073709551615" -> "SIZE" // normalize-stderr-32bit "4294967295" -> "SIZE" diff --git a/src/test/ui/consts/issue-55878.stderr b/src/test/ui/consts/issue-55878.stderr new file mode 100644 index 000000000000..924910e9cb6d --- /dev/null +++ b/src/test/ui/consts/issue-55878.stderr @@ -0,0 +1,25 @@ +error[E0080]: values of the type `[u8; SIZE]` are too big for the current architecture + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + | +LL | intrinsics::size_of::() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inside `std::mem::size_of::<[u8; SIZE]>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL + | inside `main` at $DIR/issue-55878.rs:7:26 + | + ::: $DIR/issue-55878.rs:7:26 + | +LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>()); + | ---------------------------------------------- + +error: erroneous constant used + --> $DIR/issue-55878.rs:7:26 + | +LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + | + = note: `#[deny(const_err)]` on by default + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr index 52662ef9eaf5..b65e50eb9f41 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr @@ -146,6 +146,11 @@ help: skipping check that does not even have a feature gate | LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:32:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check for `const_panic` feature --> $DIR/const_refers_to_static_cross_crate.rs:32:77 | diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/const-repeat.rs b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/const-repeat.rs new file mode 100644 index 000000000000..65d02317d34c --- /dev/null +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/const-repeat.rs @@ -0,0 +1,27 @@ +// run-pass + +// Repeating a *constant* of non-Copy type (not just a constant expression) is already stable. + +const EMPTY: Vec = Vec::new(); + +pub fn bar() -> [Vec; 2] { + [EMPTY; 2] +} + +struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + panic!("BOOM!"); + } +} + +const BOOM: Bomb = Bomb; + +fn main() { + let _x = bar(); + + // Make sure the destructor does not get called for empty arrays. `[CONST; N]` should + // instantiate (and then later drop) the const exactly `N` times. + let _x = [BOOM; 0]; +} diff --git a/src/test/compile-fail/auxiliary/crateresolve1-1.rs b/src/test/ui/crate-loading/auxiliary/crateresolve1-1.rs similarity index 100% rename from src/test/compile-fail/auxiliary/crateresolve1-1.rs rename to src/test/ui/crate-loading/auxiliary/crateresolve1-1.rs diff --git a/src/test/compile-fail/auxiliary/crateresolve1-2.rs b/src/test/ui/crate-loading/auxiliary/crateresolve1-2.rs similarity index 100% rename from src/test/compile-fail/auxiliary/crateresolve1-2.rs rename to src/test/ui/crate-loading/auxiliary/crateresolve1-2.rs diff --git a/src/test/compile-fail/auxiliary/crateresolve1-3.rs b/src/test/ui/crate-loading/auxiliary/crateresolve1-3.rs similarity index 100% rename from src/test/compile-fail/auxiliary/crateresolve1-3.rs rename to src/test/ui/crate-loading/auxiliary/crateresolve1-3.rs diff --git a/src/test/compile-fail/crateresolve1.rs b/src/test/ui/crate-loading/crateresolve1.rs similarity index 87% rename from src/test/compile-fail/crateresolve1.rs rename to src/test/ui/crate-loading/crateresolve1.rs index 453c8d976228..49e47dacc3de 100644 --- a/src/test/compile-fail/crateresolve1.rs +++ b/src/test/ui/crate-loading/crateresolve1.rs @@ -1,3 +1,4 @@ +// dont-check-compiler-stderr // aux-build:crateresolve1-1.rs // aux-build:crateresolve1-2.rs // aux-build:crateresolve1-3.rs diff --git a/src/test/ui/deprecation/rustc_deprecation-in-future.rs b/src/test/ui/deprecation/rustc_deprecation-in-future.rs index 6a619bcc49c2..11f7960b7578 100644 --- a/src/test/ui/deprecation/rustc_deprecation-in-future.rs +++ b/src/test/ui/deprecation/rustc_deprecation-in-future.rs @@ -8,8 +8,13 @@ #[rustc_deprecated(since = "99.99.99", reason = "effectively never")] #[stable(feature = "rustc_deprecation-in-future-test", since = "1.0.0")] -pub struct S; +pub struct S1; + +#[rustc_deprecated(since = "TBD", reason = "literally never")] +#[stable(feature = "rustc_deprecation-in-future-test", since = "1.0.0")] +pub struct S2; fn main() { - let _ = S; //~ ERROR use of unit struct `S` that will be deprecated in future version 99.99.99: effectively never + let _ = S1; //~ ERROR use of unit struct `S1` that will be deprecated in future version 99.99.99: effectively never + let _ = S2; //~ ERROR use of unit struct `S2` that will be deprecated in a future Rust version: literally never } diff --git a/src/test/ui/deprecation/rustc_deprecation-in-future.stderr b/src/test/ui/deprecation/rustc_deprecation-in-future.stderr index e4f50d10dadd..b5a7dd3c28da 100644 --- a/src/test/ui/deprecation/rustc_deprecation-in-future.stderr +++ b/src/test/ui/deprecation/rustc_deprecation-in-future.stderr @@ -1,8 +1,8 @@ -error: use of unit struct `S` that will be deprecated in future version 99.99.99: effectively never - --> $DIR/rustc_deprecation-in-future.rs:14:13 +error: use of unit struct `S1` that will be deprecated in future version 99.99.99: effectively never + --> $DIR/rustc_deprecation-in-future.rs:18:13 | -LL | let _ = S; - | ^ +LL | let _ = S1; + | ^^ | note: the lint level is defined here --> $DIR/rustc_deprecation-in-future.rs:3:9 @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(deprecated_in_future)] | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: use of unit struct `S2` that will be deprecated in a future Rust version: literally never + --> $DIR/rustc_deprecation-in-future.rs:19:13 + | +LL | let _ = S2; + | ^^ + +error: aborting due to 2 previous errors diff --git a/src/test/ui/dropck/dropck_trait_cycle_checked.rs b/src/test/ui/dropck/dropck_trait_cycle_checked.rs index bea77dc9f5c4..be6ec3e4ed1a 100644 --- a/src/test/ui/dropck/dropck_trait_cycle_checked.rs +++ b/src/test/ui/dropck/dropck_trait_cycle_checked.rs @@ -1,7 +1,7 @@ // Reject mixing cyclic structure and Drop when using trait // objects to hide the cross-references. // -// (Compare against compile-fail/dropck_vec_cycle_checked.rs) +// (Compare against ui/span/dropck_vec_cycle_checked.rs) use std::cell::Cell; use id::Id; diff --git a/src/test/ui/dropck/reject-specialized-drops-8142.rs b/src/test/ui/dropck/reject-specialized-drops-8142.rs index 02e8665cd2e3..c9599f6e805b 100644 --- a/src/test/ui/dropck/reject-specialized-drops-8142.rs +++ b/src/test/ui/dropck/reject-specialized-drops-8142.rs @@ -1,7 +1,5 @@ // Issue 8142: Test that Drop impls cannot be specialized beyond the // predicates attached to the type definition itself. -#![feature(min_const_generics)] - trait Bound { fn foo(&self) { } } struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } diff --git a/src/test/ui/dropck/reject-specialized-drops-8142.stderr b/src/test/ui/dropck/reject-specialized-drops-8142.stderr index 284cf59c822b..cb4d97a8b202 100644 --- a/src/test/ui/dropck/reject-specialized-drops-8142.stderr +++ b/src/test/ui/dropck/reject-specialized-drops-8142.stderr @@ -1,108 +1,108 @@ error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:26:20 + --> $DIR/reject-specialized-drops-8142.rs:24:20 | LL | impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> { // REJECT | ^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:6:1 + --> $DIR/reject-specialized-drops-8142.rs:4:1 | LL | struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:30:67 + --> $DIR/reject-specialized-drops-8142.rs:28:67 | LL | impl<'al,'adds_bnd> Drop for L<'al,'adds_bnd> where 'adds_bnd:'al { // REJECT | ^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:7:1 + --> $DIR/reject-specialized-drops-8142.rs:5:1 | LL | struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/reject-specialized-drops-8142.rs:36:1 + --> $DIR/reject-specialized-drops-8142.rs:34:1 | LL | impl Drop for N<'static> { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch | = note: expected struct `N<'n>` found struct `N<'static>` -note: the lifetime `'n` as defined on the struct at 9:10... - --> $DIR/reject-specialized-drops-8142.rs:9:10 +note: the lifetime `'n` as defined on the struct at 7:10... + --> $DIR/reject-specialized-drops-8142.rs:7:10 | LL | struct N<'n> { x: &'n i8 } | ^^ = note: ...does not necessarily outlive the static lifetime error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:43:1 + --> $DIR/reject-specialized-drops-8142.rs:41:1 | LL | impl Drop for P { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: use the same sequence of generic type, lifetime and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:11:1 + --> $DIR/reject-specialized-drops-8142.rs:9:1 | LL | struct P { x: *const Tp } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:46:14 + --> $DIR/reject-specialized-drops-8142.rs:44:14 | LL | impl Drop for Q { fn drop(&mut self) { } } // REJECT | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:12:1 + --> $DIR/reject-specialized-drops-8142.rs:10:1 | LL | struct Q { x: *const Tq } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsRBnd: 'rbnd` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:49:21 + --> $DIR/reject-specialized-drops-8142.rs:47:21 | LL | impl<'rbnd,AddsRBnd:'rbnd> Drop for R { fn drop(&mut self) { } } // REJECT | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:13:1 + --> $DIR/reject-specialized-drops-8142.rs:11:1 | LL | struct R { x: *const Tr } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:58:1 + --> $DIR/reject-specialized-drops-8142.rs:56:1 | LL | impl Drop for V { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: use the same sequence of generic type, lifetime and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:17:1 + --> $DIR/reject-specialized-drops-8142.rs:15:1 | LL | struct V { x: *const Tva, y: *const Tvb } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'lw` due to conflicting requirements - --> $DIR/reject-specialized-drops-8142.rs:61:1 + --> $DIR/reject-specialized-drops-8142.rs:59:1 | LL | impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: first, the lifetime cannot outlive the lifetime `'l1` as defined on the struct at 18:10... - --> $DIR/reject-specialized-drops-8142.rs:18:10 +note: first, the lifetime cannot outlive the lifetime `'l1` as defined on the struct at 16:10... + --> $DIR/reject-specialized-drops-8142.rs:16:10 | LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 } | ^^^ -note: ...but the lifetime must also be valid for the lifetime `'l2` as defined on the struct at 18:15... - --> $DIR/reject-specialized-drops-8142.rs:18:15 +note: ...but the lifetime must also be valid for the lifetime `'l2` as defined on the struct at 16:15... + --> $DIR/reject-specialized-drops-8142.rs:16:15 | LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 } | ^^^ note: ...so that the types are compatible - --> $DIR/reject-specialized-drops-8142.rs:61:1 + --> $DIR/reject-specialized-drops-8142.rs:59:1 | LL | impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,61 +110,61 @@ LL | impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJ found `W<'_, '_>` error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:64:1 + --> $DIR/reject-specialized-drops-8142.rs:62:1 | LL | impl Drop for X<3> { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: use the same sequence of generic type, lifetime and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:19:1 + --> $DIR/reject-specialized-drops-8142.rs:17:1 | LL | struct X; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:67:1 + --> $DIR/reject-specialized-drops-8142.rs:65:1 | LL | impl Drop for Y { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: use the same sequence of generic type, lifetime and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:20:1 + --> $DIR/reject-specialized-drops-8142.rs:18:1 | LL | struct Y; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the enum it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:70:14 + --> $DIR/reject-specialized-drops-8142.rs:68:14 | LL | impl Drop for Enum { fn drop(&mut self) { } } // REJECT | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:22:1 + --> $DIR/reject-specialized-drops-8142.rs:20:1 | LL | enum Enum { Variant(T) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:73:14 + --> $DIR/reject-specialized-drops-8142.rs:71:14 | LL | impl Drop for TupleStruct { fn drop(&mut self) { } } // REJECT | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:23:1 + --> $DIR/reject-specialized-drops-8142.rs:21:1 | LL | struct TupleStruct(T); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the union it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:76:21 + --> $DIR/reject-specialized-drops-8142.rs:74:21 | LL | impl Drop for Union { fn drop(&mut self) { } } // REJECT | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:24:1 + --> $DIR/reject-specialized-drops-8142.rs:22:1 | LL | union Union { f: T } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs index 0cfb93d46683..f927dd189038 100644 --- a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs @@ -7,7 +7,7 @@ use core::intrinsics::discriminant_value; enum MyWeirdOption { None = 0, Some(T) = std::mem::size_of::(), - //~^ ERROR constant expression depends on a generic parameter + //~^ ERROR generic parameters may not be used in const operations } fn main() { diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.stderr b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.stderr index 91d488a07cc6..4d57765e13f5 100644 --- a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.stderr +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.stderr @@ -1,10 +1,11 @@ -error: constant expression depends on a generic parameter - --> $DIR/issue-70453-generics-in-discr-ice-2.rs:9:15 +error: generic parameters may not be used in const operations + --> $DIR/issue-70453-generics-in-discr-ice-2.rs:9:35 | LL | Some(T) = std::mem::size_of::(), - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ cannot perform const operation using `T` | - = note: this may fail depending on what value the parameter takes + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: aborting due to previous error diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.rs b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.rs index 676f1115dde0..a0fb788a5109 100644 --- a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.rs +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.rs @@ -8,7 +8,7 @@ enum MyWeirdOption { //~^ ERROR parameter `T` is never used None = 0, Some = std::mem::size_of::(), - //~^ ERROR constant expression depends on a generic parameter + //~^ ERROR generic parameters may not be used in const operations } fn main() { diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr index 0dc5432d28c0..1d43903928be 100644 --- a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr @@ -1,10 +1,11 @@ -error: constant expression depends on a generic parameter - --> $DIR/issue-70453-generics-in-discr-ice.rs:10:12 +error: generic parameters may not be used in const operations + --> $DIR/issue-70453-generics-in-discr-ice.rs:10:32 | LL | Some = std::mem::size_of::(), - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ cannot perform const operation using `T` | - = note: this may fail depending on what value the parameter takes + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error[E0392]: parameter `T` is never used --> $DIR/issue-70453-generics-in-discr-ice.rs:7:20 diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs index cdc1db4c0b48..e62582fb5161 100644 --- a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs @@ -1,4 +1,3 @@ -// run-pass #![feature(arbitrary_enum_discriminant, core_intrinsics)] extern crate core; @@ -8,8 +7,7 @@ use core::intrinsics::discriminant_value; enum MyWeirdOption { None = 0, Some(T) = core::mem::size_of::<*mut T>(), - //~^ WARN cannot use constants which depend on generic parameters in types - //~| WARN this was previously accepted by the compiler but is being phased out + //~^ ERROR generic parameters may not be used } fn main() { diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr index 906927e705ee..8c97af263b28 100644 --- a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr @@ -1,12 +1,11 @@ -warning: cannot use constants which depend on generic parameters in types - --> $DIR/issue-70453-polymorphic-ctfe.rs:10:15 +error: generic parameters may not be used in const operations + --> $DIR/issue-70453-polymorphic-ctfe.rs:9:41 | LL | Some(T) = core::mem::size_of::<*mut T>(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ cannot perform const operation using `T` | - = note: `#[warn(const_evaluatable_unchecked)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #76200 + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions -warning: 1 warning emitted +error: aborting due to previous error diff --git a/src/test/ui/enum/issue-67945-1.rs b/src/test/ui/enum/issue-67945-1.rs index 7977bddae7bc..f4697344cc7a 100644 --- a/src/test/ui/enum/issue-67945-1.rs +++ b/src/test/ui/enum/issue-67945-1.rs @@ -1,6 +1,6 @@ -enum Bug { +enum Bug { //~ ERROR parameter `S` is never used Var = { - let x: S = 0; //~ ERROR: mismatched types + let x: S = 0; //~ ERROR generic parameters may not be used 0 }, } diff --git a/src/test/ui/enum/issue-67945-1.stderr b/src/test/ui/enum/issue-67945-1.stderr index 6583fe13d0c6..32ca94203e63 100644 --- a/src/test/ui/enum/issue-67945-1.stderr +++ b/src/test/ui/enum/issue-67945-1.stderr @@ -1,17 +1,20 @@ -error[E0308]: mismatched types - --> $DIR/issue-67945-1.rs:3:20 +error: generic parameters may not be used in const operations + --> $DIR/issue-67945-1.rs:3:16 + | +LL | let x: S = 0; + | ^ cannot perform const operation using `S` + | + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions + +error[E0392]: parameter `S` is never used + --> $DIR/issue-67945-1.rs:1:10 | LL | enum Bug { - | - this type parameter -LL | Var = { -LL | let x: S = 0; - | - ^ expected type parameter `S`, found integer - | | - | expected due to this + | ^ unused parameter | - = note: expected type parameter `S` - found type `{integer}` + = help: consider removing `S`, referring to it in a field, or using a marker such as `PhantomData` -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0392`. diff --git a/src/test/ui/enum/issue-67945-2.rs b/src/test/ui/enum/issue-67945-2.rs index 16bd8530ab38..e5044468da12 100644 --- a/src/test/ui/enum/issue-67945-2.rs +++ b/src/test/ui/enum/issue-67945-2.rs @@ -1,9 +1,8 @@ #![feature(type_ascription)] -enum Bug { +enum Bug { //~ ERROR parameter `S` is never used Var = 0: S, - //~^ ERROR: mismatched types - //~| ERROR: mismatched types + //~^ ERROR generic parameters may not be used } fn main() {} diff --git a/src/test/ui/enum/issue-67945-2.stderr b/src/test/ui/enum/issue-67945-2.stderr index c40506d59edd..a738d3b15a55 100644 --- a/src/test/ui/enum/issue-67945-2.stderr +++ b/src/test/ui/enum/issue-67945-2.stderr @@ -1,25 +1,20 @@ -error[E0308]: mismatched types - --> $DIR/issue-67945-2.rs:4:11 +error: generic parameters may not be used in const operations + --> $DIR/issue-67945-2.rs:4:14 | -LL | enum Bug { - | - this type parameter LL | Var = 0: S, - | ^ expected type parameter `S`, found integer + | ^ cannot perform const operation using `S` | - = note: expected type parameter `S` - found type `{integer}` + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions -error[E0308]: mismatched types - --> $DIR/issue-67945-2.rs:4:11 +error[E0392]: parameter `S` is never used + --> $DIR/issue-67945-2.rs:3:10 | LL | enum Bug { - | - this type parameter -LL | Var = 0: S, - | ^^^^ expected `isize`, found type parameter `S` + | ^ unused parameter | - = note: expected type `isize` - found type parameter `S` + = help: consider removing `S`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0392`. diff --git a/src/test/ui/error-codes/E0396.rs b/src/test/ui/error-codes/E0396.rs index b32853e483d6..58ed3c2c7227 100644 --- a/src/test/ui/error-codes/E0396.rs +++ b/src/test/ui/error-codes/E0396.rs @@ -5,5 +5,16 @@ const REG_ADDR: *const u8 = 0x5f3759df as *const u8; const VALUE: u8 = unsafe { *REG_ADDR }; //~^ ERROR dereferencing raw pointers in constants is unstable +const unsafe fn unreachable() -> ! { + use std::convert::Infallible; + + const INFALLIBLE: *const Infallible = [].as_ptr(); + match *INFALLIBLE {} + //~^ ERROR dereferencing raw pointers in constant functions is unstable + + const BAD: () = unsafe { match *INFALLIBLE {} }; + //~^ ERROR dereferencing raw pointers in constants is unstable +} + fn main() { } diff --git a/src/test/ui/error-codes/E0396.stderr b/src/test/ui/error-codes/E0396.stderr index 7d2544f939f7..20dad1b983c1 100644 --- a/src/test/ui/error-codes/E0396.stderr +++ b/src/test/ui/error-codes/E0396.stderr @@ -7,6 +7,24 @@ LL | const VALUE: u8 = unsafe { *REG_ADDR }; = note: see issue #51911 for more information = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable -error: aborting due to previous error +error[E0658]: dereferencing raw pointers in constant functions is unstable + --> $DIR/E0396.rs:12:11 + | +LL | match *INFALLIBLE {} + | ^^^^^^^^^^^ + | + = note: see issue #51911 for more information + = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable + +error[E0658]: dereferencing raw pointers in constants is unstable + --> $DIR/E0396.rs:15:36 + | +LL | const BAD: () = unsafe { match *INFALLIBLE {} }; + | ^^^^^^^^^^^ + | + = note: see issue #51911 for more information + = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/error-codes/E0730.stderr b/src/test/ui/error-codes/E0730.stderr index 356e4f36042a..f915f6edef52 100644 --- a/src/test/ui/error-codes/E0730.stderr +++ b/src/test/ui/error-codes/E0730.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error[E0730]: cannot pattern-match on an array without a fixed length --> $DIR/E0730.rs:6:9 diff --git a/src/test/ui/error-codes/E0771.stderr b/src/test/ui/error-codes/E0771.stderr index b184b5998170..60220be6b57b 100644 --- a/src/test/ui/error-codes/E0771.stderr +++ b/src/test/ui/error-codes/E0771.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error[E0771]: use of non-static lifetime `'a` in const generic --> $DIR/E0771.rs:4:41 diff --git a/src/test/compile-fail/empty-extern-arg.rs b/src/test/ui/extern-flag/empty-extern-arg.rs similarity index 100% rename from src/test/compile-fail/empty-extern-arg.rs rename to src/test/ui/extern-flag/empty-extern-arg.rs diff --git a/src/test/ui/extern-flag/empty-extern-arg.stderr b/src/test/ui/extern-flag/empty-extern-arg.stderr new file mode 100644 index 000000000000..199c4fb616b5 --- /dev/null +++ b/src/test/ui/extern-flag/empty-extern-arg.stderr @@ -0,0 +1,4 @@ +error: extern location for std does not exist: + +error: aborting due to previous error + diff --git a/src/test/ui/feature-gate-edition_macro_pats.rs b/src/test/ui/feature-gate-edition_macro_pats.rs new file mode 100644 index 000000000000..bd8a21ea36ac --- /dev/null +++ b/src/test/ui/feature-gate-edition_macro_pats.rs @@ -0,0 +1,8 @@ +// Feature gate test for `edition_macro_pats` feature. + +macro_rules! foo { + ($x:pat2018) => {}; //~ERROR `pat2018` and `pat2021` are unstable + ($x:pat2021) => {}; //~ERROR `pat2018` and `pat2021` are unstable +} + +fn main() {} diff --git a/src/test/ui/feature-gate-edition_macro_pats.stderr b/src/test/ui/feature-gate-edition_macro_pats.stderr new file mode 100644 index 000000000000..89bfb239d9ec --- /dev/null +++ b/src/test/ui/feature-gate-edition_macro_pats.stderr @@ -0,0 +1,21 @@ +error[E0658]: `pat2018` and `pat2021` are unstable. + --> $DIR/feature-gate-edition_macro_pats.rs:4:9 + | +LL | ($x:pat2018) => {}; + | ^^^^^^^ + | + = note: see issue #54883 for more information + = help: add `#![feature(edition_macro_pats)]` to the crate attributes to enable + +error[E0658]: `pat2018` and `pat2021` are unstable. + --> $DIR/feature-gate-edition_macro_pats.rs:5:9 + | +LL | ($x:pat2021) => {}; + | ^^^^^^^ + | + = note: see issue #54883 for more information + = help: add `#![feature(edition_macro_pats)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.rs b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.rs deleted file mode 100644 index dc602ba7e6f2..000000000000 --- a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.rs +++ /dev/null @@ -1,9 +0,0 @@ -struct ConstFn; -//~^ ERROR const generics are unstable -//~^^ ERROR using function pointers as const generic parameters is forbidden - -struct ConstPtr; -//~^ ERROR const generics are unstable -//~^^ ERROR using raw pointers as const generic parameters is forbidden - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr deleted file mode 100644 index eef465318a39..000000000000 --- a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0658]: const generics are unstable - --> $DIR/feature-gate-const_generics-ptr.rs:1:22 - | -LL | struct ConstFn; - | ^ - | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable - -error[E0658]: const generics are unstable - --> $DIR/feature-gate-const_generics-ptr.rs:5:23 - | -LL | struct ConstPtr; - | ^ - | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable - -error: using function pointers as const generic parameters is forbidden - --> $DIR/feature-gate-const_generics-ptr.rs:1:25 - | -LL | struct ConstFn; - | ^^^^ - -error: using raw pointers as const generic parameters is forbidden - --> $DIR/feature-gate-const_generics-ptr.rs:5:26 - | -LL | struct ConstPtr; - | ^^^^^^^^^^ - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-const_generics.rs b/src/test/ui/feature-gates/feature-gate-const_generics.rs index fe1ded1c4bbc..06364eebef91 100644 --- a/src/test/ui/feature-gates/feature-gate-const_generics.rs +++ b/src/test/ui/feature-gates/feature-gate-const_generics.rs @@ -1,5 +1,5 @@ -fn foo() {} //~ ERROR const generics are unstable +fn foo() {} //~ ERROR `()` is forbidden as the type of a const generic parameter -struct Foo([(); X]); //~ ERROR const generics are unstable +struct Foo([(); X]); fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-const_generics.stderr b/src/test/ui/feature-gates/feature-gate-const_generics.stderr index f80362252f92..b2b7e4576bf7 100644 --- a/src/test/ui/feature-gates/feature-gate-const_generics.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_generics.stderr @@ -1,21 +1,11 @@ -error[E0658]: const generics are unstable - --> $DIR/feature-gate-const_generics.rs:1:14 +error: `()` is forbidden as the type of a const generic parameter + --> $DIR/feature-gate-const_generics.rs:1:17 | LL | fn foo() {} - | ^ + | ^^ | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable + = note: the only supported types are integers, `bool` and `char` + = help: more complex types are supported with `#[feature(const_generics)]` -error[E0658]: const generics are unstable - --> $DIR/feature-gate-const_generics.rs:3:18 - | -LL | struct Foo([(); X]); - | ^ - | - = note: see issue #74878 for more information - = help: add `#![feature(min_const_generics)]` to the crate attributes to enable +error: aborting due to previous error -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/fn/fn-recover-return-sign.fixed b/src/test/ui/fn/fn-recover-return-sign.fixed new file mode 100644 index 000000000000..076be6a35a4b --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign.fixed @@ -0,0 +1,28 @@ +// run-rustfix +#![allow(unused)] +fn a() -> usize { 0 } +//~^ ERROR return types are denoted using `->` + +fn b()-> usize { 0 } +//~^ ERROR return types are denoted using `->` + +fn bar(_: u32) {} + +fn baz() -> *const dyn Fn(u32) { unimplemented!() } + +fn foo() { + match () { + _ if baz() == &bar as &dyn Fn(u32) => (), + () => (), + } +} + +fn main() { + let foo = |a: bool| -> bool { a }; + //~^ ERROR return types are denoted using `->` + dbg!(foo(false)); + + let bar = |a: bool|-> bool { a }; + //~^ ERROR return types are denoted using `->` + dbg!(bar(false)); +} diff --git a/src/test/ui/fn/fn-recover-return-sign.rs b/src/test/ui/fn/fn-recover-return-sign.rs new file mode 100644 index 000000000000..0656023c0f89 --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign.rs @@ -0,0 +1,28 @@ +// run-rustfix +#![allow(unused)] +fn a() => usize { 0 } +//~^ ERROR return types are denoted using `->` + +fn b(): usize { 0 } +//~^ ERROR return types are denoted using `->` + +fn bar(_: u32) {} + +fn baz() -> *const dyn Fn(u32) { unimplemented!() } + +fn foo() { + match () { + _ if baz() == &bar as &dyn Fn(u32) => (), + () => (), + } +} + +fn main() { + let foo = |a: bool| => bool { a }; + //~^ ERROR return types are denoted using `->` + dbg!(foo(false)); + + let bar = |a: bool|: bool { a }; + //~^ ERROR return types are denoted using `->` + dbg!(bar(false)); +} diff --git a/src/test/ui/fn/fn-recover-return-sign.stderr b/src/test/ui/fn/fn-recover-return-sign.stderr new file mode 100644 index 000000000000..983109730ff3 --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign.stderr @@ -0,0 +1,26 @@ +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign.rs:3:8 + | +LL | fn a() => usize { 0 } + | ^^ help: use `->` instead + +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign.rs:6:7 + | +LL | fn b(): usize { 0 } + | ^ help: use `->` instead + +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign.rs:21:25 + | +LL | let foo = |a: bool| => bool { a }; + | ^^ help: use `->` instead + +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign.rs:25:24 + | +LL | let bar = |a: bool|: bool { a }; + | ^ help: use `->` instead + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/fn/fn-recover-return-sign2.rs b/src/test/ui/fn/fn-recover-return-sign2.rs new file mode 100644 index 000000000000..b6a6a1ec2a6e --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign2.rs @@ -0,0 +1,8 @@ +// Separate test file because `Fn() => bool` isn't getting fixed and rustfix complained that +// even though a fix was applied the code was still incorrect + +fn foo() => impl Fn() => bool { + //~^ ERROR return types are denoted using `->` + //~| ERROR expected one of `+`, `->`, `::`, `;`, `where`, or `{`, found `=>` + unimplemented!() +} diff --git a/src/test/ui/fn/fn-recover-return-sign2.stderr b/src/test/ui/fn/fn-recover-return-sign2.stderr new file mode 100644 index 000000000000..d62cacd4bf53 --- /dev/null +++ b/src/test/ui/fn/fn-recover-return-sign2.stderr @@ -0,0 +1,14 @@ +error: return types are denoted using `->` + --> $DIR/fn-recover-return-sign2.rs:4:10 + | +LL | fn foo() => impl Fn() => bool { + | ^^ help: use `->` instead + +error: expected one of `+`, `->`, `::`, `;`, `where`, or `{`, found `=>` + --> $DIR/fn-recover-return-sign2.rs:4:23 + | +LL | fn foo() => impl Fn() => bool { + | ^^ expected one of `+`, `->`, `::`, `;`, `where`, or `{` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/fn/issue-80179.rs b/src/test/ui/fn/issue-80179.rs new file mode 100644 index 000000000000..7609b1525cc9 --- /dev/null +++ b/src/test/ui/fn/issue-80179.rs @@ -0,0 +1,27 @@ +// Functions with a type placeholder `_` as the return type should +// show a function pointer suggestion when given a function item +// and suggest how to return closures correctly from a function. +// This is a regression test of #80179 + +fn returns_i32() -> i32 { + 0 +} + +fn returns_fn_ptr() -> _ { +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures [E0121] +//~| NOTE not allowed in type signatures +//~| HELP replace with the correct return type +//~| SUGGESTION fn() -> i32 + returns_i32 +} + +fn returns_closure() -> _ { +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures [E0121] +//~| NOTE not allowed in type signatures +//~| HELP consider using an `Fn`, `FnMut`, or `FnOnce` trait bound +//~| NOTE for more information on `Fn` traits and closure types, see +// https://doc.rust-lang.org/book/ch13-01-closures.html + || 0 +} + +fn main() {} diff --git a/src/test/ui/fn/issue-80179.stderr b/src/test/ui/fn/issue-80179.stderr new file mode 100644 index 000000000000..63571e71b34f --- /dev/null +++ b/src/test/ui/fn/issue-80179.stderr @@ -0,0 +1,21 @@ +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/issue-80179.rs:10:24 + | +LL | fn returns_fn_ptr() -> _ { + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `fn() -> i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/issue-80179.rs:18:25 + | +LL | fn returns_closure() -> _ { + | ^ not allowed in type signatures + | + = help: consider using an `Fn`, `FnMut`, or `FnOnce` trait bound + = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0121`. diff --git a/src/test/ui/fsu-moves-and-copies.rs b/src/test/ui/fsu-moves-and-copies.rs index c41bcc73fa50..6a0b4ed17b96 100644 --- a/src/test/ui/fsu-moves-and-copies.rs +++ b/src/test/ui/fsu-moves-and-copies.rs @@ -36,7 +36,7 @@ impl Drop for DropMoveFoo { fn drop(&mut self) { } } fn test0() { // just copy implicitly copyable fields from `f`, no moves // (and thus it is okay that these are Drop; compare against - // compile-fail test: borrowck-struct-update-with-dtor.rs). + // test ui/borrowck/borrowck-struct-update-with-dtor.rs). // Case 1: Nocopyable let f = DropNoFoo::new(1, 2); diff --git a/src/test/ui/functions-closures/closure-expected-type/README.md b/src/test/ui/functions-closures/closure-expected-type/README.md index fd493e1ff37d..11d2c9b7fb76 100644 --- a/src/test/ui/functions-closures/closure-expected-type/README.md +++ b/src/test/ui/functions-closures/closure-expected-type/README.md @@ -5,4 +5,4 @@ inputs. This investigation was kicked off by #38714, which revealed some pretty deep flaws in the ad-hoc way that we were doing things before. -See also `src/test/compile-fail/closure-expected-type`. +See also `src/test/ui/closure-expected-type`. diff --git a/src/test/ui/generator/yielding-in-match-guards.rs b/src/test/ui/generator/yielding-in-match-guards.rs index c76726414df8..5c10a7c78118 100644 --- a/src/test/ui/generator/yielding-in-match-guards.rs +++ b/src/test/ui/generator/yielding-in-match-guards.rs @@ -10,6 +10,9 @@ // Thus, `&'_ u8` should be included in type signature // of the underlying generator. +#![feature(if_let_guard)] +#![allow(incomplete_features)] + async fn f() -> u8 { 1 } async fn foo() -> [bool; 10] { [false; 10] } @@ -36,8 +39,16 @@ async fn i(x: u8) { } } +async fn j(x: u8) { + match x { + y if let (1, 42) = (f().await, y) => (), + _ => (), + } +} + fn main() { let _ = g(10); let _ = h(9); let _ = i(8); + let _ = j(7); } diff --git a/src/test/ui/generic-associated-types/issue-74824.rs b/src/test/ui/generic-associated-types/issue-74824.rs new file mode 100644 index 000000000000..00761a97d00c --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-74824.rs @@ -0,0 +1,27 @@ +#![feature(generic_associated_types)] +#![feature(associated_type_defaults)] +#![allow(incomplete_features)] + +use std::ops::Deref; + +trait UnsafeCopy { + type Copy: Copy = Box; + //~^ ERROR the trait bound `Box: Copy` is not satisfied + //~^^ ERROR the trait bound `T: Clone` is not satisfied + fn copy(x: &Self::Copy) -> Self::Copy { + *x + } +} + +impl UnsafeCopy for T {} + +fn main() { + let b = Box::new(42usize); + let copy = <()>::copy(&b); + + let raw_b = Box::deref(&b) as *const _; + let raw_copy = Box::deref(©) as *const _; + + // assert the addresses. + assert_eq!(raw_b, raw_copy); +} diff --git a/src/test/ui/generic-associated-types/issue-74824.stderr b/src/test/ui/generic-associated-types/issue-74824.stderr new file mode 100644 index 000000000000..34a2c1932ebc --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-74824.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `Box: Copy` is not satisfied + --> $DIR/issue-74824.rs:8:5 + | +LL | type Copy: Copy = Box; + | ^^^^^^^^^^^^^^----^^^^^^^^^^ + | | | + | | required by this bound in `UnsafeCopy::Copy` + | the trait `Copy` is not implemented for `Box` + +error[E0277]: the trait bound `T: Clone` is not satisfied + --> $DIR/issue-74824.rs:8:5 + | +LL | type Copy: Copy = Box; + | ^^^^^^^^^^^^^^----^^^^^^^^^^ + | | | + | | required by this bound in `UnsafeCopy::Copy` + | the trait `Clone` is not implemented for `T` + | + = note: required because of the requirements on the impl of `Clone` for `Box` +help: consider restricting type parameter `T` + | +LL | type Copy: Copy = Box; + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/generics/param-in-ct-in-ty-param-default.rs b/src/test/ui/generics/param-in-ct-in-ty-param-default.rs index dd89bc0f7a0f..3c62e47381c0 100644 --- a/src/test/ui/generics/param-in-ct-in-ty-param-default.rs +++ b/src/test/ui/generics/param-in-ct-in-ty-param-default.rs @@ -1,4 +1,4 @@ struct Foo()]>(T, U); -//~^ ERROR constant values inside of type parameter defaults +//~^ ERROR generic parameters may not be used in const operations fn main() {} diff --git a/src/test/ui/generics/param-in-ct-in-ty-param-default.stderr b/src/test/ui/generics/param-in-ct-in-ty-param-default.stderr index ea867240269e..41a0a03ff668 100644 --- a/src/test/ui/generics/param-in-ct-in-ty-param-default.stderr +++ b/src/test/ui/generics/param-in-ct-in-ty-param-default.stderr @@ -1,8 +1,11 @@ -error: constant values inside of type parameter defaults must not depend on generic parameters +error: generic parameters may not be used in const operations --> $DIR/param-in-ct-in-ty-param-default.rs:1:44 | LL | struct Foo()]>(T, U); - | ^ the anonymous constant must not depend on the parameter `T` + | ^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: aborting due to previous error diff --git a/src/test/ui/hygiene/generic_params.stderr b/src/test/ui/hygiene/generic_params.stderr index 6de36deb5976..4ca6d1998353 100644 --- a/src/test/ui/hygiene/generic_params.stderr +++ b/src/test/ui/hygiene/generic_params.stderr @@ -6,7 +6,6 @@ LL | #![feature(decl_macro, rustc_attrs, const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete warning: 1 warning emitted diff --git a/src/test/ui/hygiene/issue-61574-const-parameters.stderr b/src/test/ui/hygiene/issue-61574-const-parameters.stderr index 3f85383eb33f..b351b8b73a0e 100644 --- a/src/test/ui/hygiene/issue-61574-const-parameters.stderr +++ b/src/test/ui/hygiene/issue-61574-const-parameters.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete warning: 1 warning emitted diff --git a/src/test/ui/inherent-impls-overlap-check/auxiliary/repeat.rs b/src/test/ui/inherent-impls-overlap-check/auxiliary/repeat.rs new file mode 100644 index 000000000000..42ed5d19deb8 --- /dev/null +++ b/src/test/ui/inherent-impls-overlap-check/auxiliary/repeat.rs @@ -0,0 +1,54 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::{Ident, Group, TokenStream, TokenTree as Tt}; + +// This constant has to be above the ALLOCATING_ALGO_THRESHOLD +// constant in inherent_impls_overlap.rs +const REPEAT_COUNT: u32 = 501; + +#[proc_macro] +/// Repeats the input many times, while replacing idents +/// named "IDENT" with "id_$v", where v is a counter. +pub fn repeat_with_idents(input: TokenStream) -> TokenStream { + let mut res = Vec::new(); + fn visit_stream(res: &mut Vec, stream :TokenStream, v: u32) { + let mut stream_iter = stream.into_iter(); + while let Some(tt) = stream_iter.next() { + match tt { + Tt::Group(group) => { + let tt = Tt::Group(visit_group(group, v)); + res.push(tt); + }, + Tt::Ident(id) => { + let id = if &id.to_string() == "IDENT" { + Ident::new(&format!("id_{}", v), id.span()) + } else { + id + }; + res.push(Tt::Ident(id)); + }, + Tt::Punct(p) => { + res.push(Tt::Punct(p)); + }, + Tt::Literal(lit) => { + res.push(Tt::Literal(lit)); + }, + } + } + } + fn visit_group(group :Group, v: u32) -> Group { + let mut res = Vec::new(); + visit_stream(&mut res, group.stream(), v); + let stream = res.into_iter().collect(); + let delim = group.delimiter(); + Group::new(delim, stream) + } + for v in 0 .. REPEAT_COUNT { + visit_stream(&mut res, input.clone(), v) + } + res.into_iter().collect() +} diff --git a/src/test/ui/inherent-impls-overlap-check/no-overlap.rs b/src/test/ui/inherent-impls-overlap-check/no-overlap.rs new file mode 100644 index 000000000000..341bfc7b605f --- /dev/null +++ b/src/test/ui/inherent-impls-overlap-check/no-overlap.rs @@ -0,0 +1,34 @@ +// run-pass +// aux-build:repeat.rs + +// This tests the allocating algo branch of the +// inherent impls overlap checker. +// This branch was added by PR: +// https://github.com/rust-lang/rust/pull/78317 +// In this test, we repeat many impl blocks +// to trigger the allocating branch. + +#![allow(unused)] + +extern crate repeat; + +// Simple case where each impl block is distinct + +struct Foo {} + +repeat::repeat_with_idents!(impl Foo { fn IDENT() {} }); + +// There are overlapping impl blocks but due to generics, +// they may overlap. + +struct Bar(T); + +struct A; +struct B; + +repeat::repeat_with_idents!(impl Bar { fn IDENT() {} }); + +impl Bar { fn foo() {} } +impl Bar { fn foo() {} } + +fn main() {} diff --git a/src/test/ui/inherent-impls-overlap-check/overlap.rs b/src/test/ui/inherent-impls-overlap-check/overlap.rs new file mode 100644 index 000000000000..6f2801197e90 --- /dev/null +++ b/src/test/ui/inherent-impls-overlap-check/overlap.rs @@ -0,0 +1,71 @@ +// aux-build:repeat.rs + +#![allow(unused)] + +// This tests the allocating algo branch of the +// inherent impls overlap checker. +// This branch was added by PR: +// https://github.com/rust-lang/rust/pull/78317 +// In this test, we repeat many impl blocks +// to trigger the allocating branch. + +// Simple overlap + +extern crate repeat; + +struct Foo {} + +repeat::repeat_with_idents!(impl Foo { fn IDENT() {} }); + +impl Foo { fn hello() {} } //~ERROR duplicate definitions with name `hello` +impl Foo { fn hello() {} } + +// Transitive overlap + +struct Foo2 {} + +repeat::repeat_with_idents!(impl Foo2 { fn IDENT() {} }); + +impl Foo2 { + fn bar() {} + fn hello2() {} //~ERROR duplicate definitions with name `hello2` +} + +impl Foo2 { + fn baz() {} + fn hello2() {} +} + +// Slightly stronger transitive overlap + +struct Foo3 {} + +repeat::repeat_with_idents!(impl Foo3 { fn IDENT() {} }); + +impl Foo3 { + fn bar() {} //~ERROR duplicate definitions with name `bar` + fn hello3() {} //~ERROR duplicate definitions with name `hello3` +} + +impl Foo3 { + fn bar() {} + fn hello3() {} +} + +// Generic overlap + +struct Bar(T); + +struct A; +struct B; + +repeat::repeat_with_idents!(impl Bar { fn IDENT() {} }); + +impl Bar { fn foo() {} fn bar2() {} } +impl Bar { + fn foo() {} + fn bar2() {} //~ERROR duplicate definitions with name `bar2` +} +impl Bar { fn bar2() {} } + +fn main() {} diff --git a/src/test/ui/inherent-impls-overlap-check/overlap.stderr b/src/test/ui/inherent-impls-overlap-check/overlap.stderr new file mode 100644 index 000000000000..3dd2793712f5 --- /dev/null +++ b/src/test/ui/inherent-impls-overlap-check/overlap.stderr @@ -0,0 +1,47 @@ +error[E0592]: duplicate definitions with name `hello` + --> $DIR/overlap.rs:20:12 + | +LL | impl Foo { fn hello() {} } + | ^^^^^^^^^^ duplicate definitions for `hello` +LL | impl Foo { fn hello() {} } + | ---------- other definition for `hello` + +error[E0592]: duplicate definitions with name `hello2` + --> $DIR/overlap.rs:31:5 + | +LL | fn hello2() {} + | ^^^^^^^^^^^ duplicate definitions for `hello2` +... +LL | fn hello2() {} + | ----------- other definition for `hello2` + +error[E0592]: duplicate definitions with name `bar` + --> $DIR/overlap.rs:46:5 + | +LL | fn bar() {} + | ^^^^^^^^ duplicate definitions for `bar` +... +LL | fn bar() {} + | -------- other definition for `bar` + +error[E0592]: duplicate definitions with name `hello3` + --> $DIR/overlap.rs:47:5 + | +LL | fn hello3() {} + | ^^^^^^^^^^^ duplicate definitions for `hello3` +... +LL | fn hello3() {} + | ----------- other definition for `hello3` + +error[E0592]: duplicate definitions with name `bar2` + --> $DIR/overlap.rs:67:5 + | +LL | fn bar2() {} + | ^^^^^^^^^ duplicate definitions for `bar2` +LL | } +LL | impl Bar { fn bar2() {} } + | --------- other definition for `bar2` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0592`. diff --git a/src/test/ui/inline-const/macro-with-const.rs b/src/test/ui/inline-const/macro-with-const.rs new file mode 100644 index 000000000000..e7393166d8df --- /dev/null +++ b/src/test/ui/inline-const/macro-with-const.rs @@ -0,0 +1,20 @@ +// check-pass + +macro_rules! exp { + (const $n:expr) => { + $n + }; +} + +macro_rules! stmt { + (exp $e:expr) => { + $e + }; + (exp $($t:tt)+) => { + exp!($($t)+) + }; +} + +fn main() { + stmt!(exp const 1); +} diff --git a/src/test/ui/issues/issue-20433.stderr b/src/test/ui/issues/issue-20433.stderr index 3f7226c79bf2..5fed02164b5b 100644 --- a/src/test/ui/issues/issue-20433.stderr +++ b/src/test/ui/issues/issue-20433.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[i32]` cannot be known at compilation LL | fn iceman(c: Vec<[i32]>) {} | ^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/alloc/src/vec.rs:LL:COL + ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | LL | pub struct Vec { | - required by this bound in `Vec` diff --git a/src/test/ui/issues/issue-21475.rs b/src/test/ui/issues/issue-21475.rs index ab0a18869632..b028fcae0775 100644 --- a/src/test/ui/issues/issue-21475.rs +++ b/src/test/ui/issues/issue-21475.rs @@ -1,5 +1,5 @@ // run-pass -#![allow(unused_imports, overlapping_patterns)] +#![allow(unused_imports, overlapping_range_endpoints)] // pretty-expanded FIXME #23616 use m::{START, END}; diff --git a/src/test/ui/issues/issue-23122-2.stderr b/src/test/ui/issues/issue-23122-2.stderr index ce3bffe602ca..ff7e884ea6f8 100644 --- a/src/test/ui/issues/issue-23122-2.stderr +++ b/src/test/ui/issues/issue-23122-2.stderr @@ -1,11 +1,10 @@ -error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized` +error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next` --> $DIR/issue-23122-2.rs:9:5 | LL | type Next = as Next>::Next; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_23122_2`) - = note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-26251.rs b/src/test/ui/issues/issue-26251.rs index edb06fea8ad5..a3e26a41232c 100644 --- a/src/test/ui/issues/issue-26251.rs +++ b/src/test/ui/issues/issue-26251.rs @@ -1,5 +1,5 @@ // run-pass -#![allow(overlapping_patterns)] +#![allow(overlapping_range_endpoints)] fn main() { let x = 'a'; diff --git a/src/test/ui/issues/issue-26996.rs b/src/test/ui/issues/issue-26996.rs index 04382be27d7a..84037b72a274 100644 --- a/src/test/ui/issues/issue-26996.rs +++ b/src/test/ui/issues/issue-26996.rs @@ -1,6 +1,6 @@ // run-pass -// This test is bogus (i.e., should be compile-fail) during the period +// This test is bogus (i.e., should be check-fail) during the period // where #54986 is implemented and #54987 is *not* implemented. For // now: just ignore it // diff --git a/src/test/ui/issues/issue-27021.rs b/src/test/ui/issues/issue-27021.rs index 305513754508..ef3b114a5fa2 100644 --- a/src/test/ui/issues/issue-27021.rs +++ b/src/test/ui/issues/issue-27021.rs @@ -1,6 +1,6 @@ // run-pass -// This test is bogus (i.e., should be compile-fail) during the period +// This test is bogus (i.e., should be check-fail) during the period // where #54986 is implemented and #54987 is *not* implemented. For // now: just ignore it // diff --git a/src/test/ui/issues/issue-28498-ugeh-with-lifetime-param.rs b/src/test/ui/issues/issue-28498-ugeh-with-lifetime-param.rs index aea9fde5309e..43c0bfb26cd8 100644 --- a/src/test/ui/issues/issue-28498-ugeh-with-lifetime-param.rs +++ b/src/test/ui/issues/issue-28498-ugeh-with-lifetime-param.rs @@ -3,7 +3,7 @@ // Demonstrate the use of the unguarded escape hatch with a lifetime param // to assert that destructor will not access any dead data. // -// Compare with compile-fail/issue28498-reject-lifetime-param.rs +// Compare with ui/span/issue28498-reject-lifetime-param.rs #![feature(dropck_eyepatch)] diff --git a/src/test/ui/issues/issue-28498-ugeh-with-passed-to-fn.rs b/src/test/ui/issues/issue-28498-ugeh-with-passed-to-fn.rs index 91ef5a7c98d6..23fd86a093b5 100644 --- a/src/test/ui/issues/issue-28498-ugeh-with-passed-to-fn.rs +++ b/src/test/ui/issues/issue-28498-ugeh-with-passed-to-fn.rs @@ -3,7 +3,7 @@ // Demonstrate the use of the unguarded escape hatch with a type param in negative position // to assert that destructor will not access any dead data. // -// Compare with compile-fail/issue28498-reject-lifetime-param.rs +// Compare with ui/span/issue28498-reject-lifetime-param.rs // Demonstrate that a type param in negative position causes dropck to reject code // that might indirectly access previously dropped value. diff --git a/src/test/ui/issues/issue-28498-ugeh-with-trait-bound.rs b/src/test/ui/issues/issue-28498-ugeh-with-trait-bound.rs index 808f3b6e81e5..61d11cf38347 100644 --- a/src/test/ui/issues/issue-28498-ugeh-with-trait-bound.rs +++ b/src/test/ui/issues/issue-28498-ugeh-with-trait-bound.rs @@ -3,7 +3,7 @@ // Demonstrate the use of the unguarded escape hatch with a trait bound // to assert that destructor will not access any dead data. // -// Compare with compile-fail/issue28498-reject-trait-bound.rs +// Compare with ui/span/issue28498-reject-trait-bound.rs #![feature(dropck_eyepatch)] diff --git a/src/test/ui/issues/issue-39559.rs b/src/test/ui/issues/issue-39559.rs index 3a75956af528..58d25940733c 100644 --- a/src/test/ui/issues/issue-39559.rs +++ b/src/test/ui/issues/issue-39559.rs @@ -12,7 +12,7 @@ impl Dim for Dim3 { pub struct Vector { entries: [T; D::dim()], - //~^ ERROR no function or associated item named `dim` found + //~^ ERROR generic parameters may not be used _dummy: D, } diff --git a/src/test/ui/issues/issue-39559.stderr b/src/test/ui/issues/issue-39559.stderr index 5e8d487f4165..91e31ca0bd8b 100644 --- a/src/test/ui/issues/issue-39559.stderr +++ b/src/test/ui/issues/issue-39559.stderr @@ -1,11 +1,11 @@ -error[E0599]: no function or associated item named `dim` found for type parameter `D` in the current scope - --> $DIR/issue-39559.rs:14:21 +error: generic parameters may not be used in const operations + --> $DIR/issue-39559.rs:14:18 | LL | entries: [T; D::dim()], - | ^^^ function or associated item not found in `D` + | ^^^^^^ cannot perform const operation using `D` | - = help: items from traits can only be used if the type parameter is bounded by the trait + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: aborting due to previous error -For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/issues/issue-40000.nll.stderr b/src/test/ui/issues/issue-40000.nll.stderr index f673fbae8b79..4e2bde06a52a 100644 --- a/src/test/ui/issues/issue-40000.nll.stderr +++ b/src/test/ui/issues/issue-40000.nll.stderr @@ -4,5 +4,11 @@ error: higher-ranked subtype error LL | foo(bar); | ^^^ -error: aborting due to previous error +error: higher-ranked subtype error + --> $DIR/issue-40000.rs:6:9 + | +LL | foo(bar); + | ^^^ + +error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-49298.rs b/src/test/ui/issues/issue-49298.rs index 697a160b4ecb..e3ffa8e7c6ef 100644 --- a/src/test/ui/issues/issue-49298.rs +++ b/src/test/ui/issues/issue-49298.rs @@ -2,7 +2,7 @@ #![feature(test)] #![allow(unused_mut)] // under NLL we get warning about `x` below: rust-lang/rust#54499 -// This test is bogus (i.e., should be compile-fail) during the period +// This test is bogus (i.e., should be check-fail) during the period // where #54986 is implemented and #54987 is *not* implemented. For // now: just ignore it // diff --git a/src/test/ui/issues/issue-59508-1.stderr b/src/test/ui/issues/issue-59508-1.stderr index 2a4ccda89291..5e97339f148c 100644 --- a/src/test/ui/issues/issue-59508-1.stderr +++ b/src/test/ui/issues/issue-59508-1.stderr @@ -12,7 +12,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/issues/issue-59508.stderr b/src/test/ui/issues/issue-59508.stderr index c0fdb2ef34ac..33e967cebffc 100644 --- a/src/test/ui/issues/issue-59508.stderr +++ b/src/test/ui/issues/issue-59508.stderr @@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/issue-59508.rs:10:25 | LL | pub fn do_things() { - | ----^^--^^----- help: reorder the parameters: lifetimes, then types: `<'a, 'b: 'a, T>` + | ----^^--^^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b: 'a, T>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-75763.rs b/src/test/ui/issues/issue-75763.rs index 2fd9f9a60de9..c311de05a1cf 100644 --- a/src/test/ui/issues/issue-75763.rs +++ b/src/test/ui/issues/issue-75763.rs @@ -1,4 +1,5 @@ -// build-pass +// ignore-test +// FIXME(const_generics): This test causes an ICE after reverting #76030. #![allow(incomplete_features)] #![feature(const_generics)] diff --git a/src/test/ui/issues/issue-78957.rs b/src/test/ui/issues/issue-78957.rs new file mode 100644 index 000000000000..263c69bbc0b6 --- /dev/null +++ b/src/test/ui/issues/issue-78957.rs @@ -0,0 +1,30 @@ +#![deny(unused_attributes)] +#![feature(min_const_generics)] + +use std::marker::PhantomData; + +pub struct Foo<#[inline] const N: usize>; +//~^ ERROR attribute should be applied to function or closure +pub struct Bar<#[cold] const N: usize>; +//~^ ERROR attribute should be applied to a function +//~| WARN this was previously accepted +pub struct Baz<#[repr(C)] const N: usize>; +//~^ ERROR attribute should be applied to a struct, enum, or union +// +pub struct Foo2<#[inline] 'a>(PhantomData<&'a ()>); +//~^ ERROR attribute should be applied to function or closure +pub struct Bar2<#[cold] 'a>(PhantomData<&'a ()>); +//~^ ERROR attribute should be applied to a function +//~| WARN this was previously accepted +pub struct Baz2<#[repr(C)] 'a>(PhantomData<&'a ()>); +//~^ ERROR attribute should be applied to a struct, enum, or union +// +pub struct Foo3<#[inline] T>(PhantomData); +//~^ ERROR attribute should be applied to function or closure +pub struct Bar3<#[cold] T>(PhantomData); +//~^ ERROR attribute should be applied to a function +//~| WARN this was previously accepted +pub struct Baz3<#[repr(C)] T>(PhantomData); +//~^ ERROR attribute should be applied to a struct, enum, or union + +fn main() {} diff --git a/src/test/ui/issues/issue-78957.stderr b/src/test/ui/issues/issue-78957.stderr new file mode 100644 index 000000000000..26437ee4befd --- /dev/null +++ b/src/test/ui/issues/issue-78957.stderr @@ -0,0 +1,69 @@ +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-78957.rs:6:16 + | +LL | pub struct Foo<#[inline] const N: usize>; + | ^^^^^^^^^ - not a function or closure + +error: attribute should be applied to a function + --> $DIR/issue-78957.rs:8:16 + | +LL | pub struct Bar<#[cold] const N: usize>; + | ^^^^^^^ - not a function + | +note: the lint level is defined here + --> $DIR/issue-78957.rs:1:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/issue-78957.rs:11:23 + | +LL | pub struct Baz<#[repr(C)] const N: usize>; + | ^ - not a struct, enum, or union + +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-78957.rs:14:17 + | +LL | pub struct Foo2<#[inline] 'a>(PhantomData<&'a ()>); + | ^^^^^^^^^ -- not a function or closure + +error: attribute should be applied to a function + --> $DIR/issue-78957.rs:16:17 + | +LL | pub struct Bar2<#[cold] 'a>(PhantomData<&'a ()>); + | ^^^^^^^ -- not a function + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/issue-78957.rs:19:24 + | +LL | pub struct Baz2<#[repr(C)] 'a>(PhantomData<&'a ()>); + | ^ -- not a struct, enum, or union + +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-78957.rs:22:17 + | +LL | pub struct Foo3<#[inline] T>(PhantomData); + | ^^^^^^^^^ - not a function or closure + +error: attribute should be applied to a function + --> $DIR/issue-78957.rs:24:17 + | +LL | pub struct Bar3<#[cold] T>(PhantomData); + | ^^^^^^^ - not a function + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/issue-78957.rs:27:24 + | +LL | pub struct Baz3<#[repr(C)] T>(PhantomData); + | ^ - not a struct, enum, or union + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0517, E0518. +For more information about an error, try `rustc --explain E0517`. diff --git a/src/test/ui/issues/issue-79593.rs b/src/test/ui/issues/issue-79593.rs new file mode 100644 index 000000000000..fb54b36940d5 --- /dev/null +++ b/src/test/ui/issues/issue-79593.rs @@ -0,0 +1,29 @@ +mod foo { + pub struct Pub { private: () } + + pub enum Enum { + Variant { x: (), y: () }, + Other + } + + fn correct() { + Pub {}; + //~^ ERROR missing field `private` in initializer of `Pub` + Enum::Variant { x: () }; + //~^ ERROR missing field `y` in initializer of `Enum` + } +} + +fn correct() { + foo::Pub {}; + //~^ ERROR cannot construct `Pub` with struct literal syntax due to inaccessible fields +} + +fn wrong() { + foo::Enum::Variant { x: () }; + //~^ ERROR missing field `y` in initializer of `Enum` + foo::Enum::Variant { }; + //~^ ERROR missing fields `x`, `y` in initializer of `Enum` +} + +fn main() {} diff --git a/src/test/ui/issues/issue-79593.stderr b/src/test/ui/issues/issue-79593.stderr new file mode 100644 index 000000000000..33dbd85032ea --- /dev/null +++ b/src/test/ui/issues/issue-79593.stderr @@ -0,0 +1,33 @@ +error[E0063]: missing field `private` in initializer of `Pub` + --> $DIR/issue-79593.rs:10:9 + | +LL | Pub {}; + | ^^^ missing `private` + +error[E0063]: missing field `y` in initializer of `Enum` + --> $DIR/issue-79593.rs:12:9 + | +LL | Enum::Variant { x: () }; + | ^^^^^^^^^^^^^ missing `y` + +error: cannot construct `Pub` with struct literal syntax due to inaccessible fields + --> $DIR/issue-79593.rs:18:5 + | +LL | foo::Pub {}; + | ^^^^^^^^ + +error[E0063]: missing field `y` in initializer of `Enum` + --> $DIR/issue-79593.rs:23:5 + | +LL | foo::Enum::Variant { x: () }; + | ^^^^^^^^^^^^^^^^^^ missing `y` + +error[E0063]: missing fields `x`, `y` in initializer of `Enum` + --> $DIR/issue-79593.rs:25:5 + | +LL | foo::Enum::Variant { }; + | ^^^^^^^^^^^^^^^^^^ missing `x`, `y` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/issues/issue-80512-param-reordering-with-defaults.rs b/src/test/ui/issues/issue-80512-param-reordering-with-defaults.rs new file mode 100644 index 000000000000..fe3e4fbc7e0b --- /dev/null +++ b/src/test/ui/issues/issue-80512-param-reordering-with-defaults.rs @@ -0,0 +1,4 @@ +#![crate_type = "lib"] + +struct S(&'a T); +//~^ ERROR lifetime parameters must be declared prior to type parameters diff --git a/src/test/ui/issues/issue-80512-param-reordering-with-defaults.stderr b/src/test/ui/issues/issue-80512-param-reordering-with-defaults.stderr new file mode 100644 index 000000000000..a1e9a903f810 --- /dev/null +++ b/src/test/ui/issues/issue-80512-param-reordering-with-defaults.stderr @@ -0,0 +1,8 @@ +error: lifetime parameters must be declared prior to type parameters + --> $DIR/issue-80512-param-reordering-with-defaults.rs:3:18 + | +LL | struct S(&'a T); + | ---------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = ()>` + +error: aborting due to previous error + diff --git a/src/test/ui/label/label_misspelled.rs b/src/test/ui/label/label_misspelled.rs new file mode 100644 index 000000000000..ebfd5642c9fa --- /dev/null +++ b/src/test/ui/label/label_misspelled.rs @@ -0,0 +1,18 @@ +fn main() { + 'LOOP: loop { + LOOP; + //~^ ERROR cannot find value `LOOP` in this scope + }; + 'while_loop: while true { //~ WARN denote infinite loops with + while_loop; + //~^ ERROR cannot find value `while_loop` in this scope + }; + 'while_let: while let Some(_) = Some(()) { + while_let; + //~^ ERROR cannot find value `while_let` in this scope + } + 'for_loop: for _ in 0..3 { + for_loop; + //~^ ERROR cannot find value `for_loop` in this scope + }; +} diff --git a/src/test/ui/label/label_misspelled.stderr b/src/test/ui/label/label_misspelled.stderr new file mode 100644 index 000000000000..1368ca4126cd --- /dev/null +++ b/src/test/ui/label/label_misspelled.stderr @@ -0,0 +1,47 @@ +error[E0425]: cannot find value `LOOP` in this scope + --> $DIR/label_misspelled.rs:3:9 + | +LL | LOOP; + | ^^^^ + | | + | not found in this scope + | help: a label with a similar name exists: `'LOOP` + +error[E0425]: cannot find value `while_loop` in this scope + --> $DIR/label_misspelled.rs:7:9 + | +LL | while_loop; + | ^^^^^^^^^^ + | | + | not found in this scope + | help: a label with a similar name exists: `'while_loop` + +error[E0425]: cannot find value `while_let` in this scope + --> $DIR/label_misspelled.rs:11:9 + | +LL | while_let; + | ^^^^^^^^^ + | | + | not found in this scope + | help: a label with a similar name exists: `'while_let` + +error[E0425]: cannot find value `for_loop` in this scope + --> $DIR/label_misspelled.rs:15:9 + | +LL | for_loop; + | ^^^^^^^^ + | | + | not found in this scope + | help: a label with a similar name exists: `'for_loop` + +warning: denote infinite loops with `loop { ... }` + --> $DIR/label_misspelled.rs:6:5 + | +LL | 'while_loop: while true { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop` + | + = note: `#[warn(while_true)]` on by default + +error: aborting due to 4 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.rs b/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.rs index 44cb74815c6a..46ae9403c03c 100644 --- a/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.rs +++ b/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.rs @@ -4,7 +4,7 @@ pub const fn sof() -> usize { fn test() { let _: [u8; sof::()]; - //~^ ERROR the size for values of type `T` + //~^ ERROR generic parameters may not be used in const operations } fn main() {} diff --git a/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.stderr b/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.stderr index 5a6c86d133ba..5c167ea08342 100644 --- a/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.stderr +++ b/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.stderr @@ -1,19 +1,11 @@ -error[E0277]: the size for values of type `T` cannot be known at compilation time +error: generic parameters may not be used in const operations --> $DIR/feature-gate-lazy_normalization_consts.rs:6:23 | -LL | pub const fn sof() -> usize { - | - required by this bound in `sof` -... -LL | fn test() { - | - this type parameter needs to be `Sized` LL | let _: [u8; sof::()]; - | ^ doesn't have a size known at compile-time + | ^ cannot perform const operation using `T` | -help: consider relaxing the implicit `Sized` restriction - | -LL | pub const fn sof() -> usize { - | ^^^^^^^^ + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/lifetime-before-type-params.stderr b/src/test/ui/lifetime-before-type-params.stderr index 76d7d0f024d6..047bc7f6d900 100644 --- a/src/test/ui/lifetime-before-type-params.stderr +++ b/src/test/ui/lifetime-before-type-params.stderr @@ -2,25 +2,25 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/lifetime-before-type-params.rs:2:13 | LL | fn first() {} - | ----^^--^^- help: reorder the parameters: lifetimes, then types: `<'a, 'b, T>` + | ----^^--^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T>` error: lifetime parameters must be declared prior to type parameters --> $DIR/lifetime-before-type-params.rs:4:18 | LL | fn second<'a, T, 'b>() {} - | --------^^- help: reorder the parameters: lifetimes, then types: `<'a, 'b, T>` + | --------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T>` error: lifetime parameters must be declared prior to type parameters --> $DIR/lifetime-before-type-params.rs:6:16 | LL | fn third() {} - | -------^^- help: reorder the parameters: lifetimes, then types: `<'a, T, U>` + | -------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, U>` error: lifetime parameters must be declared prior to type parameters --> $DIR/lifetime-before-type-params.rs:8:18 | LL | fn fourth<'a, T, 'b, U, 'c, V>() {} - | --------^^-----^^---- help: reorder the parameters: lifetimes, then types: `<'a, 'b, 'c, T, U, V>` + | --------^^-----^^---- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, 'c, T, U, V>` error: aborting due to 4 previous errors diff --git a/src/test/compile-fail/invalid-link-args.rs b/src/test/ui/linkage-attr/invalid-link-args.rs similarity index 84% rename from src/test/compile-fail/invalid-link-args.rs rename to src/test/ui/linkage-attr/invalid-link-args.rs index 1e68b4f8b702..5eb1c637f09e 100644 --- a/src/test/compile-fail/invalid-link-args.rs +++ b/src/test/ui/linkage-attr/invalid-link-args.rs @@ -1,3 +1,5 @@ +// build-fail +// dont-check-compiler-stderr // ignore-msvc due to linker-flavor=ld // error-pattern:aFdEfSeVEEE // compile-flags: -C linker-flavor=ld diff --git a/src/test/compile-fail/issue-10755.rs b/src/test/ui/linkage-attr/issue-10755.rs similarity index 72% rename from src/test/compile-fail/issue-10755.rs rename to src/test/ui/linkage-attr/issue-10755.rs index 3c6fcddc0d29..5ce69bceed37 100644 --- a/src/test/compile-fail/issue-10755.rs +++ b/src/test/ui/linkage-attr/issue-10755.rs @@ -1,3 +1,5 @@ +// build-fail +// dont-check-compiler-stderr // compile-flags: -C linker=llllll -C linker-flavor=ld // error-pattern: linker `llllll` not found diff --git a/src/test/ui/lint/expansion-time.rs b/src/test/ui/lint/expansion-time.rs index a9c7ac363b0b..f23c7cb0dca1 100644 --- a/src/test/ui/lint/expansion-time.rs +++ b/src/test/ui/lint/expansion-time.rs @@ -5,6 +5,10 @@ macro_rules! foo { ( $($i:ident)* ) => { $($i)+ }; //~ WARN meta-variable repeats with different Kleene operator } +#[warn(missing_fragment_specifier)] +macro_rules! m { ($i) => {} } //~ WARN missing fragment specifier + //~| WARN this was previously accepted + #[warn(soft_unstable)] mod benches { #[bench] //~ WARN use of unstable library feature 'test' diff --git a/src/test/ui/lint/expansion-time.stderr b/src/test/ui/lint/expansion-time.stderr index 24e2733064e4..b0fc1f8e5eec 100644 --- a/src/test/ui/lint/expansion-time.stderr +++ b/src/test/ui/lint/expansion-time.stderr @@ -12,14 +12,28 @@ note: the lint level is defined here LL | #[warn(meta_variable_misuse)] | ^^^^^^^^^^^^^^^^^^^^ +warning: missing fragment specifier + --> $DIR/expansion-time.rs:9:19 + | +LL | macro_rules! m { ($i) => {} } + | ^^ + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:8:8 + | +LL | #[warn(missing_fragment_specifier)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + warning: use of unstable library feature 'test': `bench` is a part of custom test frameworks which are unstable - --> $DIR/expansion-time.rs:10:7 + --> $DIR/expansion-time.rs:14:7 | LL | #[bench] | ^^^^^ | note: the lint level is defined here - --> $DIR/expansion-time.rs:8:8 + --> $DIR/expansion-time.rs:12:8 | LL | #[warn(soft_unstable)] | ^^^^^^^^^^^^^ @@ -33,10 +47,10 @@ LL | 2 | ^ | note: the lint level is defined here - --> $DIR/expansion-time.rs:25:8 + --> $DIR/expansion-time.rs:29:8 | LL | #[warn(incomplete_include)] | ^^^^^^^^^^^^^^^^^^ -warning: 3 warnings emitted +warning: 4 warnings emitted diff --git a/src/test/ui/lint/function-item-references.rs b/src/test/ui/lint/function-item-references.rs index 5f7f5e66eaa9..05213f4ed4bc 100644 --- a/src/test/ui/lint/function-item-references.rs +++ b/src/test/ui/lint/function-item-references.rs @@ -1,5 +1,5 @@ // check-pass -#![feature(c_variadic, min_const_generics)] +#![feature(c_variadic)] #![warn(function_item_references)] use std::fmt::Pointer; use std::fmt::Formatter; diff --git a/src/test/ui/lint/lint-const-item-mutation.rs b/src/test/ui/lint/lint-const-item-mutation.rs index ef55f31593b6..4bf5e0a9e212 100644 --- a/src/test/ui/lint/lint-const-item-mutation.rs +++ b/src/test/ui/lint/lint-const-item-mutation.rs @@ -30,6 +30,8 @@ const MUTABLE: Mutable = Mutable { msg: "" }; const MUTABLE2: Mutable2 = Mutable2 { msg: "", other: String::new() }; const VEC: Vec = Vec::new(); const PTR: *mut () = 1 as *mut _; +const PTR_TO_ARRAY: *mut [u32; 4] = 0x12345678 as _; +const ARRAY_OF_PTR: [*mut u32; 1] = [1 as *mut _]; fn main() { ARRAY[0] = 5; //~ WARN attempting to modify @@ -55,4 +57,10 @@ fn main() { // Test that we don't warn when converting a raw pointer // into a mutable reference unsafe { &mut *PTR }; + + // Test that we don't warn when there's a dereference involved. + // If we ever 'leave' the const via a deference, we're going + // to end up modifying something other than the temporary + unsafe { (*PTR_TO_ARRAY)[0] = 1 }; + unsafe { *ARRAY_OF_PTR[0] = 25; } } diff --git a/src/test/ui/lint/lint-const-item-mutation.stderr b/src/test/ui/lint/lint-const-item-mutation.stderr index ae95abc72f39..3973af540c81 100644 --- a/src/test/ui/lint/lint-const-item-mutation.stderr +++ b/src/test/ui/lint/lint-const-item-mutation.stderr @@ -1,5 +1,5 @@ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:35:5 + --> $DIR/lint-const-item-mutation.rs:37:5 | LL | ARRAY[0] = 5; | ^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | const ARRAY: [u8; 1] = [25]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:36:5 + --> $DIR/lint-const-item-mutation.rs:38:5 | LL | MY_STRUCT.field = false; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:37:5 + --> $DIR/lint-const-item-mutation.rs:39:5 | LL | MY_STRUCT.inner_array[0] = 'b'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:38:5 + --> $DIR/lint-const-item-mutation.rs:40:5 | LL | MY_STRUCT.use_mut(); | ^^^^^^^^^^^^^^^^^^^ @@ -58,7 +58,7 @@ LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:39:5 + --> $DIR/lint-const-item-mutation.rs:41:5 | LL | &mut MY_STRUCT; | ^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:40:5 + --> $DIR/lint-const-item-mutation.rs:42:5 | LL | (&mut MY_STRUCT).use_mut(); | ^^^^^^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:52:5 + --> $DIR/lint-const-item-mutation.rs:54:5 | LL | MUTABLE2.msg = "wow"; | ^^^^^^^^^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | const MUTABLE2: Mutable2 = Mutable2 { msg: "", other: String::new() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:53:5 + --> $DIR/lint-const-item-mutation.rs:55:5 | LL | VEC.push(0); | ^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL | VEC.push(0); = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: mutable reference created due to call to this method - --> $SRC_DIR/alloc/src/vec.rs:LL:COL + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | LL | / pub fn push(&mut self, value: T) { LL | | // This will panic or abort if we would allocate > isize::MAX bytes diff --git a/src/test/compile-fail/must_use-in-stdlib-traits.rs b/src/test/ui/lint/must_use-in-stdlib-traits.rs similarity index 100% rename from src/test/compile-fail/must_use-in-stdlib-traits.rs rename to src/test/ui/lint/must_use-in-stdlib-traits.rs diff --git a/src/test/ui/lint/must_use-in-stdlib-traits.stderr b/src/test/ui/lint/must_use-in-stdlib-traits.stderr new file mode 100644 index 000000000000..76978d29dc8e --- /dev/null +++ b/src/test/ui/lint/must_use-in-stdlib-traits.stderr @@ -0,0 +1,47 @@ +error: unused implementer of `Iterator` that must be used + --> $DIR/must_use-in-stdlib-traits.rs:42:4 + | +LL | iterator(); + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/must_use-in-stdlib-traits.rs:1:9 + | +LL | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ + = note: iterators are lazy and do nothing unless consumed + +error: unused implementer of `Future` that must be used + --> $DIR/must_use-in-stdlib-traits.rs:43:4 + | +LL | future(); + | ^^^^^^^^^ + | + = note: futures do nothing unless you `.await` or poll them + +error: unused implementer of `FnOnce` that must be used + --> $DIR/must_use-in-stdlib-traits.rs:44:4 + | +LL | square_fn_once(); + | ^^^^^^^^^^^^^^^^^ + | + = note: closures are lazy and do nothing unless called + +error: unused implementer of `FnMut` that must be used + --> $DIR/must_use-in-stdlib-traits.rs:45:4 + | +LL | square_fn_mut(); + | ^^^^^^^^^^^^^^^^ + | + = note: closures are lazy and do nothing unless called + +error: unused implementer of `Fn` that must be used + --> $DIR/must_use-in-stdlib-traits.rs:46:4 + | +LL | square_fn(); + | ^^^^^^^^^^^^ + | + = note: closures are lazy and do nothing unless called + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/lint/redundant-semicolon/item-stmt-semi.rs b/src/test/ui/lint/redundant-semicolon/item-stmt-semi.rs index 4592bc31a397..8c79630b7fd2 100644 --- a/src/test/ui/lint/redundant-semicolon/item-stmt-semi.rs +++ b/src/test/ui/lint/redundant-semicolon/item-stmt-semi.rs @@ -1,10 +1,6 @@ -// check-pass -// This test should stop compiling -// we decide to enable this lint for item statements. - #![deny(redundant_semicolons)] fn main() { - fn inner() {}; - struct Bar {}; + fn inner() {}; //~ ERROR unnecessary + struct Bar {}; //~ ERROR unnecessary } diff --git a/src/test/ui/lint/redundant-semicolon/item-stmt-semi.stderr b/src/test/ui/lint/redundant-semicolon/item-stmt-semi.stderr new file mode 100644 index 000000000000..451b152cbe5a --- /dev/null +++ b/src/test/ui/lint/redundant-semicolon/item-stmt-semi.stderr @@ -0,0 +1,20 @@ +error: unnecessary trailing semicolon + --> $DIR/item-stmt-semi.rs:4:18 + | +LL | fn inner() {}; + | ^ help: remove this semicolon + | +note: the lint level is defined here + --> $DIR/item-stmt-semi.rs:1:9 + | +LL | #![deny(redundant_semicolons)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unnecessary trailing semicolon + --> $DIR/item-stmt-semi.rs:5:18 + | +LL | struct Bar {}; + | ^ help: remove this semicolon + +error: aborting due to 2 previous errors + diff --git a/src/test/compile-fail/asm-src-loc-codegen-units.rs b/src/test/ui/llvm-asm/asm-src-loc-codegen-units.rs similarity index 81% rename from src/test/compile-fail/asm-src-loc-codegen-units.rs rename to src/test/ui/llvm-asm/asm-src-loc-codegen-units.rs index 5b8690c3b98c..387822d00f64 100644 --- a/src/test/compile-fail/asm-src-loc-codegen-units.rs +++ b/src/test/ui/llvm-asm/asm-src-loc-codegen-units.rs @@ -1,3 +1,5 @@ +// build-fail +// dont-check-compiler-stderr // compile-flags: -C codegen-units=2 // ignore-emscripten diff --git a/src/test/compile-fail/asm-src-loc.rs b/src/test/ui/llvm-asm/asm-src-loc.rs similarity index 77% rename from src/test/compile-fail/asm-src-loc.rs rename to src/test/ui/llvm-asm/asm-src-loc.rs index 7c87f370d4f6..063066df11c8 100644 --- a/src/test/compile-fail/asm-src-loc.rs +++ b/src/test/ui/llvm-asm/asm-src-loc.rs @@ -1,3 +1,5 @@ +// build-fail +// dont-check-compiler-stderr // ignore-emscripten #![feature(llvm_asm)] diff --git a/src/test/ui/loops/loop-break-value.rs b/src/test/ui/loops/loop-break-value.rs index 6c4160c36aa3..8a080cfdf494 100644 --- a/src/test/ui/loops/loop-break-value.rs +++ b/src/test/ui/loops/loop-break-value.rs @@ -90,4 +90,10 @@ fn main() { break; //~ ERROR mismatched types break 4; }; + + 'LOOP: for _ in 0 .. 9 { + break LOOP; + //~^ ERROR cannot find value `LOOP` in this scope + //~| ERROR `break` with value from a `for` loop + } } diff --git a/src/test/ui/loops/loop-break-value.stderr b/src/test/ui/loops/loop-break-value.stderr index 0503d3d4c781..0237435c8b46 100644 --- a/src/test/ui/loops/loop-break-value.stderr +++ b/src/test/ui/loops/loop-break-value.stderr @@ -1,3 +1,12 @@ +error[E0425]: cannot find value `LOOP` in this scope + --> $DIR/loop-break-value.rs:95:15 + | +LL | break LOOP; + | ^^^^ + | | + | not found in this scope + | help: a label with a similar name exists: `'LOOP` + warning: denote infinite loops with `loop { ... }` --> $DIR/loop-break-value.rs:26:5 | @@ -94,6 +103,17 @@ help: instead, use `break` on its own without a value inside this `for` loop LL | break; | ^^^^^ +error[E0571]: `break` with value from a `for` loop + --> $DIR/loop-break-value.rs:95:9 + | +LL | break LOOP; + | ^^^^^^^^^^ can only break with a value inside `loop` or breakable block + | +help: instead, use `break` on its own without a value inside this `for` loop + | +LL | break; + | ^^^^^ + error[E0308]: mismatched types --> $DIR/loop-break-value.rs:4:31 | @@ -151,7 +171,7 @@ LL | break; | expected integer, found `()` | help: give it a value of the expected type: `break value` -error: aborting due to 16 previous errors; 1 warning emitted +error: aborting due to 18 previous errors; 1 warning emitted -Some errors have detailed explanations: E0308, E0571. +Some errors have detailed explanations: E0308, E0425, E0571. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/macros/edition-macro-pats.rs b/src/test/ui/macros/edition-macro-pats.rs new file mode 100644 index 000000000000..ea1f9bff6bf7 --- /dev/null +++ b/src/test/ui/macros/edition-macro-pats.rs @@ -0,0 +1,14 @@ +// run-pass + +#![feature(or_patterns)] +#![feature(edition_macro_pats)] + +macro_rules! foo { + (a $x:pat2018) => {}; + (b $x:pat2021) => {}; +} + +fn main() { + foo!(a None); + foo!(b 1 | 2); +} diff --git a/src/test/ui/macros/issue-39404.rs b/src/test/ui/macros/issue-39404.rs index 054958ba00b8..2229f2c3900c 100644 --- a/src/test/ui/macros/issue-39404.rs +++ b/src/test/ui/macros/issue-39404.rs @@ -2,5 +2,6 @@ macro_rules! m { ($i) => {} } //~^ ERROR missing fragment specifier +//~| WARN previously accepted fn main() {} diff --git a/src/test/ui/macros/issue-39404.stderr b/src/test/ui/macros/issue-39404.stderr index 645f06e59d81..d2f2a823c2a6 100644 --- a/src/test/ui/macros/issue-39404.stderr +++ b/src/test/ui/macros/issue-39404.stderr @@ -3,6 +3,10 @@ error: missing fragment specifier | LL | macro_rules! m { ($i) => {} } | ^^ + | + = note: `#[deny(missing_fragment_specifier)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 error: aborting due to previous error diff --git a/src/test/ui/macros/macro-comma-behavior-rpass.rs b/src/test/ui/macros/macro-comma-behavior-rpass.rs index e5e656de6fa7..c46274d59b65 100644 --- a/src/test/ui/macros/macro-comma-behavior-rpass.rs +++ b/src/test/ui/macros/macro-comma-behavior-rpass.rs @@ -8,7 +8,7 @@ // to it being e.g., a place where the addition of an argument // causes it to go down a code path with subtly different behavior). // -// There is a companion test in compile-fail. +// There is a companion failing test. // compile-flags: --test -C debug_assertions=yes // revisions: std core @@ -68,26 +68,26 @@ fn to_format_or_not_to_format() { assert!(true, "{}",); - // assert_eq!(1, 1, "{}",); // see compile-fail - // assert_ne!(1, 2, "{}",); // see compile-fail + // assert_eq!(1, 1, "{}",); // see check-fail + // assert_ne!(1, 2, "{}",); // see check-fail debug_assert!(true, "{}",); - // debug_assert_eq!(1, 1, "{}",); // see compile-fail - // debug_assert_ne!(1, 2, "{}",); // see compile-fail - // eprint!("{}",); // see compile-fail - // eprintln!("{}",); // see compile-fail - // format!("{}",); // see compile-fail - // format_args!("{}",); // see compile-fail + // debug_assert_eq!(1, 1, "{}",); // see check-fail + // debug_assert_ne!(1, 2, "{}",); // see check-fail + // eprint!("{}",); // see check-fail + // eprintln!("{}",); // see check-fail + // format!("{}",); // see check-fail + // format_args!("{}",); // see check-fail if falsum() { panic!("{}",); } - // print!("{}",); // see compile-fail - // println!("{}",); // see compile-fail - // unimplemented!("{}",); // see compile-fail + // print!("{}",); // see check-fail + // println!("{}",); // see check-fail + // unimplemented!("{}",); // see check-fail if falsum() { unreachable!("{}",); } - // write!(&mut stdout, "{}",); // see compile-fail - // writeln!(&mut stdout, "{}",); // see compile-fail + // write!(&mut stdout, "{}",); // see check-fail + // writeln!(&mut stdout, "{}",); // see check-fail } diff --git a/src/test/ui/macros/macro-comma-behavior.core.stderr b/src/test/ui/macros/macro-comma-behavior.core.stderr index dd0cac659fd3..ac15e9fa8ea8 100644 --- a/src/test/ui/macros/macro-comma-behavior.core.stderr +++ b/src/test/ui/macros/macro-comma-behavior.core.stderr @@ -23,22 +23,28 @@ LL | debug_assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:54:19 + --> $DIR/macro-comma-behavior.rs:52:19 | LL | format_args!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:72:21 + --> $DIR/macro-comma-behavior.rs:68:21 | LL | unimplemented!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:81:24 + --> $DIR/macro-comma-behavior.rs:77:24 | LL | write!(f, "{}",)?; | ^^ -error: aborting due to 7 previous errors +error: 1 positional argument in format string, but no arguments were given + --> $DIR/macro-comma-behavior.rs:81:26 + | +LL | writeln!(f, "{}",)?; + | ^^ + +error: aborting due to 8 previous errors diff --git a/src/test/ui/macros/macro-comma-behavior.rs b/src/test/ui/macros/macro-comma-behavior.rs index 0bfe06830783..27d50ff3d57e 100644 --- a/src/test/ui/macros/macro-comma-behavior.rs +++ b/src/test/ui/macros/macro-comma-behavior.rs @@ -40,10 +40,8 @@ fn to_format_or_not_to_format() { } #[cfg(std)] { - // FIXME: compile-fail says "expected error not found" even though - // rustc does emit an error - // eprintln!("{}",); - // [std]~^ ERROR no arguments + eprintln!("{}",); + //[std]~^ ERROR no arguments } #[cfg(std)] { @@ -63,10 +61,8 @@ fn to_format_or_not_to_format() { } #[cfg(std)] { - // FIXME: compile-fail says "expected error not found" even though - // rustc does emit an error - // println!("{}",); - // [std]~^ ERROR no arguments + println!("{}",); + //[std]~^ ERROR no arguments } unimplemented!("{}",); @@ -82,11 +78,9 @@ fn to_format_or_not_to_format() { //[core]~^ ERROR no arguments //[std]~^^ ERROR no arguments - // FIXME: compile-fail says "expected error not found" even though - // rustc does emit an error - // writeln!(f, "{}",)?; - // [core]~^ ERROR no arguments - // [std]~^^ ERROR no arguments + writeln!(f, "{}",)?; + //[core]~^ ERROR no arguments + //[std]~^^ ERROR no arguments Ok(()) } } diff --git a/src/test/ui/macros/macro-comma-behavior.std.stderr b/src/test/ui/macros/macro-comma-behavior.std.stderr index 4372d89fbf52..7fd060e22249 100644 --- a/src/test/ui/macros/macro-comma-behavior.std.stderr +++ b/src/test/ui/macros/macro-comma-behavior.std.stderr @@ -29,34 +29,52 @@ LL | eprint!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:50:18 + --> $DIR/macro-comma-behavior.rs:43:20 + | +LL | eprintln!("{}",); + | ^^ + +error: 1 positional argument in format string, but no arguments were given + --> $DIR/macro-comma-behavior.rs:48:18 | LL | format!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:54:19 + --> $DIR/macro-comma-behavior.rs:52:19 | LL | format_args!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:61:17 + --> $DIR/macro-comma-behavior.rs:59:17 | LL | print!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:72:21 + --> $DIR/macro-comma-behavior.rs:64:19 + | +LL | println!("{}",); + | ^^ + +error: 1 positional argument in format string, but no arguments were given + --> $DIR/macro-comma-behavior.rs:68:21 | LL | unimplemented!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:81:24 + --> $DIR/macro-comma-behavior.rs:77:24 | LL | write!(f, "{}",)?; | ^^ -error: aborting due to 10 previous errors +error: 1 positional argument in format string, but no arguments were given + --> $DIR/macro-comma-behavior.rs:81:26 + | +LL | writeln!(f, "{}",)?; + | ^^ + +error: aborting due to 13 previous errors diff --git a/src/test/ui/macros/macro-comma-support-rpass.rs b/src/test/ui/macros/macro-comma-support-rpass.rs index 50c0ef3451d3..f6c4f896d67c 100644 --- a/src/test/ui/macros/macro-comma-support-rpass.rs +++ b/src/test/ui/macros/macro-comma-support-rpass.rs @@ -68,7 +68,7 @@ fn column() { let _ = column!(); } -// compile_error! is in a companion to this test in compile-fail +// compile_error! is in a check-fail companion to this test #[test] fn concat() { diff --git a/src/test/ui/macros/macro-match-nonterminal.rs b/src/test/ui/macros/macro-match-nonterminal.rs index 6b023e413727..b23e5c71c03f 100644 --- a/src/test/ui/macros/macro-match-nonterminal.rs +++ b/src/test/ui/macros/macro-match-nonterminal.rs @@ -2,6 +2,7 @@ macro_rules! test { ($a, $b) => { //~^ ERROR missing fragment //~| ERROR missing fragment + //~| WARN this was previously accepted () }; } diff --git a/src/test/ui/macros/macro-match-nonterminal.stderr b/src/test/ui/macros/macro-match-nonterminal.stderr index 334d62812cda..674ce3434aac 100644 --- a/src/test/ui/macros/macro-match-nonterminal.stderr +++ b/src/test/ui/macros/macro-match-nonterminal.stderr @@ -9,6 +9,10 @@ error: missing fragment specifier | LL | ($a, $b) => { | ^^ + | + = note: `#[deny(missing_fragment_specifier)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 error: aborting due to 2 previous errors diff --git a/src/test/ui/macros/macro-pat-follow-2018.rs b/src/test/ui/macros/macro-pat-follow-2018.rs new file mode 100644 index 000000000000..ce2911de986d --- /dev/null +++ b/src/test/ui/macros/macro-pat-follow-2018.rs @@ -0,0 +1,15 @@ +// run-pass +// edition:2018 + +macro_rules! pat_bar { + ($p:pat | $p2:pat) => {{ + match Some(1u8) { + $p | $p2 => {} + _ => {} + } + }}; +} + +fn main() { + pat_bar!(Some(1u8) | None); +} diff --git a/src/test/ui/macros/macro-pat-follow.rs b/src/test/ui/macros/macro-pat-follow.rs index 8673cf794678..8e02789fdd8d 100644 --- a/src/test/ui/macros/macro-pat-follow.rs +++ b/src/test/ui/macros/macro-pat-follow.rs @@ -3,29 +3,19 @@ macro_rules! pat_in { ($p:pat in $e:expr) => {{ let mut iter = $e.into_iter(); while let $p = iter.next() {} - }} + }}; } macro_rules! pat_if { ($p:pat if $e:expr) => {{ match Some(1u8) { - $p if $e => {}, + $p if $e => {} _ => {} } - }} -} - -macro_rules! pat_bar { - ($p:pat | $p2:pat) => {{ - match Some(1u8) { - $p | $p2 => {}, - _ => {} - } - }} + }}; } fn main() { pat_in!(Some(_) in 0..10); pat_if!(Some(x) if x > 0); - pat_bar!(Some(1u8) | None); } diff --git a/src/test/compile-fail/not-utf8.bin b/src/test/ui/macros/not-utf8.bin similarity index 100% rename from src/test/compile-fail/not-utf8.bin rename to src/test/ui/macros/not-utf8.bin diff --git a/src/test/compile-fail/not-utf8.rs b/src/test/ui/macros/not-utf8.rs similarity index 100% rename from src/test/compile-fail/not-utf8.rs rename to src/test/ui/macros/not-utf8.rs diff --git a/src/test/ui/macros/not-utf8.stderr b/src/test/ui/macros/not-utf8.stderr new file mode 100644 index 000000000000..f47be14fae3b --- /dev/null +++ b/src/test/ui/macros/not-utf8.stderr @@ -0,0 +1,10 @@ +error: couldn't read $DIR/not-utf8.bin: stream did not contain valid UTF-8 + --> $DIR/not-utf8.rs:4:5 + | +LL | include!("not-utf8.bin") + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/meta/meta-expected-error-wrong-rev.a.stderr b/src/test/ui/meta/meta-expected-error-wrong-rev.a.stderr new file mode 100644 index 000000000000..583b2c4cfe03 --- /dev/null +++ b/src/test/ui/meta/meta-expected-error-wrong-rev.a.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/meta-expected-error-wrong-rev.rs:13:18 + | +LL | let x: u32 = 22_usize; + | --- ^^^^^^^^ expected `u32`, found `usize` + | | + | expected due to this + | +help: change the type of the numeric literal from `usize` to `u32` + | +LL | let x: u32 = 22_u32; + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/compile-fail/meta-expected-error-wrong-rev.rs b/src/test/ui/meta/meta-expected-error-wrong-rev.rs similarity index 100% rename from src/test/compile-fail/meta-expected-error-wrong-rev.rs rename to src/test/ui/meta/meta-expected-error-wrong-rev.rs diff --git a/src/test/ui/mir/issue-78496.rs b/src/test/ui/mir/issue-78496.rs new file mode 100644 index 000000000000..1b0687cfac3f --- /dev/null +++ b/src/test/ui/mir/issue-78496.rs @@ -0,0 +1,16 @@ +// run-pass +// compile-flags: -Z mir-opt-level=2 -C opt-level=0 + +// example from #78496 +pub enum E<'a> { + Empty, + Some(&'a E<'a>), +} + +fn f(e: &E) -> u32 { + if let E::Some(E::Some(_)) = e { 1 } else { 2 } +} + +fn main() { + assert_eq!(f(&E::Empty), 2); +} diff --git a/src/test/ui/mir/mir-inlining/array-clone-with-generic-size.rs b/src/test/ui/mir/mir-inlining/array-clone-with-generic-size.rs index 8a96303e6b90..eec0a4599c30 100644 --- a/src/test/ui/mir/mir-inlining/array-clone-with-generic-size.rs +++ b/src/test/ui/mir/mir-inlining/array-clone-with-generic-size.rs @@ -3,8 +3,6 @@ // // build-pass // compile-flags: -Zmir-opt-level=2 -Zvalidate-mir -#![feature(min_const_generics)] - #[derive(Clone)] struct Array([T; N]); diff --git a/src/test/compile-fail/issue-52443.rs b/src/test/ui/never_type/issue-52443.rs similarity index 100% rename from src/test/compile-fail/issue-52443.rs rename to src/test/ui/never_type/issue-52443.rs diff --git a/src/test/ui/never_type/issue-52443.stderr b/src/test/ui/never_type/issue-52443.stderr new file mode 100644 index 000000000000..051896cb89c8 --- /dev/null +++ b/src/test/ui/never_type/issue-52443.stderr @@ -0,0 +1,57 @@ +warning: denote infinite loops with `loop { ... }` + --> $DIR/issue-52443.rs:6:11 + | +LL | [(); {while true {break}; 0}]; + | ^^^^^^^^^^ help: use `loop` + | + = note: `#[warn(while_true)]` on by default + +error[E0744]: `for` is not allowed in a `const` + --> $DIR/issue-52443.rs:9:12 + | +LL | [(); { for _ in 0usize.. {}; 0}]; + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/issue-52443.rs:2:10 + | +LL | [(); & { loop { continue } } ]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected `usize`, found reference + | help: consider removing the borrow: `{ loop { continue } }` + | + = note: expected type `usize` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/issue-52443.rs:4:17 + | +LL | [(); loop { break }]; + | ^^^^^ + | | + | expected `usize`, found `()` + | help: give it a value of the expected type: `break 42` + +error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants + --> $DIR/issue-52443.rs:9:21 + | +LL | [(); { for _ in 0usize.. {}; 0}]; + | ^^^^^^^^ + +error[E0764]: mutable references are not allowed in constants + --> $DIR/issue-52443.rs:9:21 + | +LL | [(); { for _ in 0usize.. {}; 0}]; + | ^^^^^^^^ `&mut` is only allowed in `const fn` + +error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants + --> $DIR/issue-52443.rs:9:21 + | +LL | [(); { for _ in 0usize.. {}; 0}]; + | ^^^^^^^^ + +error: aborting due to 6 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0015, E0308, E0744, E0764. +For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index 799ed89dcce3..4e122d930fc4 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -6,7 +6,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | = note: defining type: test::{closure#0} with closure substs [ i16, - for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) mut &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) i32)), + for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) i32)), (), ] diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index a094fc45178f..44d1d2327fcd 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -6,7 +6,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | = note: defining type: test::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) mut &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32)), + for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32)), (), ] diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index c4f4facae1fb..fa9f994c4fae 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -10,7 +10,7 @@ LL | | }, | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)), + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)), (), ] = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr index c1450564c45d..0555f79bcb03 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)), + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index e7b8dff4e7ec..0115f5412f21 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -10,7 +10,7 @@ LL | | }) | = note: defining type: case1::{closure#0} with closure substs [ i32, - for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>)), + for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>)), (), ] @@ -49,7 +49,7 @@ LL | | }) | = note: defining type: case2::{closure#0} with closure substs [ i32, - for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>)), + for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>)), (), ] = note: number of external vids: 2 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index c7e68d02dcf1..e55d033d2c76 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -12,7 +12,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t1)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t2)) u32>)), + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) u32>)), (), ] = note: late-bound region is '_#2r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index abbc76eaf4dd..ac4a4579c9cd 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -12,7 +12,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)), + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index c91b514a796c..60dca1baa40e 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: test::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)), + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr index 4ddf6f8323f6..cbb10eb187ed 100644 --- a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr @@ -10,7 +10,7 @@ LL | | }, | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)), + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 6dc6f4568058..f9f1d8bb6fff 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)), + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)), (), ] = note: late-bound region is '_#2r diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 6bcada5c26c8..1587c28e1bef 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)), + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr index 1da6c6d2c685..44f743310b48 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -6,7 +6,7 @@ LL | expect_sig(|a, b| b); // ought to return `a` | = note: defining type: test::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32)) -> &ReLateBound(DebruijnIndex(0), BrNamed('r)) i32, + for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32)) -> &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) i32, (), ] diff --git a/src/test/ui/nll/closures-in-loops.stderr b/src/test/ui/nll/closures-in-loops.stderr index 37638a93d77f..2f134f83ced1 100644 --- a/src/test/ui/nll/closures-in-loops.stderr +++ b/src/test/ui/nll/closures-in-loops.stderr @@ -15,7 +15,7 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time LL | v.push(|| x = String::new()); | ^^ - borrows occur due to use of `x` in closure | | - | mutable borrow starts here in previous iteration of loop + | `x` was mutably borrowed here in the previous iteration of the loop error[E0524]: two closures require unique access to `x` at the same time --> $DIR/closures-in-loops.rs:20:16 diff --git a/src/test/ui/nll/issue-62007-assign-const-index.stderr b/src/test/ui/nll/issue-62007-assign-const-index.stderr index 758a14d01770..0db9fe62c386 100644 --- a/src/test/ui/nll/issue-62007-assign-const-index.stderr +++ b/src/test/ui/nll/issue-62007-assign-const-index.stderr @@ -5,7 +5,7 @@ LL | fn to_refs(mut list: [&mut List; 2]) -> Vec<&mut T> { | - let's call the lifetime of this reference `'1` ... LL | result.push(&mut list[0].value); - | ^^^^^^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop + | ^^^^^^^^^^^^^^^^^^ `list[_].value` was mutably borrowed here in the previous iteration of the loop ... LL | return result; | ------ returning this value requires that `list[_].value` is borrowed for `'1` @@ -19,7 +19,7 @@ LL | fn to_refs(mut list: [&mut List; 2]) -> Vec<&mut T> { LL | if let Some(n) = list[0].next.as_mut() { | ^^^^^^^^^^^^--------- | | - | mutable borrow starts here in previous iteration of loop + | `list[_].next` was mutably borrowed here in the previous iteration of the loop | argument requires that `list[_].next` is borrowed for `'1` error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/issue-62007-assign-differing-fields.stderr b/src/test/ui/nll/issue-62007-assign-differing-fields.stderr index f942d7628b50..f1af2e855afe 100644 --- a/src/test/ui/nll/issue-62007-assign-differing-fields.stderr +++ b/src/test/ui/nll/issue-62007-assign-differing-fields.stderr @@ -5,7 +5,7 @@ LL | fn to_refs<'a, T>(mut list: (&'a mut List, &'a mut List)) -> Vec<&'a | -- lifetime `'a` defined here ... LL | result.push(&mut (list.0).value); - | ^^^^^^^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop + | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop ... LL | return result; | ------ returning this value requires that `list.0.value` is borrowed for `'a` @@ -19,7 +19,7 @@ LL | fn to_refs<'a, T>(mut list: (&'a mut List, &'a mut List)) -> Vec<&'a LL | if let Some(n) = (list.0).next.as_mut() { | ^^^^^^^^^^^^^--------- | | - | mutable borrow starts here in previous iteration of loop + | `list.0.next` was mutably borrowed here in the previous iteration of the loop | argument requires that `list.0.next` is borrowed for `'a` error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/polonius/assignment-to-differing-field.stderr b/src/test/ui/nll/polonius/assignment-to-differing-field.stderr index 07ca021b53bc..2fe6a53a49ae 100644 --- a/src/test/ui/nll/polonius/assignment-to-differing-field.stderr +++ b/src/test/ui/nll/polonius/assignment-to-differing-field.stderr @@ -5,7 +5,7 @@ LL | fn assignment_to_field_projection<'a, T>( | -- lifetime `'a` defined here ... LL | result.push(&mut (list.0).value); - | ^^^^^^^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop + | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop ... LL | return result; | ------ returning this value requires that `list.0.value` is borrowed for `'a` @@ -19,7 +19,7 @@ LL | fn assignment_to_field_projection<'a, T>( LL | if let Some(n) = (list.0).next.as_mut() { | ^^^^^^^^^^^^^--------- | | - | mutable borrow starts here in previous iteration of loop + | `list.0.next` was mutably borrowed here in the previous iteration of the loop | argument requires that `list.0.next` is borrowed for `'a` error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time @@ -29,7 +29,7 @@ LL | fn assignment_through_projection_chain<'a, T>( | -- lifetime `'a` defined here ... LL | result.push(&mut ((((list.0).0).0).0).0.value); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.value` was mutably borrowed here in the previous iteration of the loop ... LL | return result; | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` @@ -43,7 +43,7 @@ LL | fn assignment_through_projection_chain<'a, T>( LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^--------- | | - | mutable borrow starts here in previous iteration of loop + | `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop | argument requires that `list.0.0.0.0.0.next` is borrowed for `'a` error: aborting due to 4 previous errors diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr index 7c0d63c368be..dbf76cd1329c 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -6,7 +6,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); | = note: defining type: generic::::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BrNamed('s)) T)), + for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) T)), (), ] = note: number of external vids: 2 @@ -31,7 +31,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); | = note: defining type: generic_fail::::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BrNamed('s)) T)), + for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) T)), (), ] = note: late-bound region is '_#2r diff --git a/src/test/ui/no-std-macros.rs b/src/test/ui/no-std-macros.rs new file mode 100644 index 000000000000..ada643c7ac04 --- /dev/null +++ b/src/test/ui/no-std-macros.rs @@ -0,0 +1,13 @@ +// compile-flags: --crate-type=lib +// check-pass +// issue #55482 +#![no_std] + +macro_rules! foo { + ($e:expr) => { + $crate::core::assert!($e); + $crate::core::assert_eq!($e, true); + }; +} + +pub fn foo() { foo!(true); } diff --git a/src/test/ui/object-lifetime-default-from-rptr-box.rs b/src/test/ui/object-lifetime-default-from-rptr-box.rs index 8ac45b3db71b..b61083078ccd 100644 --- a/src/test/ui/object-lifetime-default-from-rptr-box.rs +++ b/src/test/ui/object-lifetime-default-from-rptr-box.rs @@ -23,7 +23,7 @@ fn b<'a>(t: &'a Box, mut ss: SomeStruct<'a>) { ss.u = t; } -// see also compile-fail/object-lifetime-default-from-rptr-box-error.rs +// see also ui/object-lifetime/object-lifetime-default-from-rptr-box-error.rs fn d<'a>(t: &'a Box, mut ss: SomeStruct<'a>) { ss.u = t; diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index 512f1e283cb4..184ffa85c40e 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -64,6 +64,35 @@ fn main() { | 2, ..] => {} _ => {} } + match &[][..] { + [true] => {} + [true | false, ..] => {} + _ => {} + } + match &[][..] { + [false] => {} + [true, ..] => {} + [true //~ ERROR unreachable + | false, ..] => {} + _ => {} + } + match (true, None) { + (true, Some(_)) => {} + (false, Some(true)) => {} + (true | false, None | Some(true //~ ERROR unreachable + | false)) => {} + } + macro_rules! t_or_f { + () => { + (true // FIXME: should be unreachable + | false) + }; + } + match (true, None) { + (true, Some(_)) => {} + (false, Some(true)) => {} + (true | false, None | Some(t_or_f!())) => {} + } match Some(0) { Some(0) => {} Some(0 //~ ERROR unreachable diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index e968310d108d..8b1003b5514a 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -95,28 +95,40 @@ LL | [1 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:69:14 + --> $DIR/exhaustiveness-unreachable-pattern.rs:75:10 + | +LL | [true + | ^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:82:36 + | +LL | (true | false, None | Some(true + | ^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:98:14 | LL | Some(0 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:88:19 + --> $DIR/exhaustiveness-unreachable-pattern.rs:117:19 | LL | | false) => {} | ^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:96:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:125:15 | LL | | true) => {} | ^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:102:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:131:15 | LL | | true, | ^^^^ -error: aborting due to 19 previous errors +error: aborting due to 21 previous errors diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs new file mode 100644 index 000000000000..9c3c5dd360e0 --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs @@ -0,0 +1,15 @@ +// Test that :pat doesn't accept top-level or-patterns in edition 2018. + +// edition:2018 + +#![feature(or_patterns)] + +fn main() {} + +// Test the `pat` macro fragment parser: +macro_rules! accept_pat { + ($p:pat) => {}; +} + +accept_pat!(p | q); //~ ERROR no rules expected the token `|` +accept_pat!(|p| q); //~ ERROR no rules expected the token `|` diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr new file mode 100644 index 000000000000..7dbc30876634 --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr @@ -0,0 +1,20 @@ +error: no rules expected the token `|` + --> $DIR/or-patterns-syntactic-fail-2018.rs:14:15 + | +LL | macro_rules! accept_pat { + | ----------------------- when calling this macro +... +LL | accept_pat!(p | q); + | ^ no rules expected this token in macro call + +error: no rules expected the token `|` + --> $DIR/or-patterns-syntactic-fail-2018.rs:15:13 + | +LL | macro_rules! accept_pat { + | ----------------------- when calling this macro +... +LL | accept_pat!(|p| q); + | ^ no rules expected this token in macro call + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs index d23220056524..efe90b3e3c60 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs @@ -5,16 +5,6 @@ fn main() {} -// Test the `pat` macro fragment parser: -macro_rules! accept_pat { - ($p:pat) => {} -} - -accept_pat!(p | q); //~ ERROR no rules expected the token `|` -accept_pat!(| p | q); //~ ERROR no rules expected the token `|` - -// Non-macro tests: - enum E { A, B } use E::*; diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr index 861d274ab5c7..989aeb520064 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr @@ -1,53 +1,53 @@ error: an or-pattern parameter must be wrapped in parenthesis - --> $DIR/or-patterns-syntactic-fail.rs:27:13 + --> $DIR/or-patterns-syntactic-fail.rs:17:13 | LL | fn fun1(A | B: E) {} | ^^^^^ help: wrap the pattern in parenthesis: `(A | B)` error: a leading `|` is not allowed in a parameter pattern - --> $DIR/or-patterns-syntactic-fail.rs:29:13 + --> $DIR/or-patterns-syntactic-fail.rs:19:13 | LL | fn fun2(| A | B: E) {} | ^ help: remove the `|` error: an or-pattern parameter must be wrapped in parenthesis - --> $DIR/or-patterns-syntactic-fail.rs:29:15 + --> $DIR/or-patterns-syntactic-fail.rs:19:15 | LL | fn fun2(| A | B: E) {} | ^^^^^ help: wrap the pattern in parenthesis: `(A | B)` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:40:11 + --> $DIR/or-patterns-syntactic-fail.rs:30:11 | LL | let ( | A | B) = E::A; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:41:11 + --> $DIR/or-patterns-syntactic-fail.rs:31:11 | LL | let ( | A | B,) = (E::B,); | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:42:11 + --> $DIR/or-patterns-syntactic-fail.rs:32:11 | LL | let [ | A | B ] = [E::A]; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:43:13 + --> $DIR/or-patterns-syntactic-fail.rs:33:13 | LL | let TS( | A | B ); | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:44:17 + --> $DIR/or-patterns-syntactic-fail.rs:34:17 | LL | let NS { f: | A | B }; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:46:11 + --> $DIR/or-patterns-syntactic-fail.rs:36:11 | LL | let ( || A | B) = E::A; | ^^ help: remove the `||` @@ -55,7 +55,7 @@ LL | let ( || A | B) = E::A; = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:47:11 + --> $DIR/or-patterns-syntactic-fail.rs:37:11 | LL | let [ || A | B ] = [E::A]; | ^^ help: remove the `||` @@ -63,7 +63,7 @@ LL | let [ || A | B ] = [E::A]; = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:48:13 + --> $DIR/or-patterns-syntactic-fail.rs:38:13 | LL | let TS( || A | B ); | ^^ help: remove the `||` @@ -71,33 +71,15 @@ LL | let TS( || A | B ); = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:49:17 + --> $DIR/or-patterns-syntactic-fail.rs:39:17 | LL | let NS { f: || A | B }; | ^^ help: remove the `||` | = note: alternatives in or-patterns are separated with `|`, not `||` -error: no rules expected the token `|` - --> $DIR/or-patterns-syntactic-fail.rs:13:15 - | -LL | macro_rules! accept_pat { - | ----------------------- when calling this macro -... -LL | accept_pat!(p | q); - | ^ no rules expected this token in macro call - -error: no rules expected the token `|` - --> $DIR/or-patterns-syntactic-fail.rs:14:13 - | -LL | macro_rules! accept_pat { - | ----------------------- when calling this macro -... -LL | accept_pat!(| p | q); - | ^ no rules expected this token in macro call - error[E0369]: no implementation for `E | ()` - --> $DIR/or-patterns-syntactic-fail.rs:23:22 + --> $DIR/or-patterns-syntactic-fail.rs:13:22 | LL | let _ = |A | B: E| (); | ----^ -- () @@ -107,7 +89,7 @@ LL | let _ = |A | B: E| (); = note: an implementation of `std::ops::BitOr` might be missing for `E` error[E0308]: mismatched types - --> $DIR/or-patterns-syntactic-fail.rs:51:36 + --> $DIR/or-patterns-syntactic-fail.rs:41:36 | LL | let recovery_witness: String = 0; | ------ ^ @@ -116,7 +98,7 @@ LL | let recovery_witness: String = 0; | | help: try using a conversion method: `0.to_string()` | expected due to this -error: aborting due to 16 previous errors +error: aborting due to 14 previous errors Some errors have detailed explanations: E0308, E0369. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs b/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs new file mode 100644 index 000000000000..f0ce7597aeed --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs @@ -0,0 +1,14 @@ +// Tests that :pat in macros in edition 2021 allows top-level or-patterns. + +// run-pass +// ignore-test +// edition:2021 +// FIXME(mark-i-m): unignore when 2021 machinery is in place. + +macro_rules! accept_pat { + ($p:pat) => {}; +} + +accept_pat!(p | q); + +fn main() {} diff --git a/src/test/compile-fail/auxiliary/weak-lang-items.rs b/src/test/ui/panic-handler/auxiliary/weak-lang-items.rs similarity index 100% rename from src/test/compile-fail/auxiliary/weak-lang-items.rs rename to src/test/ui/panic-handler/auxiliary/weak-lang-items.rs diff --git a/src/test/compile-fail/panic-handler-missing.rs b/src/test/ui/panic-handler/panic-handler-missing.rs similarity index 83% rename from src/test/compile-fail/panic-handler-missing.rs rename to src/test/ui/panic-handler/panic-handler-missing.rs index 1c380c99c187..6bb062ba657a 100644 --- a/src/test/compile-fail/panic-handler-missing.rs +++ b/src/test/ui/panic-handler/panic-handler-missing.rs @@ -1,3 +1,4 @@ +// dont-check-compiler-stderr // error-pattern: `#[panic_handler]` function required, but not found #![feature(lang_items)] diff --git a/src/test/compile-fail/panic-handler-twice.rs b/src/test/ui/panic-handler/panic-handler-twice.rs similarity index 90% rename from src/test/compile-fail/panic-handler-twice.rs rename to src/test/ui/panic-handler/panic-handler-twice.rs index 0c5359b9bd8c..05bef66d849a 100644 --- a/src/test/compile-fail/panic-handler-twice.rs +++ b/src/test/ui/panic-handler/panic-handler-twice.rs @@ -1,3 +1,4 @@ +// dont-check-compiler-stderr // aux-build:some-panic-impl.rs #![feature(lang_items)] diff --git a/src/test/compile-fail/weak-lang-item.rs b/src/test/ui/panic-handler/weak-lang-item.rs similarity index 100% rename from src/test/compile-fail/weak-lang-item.rs rename to src/test/ui/panic-handler/weak-lang-item.rs diff --git a/src/test/ui/panic-handler/weak-lang-item.stderr b/src/test/ui/panic-handler/weak-lang-item.stderr new file mode 100644 index 000000000000..b7c040c7a850 --- /dev/null +++ b/src/test/ui/panic-handler/weak-lang-item.stderr @@ -0,0 +1,19 @@ +error[E0259]: the name `core` is defined multiple times + --> $DIR/weak-lang-item.rs:8:1 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ `core` reimported here + | + = note: `core` must be defined only once in the type namespace of this module +help: you can use `as` to change the binding name of the import + | +LL | extern crate core as other_core; + | + +error: `#[panic_handler]` function required, but not found + +error: language item required, but not found: `eh_personality` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0259`. diff --git a/src/test/compile-fail/auxiliary/depends.rs b/src/test/ui/panic-runtime/auxiliary/depends.rs similarity index 100% rename from src/test/compile-fail/auxiliary/depends.rs rename to src/test/ui/panic-runtime/auxiliary/depends.rs diff --git a/src/test/compile-fail/auxiliary/needs-panic-runtime.rs b/src/test/ui/panic-runtime/auxiliary/needs-panic-runtime.rs similarity index 100% rename from src/test/compile-fail/auxiliary/needs-panic-runtime.rs rename to src/test/ui/panic-runtime/auxiliary/needs-panic-runtime.rs diff --git a/src/test/compile-fail/runtime-depend-on-needs-runtime.rs b/src/test/ui/panic-runtime/runtime-depend-on-needs-runtime.rs similarity index 84% rename from src/test/compile-fail/runtime-depend-on-needs-runtime.rs rename to src/test/ui/panic-runtime/runtime-depend-on-needs-runtime.rs index 866c5b2e34bb..d57f1643e98a 100644 --- a/src/test/compile-fail/runtime-depend-on-needs-runtime.rs +++ b/src/test/ui/panic-runtime/runtime-depend-on-needs-runtime.rs @@ -1,3 +1,4 @@ +// dont-check-compiler-stderr // aux-build:needs-panic-runtime.rs // aux-build:depends.rs // error-pattern:cannot depend on a crate that needs a panic runtime diff --git a/src/test/compile-fail/two-panic-runtimes.rs b/src/test/ui/panic-runtime/two-panic-runtimes.rs similarity index 90% rename from src/test/compile-fail/two-panic-runtimes.rs rename to src/test/ui/panic-runtime/two-panic-runtimes.rs index 671d44564e66..c968b5ea1e18 100644 --- a/src/test/compile-fail/two-panic-runtimes.rs +++ b/src/test/ui/panic-runtime/two-panic-runtimes.rs @@ -1,3 +1,5 @@ +// build-fail +// dont-check-compiler-stderr // error-pattern:cannot link together two panic runtimes: panic_runtime_unwind and panic_runtime_unwind2 // ignore-tidy-linelength // aux-build:panic-runtime-unwind.rs diff --git a/src/test/compile-fail/unwind-tables-panic-required.rs b/src/test/ui/panic-runtime/unwind-tables-panic-required.rs similarity index 91% rename from src/test/compile-fail/unwind-tables-panic-required.rs rename to src/test/ui/panic-runtime/unwind-tables-panic-required.rs index 314d9e778d5a..6393a27046b8 100644 --- a/src/test/compile-fail/unwind-tables-panic-required.rs +++ b/src/test/ui/panic-runtime/unwind-tables-panic-required.rs @@ -1,6 +1,7 @@ // Tests that the compiler errors if the user tries to turn off unwind tables // when they are required. // +// dont-check-compiler-stderr // compile-flags: -C panic=unwind -C force-unwind-tables=no // ignore-tidy-linelength // diff --git a/src/test/compile-fail/unwind-tables-target-required.rs b/src/test/ui/panic-runtime/unwind-tables-target-required.rs similarity index 100% rename from src/test/compile-fail/unwind-tables-target-required.rs rename to src/test/ui/panic-runtime/unwind-tables-target-required.rs diff --git a/src/test/compile-fail/want-abort-got-unwind.rs b/src/test/ui/panic-runtime/want-abort-got-unwind.rs similarity index 81% rename from src/test/compile-fail/want-abort-got-unwind.rs rename to src/test/ui/panic-runtime/want-abort-got-unwind.rs index 30782e18229e..e33c3bcc3f02 100644 --- a/src/test/compile-fail/want-abort-got-unwind.rs +++ b/src/test/ui/panic-runtime/want-abort-got-unwind.rs @@ -1,3 +1,5 @@ +// build-fail +// dont-check-compiler-stderr // error-pattern:is not compiled with this crate's panic strategy `abort` // aux-build:panic-runtime-unwind.rs // compile-flags:-C panic=abort diff --git a/src/test/compile-fail/want-abort-got-unwind2.rs b/src/test/ui/panic-runtime/want-abort-got-unwind2.rs similarity index 84% rename from src/test/compile-fail/want-abort-got-unwind2.rs rename to src/test/ui/panic-runtime/want-abort-got-unwind2.rs index 35d8d46b55e9..438f1d85a281 100644 --- a/src/test/compile-fail/want-abort-got-unwind2.rs +++ b/src/test/ui/panic-runtime/want-abort-got-unwind2.rs @@ -1,3 +1,5 @@ +// build-fail +// dont-check-compiler-stderr // error-pattern:is not compiled with this crate's panic strategy `abort` // aux-build:panic-runtime-unwind.rs // aux-build:wants-panic-runtime-unwind.rs diff --git a/src/test/ui/parser/fn-colon-return-type.rs b/src/test/ui/parser/fn-colon-return-type.rs index c791fb3ae674..0001ef57c990 100644 --- a/src/test/ui/parser/fn-colon-return-type.rs +++ b/src/test/ui/parser/fn-colon-return-type.rs @@ -1,4 +1,5 @@ -fn foo(x: i32): i32 { //~ ERROR expected one of `->`, `;`, `where`, or `{`, found `:` +fn foo(x: i32): i32 { +//~^ ERROR return types are denoted using `->` x } diff --git a/src/test/ui/parser/fn-colon-return-type.stderr b/src/test/ui/parser/fn-colon-return-type.stderr index 92df9bc60bd3..1de918782056 100644 --- a/src/test/ui/parser/fn-colon-return-type.stderr +++ b/src/test/ui/parser/fn-colon-return-type.stderr @@ -1,8 +1,8 @@ -error: expected one of `->`, `;`, `where`, or `{`, found `:` +error: return types are denoted using `->` --> $DIR/fn-colon-return-type.rs:1:15 | LL | fn foo(x: i32): i32 { - | ^ expected one of `->`, `;`, `where`, or `{` + | ^ help: use `->` instead error: aborting due to previous error diff --git a/src/test/ui/parser/incorrect-move-async-order-issue-79694.fixed b/src/test/ui/parser/incorrect-move-async-order-issue-79694.fixed new file mode 100644 index 000000000000..055800d23b6c --- /dev/null +++ b/src/test/ui/parser/incorrect-move-async-order-issue-79694.fixed @@ -0,0 +1,8 @@ +// run-rustfix +// edition:2018 + +// Regression test for issue 79694 + +fn main() { + let _ = async move { }; //~ ERROR 7:13: 7:23: the order of `move` and `async` is incorrect +} diff --git a/src/test/ui/parser/incorrect-move-async-order-issue-79694.rs b/src/test/ui/parser/incorrect-move-async-order-issue-79694.rs new file mode 100644 index 000000000000..e8be16516d6d --- /dev/null +++ b/src/test/ui/parser/incorrect-move-async-order-issue-79694.rs @@ -0,0 +1,8 @@ +// run-rustfix +// edition:2018 + +// Regression test for issue 79694 + +fn main() { + let _ = move async { }; //~ ERROR 7:13: 7:23: the order of `move` and `async` is incorrect +} diff --git a/src/test/ui/parser/incorrect-move-async-order-issue-79694.stderr b/src/test/ui/parser/incorrect-move-async-order-issue-79694.stderr new file mode 100644 index 000000000000..2add9fb33c70 --- /dev/null +++ b/src/test/ui/parser/incorrect-move-async-order-issue-79694.stderr @@ -0,0 +1,13 @@ +error: the order of `move` and `async` is incorrect + --> $DIR/incorrect-move-async-order-issue-79694.rs:7:13 + | +LL | let _ = move async { }; + | ^^^^^^^^^^ + | +help: try switching the order + | +LL | let _ = async move { }; + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-14303-enum.stderr b/src/test/ui/parser/issue-14303-enum.stderr index 46f16ea0cc41..bcecd75b1abb 100644 --- a/src/test/ui/parser/issue-14303-enum.stderr +++ b/src/test/ui/parser/issue-14303-enum.stderr @@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/issue-14303-enum.rs:1:15 | LL | enum X<'a, T, 'b> { - | --------^^- help: reorder the parameters: lifetimes, then types: `<'a, 'b, T>` + | --------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T>` error: aborting due to previous error diff --git a/src/test/ui/parser/issue-14303-fn-def.stderr b/src/test/ui/parser/issue-14303-fn-def.stderr index 8cbab4b9653a..082c37e0be79 100644 --- a/src/test/ui/parser/issue-14303-fn-def.stderr +++ b/src/test/ui/parser/issue-14303-fn-def.stderr @@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/issue-14303-fn-def.rs:1:15 | LL | fn foo<'a, T, 'b>(x: &'a T) {} - | --------^^- help: reorder the parameters: lifetimes, then types: `<'a, 'b, T>` + | --------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T>` error: aborting due to previous error diff --git a/src/test/ui/parser/issue-14303-impl.stderr b/src/test/ui/parser/issue-14303-impl.stderr index 56cd4fb38103..3b5615d2a9ec 100644 --- a/src/test/ui/parser/issue-14303-impl.stderr +++ b/src/test/ui/parser/issue-14303-impl.stderr @@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/issue-14303-impl.rs:3:13 | LL | impl<'a, T, 'b> X {} - | --------^^- help: reorder the parameters: lifetimes, then types: `<'a, 'b, T>` + | --------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T>` error: aborting due to previous error diff --git a/src/test/ui/parser/issue-14303-struct.stderr b/src/test/ui/parser/issue-14303-struct.stderr index f31cb92ad66c..dbd0b987dd19 100644 --- a/src/test/ui/parser/issue-14303-struct.stderr +++ b/src/test/ui/parser/issue-14303-struct.stderr @@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/issue-14303-struct.rs:1:17 | LL | struct X<'a, T, 'b> { - | --------^^- help: reorder the parameters: lifetimes, then types: `<'a, 'b, T>` + | --------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T>` error: aborting due to previous error diff --git a/src/test/ui/parser/issue-14303-trait.stderr b/src/test/ui/parser/issue-14303-trait.stderr index 0e7399102bf1..7dfa62d823fd 100644 --- a/src/test/ui/parser/issue-14303-trait.stderr +++ b/src/test/ui/parser/issue-14303-trait.stderr @@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/issue-14303-trait.rs:1:18 | LL | trait Foo<'a, T, 'b> {} - | --------^^- help: reorder the parameters: lifetimes, then types: `<'a, 'b, T>` + | --------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T>` error: aborting due to previous error diff --git a/src/test/ui/parser/macro/issue-33569.rs b/src/test/ui/parser/macro/issue-33569.rs index cf81f0480a2a..80e2d7c6545b 100644 --- a/src/test/ui/parser/macro/issue-33569.rs +++ b/src/test/ui/parser/macro/issue-33569.rs @@ -2,7 +2,6 @@ macro_rules! foo { { $+ } => { //~ ERROR expected identifier, found `+` //~^ ERROR missing fragment specifier $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?` - //~^ ERROR attempted to repeat an expression containing no syntax variables } } diff --git a/src/test/ui/parser/macro/issue-33569.stderr b/src/test/ui/parser/macro/issue-33569.stderr index f54efaa6996f..b4d38d3ce480 100644 --- a/src/test/ui/parser/macro/issue-33569.stderr +++ b/src/test/ui/parser/macro/issue-33569.stderr @@ -4,23 +4,17 @@ error: expected identifier, found `+` LL | { $+ } => { | ^ -error: missing fragment specifier - --> $DIR/issue-33569.rs:2:8 - | -LL | { $+ } => { - | ^ - error: expected one of: `*`, `+`, or `?` --> $DIR/issue-33569.rs:4:13 | LL | $(x)(y) | ^^^ -error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth - --> $DIR/issue-33569.rs:4:10 +error: missing fragment specifier + --> $DIR/issue-33569.rs:2:8 | -LL | $(x)(y) - | ^^^ +LL | { $+ } => { + | ^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs new file mode 100644 index 000000000000..f3ae3aba9b9e --- /dev/null +++ b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs @@ -0,0 +1,12 @@ +// Regression test for #80134. + +fn main() { + (()é); + //~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator + //~| ERROR: cannot find value `é` in this scope + //~| ERROR: non-ascii idents are not fully supported + (()氷); + //~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator + //~| ERROR: cannot find value `氷` in this scope + //~| ERROR: non-ascii idents are not fully supported +} diff --git a/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr new file mode 100644 index 000000000000..892cc92b1bde --- /dev/null +++ b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr @@ -0,0 +1,52 @@ +error: expected one of `)`, `,`, `.`, `?`, or an operator, found `é` + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8 + | +LL | (()é); + | ^ + | | + | expected one of `)`, `,`, `.`, `?`, or an operator + | help: missing `,` + +error: expected one of `)`, `,`, `.`, `?`, or an operator, found `氷` + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 + | +LL | (()氷); + | -^ + | | + | expected one of `)`, `,`, `.`, `?`, or an operator + | help: missing `,` + +error[E0425]: cannot find value `é` in this scope + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8 + | +LL | (()é); + | ^ not found in this scope + +error[E0425]: cannot find value `氷` in this scope + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 + | +LL | (()氷); + | ^^ not found in this scope + +error[E0658]: non-ascii idents are not fully supported + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8 + | +LL | (()é); + | ^ + | + = note: see issue #55467 for more information + = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable + +error[E0658]: non-ascii idents are not fully supported + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 + | +LL | (()氷); + | ^^ + | + = note: see issue #55467 for more information + = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/src/test/ui/parser/not-a-pred.rs b/src/test/ui/parser/not-a-pred.rs index 1b3d9bf66bb6..5518b554d8e5 100644 --- a/src/test/ui/parser/not-a-pred.rs +++ b/src/test/ui/parser/not-a-pred.rs @@ -1,6 +1,15 @@ fn f(a: isize, b: isize) : lt(a, b) { } -//~^ ERROR expected one of `->`, `;`, `where`, or `{`, found `:` +//~^ ERROR return types are denoted using `->` +//~| ERROR expected type, found function `lt` [E0573] +//~| ERROR expected type, found local variable `a` [E0573] +//~| ERROR expected type, found local variable `b` [E0573] fn lt(a: isize, b: isize) { } -fn main() { let a: isize = 10; let b: isize = 23; check (lt(a, b)); f(a, b); } +fn main() { + let a: isize = 10; + let b: isize = 23; + check (lt(a, b)); + //~^ ERROR cannot find function `check` in this scope [E0425] + f(a, b); +} diff --git a/src/test/ui/parser/not-a-pred.stderr b/src/test/ui/parser/not-a-pred.stderr index ec413c5594c4..bcc64a687fd0 100644 --- a/src/test/ui/parser/not-a-pred.stderr +++ b/src/test/ui/parser/not-a-pred.stderr @@ -1,8 +1,34 @@ -error: expected one of `->`, `;`, `where`, or `{`, found `:` +error: return types are denoted using `->` --> $DIR/not-a-pred.rs:1:26 | LL | fn f(a: isize, b: isize) : lt(a, b) { } - | ^ expected one of `->`, `;`, `where`, or `{` + | ^ help: use `->` instead -error: aborting due to previous error +error[E0573]: expected type, found function `lt` + --> $DIR/not-a-pred.rs:1:28 + | +LL | fn f(a: isize, b: isize) : lt(a, b) { } + | ^^^^^^^^ not a type +error[E0573]: expected type, found local variable `a` + --> $DIR/not-a-pred.rs:1:31 + | +LL | fn f(a: isize, b: isize) : lt(a, b) { } + | ^ not a type + +error[E0573]: expected type, found local variable `b` + --> $DIR/not-a-pred.rs:1:34 + | +LL | fn f(a: isize, b: isize) : lt(a, b) { } + | ^ not a type + +error[E0425]: cannot find function `check` in this scope + --> $DIR/not-a-pred.rs:12:5 + | +LL | check (lt(a, b)); + | ^^^^^ not found in this scope + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0425, E0573. +For more information about an error, try `rustc --explain E0425`. diff --git a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs index 34aee7f69359..f2d97b7bac3c 100644 --- a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs +++ b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs @@ -3,10 +3,10 @@ fn main() {} trait X { - default const A: u8; //~ ERROR `default` is only allowed on items in `impl` definitions - default const B: u8 = 0; //~ ERROR `default` is only allowed on items in `impl` definitions - default type D; //~ ERROR `default` is only allowed on items in `impl` definitions - default type C: Ord; //~ ERROR `default` is only allowed on items in `impl` definitions - default fn f1(); //~ ERROR `default` is only allowed on items in `impl` definitions - default fn f2() {} //~ ERROR `default` is only allowed on items in `impl` definitions + default const A: u8; //~ ERROR `default` is only allowed on items in trait impls + default const B: u8 = 0; //~ ERROR `default` is only allowed on items in trait impls + default type D; //~ ERROR `default` is only allowed on items in trait impls + default type C: Ord; //~ ERROR `default` is only allowed on items in trait impls + default fn f1(); //~ ERROR `default` is only allowed on items in trait impls + default fn f2() {} //~ ERROR `default` is only allowed on items in trait impls } diff --git a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr index cdc9ee8d9e7c..76fa860334d7 100644 --- a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr +++ b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr @@ -1,4 +1,4 @@ -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:6:5 | LL | default const A: u8; @@ -6,7 +6,7 @@ LL | default const A: u8; | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:7:5 | LL | default const B: u8 = 0; @@ -14,7 +14,7 @@ LL | default const B: u8 = 0; | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:8:5 | LL | default type D; @@ -22,7 +22,7 @@ LL | default type D; | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:9:5 | LL | default type C: Ord; @@ -30,7 +30,7 @@ LL | default type C: Ord; | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:10:5 | LL | default fn f1(); @@ -38,7 +38,7 @@ LL | default fn f1(); | | | `default` because of this -error: `default` is only allowed on items in `impl` definitions +error: `default` is only allowed on items in trait impls --> $DIR/trait-item-with-defaultness-fail-semantic.rs:11:5 | LL | default fn f2() {} diff --git a/src/test/ui/pattern/issue-80186-mut-binding-help-suggestion.rs b/src/test/ui/pattern/issue-80186-mut-binding-help-suggestion.rs new file mode 100644 index 000000000000..a5e9b1db5467 --- /dev/null +++ b/src/test/ui/pattern/issue-80186-mut-binding-help-suggestion.rs @@ -0,0 +1,9 @@ +// Regression test for correct pretty-printing of an AST representing `&(mut x)` in help +// suggestion diagnostic. + +fn main() { + let mut &x = &0; + //~^ ERROR `mut` must be attached to each individual binding + //~| HELP add `mut` to each binding + //~| SUGGESTION &(mut x) +} diff --git a/src/test/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr b/src/test/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr new file mode 100644 index 000000000000..75b6c163b2cc --- /dev/null +++ b/src/test/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr @@ -0,0 +1,10 @@ +error: `mut` must be attached to each individual binding + --> $DIR/issue-80186-mut-binding-help-suggestion.rs:5:9 + | +LL | let mut &x = &0; + | ^^^^^^ help: add `mut` to each binding: `&(mut x)` + | + = note: `mut` may be followed by `variable` and `variable @ pattern` + +error: aborting due to previous error + diff --git a/src/test/ui/pattern/or-pattern-macro-pat.rs b/src/test/ui/pattern/or-pattern-macro-pat.rs new file mode 100644 index 000000000000..8749407675b3 --- /dev/null +++ b/src/test/ui/pattern/or-pattern-macro-pat.rs @@ -0,0 +1,44 @@ +// run-pass +// edition:2021 +// ignore-test +// FIXME(mark-i-m): enable this test again when 2021 machinery is available + +#![feature(or_patterns)] + +use Foo::*; + +#[derive(Eq, PartialEq, Debug)] +enum Foo { + A(u64), + B(u64), + C, + D, +} + +macro_rules! foo { + ($orpat:pat, $val:expr) => { + match $val { + x @ ($orpat) => x, // leading vert would not be allowed in $orpat + _ => B(0xDEADBEEFu64), + } + }; +} + +macro_rules! bar { + ($orpat:pat, $val:expr) => { + match $val { + $orpat => 42, // leading vert allowed here + _ => 0xDEADBEEFu64, + } + }; +} + +fn main() { + // Test or-pattern. + let y = foo!(A(_)|B(_), A(32)); + assert_eq!(y, A(32)); + + // Leading vert in or-pattern. + let y = bar!(|C| D, C); + assert_eq!(y, 42u64); +} diff --git a/src/test/ui/pattern/usefulness/consts-opaque.rs b/src/test/ui/pattern/usefulness/consts-opaque.rs index f87f96e34fcc..ca4fcd85bb6d 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.rs +++ b/src/test/ui/pattern/usefulness/consts-opaque.rs @@ -25,10 +25,6 @@ enum Baz { impl Eq for Baz {} const BAZ: Baz = Baz::Baz1; -type Quux = fn(usize, usize) -> usize; -fn quux(a: usize, b: usize) -> usize { a + b } -const QUUX: Quux = quux; - fn main() { match FOO { FOO => {} @@ -106,9 +102,44 @@ fn main() { //~^ ERROR unreachable pattern } + type Quux = fn(usize, usize) -> usize; + fn quux(a: usize, b: usize) -> usize { a + b } + const QUUX: Quux = quux; + match QUUX { QUUX => {} QUUX => {} _ => {} } + + #[derive(PartialEq, Eq)] + struct Wrap(T); + const WRAPQUUX: Wrap = Wrap(quux); + + match WRAPQUUX { + WRAPQUUX => {} + WRAPQUUX => {} + Wrap(_) => {} + } + + match WRAPQUUX { + Wrap(_) => {} + WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer + //~^ ERROR unreachable pattern + } + + #[derive(PartialEq, Eq)] + enum WhoKnows { + Yay(T), + Nope, + }; + const WHOKNOWSQUUX: WhoKnows = WhoKnows::Yay(quux); + + match WHOKNOWSQUUX { + WHOKNOWSQUUX => {} + WhoKnows::Yay(_) => {} + WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer + //~^ ERROR unreachable pattern + WhoKnows::Nope => {} + } } diff --git a/src/test/ui/pattern/usefulness/consts-opaque.stderr b/src/test/ui/pattern/usefulness/consts-opaque.stderr index f10166d5a358..68451043cf54 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.stderr +++ b/src/test/ui/pattern/usefulness/consts-opaque.stderr @@ -1,11 +1,11 @@ error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:34:9 + --> $DIR/consts-opaque.rs:30:9 | LL | FOO => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:36:9 + --> $DIR/consts-opaque.rs:32:9 | LL | _ => {} // should not be emitting unreachable warning | ^ @@ -17,19 +17,19 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:41:9 + --> $DIR/consts-opaque.rs:37:9 | LL | FOO_REF => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:43:9 + --> $DIR/consts-opaque.rs:39:9 | LL | Foo(_) => {} // should not be emitting unreachable warning | ^^^^^^ warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:49:9 + --> $DIR/consts-opaque.rs:45:9 | LL | FOO_REF_REF => {} | ^^^^^^^^^^^ @@ -39,13 +39,13 @@ LL | FOO_REF_REF => {} = note: for more information, see issue #62411 error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:57:9 + --> $DIR/consts-opaque.rs:53:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:57:9 + --> $DIR/consts-opaque.rs:53:9 | LL | Bar => {} | --- matches any value @@ -53,7 +53,7 @@ LL | BAR => {} // should not be emitting unreachable warning | ^^^ unreachable pattern error: unreachable pattern - --> $DIR/consts-opaque.rs:60:9 + --> $DIR/consts-opaque.rs:56:9 | LL | Bar => {} | --- matches any value @@ -62,19 +62,19 @@ LL | _ => {} | ^ unreachable pattern error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:65:9 + --> $DIR/consts-opaque.rs:61:9 | LL | BAR => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:67:9 + --> $DIR/consts-opaque.rs:63:9 | LL | Bar => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:69:9 + --> $DIR/consts-opaque.rs:65:9 | LL | Bar => {} // should not be emitting unreachable warning | --- matches any value @@ -83,76 +83,88 @@ LL | _ => {} | ^ unreachable pattern error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:74:9 + --> $DIR/consts-opaque.rs:70:9 | LL | BAR => {} | ^^^ error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:76:9 + --> $DIR/consts-opaque.rs:72:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:76:9 + --> $DIR/consts-opaque.rs:72:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:79:9 + --> $DIR/consts-opaque.rs:75:9 | LL | _ => {} // should not be emitting unreachable warning | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:84:9 + --> $DIR/consts-opaque.rs:80:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:86:9 + --> $DIR/consts-opaque.rs:82:9 | LL | Baz::Baz1 => {} // should not be emitting unreachable warning | ^^^^^^^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:88:9 + --> $DIR/consts-opaque.rs:84:9 | LL | _ => {} | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:94:9 + --> $DIR/consts-opaque.rs:90:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:96:9 + --> $DIR/consts-opaque.rs:92:9 | LL | _ => {} | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:101:9 + --> $DIR/consts-opaque.rs:97:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:103:9 + --> $DIR/consts-opaque.rs:99:9 | LL | Baz::Baz2 => {} // should not be emitting unreachable warning | ^^^^^^^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:105:9 + --> $DIR/consts-opaque.rs:101:9 | LL | _ => {} // should not be emitting unreachable warning | ^ -error: aborting due to 22 previous errors; 1 warning emitted +error: unreachable pattern + --> $DIR/consts-opaque.rs:127:9 + | +LL | WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer + | ^^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:141:9 + | +LL | WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer + | ^^^^^^^^^^^^ + +error: aborting due to 24 previous errors; 1 warning emitted diff --git a/src/test/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs b/src/test/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs index 5a44dfc28bb4..ef573db82104 100644 --- a/src/test/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs +++ b/src/test/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs @@ -1,5 +1,6 @@ #![feature(exclusive_range_pattern)] #![feature(assoc_char_consts)] +#![allow(overlapping_range_endpoints)] #![deny(unreachable_patterns)] macro_rules! m { diff --git a/src/test/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr b/src/test/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr index 2e0023348e4d..b1440375494b 100644 --- a/src/test/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr +++ b/src/test/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `u8::MAX` not covered - --> $DIR/exhaustiveness.rs:47:8 + --> $DIR/exhaustiveness.rs:48:8 | LL | m!(0u8, 0..255); | ^^^ pattern `u8::MAX` not covered @@ -8,7 +8,7 @@ LL | m!(0u8, 0..255); = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `u8::MAX` not covered - --> $DIR/exhaustiveness.rs:48:8 + --> $DIR/exhaustiveness.rs:49:8 | LL | m!(0u8, 0..=254); | ^^^ pattern `u8::MAX` not covered @@ -17,7 +17,7 @@ LL | m!(0u8, 0..=254); = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `0_u8` not covered - --> $DIR/exhaustiveness.rs:49:8 + --> $DIR/exhaustiveness.rs:50:8 | LL | m!(0u8, 1..=255); | ^^^ pattern `0_u8` not covered @@ -26,7 +26,7 @@ LL | m!(0u8, 1..=255); = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `42_u8` not covered - --> $DIR/exhaustiveness.rs:50:8 + --> $DIR/exhaustiveness.rs:51:8 | LL | m!(0u8, 0..42 | 43..=255); | ^^^ pattern `42_u8` not covered @@ -35,7 +35,7 @@ LL | m!(0u8, 0..42 | 43..=255); = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `i8::MAX` not covered - --> $DIR/exhaustiveness.rs:51:8 + --> $DIR/exhaustiveness.rs:52:8 | LL | m!(0i8, -128..127); | ^^^ pattern `i8::MAX` not covered @@ -44,7 +44,7 @@ LL | m!(0i8, -128..127); = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `i8::MAX` not covered - --> $DIR/exhaustiveness.rs:52:8 + --> $DIR/exhaustiveness.rs:53:8 | LL | m!(0i8, -128..=126); | ^^^ pattern `i8::MAX` not covered @@ -53,7 +53,7 @@ LL | m!(0i8, -128..=126); = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `i8::MIN` not covered - --> $DIR/exhaustiveness.rs:53:8 + --> $DIR/exhaustiveness.rs:54:8 | LL | m!(0i8, -127..=127); | ^^^ pattern `i8::MIN` not covered @@ -62,7 +62,7 @@ LL | m!(0i8, -127..=127); = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `0_i8` not covered - --> $DIR/exhaustiveness.rs:54:11 + --> $DIR/exhaustiveness.rs:55:11 | LL | match 0i8 { | ^^^ pattern `0_i8` not covered @@ -71,7 +71,7 @@ LL | match 0i8 { = note: the matched value is of type `i8` error[E0004]: non-exhaustive patterns: `u128::MAX` not covered - --> $DIR/exhaustiveness.rs:59:8 + --> $DIR/exhaustiveness.rs:60:8 | LL | m!(0u128, 0..=ALMOST_MAX); | ^^^^^ pattern `u128::MAX` not covered @@ -80,7 +80,7 @@ LL | m!(0u128, 0..=ALMOST_MAX); = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `5_u128..=u128::MAX` not covered - --> $DIR/exhaustiveness.rs:60:8 + --> $DIR/exhaustiveness.rs:61:8 | LL | m!(0u128, 0..=4); | ^^^^^ pattern `5_u128..=u128::MAX` not covered @@ -89,7 +89,7 @@ LL | m!(0u128, 0..=4); = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `0_u128` not covered - --> $DIR/exhaustiveness.rs:61:8 + --> $DIR/exhaustiveness.rs:62:8 | LL | m!(0u128, 1..=u128::MAX); | ^^^^^ pattern `0_u128` not covered @@ -98,7 +98,7 @@ LL | m!(0u128, 1..=u128::MAX); = note: the matched value is of type `u128` error[E0004]: non-exhaustive patterns: `(126_u8..=127_u8, false)` not covered - --> $DIR/exhaustiveness.rs:69:11 + --> $DIR/exhaustiveness.rs:70:11 | LL | match (0u8, true) { | ^^^^^^^^^^^ pattern `(126_u8..=127_u8, false)` not covered diff --git a/src/test/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs b/src/test/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs index af720a056932..5ea92b07081a 100644 --- a/src/test/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs +++ b/src/test/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs @@ -1,5 +1,5 @@ #![feature(exclusive_range_pattern)] -#![deny(overlapping_patterns)] +#![deny(overlapping_range_endpoints)] macro_rules! m { ($s:expr, $t1:pat, $t2:pat) => { @@ -12,27 +12,33 @@ macro_rules! m { } fn main() { - m!(0u8, 20..=30, 30..=40); //~ ERROR multiple patterns covering the same range - m!(0u8, 30..=40, 20..=30); //~ ERROR multiple patterns covering the same range + m!(0u8, 20..=30, 30..=40); //~ ERROR multiple patterns overlap on their endpoints + m!(0u8, 30..=40, 20..=30); //~ ERROR multiple patterns overlap on their endpoints m!(0u8, 20..=30, 31..=40); m!(0u8, 20..=30, 29..=40); - m!(0u8, 20.. 30, 29..=40); //~ ERROR multiple patterns covering the same range + m!(0u8, 20.. 30, 29..=40); //~ ERROR multiple patterns overlap on their endpoints m!(0u8, 20.. 30, 28..=40); m!(0u8, 20.. 30, 30..=40); m!(0u8, 20..=30, 30..=30); - m!(0u8, 20..=30, 30..=31); //~ ERROR multiple patterns covering the same range + m!(0u8, 20..=30, 30..=31); //~ ERROR multiple patterns overlap on their endpoints m!(0u8, 20..=30, 29..=30); m!(0u8, 20..=30, 20..=20); m!(0u8, 20..=30, 20..=21); - m!(0u8, 20..=30, 19..=20); //~ ERROR multiple patterns covering the same range + m!(0u8, 20..=30, 19..=20); //~ ERROR multiple patterns overlap on their endpoints m!(0u8, 20..=30, 20); m!(0u8, 20..=30, 25); m!(0u8, 20..=30, 30); m!(0u8, 20.. 30, 29); - m!(0u8, 20, 20..=30); //~ ERROR multiple patterns covering the same range + m!(0u8, 20, 20..=30); m!(0u8, 25, 20..=30); - m!(0u8, 30, 20..=30); //~ ERROR multiple patterns covering the same range + m!(0u8, 30, 20..=30); + match 0u8 { + 0..=10 => {} + 20..=30 => {} + 10..=20 => {} //~ ERROR multiple patterns overlap on their endpoints + _ => {} + } match (0u8, true) { (0..=10, true) => {} (10..20, true) => {} // not detected @@ -41,13 +47,13 @@ fn main() { } match (true, 0u8) { (true, 0..=10) => {} - (true, 10..20) => {} //~ ERROR multiple patterns covering the same range + (true, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints (false, 10..20) => {} _ => {} } match Some(0u8) { Some(0..=10) => {} - Some(10..20) => {} //~ ERROR multiple patterns covering the same range + Some(10..20) => {} //~ ERROR multiple patterns overlap on their endpoints _ => {} } } diff --git a/src/test/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr b/src/test/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr index 7bb747cdf6fc..24c0419e1dde 100644 --- a/src/test/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr +++ b/src/test/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr @@ -1,80 +1,89 @@ -error: multiple patterns covering the same range +error: multiple patterns overlap on their endpoints --> $DIR/overlapping_range_endpoints.rs:15:22 | LL | m!(0u8, 20..=30, 30..=40); - | ------- ^^^^^^^ overlapping patterns + | ------- ^^^^^^^ ... with this range | | - | this range overlaps on `30_u8` + | this range overlaps on `30_u8`... | note: the lint level is defined here --> $DIR/overlapping_range_endpoints.rs:2:9 | -LL | #![deny(overlapping_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(overlapping_range_endpoints)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: you likely meant to write mutually exclusive ranges -error: multiple patterns covering the same range +error: multiple patterns overlap on their endpoints --> $DIR/overlapping_range_endpoints.rs:16:22 | LL | m!(0u8, 30..=40, 20..=30); - | ------- ^^^^^^^ overlapping patterns + | ------- ^^^^^^^ ... with this range | | - | this range overlaps on `30_u8` + | this range overlaps on `30_u8`... + | + = note: you likely meant to write mutually exclusive ranges -error: multiple patterns covering the same range +error: multiple patterns overlap on their endpoints --> $DIR/overlapping_range_endpoints.rs:19:22 | LL | m!(0u8, 20.. 30, 29..=40); - | ------- ^^^^^^^ overlapping patterns + | ------- ^^^^^^^ ... with this range | | - | this range overlaps on `29_u8` + | this range overlaps on `29_u8`... + | + = note: you likely meant to write mutually exclusive ranges -error: multiple patterns covering the same range +error: multiple patterns overlap on their endpoints --> $DIR/overlapping_range_endpoints.rs:23:22 | LL | m!(0u8, 20..=30, 30..=31); - | ------- ^^^^^^^ overlapping patterns + | ------- ^^^^^^^ ... with this range | | - | this range overlaps on `30_u8` + | this range overlaps on `30_u8`... + | + = note: you likely meant to write mutually exclusive ranges -error: multiple patterns covering the same range +error: multiple patterns overlap on their endpoints --> $DIR/overlapping_range_endpoints.rs:27:22 | LL | m!(0u8, 20..=30, 19..=20); - | ------- ^^^^^^^ overlapping patterns + | ------- ^^^^^^^ ... with this range | | - | this range overlaps on `20_u8` - -error: multiple patterns covering the same range - --> $DIR/overlapping_range_endpoints.rs:32:17 + | this range overlaps on `20_u8`... | -LL | m!(0u8, 20, 20..=30); - | -- ^^^^^^^ overlapping patterns - | | - | this range overlaps on `20_u8` + = note: you likely meant to write mutually exclusive ranges -error: multiple patterns covering the same range - --> $DIR/overlapping_range_endpoints.rs:34:17 +error: multiple patterns overlap on their endpoints + --> $DIR/overlapping_range_endpoints.rs:39:9 | -LL | m!(0u8, 30, 20..=30); - | -- ^^^^^^^ overlapping patterns - | | - | this range overlaps on `30_u8` +LL | 0..=10 => {} + | ------ this range overlaps on `10_u8`... +LL | 20..=30 => {} + | ------- this range overlaps on `20_u8`... +LL | 10..=20 => {} + | ^^^^^^^ ... with this range + | + = note: you likely meant to write mutually exclusive ranges -error: multiple patterns covering the same range - --> $DIR/overlapping_range_endpoints.rs:44:16 +error: multiple patterns overlap on their endpoints + --> $DIR/overlapping_range_endpoints.rs:50:16 | LL | (true, 0..=10) => {} - | ------ this range overlaps on `10_u8` + | ------ this range overlaps on `10_u8`... LL | (true, 10..20) => {} - | ^^^^^^ overlapping patterns + | ^^^^^^ ... with this range + | + = note: you likely meant to write mutually exclusive ranges -error: multiple patterns covering the same range - --> $DIR/overlapping_range_endpoints.rs:50:14 +error: multiple patterns overlap on their endpoints + --> $DIR/overlapping_range_endpoints.rs:56:14 | LL | Some(0..=10) => {} - | ------ this range overlaps on `10_u8` + | ------ this range overlaps on `10_u8`... LL | Some(10..20) => {} - | ^^^^^^ overlapping patterns + | ^^^^^^ ... with this range + | + = note: you likely meant to write mutually exclusive ranges -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors diff --git a/src/test/ui/pattern/usefulness/integer-ranges/reachability.rs b/src/test/ui/pattern/usefulness/integer-ranges/reachability.rs index 6516925e9391..fb4d59b05780 100644 --- a/src/test/ui/pattern/usefulness/integer-ranges/reachability.rs +++ b/src/test/ui/pattern/usefulness/integer-ranges/reachability.rs @@ -1,4 +1,5 @@ #![feature(exclusive_range_pattern)] +#![allow(overlapping_range_endpoints)] #![deny(unreachable_patterns)] macro_rules! m { diff --git a/src/test/ui/pattern/usefulness/integer-ranges/reachability.stderr b/src/test/ui/pattern/usefulness/integer-ranges/reachability.stderr index e6878d950d62..9a02fac6a75d 100644 --- a/src/test/ui/pattern/usefulness/integer-ranges/reachability.stderr +++ b/src/test/ui/pattern/usefulness/integer-ranges/reachability.stderr @@ -1,149 +1,149 @@ error: unreachable pattern - --> $DIR/reachability.rs:16:17 + --> $DIR/reachability.rs:17:17 | LL | m!(0u8, 42, 42); | ^^ | note: the lint level is defined here - --> $DIR/reachability.rs:2:9 + --> $DIR/reachability.rs:3:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:20:22 + --> $DIR/reachability.rs:21:22 | LL | m!(0u8, 20..=30, 20); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:21:22 + --> $DIR/reachability.rs:22:22 | LL | m!(0u8, 20..=30, 21); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:22:22 + --> $DIR/reachability.rs:23:22 | LL | m!(0u8, 20..=30, 25); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:23:22 + --> $DIR/reachability.rs:24:22 | LL | m!(0u8, 20..=30, 29); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:24:22 + --> $DIR/reachability.rs:25:22 | LL | m!(0u8, 20..=30, 30); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:27:21 + --> $DIR/reachability.rs:28:21 | LL | m!(0u8, 20..30, 20); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:28:21 + --> $DIR/reachability.rs:29:21 | LL | m!(0u8, 20..30, 21); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:29:21 + --> $DIR/reachability.rs:30:21 | LL | m!(0u8, 20..30, 25); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:30:21 + --> $DIR/reachability.rs:31:21 | LL | m!(0u8, 20..30, 29); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:34:22 + --> $DIR/reachability.rs:35:22 | LL | m!(0u8, 20..=30, 20..=30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:35:22 + --> $DIR/reachability.rs:36:22 | LL | m!(0u8, 20.. 30, 20.. 30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:36:22 + --> $DIR/reachability.rs:37:22 | LL | m!(0u8, 20..=30, 20.. 30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:38:22 + --> $DIR/reachability.rs:39:22 | LL | m!(0u8, 20..=30, 21..=30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:39:22 + --> $DIR/reachability.rs:40:22 | LL | m!(0u8, 20..=30, 20..=29); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:41:24 + --> $DIR/reachability.rs:42:24 | LL | m!('a', 'A'..='z', 'a'..='z'); | ^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:48:9 + --> $DIR/reachability.rs:49:9 | LL | 5..=8 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:54:9 + --> $DIR/reachability.rs:55:9 | LL | 5..15 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:61:9 + --> $DIR/reachability.rs:62:9 | LL | 5..25 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:69:9 + --> $DIR/reachability.rs:70:9 | LL | 5..25 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:75:9 + --> $DIR/reachability.rs:76:9 | LL | 5..15 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:82:9 + --> $DIR/reachability.rs:83:9 | LL | '\u{D7FF}'..='\u{E000}' => {}, | ^^^^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:103:9 + --> $DIR/reachability.rs:104:9 | LL | &FOO => {} | ^^^^ error: unreachable pattern - --> $DIR/reachability.rs:104:9 + --> $DIR/reachability.rs:105:9 | LL | BAR => {} | ^^^ diff --git a/src/test/ui/pattern/usefulness/issue-15129.rs b/src/test/ui/pattern/usefulness/issue-15129.rs index ed134c175edd..d2b72a86b74c 100644 --- a/src/test/ui/pattern/usefulness/issue-15129.rs +++ b/src/test/ui/pattern/usefulness/issue-15129.rs @@ -1,17 +1,17 @@ pub enum T { T1(()), - T2(()) + T2(()), } pub enum V { V1(isize), - V2(bool) + V2(bool), } fn main() { match (T::T1(()), V::V2(true)) { - //~^ ERROR non-exhaustive patterns: `(T1(()), V2(_))` not covered + //~^ ERROR non-exhaustive patterns: `(T1(()), V2(_))` and `(T2(()), V1(_))` not covered (T::T1(()), V::V1(i)) => (), - (T::T2(()), V::V2(b)) => () + (T::T2(()), V::V2(b)) => (), } } diff --git a/src/test/ui/pattern/usefulness/issue-15129.stderr b/src/test/ui/pattern/usefulness/issue-15129.stderr index aa4434e72b5c..79a77240937a 100644 --- a/src/test/ui/pattern/usefulness/issue-15129.stderr +++ b/src/test/ui/pattern/usefulness/issue-15129.stderr @@ -1,8 +1,8 @@ -error[E0004]: non-exhaustive patterns: `(T1(()), V2(_))` not covered +error[E0004]: non-exhaustive patterns: `(T1(()), V2(_))` and `(T2(()), V1(_))` not covered --> $DIR/issue-15129.rs:12:11 | LL | match (T::T1(()), V::V2(true)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `(T1(()), V2(_))` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `(T1(()), V2(_))` and `(T2(()), V1(_))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `(T, V)` diff --git a/src/test/ui/pattern/usefulness/issue-2111.rs b/src/test/ui/pattern/usefulness/issue-2111.rs index 7e5835e8697a..d27beaeffd63 100644 --- a/src/test/ui/pattern/usefulness/issue-2111.rs +++ b/src/test/ui/pattern/usefulness/issue-2111.rs @@ -1,12 +1,11 @@ fn foo(a: Option, b: Option) { - match (a,b) { - //~^ ERROR: non-exhaustive patterns: `(None, None)` not covered - (Some(a), Some(b)) if a == b => { } - (Some(_), None) | - (None, Some(_)) => { } - } + match (a, b) { + //~^ ERROR: non-exhaustive patterns: `(None, None)` and `(Some(_), Some(_))` not covered + (Some(a), Some(b)) if a == b => {} + (Some(_), None) | (None, Some(_)) => {} + } } fn main() { - foo(None, None); + foo(None, None); } diff --git a/src/test/ui/pattern/usefulness/issue-2111.stderr b/src/test/ui/pattern/usefulness/issue-2111.stderr index a39a479e078d..60d9b8514b7f 100644 --- a/src/test/ui/pattern/usefulness/issue-2111.stderr +++ b/src/test/ui/pattern/usefulness/issue-2111.stderr @@ -1,8 +1,8 @@ -error[E0004]: non-exhaustive patterns: `(None, None)` not covered - --> $DIR/issue-2111.rs:2:9 +error[E0004]: non-exhaustive patterns: `(None, None)` and `(Some(_), Some(_))` not covered + --> $DIR/issue-2111.rs:2:11 | -LL | match (a,b) { - | ^^^^^ pattern `(None, None)` not covered +LL | match (a, b) { + | ^^^^^^ patterns `(None, None)` and `(Some(_), Some(_))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `(Option, Option)` diff --git a/src/test/ui/pattern/usefulness/issue-56379.rs b/src/test/ui/pattern/usefulness/issue-56379.rs new file mode 100644 index 000000000000..9bccccca9c2b --- /dev/null +++ b/src/test/ui/pattern/usefulness/issue-56379.rs @@ -0,0 +1,14 @@ +enum Foo { + A(bool), + B(bool), + C(bool), +} + +fn main() { + match Foo::A(true) { + //~^ ERROR non-exhaustive patterns: `A(false)`, `B(false)` and `C(false)` not covered + Foo::A(true) => {} + Foo::B(true) => {} + Foo::C(true) => {} + } +} diff --git a/src/test/ui/pattern/usefulness/issue-56379.stderr b/src/test/ui/pattern/usefulness/issue-56379.stderr new file mode 100644 index 000000000000..6a231b868c8c --- /dev/null +++ b/src/test/ui/pattern/usefulness/issue-56379.stderr @@ -0,0 +1,22 @@ +error[E0004]: non-exhaustive patterns: `A(false)`, `B(false)` and `C(false)` not covered + --> $DIR/issue-56379.rs:8:11 + | +LL | / enum Foo { +LL | | A(bool), + | | - not covered +LL | | B(bool), + | | - not covered +LL | | C(bool), + | | - not covered +LL | | } + | |_- `Foo` defined here +... +LL | match Foo::A(true) { + | ^^^^^^^^^^^^ patterns `A(false)`, `B(false)` and `C(false)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs index a28cfb579f4f..4ff12aa2ff5e 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs @@ -15,7 +15,7 @@ fn main() { // and `(_, _, 5_i32..=i32::MAX)` not covered (_, _, 4) => {} } - match (T::A, T::A) { //~ ERROR non-exhaustive patterns: `(A, A)` not covered + match (T::A, T::A) { //~ ERROR non-exhaustive patterns: `(A, A)` and `(B, B)` not covered (T::A, T::B) => {} (T::B, T::A) => {} } diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr index 12412743b83f..c953cd314406 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr @@ -45,11 +45,11 @@ LL | match (2, 3, 4) { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `(i32, i32, i32)` -error[E0004]: non-exhaustive patterns: `(A, A)` not covered +error[E0004]: non-exhaustive patterns: `(A, A)` and `(B, B)` not covered --> $DIR/non-exhaustive-match.rs:18:11 | LL | match (T::A, T::A) { - | ^^^^^^^^^^^^ pattern `(A, A)` not covered + | ^^^^^^^^^^^^ patterns `(A, A)` and `(B, B)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `(T, T)` diff --git a/src/test/ui/polymorphization/const_parameters/closures.stderr b/src/test/ui/polymorphization/const_parameters/closures.stderr index 63335586b765..266b6e62afd0 100644 --- a/src/test/ui/polymorphization/const_parameters/closures.stderr +++ b/src/test/ui/polymorphization/const_parameters/closures.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics, rustc_attrs)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error: item has unused generic parameters --> $DIR/closures.rs:19:19 diff --git a/src/test/ui/polymorphization/const_parameters/functions.stderr b/src/test/ui/polymorphization/const_parameters/functions.stderr index c976a5b62aa3..e379e32c1fce 100644 --- a/src/test/ui/polymorphization/const_parameters/functions.stderr +++ b/src/test/ui/polymorphization/const_parameters/functions.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics, rustc_attrs)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error: item has unused generic parameters --> $DIR/functions.rs:15:8 diff --git a/src/test/ui/polymorphization/generators.stderr b/src/test/ui/polymorphization/generators.stderr index b2b32db045d6..c59055ba9d65 100644 --- a/src/test/ui/polymorphization/generators.stderr +++ b/src/test/ui/polymorphization/generators.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics, generators, generator_trait, rustc_attrs)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error: item has unused generic parameters --> $DIR/generators.rs:36:5 diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs index 37ac45f7e053..0716cee21c66 100644 --- a/src/test/ui/print_type_sizes/niche-filling.rs +++ b/src/test/ui/print_type_sizes/niche-filling.rs @@ -15,12 +15,19 @@ // padding and overall computed sizes can be quite different. #![feature(start)] +#![feature(rustc_attrs)] #![allow(dead_code)] use std::num::NonZeroU32; pub enum MyOption { None, Some(T) } +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] +pub struct MyNotNegativeOne { + _i: i32, +} + impl Default for MyOption { fn default() -> Self { MyOption::None } } @@ -77,17 +84,18 @@ fn start(_: isize, _: *const *const u8) -> isize { let _a: MyOption = Default::default(); let _b: MyOption = Default::default(); let _c: MyOption = Default::default(); - let _b: MyOption> = Default::default(); + let _d: MyOption> = Default::default(); let _e: Enum4<(), char, (), ()> = Enum4::One(()); let _f: Enum4<(), (), bool, ()> = Enum4::One(()); let _g: Enum4<(), (), (), MyOption> = Enum4::One(()); + let _h: MyOption = Default::default(); // Unions do not currently participate in niche filling. - let _h: MyOption> = Default::default(); + let _i: MyOption> = Default::default(); // ...even when theoretically possible. - let _i: MyOption> = Default::default(); - let _j: MyOption> = Default::default(); + let _j: MyOption> = Default::default(); + let _k: MyOption> = Default::default(); 0 } diff --git a/src/test/ui/print_type_sizes/niche-filling.stdout b/src/test/ui/print_type_sizes/niche-filling.stdout index 1894cd218ee3..d1753c26ca83 100644 --- a/src/test/ui/print_type_sizes/niche-filling.stdout +++ b/src/test/ui/print_type_sizes/niche-filling.stdout @@ -43,6 +43,12 @@ print-type-size variant `Three`: 0 bytes print-type-size field `.0`: 0 bytes print-type-size variant `Four`: 0 bytes print-type-size field `.0`: 0 bytes +print-type-size type: `MyNotNegativeOne`: 4 bytes, alignment: 4 bytes +print-type-size field `._i`: 4 bytes +print-type-size type: `MyOption`: 4 bytes, alignment: 4 bytes +print-type-size variant `Some`: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size variant `None`: 0 bytes print-type-size type: `MyOption`: 4 bytes, alignment: 4 bytes print-type-size variant `Some`: 4 bytes print-type-size field `.0`: 4 bytes diff --git a/src/test/compile-fail/issue-46209-private-enum-variant-reexport.rs b/src/test/ui/privacy/issue-46209-private-enum-variant-reexport.rs similarity index 100% rename from src/test/compile-fail/issue-46209-private-enum-variant-reexport.rs rename to src/test/ui/privacy/issue-46209-private-enum-variant-reexport.rs diff --git a/src/test/ui/privacy/issue-46209-private-enum-variant-reexport.stderr b/src/test/ui/privacy/issue-46209-private-enum-variant-reexport.stderr new file mode 100644 index 000000000000..b876bab6c542 --- /dev/null +++ b/src/test/ui/privacy/issue-46209-private-enum-variant-reexport.stderr @@ -0,0 +1,44 @@ +error: variant `JuniorGrade` is private and cannot be re-exported + --> $DIR/issue-46209-private-enum-variant-reexport.rs:6:32 + | +LL | pub use self::Lieutenant::{JuniorGrade, Full}; + | ^^^^^^^^^^^ +... +LL | enum Lieutenant { + | --------------- help: consider making the enum public: `pub enum Lieutenant` + +error: variant `Full` is private and cannot be re-exported + --> $DIR/issue-46209-private-enum-variant-reexport.rs:6:45 + | +LL | pub use self::Lieutenant::{JuniorGrade, Full}; + | ^^^^ + +error: enum is private and its variants cannot be re-exported + --> $DIR/issue-46209-private-enum-variant-reexport.rs:4:13 + | +LL | pub use self::Professor::*; + | ^^^^^^^^^^^^^^^^^^ +... +LL | enum Professor { + | -------------- help: consider making the enum public: `pub enum Professor` + +error: enum is private and its variants cannot be re-exported + --> $DIR/issue-46209-private-enum-variant-reexport.rs:9:13 + | +LL | pub use self::PettyOfficer::*; + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | pub(in rank) enum PettyOfficer { + | ------------------------------ help: consider making the enum public: `pub enum PettyOfficer` + +error: enum is private and its variants cannot be re-exported + --> $DIR/issue-46209-private-enum-variant-reexport.rs:11:13 + | +LL | pub use self::Crewman::*; + | ^^^^^^^^^^^^^^^^ +... +LL | crate enum Crewman { + | ------------------ help: consider making the enum public: `pub enum Crewman` + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/proc-macro/ambiguous-builtin-attrs.rs b/src/test/ui/proc-macro/ambiguous-builtin-attrs.rs index 9f4f0abf3248..142efb3c6cdc 100644 --- a/src/test/ui/proc-macro/ambiguous-builtin-attrs.rs +++ b/src/test/ui/proc-macro/ambiguous-builtin-attrs.rs @@ -17,7 +17,9 @@ fn test() {} #[bench] // OK, shadowed fn bench() {} -fn non_macro_expanded_location<#[repr(C)] T>() { //~ ERROR `repr` is ambiguous +fn non_macro_expanded_location<#[repr(C)] T>() { + //~^ ERROR `repr` is ambiguous + //~| ERROR attribute should be applied to a struct, enum, or union match 0u8 { #[repr(C)] //~ ERROR `repr` is ambiguous _ => {} diff --git a/src/test/ui/proc-macro/ambiguous-builtin-attrs.stderr b/src/test/ui/proc-macro/ambiguous-builtin-attrs.stderr index 23310f6c6f52..276ee1cfd356 100644 --- a/src/test/ui/proc-macro/ambiguous-builtin-attrs.stderr +++ b/src/test/ui/proc-macro/ambiguous-builtin-attrs.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `NonExistent` in this scope - --> $DIR/ambiguous-builtin-attrs.rs:30:5 + --> $DIR/ambiguous-builtin-attrs.rs:32:5 | LL | NonExistent; | ^^^^^^^^^^^ not found in this scope @@ -47,7 +47,7 @@ LL | use builtin_attrs::*; = help: use `crate::repr` to refer to this attribute macro unambiguously error[E0659]: `repr` is ambiguous (built-in attribute vs any other name) - --> $DIR/ambiguous-builtin-attrs.rs:22:11 + --> $DIR/ambiguous-builtin-attrs.rs:24:11 | LL | #[repr(C)] | ^^^^ ambiguous name @@ -74,7 +74,13 @@ LL | use builtin_attrs::*; | ^^^^^^^^^^^^^^^^ = help: use `crate::feature` to refer to this attribute macro unambiguously -error: aborting due to 6 previous errors +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/ambiguous-builtin-attrs.rs:20:39 + | +LL | fn non_macro_expanded_location<#[repr(C)] T>() { + | ^ - not a struct, enum, or union -Some errors have detailed explanations: E0425, E0659. +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0425, E0517, E0659. For more information about an error, try `rustc --explain E0425`. diff --git a/src/test/ui/proc-macro/capture-unglued-token.rs b/src/test/ui/proc-macro/capture-unglued-token.rs new file mode 100644 index 000000000000..727b779776b9 --- /dev/null +++ b/src/test/ui/proc-macro/capture-unglued-token.rs @@ -0,0 +1,20 @@ +// aux-build:test-macros.rs +// compile-flags: -Z span-debug +// check-pass + +// Tests that we properly handle parsing a nonterminal +// where we have two consecutive angle brackets (one inside +// the nonterminal, and one outside) + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; +extern crate test_macros; + +macro_rules! trailing_angle { + (Option<$field:ty>) => { + test_macros::print_bang_consume!($field); + } +} + +trailing_angle!(Option>); +fn main() {} diff --git a/src/test/ui/proc-macro/capture-unglued-token.stdout b/src/test/ui/proc-macro/capture-unglued-token.stdout new file mode 100644 index 000000000000..7e6b540332c7 --- /dev/null +++ b/src/test/ui/proc-macro/capture-unglued-token.stdout @@ -0,0 +1,28 @@ +PRINT-BANG INPUT (DISPLAY): Vec +PRINT-BANG RE-COLLECTED (DISPLAY): Vec < u8 > +PRINT-BANG INPUT (DEBUG): TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "Vec", + span: $DIR/capture-unglued-token.rs:19:24: 19:27 (#0), + }, + Punct { + ch: '<', + spacing: Alone, + span: $DIR/capture-unglued-token.rs:19:27: 19:28 (#0), + }, + Ident { + ident: "u8", + span: $DIR/capture-unglued-token.rs:19:28: 19:30 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/capture-unglued-token.rs:19:30: 19:31 (#0), + }, + ], + span: $DIR/capture-unglued-token.rs:15:42: 15:48 (#4), + }, +] diff --git a/src/test/ui/regions/regions-early-bound-trait-param.rs b/src/test/ui/regions/regions-early-bound-trait-param.rs index cc2bde78d859..276a64b8e9a7 100644 --- a/src/test/ui/regions/regions-early-bound-trait-param.rs +++ b/src/test/ui/regions/regions-early-bound-trait-param.rs @@ -117,7 +117,7 @@ pub fn main() { let m : Box = make_val(); // assert_eq!(object_invoke1(&*m), (4,5)); // ~~~~~~~~~~~~~~~~~~~ - // this call yields a compilation error; see compile-fail/dropck-object-cycle.rs + // this call yields a compilation error; see ui/span/dropck-object-cycle.rs // for details. assert_eq!(object_invoke2(&*m), 5); diff --git a/src/test/ui/regions/regions-variance-contravariant-use-contravariant.rs b/src/test/ui/regions/regions-variance-contravariant-use-contravariant.rs index f10d5a25f162..e6377867018c 100644 --- a/src/test/ui/regions/regions-variance-contravariant-use-contravariant.rs +++ b/src/test/ui/regions/regions-variance-contravariant-use-contravariant.rs @@ -4,7 +4,7 @@ // Test that a type which is contravariant with respect to its region // parameter compiles successfully when used in a contravariant way. // -// Note: see compile-fail/variance-regions-*.rs for the tests that check that the +// Note: see ui/variance/variance-regions-*.rs for the tests that check that the // variance inference works in the first place. // pretty-expanded FIXME #23616 diff --git a/src/test/ui/regions/regions-variance-covariant-use-covariant.rs b/src/test/ui/regions/regions-variance-covariant-use-covariant.rs index 9316aa15d32a..c5c80ce54f12 100644 --- a/src/test/ui/regions/regions-variance-covariant-use-covariant.rs +++ b/src/test/ui/regions/regions-variance-covariant-use-covariant.rs @@ -3,7 +3,7 @@ // Test that a type which is covariant with respect to its region // parameter is successful when used in a covariant way. // -// Note: see compile-fail/variance-regions-*.rs for the tests that +// Note: see ui/variance/variance-regions-*.rs for the tests that // check that the variance inference works in the first place. // This is covariant with respect to 'a, meaning that diff --git a/src/test/ui/resolve/issue-65035-static-with-parent-generics.stderr b/src/test/ui/resolve/issue-65035-static-with-parent-generics.stderr index 5de8eb215821..7f8151db06f5 100644 --- a/src/test/ui/resolve/issue-65035-static-with-parent-generics.stderr +++ b/src/test/ui/resolve/issue-65035-static-with-parent-generics.stderr @@ -48,7 +48,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete error: aborting due to 5 previous errors; 1 warning emitted diff --git a/src/test/ui/rfc-2294-if-let-guard/bindings.rs b/src/test/ui/rfc-2294-if-let-guard/bindings.rs new file mode 100644 index 000000000000..4e2d70e3290e --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/bindings.rs @@ -0,0 +1,10 @@ +#![feature(if_let_guard)] +#![allow(incomplete_features)] + +fn main() { + match Some(None) { + Some(x) if let Some(y) = x => (x, y), + _ => y, //~ ERROR cannot find value `y` + } + y //~ ERROR cannot find value `y` +} diff --git a/src/test/ui/rfc-2294-if-let-guard/bindings.stderr b/src/test/ui/rfc-2294-if-let-guard/bindings.stderr new file mode 100644 index 000000000000..9c5d92a33ada --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/bindings.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find value `y` in this scope + --> $DIR/bindings.rs:7:14 + | +LL | _ => y, + | ^ not found in this scope + +error[E0425]: cannot find value `y` in this scope + --> $DIR/bindings.rs:9:5 + | +LL | y + | ^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs index c0a9bbc36b24..4ba7e1eeefaa 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs @@ -5,8 +5,7 @@ use std::ops::Range; fn _if_let_guard() { match () { () if let 0 = 1 => {} - //~^ ERROR `if let` guard is not implemented - //~| ERROR `let` expressions are not supported here + //~^ ERROR `if let` guards are experimental () if (let 0 = 1) => {} //~^ ERROR `let` expressions in this position are experimental @@ -75,7 +74,7 @@ fn _macros() { match () { #[cfg(FALSE)] () if let 0 = 1 => {} - //~^ ERROR `if let` guard is not implemented + //~^ ERROR `if let` guards are experimental _ => {} } use_expr!(let 0 = 1); diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr index 5c7f8190dd6e..113870c19f5d 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `let` - --> $DIR/feature-gate.rs:81:15 + --> $DIR/feature-gate.rs:80:15 | LL | macro_rules! use_expr { | --------------------- when calling this macro @@ -7,7 +7,7 @@ LL | macro_rules! use_expr { LL | use_expr!(let 0 = 1); | ^^^ no rules expected this token in macro call -error[E0658]: `if let` guard is not implemented +error[E0658]: `if let` guards are experimental --> $DIR/feature-gate.rs:7:12 | LL | () if let 0 = 1 => {} @@ -16,8 +16,8 @@ LL | () if let 0 = 1 => {} = note: see issue #51114 for more information = help: add `#![feature(if_let_guard)]` to the crate attributes to enable -error[E0658]: `if let` guard is not implemented - --> $DIR/feature-gate.rs:77:12 +error[E0658]: `if let` guards are experimental + --> $DIR/feature-gate.rs:76:12 | LL | () if let 0 = 1 => {} | ^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | () if let 0 = 1 => {} = help: add `#![feature(if_let_guard)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:11:16 + --> $DIR/feature-gate.rs:10:16 | LL | () if (let 0 = 1) => {} | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | () if (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:15:18 + --> $DIR/feature-gate.rs:14:18 | LL | () if (((let 0 = 1))) => {} | ^^^^^^^^^ @@ -44,7 +44,7 @@ LL | () if (((let 0 = 1))) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:19:23 + --> $DIR/feature-gate.rs:18:23 | LL | () if true && let 0 = 1 => {} | ^^^^^^^^^ @@ -53,7 +53,7 @@ LL | () if true && let 0 = 1 => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:23:15 + --> $DIR/feature-gate.rs:22:15 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^ @@ -62,7 +62,7 @@ LL | () if let 0 = 1 && true => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:27:16 + --> $DIR/feature-gate.rs:26:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | () if (let 0 = 1) && true => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:31:24 + --> $DIR/feature-gate.rs:30:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ @@ -80,7 +80,7 @@ LL | () if true && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:35:16 + --> $DIR/feature-gate.rs:34:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:35:31 + --> $DIR/feature-gate.rs:34:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -98,7 +98,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:41:15 + --> $DIR/feature-gate.rs:40:15 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:41:28 + --> $DIR/feature-gate.rs:40:28 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -116,7 +116,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:41:42 + --> $DIR/feature-gate.rs:40:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:41:55 + --> $DIR/feature-gate.rs:40:55 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -134,7 +134,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:41:68 + --> $DIR/feature-gate.rs:40:68 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -143,7 +143,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:53:15 + --> $DIR/feature-gate.rs:52:15 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,7 +152,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:69:16 + --> $DIR/feature-gate.rs:68:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ @@ -161,7 +161,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are experimental - --> $DIR/feature-gate.rs:72:16 + --> $DIR/feature-gate.rs:71:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ @@ -170,16 +170,7 @@ LL | use_expr!((let 0 = 1)); = help: add `#![feature(let_chains)]` to the crate attributes to enable error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:7:15 - | -LL | () if let 0 = 1 => {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions - -error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:11:16 + --> $DIR/feature-gate.rs:10:16 | LL | () if (let 0 = 1) => {} | ^^^^^^^^^ @@ -188,7 +179,7 @@ LL | () if (let 0 = 1) => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:15:18 + --> $DIR/feature-gate.rs:14:18 | LL | () if (((let 0 = 1))) => {} | ^^^^^^^^^ @@ -197,7 +188,7 @@ LL | () if (((let 0 = 1))) => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:19:23 + --> $DIR/feature-gate.rs:18:23 | LL | () if true && let 0 = 1 => {} | ^^^^^^^^^ @@ -206,7 +197,7 @@ LL | () if true && let 0 = 1 => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:23:15 + --> $DIR/feature-gate.rs:22:15 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^ @@ -215,7 +206,7 @@ LL | () if let 0 = 1 && true => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:27:16 + --> $DIR/feature-gate.rs:26:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ @@ -224,7 +215,7 @@ LL | () if (let 0 = 1) && true => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:31:24 + --> $DIR/feature-gate.rs:30:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ @@ -233,7 +224,7 @@ LL | () if true && (let 0 = 1) => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:35:16 + --> $DIR/feature-gate.rs:34:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -242,7 +233,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:35:31 + --> $DIR/feature-gate.rs:34:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -251,7 +242,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:41:15 + --> $DIR/feature-gate.rs:40:15 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -260,7 +251,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:41:28 + --> $DIR/feature-gate.rs:40:28 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -269,7 +260,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:41:42 + --> $DIR/feature-gate.rs:40:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -278,7 +269,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:41:55 + --> $DIR/feature-gate.rs:40:55 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -287,7 +278,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:41:68 + --> $DIR/feature-gate.rs:40:68 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -296,7 +287,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:53:15 + --> $DIR/feature-gate.rs:52:15 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -305,7 +296,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:69:16 + --> $DIR/feature-gate.rs:68:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ @@ -314,7 +305,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = note: as well as when nested within `&&` and parenthesis in those conditions error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:72:16 + --> $DIR/feature-gate.rs:71:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ @@ -322,6 +313,6 @@ LL | use_expr!((let 0 = 1)); = note: only supported directly in conditions of `if`- and `while`-expressions = note: as well as when nested within `&&` and parenthesis in those conditions -error: aborting due to 36 previous errors +error: aborting due to 35 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2294-if-let-guard/run-pass.rs b/src/test/ui/rfc-2294-if-let-guard/run-pass.rs new file mode 100644 index 000000000000..a3663003790f --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/run-pass.rs @@ -0,0 +1,34 @@ +// run-pass + +#![feature(if_let_guard)] +#![allow(incomplete_features)] + +enum Foo { + Bar, + Baz, + Qux(u8), +} + +fn bar(x: bool) -> Foo { + if x { Foo::Baz } else { Foo::Bar } +} + +fn baz(x: u8) -> Foo { + if x % 2 == 0 { Foo::Bar } else { Foo::Baz } +} + +fn qux(x: u8) -> Foo { + Foo::Qux(x.rotate_left(1)) +} + +fn main() { + match Some((true, 3)) { + Some((x, _)) if let Foo::Bar = bar(x) => panic!(), + Some((_, x)) if let Foo::Baz = baz(x) => {}, + _ => panic!(), + } + match Some(42) { + Some(x) if let Foo::Qux(y) = qux(x) => assert_eq!(y, 84), + _ => panic!(), + } +} diff --git a/src/test/ui/rfc-2294-if-let-guard/typeck.rs b/src/test/ui/rfc-2294-if-let-guard/typeck.rs new file mode 100644 index 000000000000..a4fc7f8cf2b2 --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/typeck.rs @@ -0,0 +1,16 @@ +#![feature(if_let_guard)] +#![allow(incomplete_features)] + +fn ok() -> Result, ()> { + Ok(Some(true)) +} + +fn main() { + match ok() { + Ok(x) if let Err(_) = x => {}, + //~^ ERROR mismatched types + Ok(x) if let 0 = x => {}, + //~^ ERROR mismatched types + _ => {} + } +} diff --git a/src/test/ui/rfc-2294-if-let-guard/typeck.stderr b/src/test/ui/rfc-2294-if-let-guard/typeck.stderr new file mode 100644 index 000000000000..7ce93fe7348f --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/typeck.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/typeck.rs:10:22 + | +LL | Ok(x) if let Err(_) = x => {}, + | ^^^^^^ expected enum `Option`, found enum `std::result::Result` + | + = note: expected enum `Option` + found enum `std::result::Result<_, _>` + +error[E0308]: mismatched types + --> $DIR/typeck.rs:12:22 + | +LL | Ok(x) if let 0 = x => {}, + | ^ expected enum `Option`, found integer + | + = note: expected enum `Option` + found type `{integer}` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/rfc-2294-if-let-guard/warns.rs b/src/test/ui/rfc-2294-if-let-guard/warns.rs new file mode 100644 index 000000000000..9691a12f45b0 --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/warns.rs @@ -0,0 +1,22 @@ +#![feature(if_let_guard)] +#![allow(incomplete_features)] + +#[deny(irrefutable_let_patterns)] +fn irrefutable_let_guard() { + match Some(()) { + Some(x) if let () = x => {} + //~^ ERROR irrefutable if-let guard + _ => {} + } +} + +#[deny(unreachable_patterns)] +fn unreachable_pattern() { + match Some(()) { + x if let None | None = x => {} + //~^ ERROR unreachable pattern + _ => {} + } +} + +fn main() {} diff --git a/src/test/ui/rfc-2294-if-let-guard/warns.stderr b/src/test/ui/rfc-2294-if-let-guard/warns.stderr new file mode 100644 index 000000000000..45720f9fbc55 --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/warns.stderr @@ -0,0 +1,26 @@ +error: irrefutable if-let guard + --> $DIR/warns.rs:7:24 + | +LL | Some(x) if let () = x => {} + | ^^ + | +note: the lint level is defined here + --> $DIR/warns.rs:4:8 + | +LL | #[deny(irrefutable_let_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/warns.rs:16:25 + | +LL | x if let None | None = x => {} + | ^^^^ + | +note: the lint level is defined here + --> $DIR/warns.rs:13:8 + | +LL | #[deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 11155038a913..861a4a80ad65 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -521,7 +521,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/disallowed-positions.rs:22:12 diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs b/src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs new file mode 100644 index 000000000000..936c90e88aae --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs @@ -0,0 +1,13 @@ +#![feature(const_trait_impl)] +#![allow(incomplete_features)] + +struct Foo; + +const impl Foo { //~ ERROR: expected identifier, found keyword + fn bar() {} +} + +fn main() { + // shouldn't error here because we shouldn't have been able to recover above + Foo::bar(); +} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.stderr new file mode 100644 index 000000000000..612511a47995 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.stderr @@ -0,0 +1,8 @@ +error: expected identifier, found keyword `impl` + --> $DIR/const-impl-norecover.rs:6:7 + | +LL | const impl Foo { + | ^^^^ expected identifier, found keyword + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs b/src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs new file mode 100644 index 000000000000..fd3dd2cef9d1 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs @@ -0,0 +1,16 @@ +#![feature(const_trait_impl)] +#![allow(incomplete_features)] + +trait Foo {} + +const impl Foo for i32 {} //~ ERROR: expected identifier, found keyword + +trait Bar {} + +const impl Bar for T {} //~ ERROR: expected identifier, found keyword + +const fn still_implements() {} + +const _: () = still_implements::(); + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr new file mode 100644 index 000000000000..84fb619dc964 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr @@ -0,0 +1,24 @@ +error: expected identifier, found keyword `impl` + --> $DIR/const-impl-recovery.rs:6:7 + | +LL | const impl Foo for i32 {} + | ^^^^ expected identifier, found keyword + | +help: you might have meant to write a const trait impl + | +LL | impl const Foo for i32 {} + |-- ^^^^^ + +error: expected identifier, found keyword `impl` + --> $DIR/const-impl-recovery.rs:10:7 + | +LL | const impl Bar for T {} + | ^^^^ expected identifier, found keyword + | +help: you might have meant to write a const trait impl + | +LL | impl const Bar for T {} + |-- ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/simd/simd-array-type.rs b/src/test/ui/simd/simd-array-type.rs index e84186a42adf..7d66395a3c80 100644 --- a/src/test/ui/simd/simd-array-type.rs +++ b/src/test/ui/simd/simd-array-type.rs @@ -3,7 +3,7 @@ // pretty-expanded FIXME #23616 -#![feature(repr_simd, platform_intrinsics, min_const_generics)] +#![feature(repr_simd, platform_intrinsics)] #[repr(simd)] #[derive(Copy, Clone)] diff --git a/src/test/ui/simd/simd-generics.rs b/src/test/ui/simd/simd-generics.rs index dedca6276cda..50a4bfd9f518 100644 --- a/src/test/ui/simd/simd-generics.rs +++ b/src/test/ui/simd/simd-generics.rs @@ -1,6 +1,6 @@ // run-pass #![allow(non_camel_case_types)] -#![feature(repr_simd, platform_intrinsics, min_const_generics)] +#![feature(repr_simd, platform_intrinsics)] use std::ops; diff --git a/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs b/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs index 4c459fa4bc80..c11d14b99d48 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs @@ -2,7 +2,7 @@ // ignore-emscripten #![allow(non_camel_case_types)] -#![feature(repr_simd, platform_intrinsics, min_const_generics)] +#![feature(repr_simd, platform_intrinsics)] #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] diff --git a/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs b/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs index 5b0ff88432ed..96c2c6c53992 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs @@ -3,7 +3,7 @@ // ignore-emscripten FIXME(#45351) hits an LLVM assert -#![feature(repr_simd, platform_intrinsics, min_const_generics)] +#![feature(repr_simd, platform_intrinsics)] #[repr(simd)] #[derive(Copy, Clone)] diff --git a/src/test/ui/similar-tokens.fixed b/src/test/ui/similar-tokens.fixed deleted file mode 100644 index addba76ae3b5..000000000000 --- a/src/test/ui/similar-tokens.fixed +++ /dev/null @@ -1,13 +0,0 @@ -// run-rustfix - -#![allow(unused_imports)] - -pub mod x { - pub struct A; - pub struct B; -} - -// `.` is similar to `,` so list parsing should continue to closing `}` -use x::{A, B}; //~ ERROR expected one of `,`, `::`, `as`, or `}`, found `.` - -fn main() {} diff --git a/src/test/ui/similar-tokens.rs b/src/test/ui/similar-tokens.rs index 3d1bf5fe54ae..e3024c61ad2b 100644 --- a/src/test/ui/similar-tokens.rs +++ b/src/test/ui/similar-tokens.rs @@ -1,5 +1,3 @@ -// run-rustfix - #![allow(unused_imports)] pub mod x { diff --git a/src/test/ui/similar-tokens.stderr b/src/test/ui/similar-tokens.stderr index 6a8d09ebae66..90acfc052ddd 100644 --- a/src/test/ui/similar-tokens.stderr +++ b/src/test/ui/similar-tokens.stderr @@ -1,5 +1,5 @@ error: expected one of `,`, `::`, `as`, or `}`, found `.` - --> $DIR/similar-tokens.rs:11:10 + --> $DIR/similar-tokens.rs:9:10 | LL | use x::{A. B}; | ^ diff --git a/src/test/ui/span/dropck_arr_cycle_checked.rs b/src/test/ui/span/dropck_arr_cycle_checked.rs index ac31e4910d5a..a14db5ff0896 100644 --- a/src/test/ui/span/dropck_arr_cycle_checked.rs +++ b/src/test/ui/span/dropck_arr_cycle_checked.rs @@ -1,7 +1,7 @@ // Reject mixing cyclic structure and Drop when using fixed length // arrays. // -// (Compare against compile-fail/dropck_vec_cycle_checked.rs) +// (Compare against ui/span/dropck_vec_cycle_checked.rs) diff --git a/src/test/ui/span/dropck_vec_cycle_checked.rs b/src/test/ui/span/dropck_vec_cycle_checked.rs index bacd99c68254..c5d21507d76c 100644 --- a/src/test/ui/span/dropck_vec_cycle_checked.rs +++ b/src/test/ui/span/dropck_vec_cycle_checked.rs @@ -1,6 +1,6 @@ // Reject mixing cyclic structure and Drop when using Vec. // -// (Compare against compile-fail/dropck_arr_cycle_checked.rs) +// (Compare against ui/span/dropck_arr_cycle_checked.rs) use std::cell::Cell; use id::Id; diff --git a/src/test/ui/specialization/defaultimpl/projection.rs b/src/test/ui/specialization/defaultimpl/projection.rs index 4a9140969324..f19c55b043b4 100644 --- a/src/test/ui/specialization/defaultimpl/projection.rs +++ b/src/test/ui/specialization/defaultimpl/projection.rs @@ -4,7 +4,7 @@ #![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we *can* project non-defaulted associated types -// cf compile-fail/specialization-default-projection.rs +// cf ui/specialization/specialization-default-projection.rs // First, do so without any use of specialization diff --git a/src/test/compile-fail/specialization/issue-50452.rs b/src/test/ui/specialization/issue-50452-fail.rs similarity index 95% rename from src/test/compile-fail/specialization/issue-50452.rs rename to src/test/ui/specialization/issue-50452-fail.rs index 958f0eb26680..fe21e9b6ede5 100644 --- a/src/test/compile-fail/specialization/issue-50452.rs +++ b/src/test/ui/specialization/issue-50452-fail.rs @@ -1,4 +1,3 @@ -// compile-fail #![feature(specialization)] //~^ WARN the feature `specialization` is incomplete diff --git a/src/test/ui/specialization/issue-50452-fail.stderr b/src/test/ui/specialization/issue-50452-fail.stderr new file mode 100644 index 000000000000..8e7c5037eff8 --- /dev/null +++ b/src/test/ui/specialization/issue-50452-fail.stderr @@ -0,0 +1,26 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-50452-fail.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + +error[E0520]: `foo` specializes an item from a parent `impl`, but that item is not marked `default` + --> $DIR/issue-50452-fail.rs:10:5 + | +LL | fn foo() {} + | ^^^^^^^^^^^ cannot specialize default item `foo` +... +LL | / impl Foo for T { +LL | | fn foo() {} +LL | | } + | |_- parent `impl` is here + | + = note: to specialize, `foo` in the parent `impl` must be marked `default` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0520`. diff --git a/src/test/ui/specialization/specialization-projection.rs b/src/test/ui/specialization/specialization-projection.rs index 700975e3b828..78afe7a94954 100644 --- a/src/test/ui/specialization/specialization-projection.rs +++ b/src/test/ui/specialization/specialization-projection.rs @@ -4,7 +4,7 @@ #![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we *can* project non-defaulted associated types -// cf compile-fail/specialization-default-projection.rs +// cf ui/specialization/specialization-default-projection.rs // First, do so without any use of specialization diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.rs b/src/test/ui/stability-attribute/stability-attribute-sanity.rs index abd603b356ee..0c40f8ae1c67 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.rs +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.rs @@ -63,7 +63,11 @@ fn multiple3() { } #[rustc_const_unstable(feature = "c", issue = "none")] #[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels pub const fn multiple4() { } -//~^ ERROR Invalid stability or deprecation version found +//~^ ERROR Invalid stability version found + +#[stable(feature = "a", since = "1.0.0")] +#[rustc_deprecated(since = "invalid", reason = "text")] +fn invalid_deprecation_version() {} //~ ERROR Invalid deprecation version found #[rustc_deprecated(since = "a", reason = "text")] fn deprecated_without_unstable_or_stable() { } diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr index 97089f7df526..ee9a93359f03 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr @@ -96,19 +96,25 @@ error[E0544]: multiple stability levels LL | #[rustc_const_unstable(feature = "d", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Invalid stability or deprecation version found +error: Invalid stability version found --> $DIR/stability-attribute-sanity.rs:65:1 | LL | pub const fn multiple4() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: Invalid deprecation version found + --> $DIR/stability-attribute-sanity.rs:70:1 + | +LL | fn invalid_deprecation_version() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0549]: rustc_deprecated attribute must be paired with either stable or unstable attribute - --> $DIR/stability-attribute-sanity.rs:68:1 + --> $DIR/stability-attribute-sanity.rs:72:1 | LL | #[rustc_deprecated(since = "a", reason = "text")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors Some errors have detailed explanations: E0539, E0541, E0546, E0550. For more information about an error, try `rustc --explain E0539`. diff --git a/src/test/ui/structs-enums/discrim-explicit-23030.rs b/src/test/ui/structs-enums/discrim-explicit-23030.rs index af7ab865e322..e17025e9e88c 100644 --- a/src/test/ui/structs-enums/discrim-explicit-23030.rs +++ b/src/test/ui/structs-enums/discrim-explicit-23030.rs @@ -2,7 +2,7 @@ // Issue 23030: Workaround overflowing discriminant // with explicit assignments. -// See also compile-fail/overflow-discrim.rs, which shows what +// See also ui/discrim/discrim-overflow.rs, which shows what // happens if you leave the OhNo explicit cases out here. fn f_i8() { diff --git a/src/test/ui/structs-enums/object-lifetime-default-from-rptr-struct.rs b/src/test/ui/structs-enums/object-lifetime-default-from-rptr-struct.rs index 1fc52ead48e0..d3e92e162469 100644 --- a/src/test/ui/structs-enums/object-lifetime-default-from-rptr-struct.rs +++ b/src/test/ui/structs-enums/object-lifetime-default-from-rptr-struct.rs @@ -27,7 +27,7 @@ fn b<'a>(t: &'a MyBox, mut ss: SomeStruct<'a>) { ss.u = t; } -// see also compile-fail/object-lifetime-default-from-rptr-box-error.rs +// see also ui/object-lifetime/object-lifetime-default-from-rptr-box-error.rs fn d<'a>(t: &'a MyBox, mut ss: SomeStruct<'a>) { ss.u = t; diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs b/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs index d401328077aa..949b23600715 100644 --- a/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs +++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs @@ -39,6 +39,14 @@ fn bak(constraints: impl Iterator + std::fmt::Debug) { } } +#[rustfmt::skip] +fn baw<>(constraints: impl Iterator) { + for constraint in constraints { + qux(constraint); +//~^ ERROR `::Item` doesn't implement `Debug` + } +} + fn qux(_: impl std::fmt::Debug) {} fn main() {} diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr index 099eb1c9d005..0de3b9aec19e 100644 --- a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr +++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr @@ -73,6 +73,21 @@ help: introduce a type parameter with a trait bound instead of using `impl Trait LL | fn bak(constraints: I) where ::Item: Debug { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error[E0277]: `::Item` doesn't implement `Debug` + --> $DIR/impl-trait-with-missing-bounds.rs:45:13 + | +LL | qux(constraint); + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` +... +LL | fn qux(_: impl std::fmt::Debug) {} + | --------------- required by this bound in `qux` + | + = help: the trait `Debug` is not implemented for `::Item` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | fn baw(constraints: I) where ::Item: Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs b/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs new file mode 100644 index 000000000000..3cd6d336e134 --- /dev/null +++ b/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs @@ -0,0 +1,32 @@ +// Regression test: if we suggest replacing an `impl Trait` argument to an async +// fn with a named type parameter in order to add bounds, the suggested function +// signature should be well-formed. +// +// edition:2018 + +trait Foo { + type Bar; + fn bar(&self) -> Self::Bar; +} + +async fn run(_: &(), foo: impl Foo) -> std::io::Result<()> { + let bar = foo.bar(); + assert_is_send(&bar); +//~^ ERROR: `::Bar` cannot be sent between threads safely + + Ok(()) +} + +// Test our handling of cases where there is a generic parameter list in the +// source, but only synthetic generic parameters +async fn run2< >(_: &(), foo: impl Foo) -> std::io::Result<()> { + let bar = foo.bar(); + assert_is_send(&bar); +//~^ ERROR: `::Bar` cannot be sent between threads safely + + Ok(()) +} + +fn assert_is_send(_: &T) {} + +fn main() {} diff --git a/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.stderr b/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.stderr new file mode 100644 index 000000000000..9404c3bb5831 --- /dev/null +++ b/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.stderr @@ -0,0 +1,33 @@ +error[E0277]: `::Bar` cannot be sent between threads safely + --> $DIR/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs:14:20 + | +LL | assert_is_send(&bar); + | ^^^^ `::Bar` cannot be sent between threads safely +... +LL | fn assert_is_send(_: &T) {} + | ---- required by this bound in `assert_is_send` + | + = help: the trait `Send` is not implemented for `::Bar` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | async fn run(_: &(), foo: F) -> std::io::Result<()> where ::Bar: Send { + | ^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `::Bar` cannot be sent between threads safely + --> $DIR/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs:24:20 + | +LL | assert_is_send(&bar); + | ^^^^ `::Bar` cannot be sent between threads safely +... +LL | fn assert_is_send(_: &T) {} + | ---- required by this bound in `assert_is_send` + | + = help: the trait `Send` is not implemented for `::Bar` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | async fn run2(_: &(), foo: F) -> std::io::Result<()> where ::Bar: Send { + | ^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/suggest-move-lifetimes.stderr b/src/test/ui/suggestions/suggest-move-lifetimes.stderr index 1851c8deaa8b..657914d1c8c0 100644 --- a/src/test/ui/suggestions/suggest-move-lifetimes.stderr +++ b/src/test/ui/suggestions/suggest-move-lifetimes.stderr @@ -2,25 +2,25 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/suggest-move-lifetimes.rs:1:13 | LL | struct A { - | ----^^- help: reorder the parameters: lifetimes, then types: `<'a, T>` + | ----^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T>` error: lifetime parameters must be declared prior to type parameters --> $DIR/suggest-move-lifetimes.rs:5:13 | LL | struct B { - | ----^^---- help: reorder the parameters: lifetimes, then types: `<'a, T, U>` + | ----^^---- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, U>` error: lifetime parameters must be declared prior to type parameters --> $DIR/suggest-move-lifetimes.rs:10:16 | LL | struct C { - | -------^^- help: reorder the parameters: lifetimes, then types: `<'a, T, U>` + | -------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, U>` error: lifetime parameters must be declared prior to type parameters --> $DIR/suggest-move-lifetimes.rs:15:16 | LL | struct D { - | -------^^--^^-----^^- help: reorder the parameters: lifetimes, then types: `<'a, 'b, 'c, T, U, V>` + | -------^^--^^-----^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, 'c, T, U, V>` error: aborting due to 4 previous errors diff --git a/src/test/ui/svh/auxiliary/svh-uta-base.rs b/src/test/ui/svh/auxiliary/svh-uta-base.rs index c138f1a5ba84..221a096e0831 100644 --- a/src/test/ui/svh/auxiliary/svh-uta-base.rs +++ b/src/test/ui/svh/auxiliary/svh-uta-base.rs @@ -1,4 +1,4 @@ -//! "compile-fail/svh-uta-trait.rs" is checking that we detect a +//! "svh-uta-trait.rs" is checking that we detect a //! change from `use foo::TraitB` to use `foo::TraitB` in the hash //! (SVH) computation (#14132), since that will affect method //! resolution. diff --git a/src/test/ui/svh/auxiliary/svh-uta-change-use-trait.rs b/src/test/ui/svh/auxiliary/svh-uta-change-use-trait.rs index 76a472b5b26b..823d29571aa8 100644 --- a/src/test/ui/svh/auxiliary/svh-uta-change-use-trait.rs +++ b/src/test/ui/svh/auxiliary/svh-uta-change-use-trait.rs @@ -1,4 +1,4 @@ -//! "compile-fail/svh-uta-trait.rs" is checking that we detect a +//! "svh-uta-trait.rs" is checking that we detect a //! change from `use foo::TraitB` to use `foo::TraitB` in the hash //! (SVH) computation (#14132), since that will affect method //! resolution. diff --git a/src/test/ui/svh/auxiliary/svh-utb.rs b/src/test/ui/svh/auxiliary/svh-utb.rs index 2f27e99a961a..a03e29dcedc4 100644 --- a/src/test/ui/svh/auxiliary/svh-utb.rs +++ b/src/test/ui/svh/auxiliary/svh-utb.rs @@ -1,4 +1,4 @@ -//! "compile-fail/svh-uta-trait.rs" is checking that we detect a +//! "svh-uta-trait.rs" is checking that we detect a //! change from `use foo::TraitB` to use `foo::TraitB` in the hash //! (SVH) computation (#14132), since that will affect method //! resolution. diff --git a/src/test/ui/svh/svh-use-trait.rs b/src/test/ui/svh/svh-use-trait.rs index 93daca034c06..e5c427e096a7 100644 --- a/src/test/ui/svh/svh-use-trait.rs +++ b/src/test/ui/svh/svh-use-trait.rs @@ -6,7 +6,7 @@ // aux-build:svh-uta-change-use-trait.rs // normalize-stderr-test: "(crate `(\w+)`:) .*" -> "$1 $$PATH_$2" -//! "compile-fail/svh-uta-trait.rs" is checking that we detect a +//! "svh-uta-trait.rs" is checking that we detect a //! change from `use foo::TraitB` to use `foo::TraitB` in the hash //! (SVH) computation (#14132), since that will affect method //! resolution. diff --git a/src/test/ui/symbol-names/const-generics-demangling.rs b/src/test/ui/symbol-names/const-generics-demangling.rs index e002124059f8..55ab17fcd5a9 100644 --- a/src/test/ui/symbol-names/const-generics-demangling.rs +++ b/src/test/ui/symbol-names/const-generics-demangling.rs @@ -1,7 +1,6 @@ // build-fail // compile-flags: -Z symbol-mangling-version=v0 - -#![feature(min_const_generics, rustc_attrs)] +#![feature(rustc_attrs)] pub struct Unsigned; diff --git a/src/test/ui/symbol-names/const-generics-demangling.stderr b/src/test/ui/symbol-names/const-generics-demangling.stderr index 022b3188373c..a9574cacea3e 100644 --- a/src/test/ui/symbol-names/const-generics-demangling.stderr +++ b/src/test/ui/symbol-names/const-generics-demangling.stderr @@ -1,71 +1,71 @@ error: symbol-name(_RMCs4fqI2P2rA04_25const_generics_demanglingINtB0_8UnsignedKhb_E) - --> $DIR/const-generics-demangling.rs:8:1 + --> $DIR/const-generics-demangling.rs:7:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-demangling.rs:8:1 + --> $DIR/const-generics-demangling.rs:7:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-demangling.rs:8:1 + --> $DIR/const-generics-demangling.rs:7:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs_Cs4fqI2P2rA04_25const_generics_demanglingINtB2_6SignedKsn98_E) - --> $DIR/const-generics-demangling.rs:16:1 + --> $DIR/const-generics-demangling.rs:15:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-demangling.rs:16:1 + --> $DIR/const-generics-demangling.rs:15:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-demangling.rs:16:1 + --> $DIR/const-generics-demangling.rs:15:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs0_Cs4fqI2P2rA04_25const_generics_demanglingINtB3_4BoolKb1_E) - --> $DIR/const-generics-demangling.rs:24:1 + --> $DIR/const-generics-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-demangling.rs:24:1 + --> $DIR/const-generics-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-demangling.rs:24:1 + --> $DIR/const-generics-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs1_Cs4fqI2P2rA04_25const_generics_demanglingINtB3_4CharKc2202_E) - --> $DIR/const-generics-demangling.rs:32:1 + --> $DIR/const-generics-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-demangling.rs:32:1 + --> $DIR/const-generics-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-demangling.rs:32:1 + --> $DIR/const-generics-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/symbol-names/const-generics.rs b/src/test/ui/symbol-names/const-generics.rs index 823995e5be3a..2d136e6a99a8 100644 --- a/src/test/ui/symbol-names/const-generics.rs +++ b/src/test/ui/symbol-names/const-generics.rs @@ -1,87 +1,85 @@ // check-pass // revisions: legacy v0 //[legacy]compile-flags: -Z symbol-mangling-version=legacy --crate-type=lib - //[v0]compile-flags: -Z symbol-mangling-version=v0 --crate-type=lib +//[v0]compile-flags: -Z symbol-mangling-version=v0 --crate-type=lib - #![feature(min_const_generics)] +// `char` +pub struct Char; - // `char` - pub struct Char; +impl Char<'A'> { + pub fn foo() {} +} - impl Char<'A'> { - pub fn foo() {} - } +impl Char { + pub fn bar() {} +} - impl Char { - pub fn bar() {} - } +// `i8` +pub struct I8; - // `i8` - pub struct I8; +impl I8<{i8::MIN}> { + pub fn foo() {} +} - impl I8<{i8::MIN}> { - pub fn foo() {} - } +impl I8<{i8::MAX}> { + pub fn foo() {} +} - impl I8<{i8::MAX}> { - pub fn foo() {} - } +impl I8 { + pub fn bar() {} +} - impl I8 { - pub fn bar() {} - } +// `i16` +pub struct I16; - // `i16` - pub struct I16; +impl I16<{i16::MIN}> { + pub fn foo() {} +} - impl I16<{i16::MIN}> { - pub fn foo() {} - } +impl I16 { + pub fn bar() {} +} - impl I16 { - pub fn bar() {} - } +// `i32` +pub struct I32; - // `i32` - pub struct I32; +impl I32<{i32::MIN}> { + pub fn foo() {} +} - impl I32<{i32::MIN}> { - pub fn foo() {} - } +impl I32 { + pub fn bar() {} +} - impl I32 { - pub fn bar() {} - } +// `i64` +pub struct I64; - // `i64` - pub struct I64; +impl I64<{i64::MIN}> { + pub fn foo() {} +} - impl I64<{i64::MIN}> { - pub fn foo() {} - } +impl I64 { + pub fn bar() {} +} - impl I64 { - pub fn bar() {} - } +// `i128` +pub struct I128; - // `i128` - pub struct I128; +impl I128<{i128::MIN}> { + pub fn foo() {} +} - impl I128<{i128::MIN}> { - pub fn foo() {} - } +impl I128 { + pub fn bar() {} +} - impl I128 { - pub fn bar() {} - } +// `isize` +pub struct ISize; - // `isize` - pub struct ISize; +impl ISize<3> { + pub fn foo() {} +} - impl ISize<3> { - pub fn foo() {} - } - - impl ISize { - pub fn bar() {} - } +impl ISize { + pub fn bar() {} +} diff --git a/src/test/ui/symbol-names/issue-76365.rs b/src/test/ui/symbol-names/issue-76365.rs index 61ba255dac04..c2e9f92f7b53 100644 --- a/src/test/ui/symbol-names/issue-76365.rs +++ b/src/test/ui/symbol-names/issue-76365.rs @@ -1,9 +1,8 @@ // check-pass // revisions: legacy v0 //[legacy]compile-flags: -Z symbol-mangling-version=legacy --crate-type=lib - //[v0]compile-flags: -Z symbol-mangling-version=v0 --crate-type=lib +//[v0]compile-flags: -Z symbol-mangling-version=v0 --crate-type=lib -#![feature(min_const_generics)] pub struct Bar; diff --git a/src/test/compile-fail/issue-43733-2.rs b/src/test/ui/threads-sendsync/issue-43733-2.rs similarity index 96% rename from src/test/compile-fail/issue-43733-2.rs rename to src/test/ui/threads-sendsync/issue-43733-2.rs index 61dd3a923f26..21ea8e9a2098 100644 --- a/src/test/compile-fail/issue-43733-2.rs +++ b/src/test/ui/threads-sendsync/issue-43733-2.rs @@ -1,3 +1,5 @@ +// dont-check-compiler-stderr + #![feature(cfg_target_thread_local, thread_local_internals)] // On platforms *without* `#[thread_local]`, use diff --git a/src/test/ui/issues/issue-43733.rs b/src/test/ui/threads-sendsync/issue-43733.rs similarity index 100% rename from src/test/ui/issues/issue-43733.rs rename to src/test/ui/threads-sendsync/issue-43733.rs diff --git a/src/test/ui/issues/issue-43733.stderr b/src/test/ui/threads-sendsync/issue-43733.stderr similarity index 100% rename from src/test/ui/issues/issue-43733.stderr rename to src/test/ui/threads-sendsync/issue-43733.stderr diff --git a/src/test/ui/traits/impl-evaluation-order.rs b/src/test/ui/traits/impl-evaluation-order.rs new file mode 100644 index 000000000000..57809d89aa64 --- /dev/null +++ b/src/test/ui/traits/impl-evaluation-order.rs @@ -0,0 +1,39 @@ +// Regression test for #79902 + +// Check that evaluation (which is used to determine whether to copy a type in +// MIR building) evaluates bounds from normalizing an impl after evaluating +// any bounds on the impl. + +// check-pass + +trait A { + type B; +} +trait M {} + +struct G(*const T, *const U); + +impl Clone for G { + fn clone(&self) -> Self { + G { ..*self } + } +} + +impl Copy for G +where + T: A, + U: A, +{ +} + +impl A for () { + type B = (); +} + +fn is_m(_: T) {} + +fn main() { + let x = G(&(), &()); + drop(x); + drop(x); +} diff --git a/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.rs b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.rs new file mode 100644 index 000000000000..3e336933937d --- /dev/null +++ b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.rs @@ -0,0 +1,53 @@ +// This tests issue #79683: note in the error message that the trait is +// explicitely unimplemented instead of suggesting to implement it. + +#![feature(negative_impls)] + +struct Qux; +//~^ NOTE method `clone` not found for this +//~^^ NOTE method `foo` not found for this + +impl !Clone for Qux {} + +trait Bar { + fn bar(&self); +} + +impl !Bar for u32 {} + +trait Foo { + fn foo(&self); +} +//~^^^ NOTE `Foo` defines an item `foo`, perhaps you need to implement it + +trait FooBar { + fn foo(&self); +} + +impl !Foo for Qux {} + +impl !FooBar for Qux {} + +impl !FooBar for u32 {} + +fn main() { + Qux.clone(); + //~^ ERROR no method named `clone` found for struct `Qux` + //~| NOTE method not found in `Qux` + //~| NOTE `Clone` defines an item `clone`, but is explicitely unimplemented + + 0_u32.bar(); + //~^ ERROR no method named `bar` found for type `u32` + //~| NOTE method not found in `u32` + //~| NOTE `Bar` defines an item `bar`, but is explicitely unimplemented + + Qux.foo(); + //~^ ERROR no method named `foo` found for struct `Qux` + //~| NOTE method not found in `Qux` + //~| NOTE the following traits define an item `foo`, but are explicitely unimplemented + + 0_u32.foo(); + //~^ ERROR no method named `foo` found for type `u32` + //~| NOTE method not found in `u32` + //~| NOTE `FooBar` defines an item `foo`, but is explicitely unimplemented +} diff --git a/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr new file mode 100644 index 000000000000..d39daaba206d --- /dev/null +++ b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr @@ -0,0 +1,60 @@ +error[E0599]: no method named `clone` found for struct `Qux` in the current scope + --> $DIR/explicitly-unimplemented-error-message.rs:34:9 + | +LL | struct Qux; + | ----------- method `clone` not found for this +... +LL | Qux.clone(); + | ^^^^^ method not found in `Qux` + | + ::: $SRC_DIR/core/src/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `Arc` here + | the method is available for `Rc` here + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the trait `Clone` defines an item `clone`, but is explicitely unimplemented + +error[E0599]: no method named `bar` found for type `u32` in the current scope + --> $DIR/explicitly-unimplemented-error-message.rs:39:11 + | +LL | 0_u32.bar(); + | ^^^ method not found in `u32` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the trait `Bar` defines an item `bar`, but is explicitely unimplemented + +error[E0599]: no method named `foo` found for struct `Qux` in the current scope + --> $DIR/explicitly-unimplemented-error-message.rs:44:9 + | +LL | struct Qux; + | ----------- method `foo` not found for this +... +LL | Qux.foo(); + | ^^^ method not found in `Qux` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `foo`, but are explicitely unimplemented: + Foo + FooBar + +error[E0599]: no method named `foo` found for type `u32` in the current scope + --> $DIR/explicitly-unimplemented-error-message.rs:49:11 + | +LL | 0_u32.foo(); + | ^^^ method not found in `u32` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Foo` defines an item `foo`, perhaps you need to implement it + --> $DIR/explicitly-unimplemented-error-message.rs:18:1 + | +LL | trait Foo { + | ^^^^^^^^^ + = note: the trait `FooBar` defines an item `foo`, but is explicitely unimplemented + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/traits/traits-repeated-supertrait.rs b/src/test/ui/traits/traits-repeated-supertrait.rs index 391d19c43855..339f9c37eeae 100644 --- a/src/test/ui/traits/traits-repeated-supertrait.rs +++ b/src/test/ui/traits/traits-repeated-supertrait.rs @@ -2,7 +2,7 @@ // Test a case of a trait which extends the same supertrait twice, but // with difference type parameters. Test that we can invoke the // various methods in various ways successfully. -// See also `compile-fail/trait-repeated-supertrait-ambig.rs`. +// See also `ui/traits/trait-repeated-supertrait-ambig.rs`. trait CompareTo { diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr index c5b22f0026de..e0c1b0238612 100644 --- a/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr +++ b/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr @@ -6,7 +6,6 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information - = help: consider using `min_const_generics` instead, which is more stable and complete warning: 1 warning emitted diff --git a/src/test/ui/union/union-unsafe.rs b/src/test/ui/union/union-unsafe.rs index 10f0c467560f..6adf0ac59b93 100644 --- a/src/test/ui/union/union-unsafe.rs +++ b/src/test/ui/union/union-unsafe.rs @@ -1,4 +1,6 @@ +#![feature(untagged_unions)] use std::mem::ManuallyDrop; +use std::cell::RefCell; union U1 { a: u8 @@ -16,9 +18,28 @@ union U4 { a: T } +union URef { + p: &'static mut i32, +} + +union URefCell { // field that does not drop but is not `Copy`, either + a: (RefCell, i32), +} + +fn deref_union_field(mut u: URef) { + // Not an assignment but an access to the union field! + *(u.p) = 13; //~ ERROR access to union field is unsafe +} + +fn assign_noncopy_union_field(mut u: URefCell) { + u.a = (RefCell::new(0), 1); //~ ERROR assignment to union field that might need dropping + u.a.0 = RefCell::new(0); //~ ERROR assignment to union field that might need dropping + u.a.1 = 1; // OK +} + fn generic_noncopy() { let mut u3 = U3 { a: ManuallyDrop::new(T::default()) }; - u3.a = ManuallyDrop::new(T::default()); //~ ERROR assignment to non-`Copy` union field is unsafe + u3.a = ManuallyDrop::new(T::default()); // OK (assignment does not drop) *u3.a = T::default(); //~ ERROR access to union field is unsafe } @@ -41,7 +62,7 @@ fn main() { // let U1 { .. } = u1; // OK let mut u2 = U2 { a: ManuallyDrop::new(String::from("old")) }; // OK - u2.a = ManuallyDrop::new(String::from("new")); //~ ERROR assignment to non-`Copy` union + u2.a = ManuallyDrop::new(String::from("new")); // OK (assignment does not drop) *u2.a = String::from("new"); //~ ERROR access to union field is unsafe let mut u3 = U3 { a: ManuallyDrop::new(0) }; // OK @@ -49,6 +70,6 @@ fn main() { *u3.a = 1; //~ ERROR access to union field is unsafe let mut u3 = U3 { a: ManuallyDrop::new(String::from("old")) }; // OK - u3.a = ManuallyDrop::new(String::from("new")); //~ ERROR assignment to non-`Copy` union + u3.a = ManuallyDrop::new(String::from("new")); // OK (assignment does not drop) *u3.a = String::from("new"); //~ ERROR access to union field is unsafe } diff --git a/src/test/ui/union/union-unsafe.stderr b/src/test/ui/union/union-unsafe.stderr index b50d9e175065..a25c09144f74 100644 --- a/src/test/ui/union/union-unsafe.stderr +++ b/src/test/ui/union/union-unsafe.stderr @@ -1,85 +1,85 @@ -error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:21:5 +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:31:5 | -LL | u3.a = ManuallyDrop::new(T::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field +LL | *(u.p) = 13; + | ^^^^^^^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:35:5 + | +LL | u.a = (RefCell::new(0), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping + | + = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized + +error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:36:5 + | +LL | u.a.0 = RefCell::new(0); + | ^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping | = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:22:6 + --> $DIR/union-unsafe.rs:43:6 | LL | *u3.a = T::default(); | ^^^^ access to union field | = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior -error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:28:6 - | -LL | *u3.a = T::default(); - | ^^^^ access to union field - | - = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior - -error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:36:13 - | -LL | let a = u1.a; - | ^^^^ access to union field - | - = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior - -error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:39:14 - | -LL | let U1 { a } = u1; - | ^ access to union field - | - = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior - -error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:40:20 - | -LL | if let U1 { a: 12 } = u1 {} - | ^^ access to union field - | - = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior - -error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:44:5 - | -LL | u2.a = ManuallyDrop::new(String::from("new")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field - | - = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized - -error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:45:6 - | -LL | *u2.a = String::from("new"); - | ^^^^ access to union field - | - = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior - error[E0133]: access to union field is unsafe and requires unsafe function or block --> $DIR/union-unsafe.rs:49:6 | +LL | *u3.a = T::default(); + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:57:13 + | +LL | let a = u1.a; + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:60:14 + | +LL | let U1 { a } = u1; + | ^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:61:20 + | +LL | if let U1 { a: 12 } = u1 {} + | ^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:66:6 + | +LL | *u2.a = String::from("new"); + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:70:6 + | LL | *u3.a = 1; | ^^^^ access to union field | = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior -error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:52:5 - | -LL | u3.a = ManuallyDrop::new(String::from("new")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field - | - = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized - error[E0133]: access to union field is unsafe and requires unsafe function or block - --> $DIR/union-unsafe.rs:53:6 + --> $DIR/union-unsafe.rs:74:6 | LL | *u3.a = String::from("new"); | ^^^^ access to union field diff --git a/src/test/ui/unsafe-fn-called-from-unsafe-blk.rs b/src/test/ui/unsafe-fn-called-from-unsafe-blk.rs index 38271cc3c78b..3713a7065f5e 100644 --- a/src/test/ui/unsafe-fn-called-from-unsafe-blk.rs +++ b/src/test/ui/unsafe-fn-called-from-unsafe-blk.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] // -// See also: compile-fail/unsafe-fn-called-from-safe.rs +// See also: ui/unsafe/unsafe-fn-called-from-safe.rs // pretty-expanded FIXME #23616 diff --git a/src/test/ui/unsafe-fn-called-from-unsafe-fn.rs b/src/test/ui/unsafe-fn-called-from-unsafe-fn.rs index 26acc913e872..5e953107686f 100644 --- a/src/test/ui/unsafe-fn-called-from-unsafe-fn.rs +++ b/src/test/ui/unsafe-fn-called-from-unsafe-fn.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] // -// See also: compile-fail/unsafe-fn-called-from-safe.rs +// See also: ui/unsafe/unsafe-fn-called-from-safe.rs // pretty-expanded FIXME #23616 diff --git a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs new file mode 100644 index 000000000000..8386959cfb3a --- /dev/null +++ b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs @@ -0,0 +1,17 @@ +// Regression test for #80468. + +#![crate_type = "lib"] + +pub trait Trait {} + +#[repr(transparent)] +pub struct Wrapper(T); + +#[repr(transparent)] +pub struct Ref<'a>(&'a u8); + +impl Trait for Ref {} //~ ERROR: implicit elided lifetime not allowed here + +extern "C" { + pub fn repro(_: Wrapper); //~ ERROR: mismatched types +} diff --git a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr new file mode 100644 index 000000000000..bb839d0a5eca --- /dev/null +++ b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr @@ -0,0 +1,24 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:13:16 + | +LL | impl Trait for Ref {} + | ^^^- help: indicate the anonymous lifetime: `<'_>` + +error[E0308]: mismatched types + --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21 + | +LL | pub fn repro(_: Wrapper); + | ^^^^^^^^^^^^ lifetime mismatch + | + = note: expected trait `Trait` + found trait `Trait` +note: the anonymous lifetime #1 defined on the method body at 16:5... + --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:5 + | +LL | pub fn repro(_: Wrapper); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 0462efaa9b00..73a4cbd07924 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -299,6 +299,7 @@ impl Builder { let mut package = |name, targets| self.package(name, &mut manifest.pkg, targets); package("rustc", HOSTS); package("rustc-dev", HOSTS); + package("reproducible-artifacts", HOSTS); package("rustc-docs", HOSTS); package("cargo", HOSTS); package("rust-mingw", MINGW); diff --git a/src/tools/cargo b/src/tools/cargo index d274fcf862b8..75d5d8cffe34 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit d274fcf862b89264fa2c6b917b15230705257317 +Subproject commit 75d5d8cffe3464631f82dcd3c470b78dc1dda8bb diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index cf4aa39e49b9..530e60001f72 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -35,29 +35,11 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: nightly - target: x86_64-unknown-linux-gnu - profile: minimal - - name: Checkout uses: actions/checkout@v2.3.3 - - name: Run cargo update - run: cargo update - - - name: Cache cargo dir - uses: actions/cache@v2 - with: - path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-x86_64-unknown-linux-gnu - - - name: Master Toolchain Setup - run: bash setup-toolchain.sh + - name: Install toolchain + run: rustup show active-toolchain # Run - name: Set LD_LIBRARY_PATH (Linux) @@ -66,13 +48,13 @@ jobs: echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build - run: cargo build --features deny-warnings + run: cargo build --features deny-warnings,internal-lints - name: Test - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings,internal-lints - name: Test clippy_lints - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings,internal-lints working-directory: clippy_lints - name: Test rustc_tools_util @@ -98,9 +80,3 @@ jobs: cargo dev new_lint --name new_late_pass --pass late cargo check git reset --hard HEAD - - # Cleanup - - name: Run cargo-cache --autoclean - run: | - cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache - cargo cache diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 784463fe0df9..5d846eb64c78 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -23,6 +23,7 @@ jobs: - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master with: github_token: "${{ secrets.github_token }}" + - name: Checkout uses: actions/checkout@v2.3.3 with: @@ -84,31 +85,11 @@ jobs: sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386 if: matrix.host == 'i686-unknown-linux-gnu' - - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: nightly - target: ${{ matrix.host }} - profile: minimal - - name: Checkout uses: actions/checkout@v2.3.3 - - name: Run cargo update - run: cargo update - - - name: Cache cargo dir - uses: actions/cache@v2 - with: - path: ~/.cargo - key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.host }} - - - name: Master Toolchain Setup - run: bash setup-toolchain.sh - env: - HOST_TOOLCHAIN: ${{ matrix.host }} + - name: Install toolchain + run: rustup show active-toolchain # Run - name: Set LD_LIBRARY_PATH (Linux) @@ -128,13 +109,13 @@ jobs: SYSROOT=$(rustc --print sysroot) echo "$SYSROOT/bin" >> $GITHUB_PATH - - name: Build with internal lints + - name: Build run: cargo build --features deny-warnings,internal-lints - - name: Test with internal lints + - name: Test run: cargo test --features deny-warnings,internal-lints - - name: Test clippy_lints with internal lints + - name: Test clippy_lints run: cargo test --features deny-warnings,internal-lints working-directory: clippy_lints @@ -155,12 +136,6 @@ jobs: env: OS: ${{ runner.os }} - # Cleanup - - name: Run cargo-cache --autoclean - run: | - cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache - cargo cache - integration_build: needs: changelog runs-on: ubuntu-latest @@ -171,29 +146,11 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: nightly - target: x86_64-unknown-linux-gnu - profile: minimal - - name: Checkout uses: actions/checkout@v2.3.3 - - name: Run cargo update - run: cargo update - - - name: Cache cargo dir - uses: actions/cache@v2 - with: - path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-x86_64-unknown-linux-gnu - - - name: Master Toolchain Setup - run: bash setup-toolchain.sh + - name: Install toolchain + run: rustup show active-toolchain # Run - name: Build Integration Test @@ -214,11 +171,6 @@ jobs: name: target path: target - # Cleanup - - name: Run cargo-cache --autoclean - run: | - cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache - cargo cache integration: needs: integration_build strategy: @@ -252,29 +204,11 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.6 - with: - toolchain: nightly - target: x86_64-unknown-linux-gnu - profile: minimal - - name: Checkout uses: actions/checkout@v2.3.3 - - name: Run cargo update - run: cargo update - - - name: Cache cargo dir - uses: actions/cache@v2 - with: - path: ~/.cargo - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-x86_64-unknown-linux-gnu - - - name: Master Toolchain Setup - run: bash setup-toolchain.sh + - name: Install toolchain + run: rustup show active-toolchain # Download - name: Download target dir @@ -288,16 +222,11 @@ jobs: # Run - name: Test ${{ matrix.integration }} - run: $CARGO_TARGET_DIR/debug/integration + run: | + RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \ + $CARGO_TARGET_DIR/debug/integration env: INTEGRATION: ${{ matrix.integration }} - RUSTUP_TOOLCHAIN: master - - # Cleanup - - name: Run cargo-cache --autoclean - run: | - cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache - cargo cache # These jobs doesn't actually test anything, but they're only used to tell # bors the build completed, as there is no practical way to detect when a diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 5ee157cf23b8..95da775b7bc3 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -22,6 +22,12 @@ jobs: steps: # Setup + - name: Checkout + uses: actions/checkout@v2.3.3 + + - name: remove toolchain file + run: rm rust-toolchain + - name: rust-toolchain uses: actions-rs/toolchain@v1.0.6 with: @@ -29,9 +35,7 @@ jobs: target: x86_64-unknown-linux-gnu profile: minimal components: rustfmt - - - name: Checkout - uses: actions/checkout@v2.3.3 + default: true # Run - name: Build diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index c7e02aaf4e18..af3b1c1db2ae 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -2006,6 +2006,7 @@ Released 2018-09-13 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr [`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string @@ -2024,6 +2025,7 @@ Released 2018-09-13 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching @@ -2169,5 +2171,6 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index f8c26e2d456d..4cfeaa153a01 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -19,10 +19,10 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Writing code](#writing-code) - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) - [How Clippy works](#how-clippy-works) - - [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust) + - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust) - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos) - - [Performing the sync](#performing-the-sync) - - [Syncing back changes in Clippy to [`rust-lang/rust`]](#syncing-back-changes-in-clippy-to-rust-langrust) + - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy) + - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust) - [Defining remotes](#defining-remotes) - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) @@ -49,7 +49,7 @@ first read the [Basics docs](doc/basics.md).** All issues on Clippy are mentored, if you want help with a bug just ask @Manishearth, @flip1995, @phansch or @yaahc. -Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues. +Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy issues. If you want to work on an issue, please leave a comment so that we can assign it to you! There are also some abandoned PRs, marked with [`S-inactive-closed`]. @@ -68,16 +68,16 @@ To figure out how this syntax structure is encoded in the AST, it is recommended Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. -[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`] +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. -Note that [`E-medium`] issues may require some knowledge of Clippy internals or some -debugging to find the actual problem behind the issue. +Note that [`E-medium`] issues may require some knowledge of Clippy internals or some +debugging to find the actual problem behind the issue. [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful. -[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue +[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed [`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST [`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle @@ -111,7 +111,7 @@ To work around this, you need to have a copy of the [rustc-repo][rustc_repo] ava `git clone https://github.com/rust-lang/rust/`. Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies which rust-analyzer will be able to understand. -Run `cargo dev ra-setup --repo-path ` where `` is an absolute path to the rustc repo +Run `cargo dev ra_setup --repo-path ` where `` is an absolute path to the rustc repo you just cloned. The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses. @@ -182,18 +182,26 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun [early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html [late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html -## Fixing build failures caused by Rust +## Syncing changes between Clippy and [`rust-lang/rust`] -Clippy currently gets built with `rustc` of the `rust-lang/rust` `master` -branch. Most of the times we have to adapt to the changes and only very rarely -there's an actual bug in Rust. +Clippy currently gets built with a pinned nightly version. -If you decide to make Clippy work again with a Rust commit that breaks it, you -have to sync the `rust-lang/rust-clippy` repository with the `subtree` copy of -Clippy in the `rust-lang/rust` repository. +In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy +that compiler hackers modify from time to time to adapt to changes in the unstable +API of the compiler. -For general information about `subtree`s in the Rust repository see [Rust's -`CONTRIBUTING.md`][subtree]. +We need to sync these changes back to this repository periodically, and the changes +made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository. + +To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done +in a bi-weekly basis if there's no urgent changes. This is done starting on the day of +the Rust stable release and then every other week. That way we guarantee that we keep +this repo up to date with the latest compiler API, and every feature in Clippy is available +for 2 weeks in nightly, before it can get to beta. For reference, the first sync +following this cadence was performed the 2020-08-27. + +This process is described in detail in the following sections. For general information +about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. ### Patching git-subtree to work with big repos @@ -222,13 +230,14 @@ This shell has a hardcoded recursion limit set to 1000. In order to make this pr you need to force the script to run `bash` instead. You can do this by editing the first line of the `git-subtree` script and changing `sh` to `bash`. -### Performing the sync +### Performing the sync from [`rust-lang/rust`] to Clippy Here is a TL;DR version of the sync process (all of the following commands have to be run inside the `rust` directory): -1. Clone the [`rust-lang/rust`] repository -2. Sync the changes to the rust-copy of Clippy to your Clippy fork: +1. Clone the [`rust-lang/rust`] repository or make sure it is up to date. +2. Checkout the commit from the latest available nightly. You can get it using `rustup check`. +3. Sync the changes to the rust-copy of Clippy to your Clippy fork: ```bash # Make sure to change `your-github-name` to your github name in the following command git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust @@ -246,17 +255,11 @@ to be run inside the `rust` directory): git checkout sync-from-rust git merge upstream/master ``` -3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to +4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ~~annoy~~ ask them in the [Zulip] stream.) - -### Syncing back changes in Clippy to [`rust-lang/rust`] -To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back -in a bi-weekly basis if there's no urgent changes. This is done starting on the day of -the Rust stable release and then every other week. That way we guarantee that -every feature in Clippy is available for 2 weeks in nightly, before it can get to beta. -For reference, the first sync following this cadence was performed the 2020-08-27. +### Performing the sync from Clippy to [`rust-lang/rust`] All of the following commands have to be run inside the `rust` directory. diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index fddf0614a0b8..aaa55e11c7db 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -82,6 +82,22 @@ Note that this is still experimental and only supported on the nightly channel: cargo clippy --fix -Z unstable-options ``` +#### Workspaces + +All the usual workspace options should work with Clippy. For example the following command +will run Clippy on the `example` crate: + +```terminal +cargo clippy -p example +``` + +As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies. +If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this: + +```terminal +cargo clippy -p example -- --no-deps +``` + ### Running Clippy from the command line without installing it To have cargo compile your crate with Clippy without Clippy installation diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs new file mode 100644 index 000000000000..645098e4cfcd --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/bless.rs @@ -0,0 +1,74 @@ +//! `bless` updates the reference files in the repo with changed output files +//! from the last test run. + +use std::env; +use std::ffi::OsStr; +use std::fs; +use std::lazy::SyncLazy; +use std::path::PathBuf; +use walkdir::WalkDir; + +use crate::clippy_project_root; + +// NOTE: this is duplicated with tests/cargo/mod.rs What to do? +pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") { + Some(v) => v.into(), + None => env::current_dir().unwrap().join("target"), +}); + +pub fn bless() { + let test_dirs = [ + clippy_project_root().join("tests").join("ui"), + clippy_project_root().join("tests").join("ui-toml"), + clippy_project_root().join("tests").join("ui-cargo"), + ]; + for test_dir in &test_dirs { + WalkDir::new(test_dir) + .into_iter() + .filter_map(Result::ok) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .for_each(|f| { + update_reference_file(f.path().with_extension("stdout")); + update_reference_file(f.path().with_extension("stderr")); + update_reference_file(f.path().with_extension("fixed")); + }); + } +} + +fn update_reference_file(reference_file_path: PathBuf) { + let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap())); + let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap(); + + // If compiletest did not write any changes during the test run, + // we don't have to update anything + if !test_output_path.exists() { + return; + } + + let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file"); + let reference_file = fs::read(&reference_file_path).unwrap_or_default(); + + if test_output_file != reference_file { + // If a test run caused an output file to change, update the reference file + println!("updating {}", &relative_reference_file_path.display()); + fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file"); + + // We need to re-read the file now because it was potentially updated from copying + let reference_file = fs::read(&reference_file_path).unwrap_or_default(); + + if reference_file.is_empty() { + // If we copied over an empty output file, we remove the now empty reference file + println!("removing {}", &relative_reference_file_path.display()); + fs::remove_file(reference_file_path).expect("Could not remove reference file"); + } + } +} + +fn build_dir() -> PathBuf { + let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); + let mut path = PathBuf::new(); + path.push(CARGO_TARGET_DIR.clone()); + path.push(profile); + path.push("test_build_base"); + path +} diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 6ae3f58c1f2a..6b528d219df2 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,9 +1,9 @@ use crate::clippy_project_root; use shell_escape::escape; use std::ffi::OsStr; -use std::io; use std::path::Path; use std::process::{self, Command}; +use std::{fs, io}; use walkdir::WalkDir; #[derive(Debug)] @@ -12,6 +12,7 @@ pub enum CliError { IoError(io::Error), RustfmtNotInstalled, WalkDirError(walkdir::Error), + RaSetupActive, } impl From for CliError { @@ -31,12 +32,23 @@ struct FmtContext { verbose: bool, } +// the "main" function of cargo dev fmt pub fn run(check: bool, verbose: bool) { fn try_run(context: &FmtContext) -> Result { let mut success = true; let project_root = clippy_project_root(); + // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to + // format because rustfmt would also format the entire rustc repo as it is a local + // dependency + if fs::read_to_string(project_root.join("Cargo.toml")) + .expect("Failed to read clippy Cargo.toml") + .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]") + { + return Err(CliError::RaSetupActive); + } + rustfmt_test(context)?; success &= cargo_fmt(context, project_root.as_path())?; @@ -75,6 +87,13 @@ pub fn run(check: bool, verbose: bool) { CliError::WalkDirError(err) => { eprintln!("error: {}", err); }, + CliError::RaSetupActive => { + eprintln!( + "error: a local rustc repo is enabled as path dependency via `cargo dev ra_setup`. +Not formatting because that would format the local repo as well! +Please revert the changes to Cargo.tomls first." + ); + }, } } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index f51c45e9eb59..17cc08ee10fe 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -10,6 +10,7 @@ use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use walkdir::WalkDir; +pub mod bless; pub mod fmt; pub mod new_lint; pub mod ra_setup; diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 7a8cbd5251da..4fdae38e3ab7 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -1,10 +1,53 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] -use clap::{App, Arg, SubCommand}; -use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; +use clap::{App, Arg, ArgMatches, SubCommand}; +use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; fn main() { - let matches = App::new("Clippy developer tooling") + let matches = get_clap_config(); + + match matches.subcommand() { + ("bless", Some(_)) => { + bless::bless(); + }, + ("fmt", Some(matches)) => { + fmt::run(matches.is_present("check"), matches.is_present("verbose")); + }, + ("update_lints", Some(matches)) => { + if matches.is_present("print-only") { + update_lints::print_lints(); + } else if matches.is_present("check") { + update_lints::run(update_lints::UpdateMode::Check); + } else { + update_lints::run(update_lints::UpdateMode::Change); + } + }, + ("new_lint", Some(matches)) => { + match new_lint::create( + matches.value_of("pass"), + matches.value_of("name"), + matches.value_of("category"), + ) { + Ok(_) => update_lints::run(update_lints::UpdateMode::Change), + Err(e) => eprintln!("Unable to create lint: {}", e), + } + }, + ("limit_stderr_length", _) => { + stderr_length_check::check(); + }, + ("ra_setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), + ("serve", Some(matches)) => { + let port = matches.value_of("port").unwrap().parse().unwrap(); + let lint = matches.value_of("lint"); + serve::run(port, lint); + }, + _ => {}, + } +} + +fn get_clap_config<'a>() -> ArgMatches<'a> { + App::new("Clippy developer tooling") + .subcommand(SubCommand::with_name("bless").about("bless the test output changes")) .subcommand( SubCommand::with_name("fmt") .about("Run rustfmt on all projects and tests") @@ -25,16 +68,16 @@ fn main() { .about("Updates lint registration and information from the source code") .long_about( "Makes sure that:\n \ - * the lint count in README.md is correct\n \ - * the changelog contains markdown link references at the bottom\n \ - * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ - * all lints are registered in the lint store", + * the lint count in README.md is correct\n \ + * the changelog contains markdown link references at the bottom\n \ + * all lint groups include the correct lints\n \ + * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \ + * all lints are registered in the lint store", ) .arg(Arg::with_name("print-only").long("print-only").help( "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", + This does not include deprecated and internal lints. \ + (Does not modify any files)", )) .arg( Arg::with_name("check") @@ -88,7 +131,7 @@ fn main() { .about("Ensures that stderr files do not grow longer than a certain amount of lines."), ) .subcommand( - SubCommand::with_name("ra-setup") + SubCommand::with_name("ra_setup") .about("Alter dependencies so rust-analyzer can find rustc internals") .arg( Arg::with_name("rustc-repo-path") @@ -113,40 +156,5 @@ fn main() { ) .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")), ) - .get_matches(); - - match matches.subcommand() { - ("fmt", Some(matches)) => { - fmt::run(matches.is_present("check"), matches.is_present("verbose")); - }, - ("update_lints", Some(matches)) => { - if matches.is_present("print-only") { - update_lints::print_lints(); - } else if matches.is_present("check") { - update_lints::run(update_lints::UpdateMode::Check); - } else { - update_lints::run(update_lints::UpdateMode::Change); - } - }, - ("new_lint", Some(matches)) => { - match new_lint::create( - matches.value_of("pass"), - matches.value_of("name"), - matches.value_of("category"), - ) { - Ok(_) => update_lints::run(update_lints::UpdateMode::Change), - Err(e) => eprintln!("Unable to create lint: {}", e), - } - }, - ("limit_stderr_length", _) => { - stderr_length_check::check(); - }, - ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), - ("serve", Some(matches)) => { - let port = matches.value_of("port").unwrap().parse().unwrap(); - let lint = matches.value_of("lint"); - serve::run(port, lint); - }, - _ => {}, - } + .get_matches() } diff --git a/src/tools/clippy/clippy_dev/src/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs index 9d9e836cc08a..40bf4a9505a8 100644 --- a/src/tools/clippy/clippy_dev/src/ra_setup.rs +++ b/src/tools/clippy/clippy_dev/src/ra_setup.rs @@ -52,7 +52,7 @@ fn inject_deps_into_manifest( // do not inject deps if we have aleady done so if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { eprintln!( - "cargo dev ra-setup: warning: deps already found inside {}, doing nothing.", + "cargo dev ra_setup: warning: deps already found inside {}, doing nothing.", manifest_path ); return Ok(()); diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index 58892024ce24..f136aa4572a8 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -99,7 +99,11 @@ impl LateLintPass<'_> for AwaitHolding { }; let def_id = cx.tcx.hir().body_owner_def_id(body_id); let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + check_interior_types( + cx, + &typeck_results.generator_interior_types.as_ref().skip_binder(), + body.value.span, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 28c1a54d2c5a..54bc69e058bc 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -6,9 +6,12 @@ use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; + +const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); declare_clippy_lint! { /// **What it does:** Checks for explicit bounds checking when casting. @@ -39,10 +42,25 @@ declare_clippy_lint! { "`try_from` could replace manual bounds checking when casting" } -declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); +pub struct CheckedConversions { + msrv: Option, +} + +impl CheckedConversions { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) { + return; + } + let result = if_chain! { if !in_external_macro(cx.sess(), item.span); if let ExprKind::Binary(op, ref left, ref right) = &item.kind; @@ -74,6 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { } } } + + extract_msrv_attr!(LateContext); } /// Searches for a single check from unsigned to _ is done diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 4002fb655a5e..200b6a565cc4 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. /// - /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`. /// /// **Known problems:** None. /// diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index edecba57e44f..aba655327959 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -14,6 +14,7 @@ use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::edition::Edition; use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; use rustc_span::{sym, FileName, Pos}; use std::io; @@ -377,7 +378,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs check_doc(cx, valid_idents, events, &spans) } -const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"]; +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; fn check_doc<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, @@ -400,13 +401,24 @@ fn check_doc<'a, Events: Iterator, Range { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { - is_rust = - lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i)); + for item in lang.split(',') { + if item == "ignore" { + is_rust = false; + break; + } + if let Some(stripped) = item.strip_prefix("edition") { + is_rust = true; + edition = stripped.parse::().ok(); + } else if item.is_empty() || RUST_CODE.contains(&item) { + is_rust = true; + } + } } }, End(CodeBlock(_)) => { @@ -436,7 +448,8 @@ fn check_doc<'a, Events: Iterator, Range, Range, text: &str, span: Span) { - fn has_needless_main(code: &str) -> bool { - let filename = FileName::anon_source_code(code); +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { + fn has_needless_main(code: &str, edition: Edition) -> bool { + rustc_driver::catch_fatal_errors(|| { + rustc_span::with_session_globals(edition, || { + let filename = FileName::anon_source_code(code); - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); - let handler = Handler::with_emitter(false, None, box emitter); - let sess = ParseSess::with_span_handler(handler, sm); + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); - let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { - Ok(p) => p, - Err(errs) => { - for mut err in errs { - err.cancel(); - } - return false; - }, - }; - - let mut relevant_main_found = false; - loop { - match parser.parse_item() { - Ok(Some(item)) => match &item.kind { - // Tests with one of these items are ignored - ItemKind::Static(..) - | ItemKind::Const(..) - | ItemKind::ExternCrate(..) - | ItemKind::ForeignMod(..) => return false, - // We found a main function ... - ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => { - let is_async = matches!(sig.header.asyncness, Async::Yes{..}); - let returns_nothing = match &sig.decl.output { - FnRetTy::Default(..) => true, - FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - _ => false, - }; - - if returns_nothing && !is_async && !block.stmts.is_empty() { - // This main function should be linted, but only if there are no other functions - relevant_main_found = true; - } else { - // This main function should not be linted, we're done - return false; + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); } + return false; }, - // Another function was found; this case is ignored too - ItemKind::Fn(..) => return false, - _ => {}, - }, - Ok(None) => break, - Err(mut e) => { - e.cancel(); - return false; - }, - } - } + }; - relevant_main_found + let mut relevant_main_found = false; + loop { + match parser.parse_item() { + Ok(Some(item)) => match &item.kind { + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) => return false, + // We found a main function ... + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => { + let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + _ => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Another function was found; this case is ignored too + ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(mut e) => { + e.cancel(); + return false; + }, + } + } + + relevant_main_found + }) + }) + .ok() + .unwrap_or_default() } - if has_needless_main(text) { + if has_needless_main(text, edition) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs index 8b58d1f26013..fd93548b55c6 100644 --- a/src/tools/clippy/clippy_lints/src/functions.rs +++ b/src/tools/clippy/clippy_lints/src/functions.rs @@ -405,13 +405,10 @@ impl<'tcx> Functions { break; } if in_comment { - match line.find("*/") { - Some(i) => { - line = &line[i + 2..]; - in_comment = false; - continue; - }, - None => break, + if let Some(i) = line.find("*/") { + line = &line[i + 2..]; + in_comment = false; + continue; } } else { let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); @@ -423,8 +420,8 @@ impl<'tcx> Functions { in_comment = true; continue; } - break; } + break; } if code_in_line { line_count += 1; diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 8842901d90b8..6fe533510904 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -222,9 +222,8 @@ fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplIte let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) { if cx.access_levels.is_exported(is_empty.id.hir_id) { return; - } else { - "a private" } + "a private" } else { "no corresponding" }; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 2b99ed570b14..02ba422a2f5b 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -27,6 +27,7 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; extern crate rustc_data_structures; +extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_pretty; @@ -294,6 +295,7 @@ mod question_mark; mod ranges; mod redundant_clone; mod redundant_closure_call; +mod redundant_else; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; @@ -344,6 +346,7 @@ mod wildcard_dependencies; mod wildcard_imports; mod write; mod zero_div_zero; +mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` pub use crate::utils::conf::Conf; @@ -509,6 +512,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "internal-lints")] &utils::internal_lints::DEFAULT_LINT, #[cfg(feature = "internal-lints")] + &utils::internal_lints::INTERNING_DEFINED_SYMBOL, + #[cfg(feature = "internal-lints")] &utils::internal_lints::INVALID_PATHS, #[cfg(feature = "internal-lints")] &utils::internal_lints::LINT_WITHOUT_LINT_PASS, @@ -831,6 +836,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, &redundant_closure_call::REDUNDANT_CLOSURE_CALL, + &redundant_else::REDUNDANT_ELSE, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, @@ -934,6 +940,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &wildcard_imports::WILDCARD_IMPORTS, &write::PRINTLN_EMPTY_STRING, &write::PRINT_LITERAL, + &write::PRINT_STDERR, &write::PRINT_STDOUT, &write::PRINT_WITH_NEWLINE, &write::USE_DEBUG, @@ -941,6 +948,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &write::WRITE_LITERAL, &write::WRITE_WITH_NEWLINE, &zero_div_zero::ZERO_DIVIDED_BY_ZERO, + &zero_sized_map_values::ZERO_SIZED_MAP_VALUES, ]); // end register lints, do not remove this comment, it’s used in `update_lints` @@ -953,6 +961,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); @@ -1000,6 +1009,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); + store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)); + store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv)); + store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); + store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); + store.register_late_pass(move || box ranges::Ranges::new(msrv)); + store.register_late_pass(move || box use_self::UseSelf::new(msrv)); + store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); @@ -1010,7 +1027,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box main_recursion::MainRecursion::default()); store.register_late_pass(|| box lifetimes::Lifetimes); store.register_late_pass(|| box entry::HashMapPass); - store.register_late_pass(|| box ranges::Ranges); store.register_late_pass(|| box types::Casts); let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); @@ -1055,7 +1071,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box neg_multiply::NegMultiply); store.register_late_pass(|| box mem_discriminant::MemDiscriminant); store.register_late_pass(|| box mem_forget::MemForget); - store.register_late_pass(|| box mem_replace::MemReplace); store.register_late_pass(|| box arithmetic::Arithmetic::default()); store.register_late_pass(|| box assign_ops::AssignOps); store.register_late_pass(|| box let_if_seq::LetIfSeq); @@ -1077,7 +1092,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box pass_by_ref_or_value); store.register_late_pass(|| box ref_option_ref::RefOptionRef); store.register_late_pass(|| box try_err::TryErr); - store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); @@ -1103,10 +1117,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); - store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); store.register_late_pass(|| box transmuting_null::TransmutingNull); store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); - store.register_late_pass(|| box checked_conversions::CheckedConversions); store.register_late_pass(|| box integer_division::IntegerDivision); store.register_late_pass(|| box inherent_to_string::InherentToString); let max_trait_bounds = conf.max_trait_bounds; @@ -1132,9 +1144,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box redundant_else::RedundantElse); store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); - store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); @@ -1174,7 +1186,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); - store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; @@ -1200,6 +1211,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); + store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1247,6 +1259,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::RC_BUFFER), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), + LintId::of(&write::PRINT_STDERR), LintId::of(&write::PRINT_STDOUT), LintId::of(&write::USE_DEBUG), ]); @@ -1308,6 +1321,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE), + LintId::of(&redundant_else::REDUNDANT_ELSE), LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), @@ -1330,6 +1344,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unused_self::UNUSED_SELF), LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), + LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES), ]); #[cfg(feature = "internal-lints")] @@ -1338,6 +1353,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), + LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 4d737b3f49b0..e84c8b4e5b3e 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -501,7 +501,7 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - if lifetime.name.ident().name != kw::Invalid && lifetime.name.ident().name != kw::StaticLifetime { + if lifetime.name.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime { self.lifetimes_used_in_body = true; } } diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs index c99d2e35b94a..b97d97ea1a5e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs @@ -8,6 +8,7 @@ use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// **What it does:** @@ -51,7 +52,7 @@ impl LateLintPass<'_> for ManualOkOr { if args.len() == 3; let method_receiver = &args[0]; let ty = cx.typeck_results().expr_ty(method_receiver); - if is_type_diagnostic_item(cx, ty, sym!(option_type)); + if is_type_diagnostic_item(cx, ty, sym::option_type); let or_expr = &args[1]; if is_ok_wrapping(cx, &args[2]); if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index 274d20cfa800..04b35835c6b8 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -4,8 +4,8 @@ use crate::utils::usage::is_unused; use crate::utils::{ expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, - snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, + snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, + span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -689,10 +689,9 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { // single statement/expr "else" block, don't lint return; - } else { - // block with 2+ statements or 1 expr and 1+ statement - Some(els) } + // block with 2+ statements or 1 expr and 1+ statement + Some(els) } else { // not a block, don't lint return; @@ -1238,6 +1237,24 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; } + + // HACK: + // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here + // to prevent false positives as there is currently no better way to detect if code was excluded by + // a macro. See PR #6435 + if_chain! { + if let Some(match_snippet) = snippet_opt(cx, expr.span); + if let Some(arm_snippet) = snippet_opt(cx, arms[0].span); + if let Some(ex_snippet) = snippet_opt(cx, ex.span); + let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, ""); + if rest_snippet.contains("=>"); + then { + // The code it self contains another thick arrow "=>" + // -> Either another arm or a comment + return; + } + } + let matched_vars = ex.span; let bind_names = arms[0].pat.span; let match_body = remove_blocks(&arms[0].body); diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index bb0acecc5a92..19087b020771 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -1,13 +1,14 @@ use crate::utils::{ - in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help, + in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; @@ -94,7 +95,7 @@ declare_clippy_lint! { "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`" } -declare_lint_pass!(MemReplace => +impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { @@ -224,6 +225,19 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } } +const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); + +pub struct MemReplace { + msrv: Option, +} + +impl MemReplace { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { @@ -236,8 +250,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - check_replace_with_default(cx, src, dest, expr.span); + if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) { + check_replace_with_default(cx, src, dest, expr.span); + } } } } + extract_msrv_attr!(LateContext); } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 8002c27a5e91..e99fe1b97ff6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -22,6 +22,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; @@ -1487,7 +1488,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { - if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { + if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) { unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"); } }, @@ -1509,7 +1510,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), - ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]), + ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -1568,7 +1569,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); - if args.len() == 1 && method_call.ident.name == sym!(clone) { + if args.len() == 1 && method_call.ident.name == sym::clone { lint_clone_on_copy(cx, expr, &args[0], self_ty); lint_clone_on_ref_ptr(cx, expr, &args[0]); } @@ -1592,7 +1593,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } }, - ty::Ref(..) if method_call.ident.name == sym!(into_iter) => { + ty::Ref(..) if method_call.ident.name == sym::into_iter => { lint_into_iter(cx, expr, self_ty, *method_span); }, _ => (), @@ -1623,10 +1624,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let item = cx.tcx.hir().expect_item(parent); let def_id = cx.tcx.hir().local_def_id(item.hir_id); let self_ty = cx.tcx.type_of(def_id); + + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); - if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind; let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); let method_sig = cx.tcx.fn_sig(method_def_id); @@ -1668,40 +1674,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - if let Some((ref conv, self_kinds)) = &CONVENTIONS - .iter() - .find(|(ref conv, _)| conv.check(&name)) - { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - let lint = if item.vis.node.is_pub() { - WRONG_PUB_SELF_CONVENTION - } else { - WRONG_SELF_CONVENTION - }; - - span_lint( - cx, - lint, - first_arg.pat.span, - &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") - ), - ); - } - } + lint_wrong_self_convention( + cx, + &name, + item.vis.node.is_pub(), + self_ty, + first_arg_ty, + first_arg.pat.span + ); } } - // if this impl block implements a trait, lint in trait definition instead - if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { - return; - } - if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); @@ -1735,8 +1718,23 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } + + if_chain! { + if let TraitItemKind::Fn(ref sig, _) = item.kind; + if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); + let first_arg_span = first_arg_ty.span; + let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); + let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); + + then { + lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span); + } + } + if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id); @@ -1757,6 +1755,39 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } +fn lint_wrong_self_convention<'tcx>( + cx: &LateContext<'tcx>, + item_name: &str, + is_pub: bool, + self_ty: &'tcx TyS<'tcx>, + first_arg_ty: &'tcx TyS<'tcx>, + first_arg_span: Span, +) { + let lint = if is_pub { + WRONG_PUB_SELF_CONVENTION + } else { + WRONG_SELF_CONVENTION + }; + if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + span_lint( + cx, + lint, + first_arg_span, + &format!( + "methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } +} + /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] fn lint_or_fun_call<'tcx>( @@ -2100,8 +2131,11 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp cx, CLONE_DOUBLE_REF, expr.span, - "using `clone` on a double-reference; \ - this will copy the reference instead of cloning the inner type", + &format!( + "using `clone` on a double-reference; \ + this will copy the reference of type `{}` instead of cloning the inner type", + ty + ), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; @@ -2174,11 +2208,17 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp } else { snip = None; } - span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| { - if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); - } - }); + span_lint_and_then( + cx, + CLONE_ON_COPY, + expr.span, + &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + |diag| { + if let Some((text, snip)) = snip { + diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); + } + }, + ); } } @@ -2638,9 +2678,9 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) { let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs(); - let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { Some((EXPECT_USED, "an Option", "None")) - } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { + } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { Some((EXPECT_USED, "a Result", "Err")) } else { None @@ -2733,6 +2773,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } +const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); + /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s /// Return true if lint triggered fn lint_map_unwrap_or_else<'tcx>( @@ -2740,7 +2782,11 @@ fn lint_map_unwrap_or_else<'tcx>( expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, ) -> bool { + if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { + return false; + } // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); @@ -2923,9 +2969,20 @@ fn lint_filter_map<'tcx>( } } +const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); + /// lint use of `filter_map().next()` for `Iterators` -fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { +fn lint_filter_map_next<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, +) { if match_trait_method(cx, expr, &paths::ITERATOR) { + if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { + return; + } + let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); @@ -3116,7 +3173,7 @@ fn lint_search_is_some<'tcx>( else if search_method == "find" { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + if is_type_diagnostic_item(cx, self_ty, sym::string_type) { true } else { *self_ty.kind() == ty::Str diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index 75e123eb5939..d082a88cd2db 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -69,10 +69,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } } return (true, false); - } else { - // We don't know. It might do anything. - return (true, true); } + // We don't know. It might do anything. + return (true, true); } } (true, true) diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 25245b3dbf08..6ebeaced62a3 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -1,14 +1,19 @@ use crate::utils::qualify_min_const_fn::is_min_const_fn; -use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; +use crate::utils::{ + fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method, +}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; +const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); + declare_clippy_lint! { /// **What it does:** /// @@ -69,7 +74,18 @@ declare_clippy_lint! { "Lint functions definitions that could be made `const fn`" } -declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); +impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); + +pub struct MissingConstForFn { + msrv: Option, +} + +impl MissingConstForFn { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { fn check_fn( @@ -81,6 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { + if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) { + return; + } + let def_id = cx.tcx.hir().local_def_id(hir_id); if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) { @@ -99,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let has_const_generic_params = generics .params .iter() - .any(|param| matches!(param.kind, GenericParamKind::Const{ .. })); + .any(|param| matches!(param.kind, GenericParamKind::Const { .. })); if already_const(header) || has_const_generic_params { return; @@ -126,6 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); } } + extract_msrv_attr!(LateContext); } /// Returns true if any of the method parameters is a type that implements `Drop`. The method diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 4678f6872f37..27f1074a0dd8 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -2,7 +2,7 @@ // *rustc*'s // [`missing_doc`]. // -// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246 +// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // use crate::utils::span_lint; @@ -70,7 +70,14 @@ impl MissingDoc { } } - fn check_missing_docs_attrs(&self, cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { + fn check_missing_docs_attrs( + &self, + cx: &LateContext<'_>, + attrs: &[ast::Attribute], + sp: Span, + article: &'static str, + desc: &'static str, + ) { // If we're building a test harness, then warning about // documentation is probably not really relevant right now. if cx.sess().opts.test { @@ -94,7 +101,7 @@ impl MissingDoc { cx, MISSING_DOCS_IN_PRIVATE_ITEMS, sp, - &format!("missing documentation for {}", desc), + &format!("missing documentation for {} {}", article, desc), ); } } @@ -120,13 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate"); + self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "the", "crate"); } fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - let desc = match it.kind { - hir::ItemKind::Const(..) => "a constant", - hir::ItemKind::Enum(..) => "an enum", + match it.kind { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym::main { @@ -136,16 +141,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { return; } } - "a function" }, - hir::ItemKind::Mod(..) => "a module", - hir::ItemKind::Static(..) => "a static", - hir::ItemKind::Struct(..) => "a struct", - hir::ItemKind::Trait(..) => "a trait", - hir::ItemKind::TraitAlias(..) => "a trait alias", - hir::ItemKind::TyAlias(..) => "a type alias", - hir::ItemKind::Union(..) => "a union", - hir::ItemKind::OpaqueTy(..) => "an existential type", + hir::ItemKind::Const(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Mod(..) + | hir::ItemKind::Static(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::OpaqueTy(..) => {}, hir::ItemKind::ExternCrate(..) | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::GlobalAsm(..) @@ -153,17 +159,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::Use(..) => return, }; - self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc); + let def_id = cx.tcx.hir().local_def_id(it.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + + self.check_missing_docs_attrs(cx, &it.attrs, it.span, article, desc); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { - let desc = match trait_item.kind { - hir::TraitItemKind::Const(..) => "an associated constant", - hir::TraitItemKind::Fn(..) => "a trait method", - hir::TraitItemKind::Type(..) => "an associated type", - }; + let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); - self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc); + self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, article, desc); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { @@ -178,21 +184,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { }, } - let desc = match impl_item.kind { - hir::ImplItemKind::Const(..) => "an associated constant", - hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::TyAlias(_) => "an associated type", - }; - self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, article, desc); } fn check_struct_field(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::StructField<'_>) { if !sf.is_positional() { - self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field"); + self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a", "struct field"); } } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant"); + self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a", "variant"); } } diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs index 405c21d608d9..bff53eb8ccad 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrow.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { return; } if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { kind: Adjust::Deref(_), .. @@ -62,8 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { cx, NEEDLESS_BORROW, e.span, - "this expression borrows a reference that is immediately dereferenced \ + &format!( + "this expression borrows a reference (`&{}`) that is immediately dereferenced \ by the compiler", + ty + ), |diag| { if let Some(snippet) = snippet_opt(cx, inner.span) { diag.span_suggestion( diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 532c0266946b..5043b7b1fc3c 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -90,9 +90,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Exclude non-inherent impls if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs index 98e9078094a2..41cf541ecf5e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_update.rs +++ b/src/tools/clippy/clippy_lints/src/needless_update.rs @@ -8,6 +8,9 @@ declare_clippy_lint! { /// **What it does:** Checks for needlessly including a base struct on update /// when all fields are changed anyway. /// + /// This lint is not applied to structs marked with + /// [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html). + /// /// **Why is this bad?** This will cost resources (because the base has to be /// somewhere), and make the code less readable. /// @@ -49,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { - if fields.len() == def.non_enum_variant().fields.len() { + if fields.len() == def.non_enum_variant().fields.len() + && !def.variants[0_usize.into()].is_field_list_non_exhaustive() + { span_lint( cx, NEEDLESS_UPDATE, diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 5b42b61fcde9..446426b3e611 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -409,11 +409,10 @@ fn levenstein_not_1(a_name: &str, b_name: &str) -> bool { if let Some(b2) = b_chars.next() { // check if there's just one character inserted return a != b2 || a_chars.ne(b_chars); - } else { - // tuple - // ntuple - return true; } + // tuple + // ntuple + return true; } // for item in items true diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index 72dfccc1089e..37e2b50def17 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -1,18 +1,16 @@ -use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then}; use rustc_hir as hir; -use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::Expr; +use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; declare_clippy_lint! { - /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. + /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result. /// - /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided. /// - /// **Known problems:** None. + /// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked. /// /// **Example:** /// @@ -22,9 +20,15 @@ declare_clippy_lint! { /// panic!("error"); /// } /// ``` + /// Use instead: + /// ```rust + /// fn result_without_panic() -> Result { + /// Err(String::from("error")) + /// } + /// ``` pub PANIC_IN_RESULT_FN, restriction, - "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " + "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion" } declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); @@ -47,43 +51,33 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } } -struct FindPanicUnimplementedUnreachable { - result: Vec, -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if ["unimplemented", "unreachable", "panic", "todo"] - .iter() - .any(|fun| is_expn_of(expr.span, fun).is_some()) - { - self.result.push(expr.span); - } - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; - panics.visit_expr(&body.value); - if !panics.result.is_empty() { + let panics = find_macro_calls( + &[ + "unimplemented", + "unreachable", + "panic", + "todo", + "assert", + "assert_eq", + "assert_ne", + "debug_assert", + "debug_assert_eq", + "debug_assert_ne", + ], + body, + ); + if !panics.is_empty() { span_lint_and_then( cx, PANIC_IN_RESULT_FN, impl_span, - "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", + "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`", move |diag| { diag.help( - "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", ); - diag.span_note(panics.result, "return Err() instead of panicking"); + diag.span_note(panics, "return Err() instead of panicking"); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index f03facc235e2..6a17d654ac94 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -244,9 +244,10 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { // Exclude non-inherent impls if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 4b514bbd42ca..3e454eecd970 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -3,9 +3,10 @@ use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::sym; use rustc_span::symbol::Ident; @@ -13,8 +14,8 @@ use std::cmp::Ordering; use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability, - span_lint, span_lint_and_sugg, span_lint_and_then, + get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, + snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{higher, SpanlessEq}; @@ -160,7 +161,20 @@ declare_clippy_lint! { "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" } -declare_lint_pass!(Ranges => [ +const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0); + +pub struct Ranges { + msrv: Option, +} + +impl Ranges { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, RANGE_MINUS_ONE, @@ -175,7 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, ref l, ref r) => { - check_possible_range_contains(cx, op.node, l, r, expr.span); + if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { + check_possible_range_contains(cx, op.node, l, r, expr); + } }, _ => {}, } @@ -184,9 +200,15 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_inclusive_range_minus_one(cx, expr); check_reversed_empty_range(cx, expr); } + extract_msrv_attr!(LateContext); } -fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { +fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) { + if in_constant(cx, expr.hir_id) { + return; + } + + let span = expr.span; let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index f0e507105a6a..06adbb523d70 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -390,7 +390,10 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { let local = place.local; if local == self.used.0 - && !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)) + && !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) { self.used.1 = true; } diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs new file mode 100644 index 000000000000..3d585cd27a3d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -0,0 +1,135 @@ +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_ast::visit::{walk_expr, Visitor}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `else` blocks that can be removed without changing semantics. + /// + /// **Why is this bad?** The `else` block adds unnecessary indentation and verbosity. + /// + /// **Known problems:** Some may prefer to keep the `else` block for clarity. + /// + /// **Example:** + /// + /// ```rust + /// fn my_func(count: u32) { + /// if count == 0 { + /// print!("Nothing to do"); + /// return; + /// } else { + /// print!("Moving on..."); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn my_func(count: u32) { + /// if count == 0 { + /// print!("Nothing to do"); + /// return; + /// } + /// print!("Moving on..."); + /// } + /// ``` + pub REDUNDANT_ELSE, + pedantic, + "`else` branch that can be removed without changing semantics" +} + +declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]); + +impl EarlyLintPass for RedundantElse { + fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) { + if in_external_macro(cx.sess, stmt.span) { + return; + } + // Only look at expressions that are a whole statement + let expr: &Expr = match &stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, + _ => return, + }; + // if else + let (mut then, mut els): (&Block, &Expr) = match &expr.kind { + ExprKind::If(_, then, Some(els)) => (then, els), + _ => return, + }; + loop { + if !BreakVisitor::default().check_block(then) { + // then block does not always break + return; + } + match &els.kind { + // else if else + ExprKind::If(_, next_then, Some(next_els)) => { + then = next_then; + els = next_els; + continue; + }, + // else if without else + ExprKind::If(..) => return, + // done + _ => break, + } + } + span_lint_and_help( + cx, + REDUNDANT_ELSE, + els.span, + "redundant else block", + None, + "remove the `else` block and move the contents out", + ); + } +} + +/// Call `check` functions to check if an expression always breaks control flow +#[derive(Default)] +struct BreakVisitor { + is_break: bool, +} + +impl<'ast> Visitor<'ast> for BreakVisitor { + fn visit_block(&mut self, block: &'ast Block) { + self.is_break = match block.stmts.as_slice() { + [.., last] => self.check_stmt(last), + _ => false, + }; + } + + fn visit_expr(&mut self, expr: &'ast Expr) { + self.is_break = match expr.kind { + ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true, + ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)), + ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els), + ExprKind::If(_, _, None) + // ignore loops for simplicity + | ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false, + _ => { + walk_expr(self, expr); + return; + }, + }; + } +} + +impl BreakVisitor { + fn check(&mut self, item: T, visit: fn(&mut Self, T)) -> bool { + visit(self, item); + std::mem::replace(&mut self.is_break, false) + } + + fn check_block(&mut self, block: &Block) -> bool { + self.check(block, Self::visit_block) + } + + fn check_expr(&mut self, expr: &Expr) -> bool { + self.check(expr, Self::visit_expr) + } + + fn check_stmt(&mut self, stmt: &Stmt) -> bool { + self.check(stmt, Self::visit_stmt) + } +} diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs index 2a81170e49e7..38dcf7a192c8 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs @@ -1,9 +1,12 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{meets_msrv, span_lint_and_sugg}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); declare_clippy_lint! { /// **What it does:** Checks for fields in struct literals where shorthands @@ -33,10 +36,25 @@ declare_clippy_lint! { "checks for fields in struct literals where shorthands could be used" } -declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); +pub struct RedundantFieldNames { + msrv: Option, +} + +impl RedundantFieldNames { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) { + return; + } + if in_external_macro(cx.sess, expr.span) { return; } @@ -64,4 +82,5 @@ impl EarlyLintPass for RedundantFieldNames { } } } + extract_msrv_attr!(EarlyContext); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs index 7bbcc67aa2dd..fcfa3c12755a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,8 +1,11 @@ -use crate::utils::{snippet, span_lint_and_then}; +use crate::utils::{meets_msrv, snippet, span_lint_and_then}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); declare_clippy_lint! { /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime. @@ -29,7 +32,18 @@ declare_clippy_lint! { "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them." } -declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); +pub struct RedundantStaticLifetimes { + msrv: Option, +} + +impl RedundantStaticLifetimes { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); impl RedundantStaticLifetimes { // Recursively visit types @@ -84,6 +98,10 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) { + return; + } + if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); @@ -96,4 +114,6 @@ impl EarlyLintPass for RedundantStaticLifetimes { } } } + + extract_msrv_attr!(EarlyContext); } diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs index a914a77d48b4..803ebada54b7 100644 --- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs +++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs @@ -2,6 +2,7 @@ use crate::utils::{last_path_segment, snippet, span_lint_and_sugg}; use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; use if_chain::if_chain; use rustc_errors::Applicability; @@ -41,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { if let Some(res) = last.res; if let Some(def_id) = res.opt_def_id(); - if cx.tcx.is_diagnostic_item(sym!(option_type), def_id); + if cx.tcx.is_diagnostic_item(sym::option_type, def_id); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 225fe58906f7..f83965926782 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -342,6 +342,10 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut if let Some(ref guard) = arm.guard { match guard { Guard::If(if_expr) => check_expr(cx, if_expr, bindings), + Guard::IfLet(guard_pat, guard_expr) => { + check_pat(cx, guard_pat, Some(*guard_expr), guard_pat.span, bindings); + check_expr(cx, guard_expr, bindings); + }, } } check_expr(cx, &arm.body, bindings); diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 77e790733789..31dd5965473d 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -372,7 +372,7 @@ impl LateLintPass<'_> for StringToString { if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; if path.ident.name == sym!(to_string); let ty = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym!(string_type)); + if is_type_diagnostic_item(cx, ty, sym::string_type); then { span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs index 74ba53e6a9a0..fd74783335d5 100644 --- a/src/tools/clippy/clippy_lints/src/types.rs +++ b/src/tools/clippy/clippy_lints/src/types.rs @@ -1104,7 +1104,9 @@ fn is_empty_block(expr: &Expr<'_>) -> bool { expr.kind, ExprKind::Block( Block { - stmts: &[], expr: None, .. + stmts: &[], + expr: None, + .. }, _, ) diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 5d801511a0b1..5b9a80f92db6 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -9,6 +9,7 @@ use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; use rustc_span::Span; declare_clippy_lint! { @@ -74,14 +75,17 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { + if matches!( + item.kind, + ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..) + ) { return; } } - let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) { ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { ("Result", &paths::RESULT_OK) } else { return; diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 5ac4797680bc..3b23f885e08d 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -12,11 +12,12 @@ use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_middle::ty::{DefIdTree, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::kw; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{differing_macro_contexts, span_lint_and_sugg}; +use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for unnecessary repetition of structure name when a @@ -53,7 +54,7 @@ declare_clippy_lint! { "unnecessary structure name repetition whereas `Self` is applicable" } -declare_lint_pass!(UseSelf => [USE_SELF]); +impl_lint_pass!(UseSelf => [USE_SELF]); const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; @@ -157,8 +158,25 @@ fn check_trait_method_impl_decl<'tcx>( } } +const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); + +pub struct UseSelf { + msrv: Option, +} + +impl UseSelf { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + return; + } + if in_external_macro(cx.sess(), item.span) { return; } @@ -204,6 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } } + extract_msrv_attr!(LateContext); } struct UseSelfVisitor<'a, 'tcx> { diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs index 31b4e25411bd..f0267e4c7928 100644 --- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs @@ -408,7 +408,10 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { } pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { - matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))) + matches!( + (l, r), + (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) + ) } pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 7250de3a41c0..4249dbb4e651 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -372,6 +372,18 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = if_expr_pat; self.visit_expr(if_expr); }, + hir::Guard::IfLet(ref if_let_pat, ref if_let_expr) => { + let if_let_pat_pat = self.next("pat"); + let if_let_expr_pat = self.next("expr"); + println!( + " if let Guard::IfLet(ref {}, ref {}) = {};", + if_let_pat_pat, if_let_expr_pat, guard_pat + ); + self.current = if_let_expr_pat; + self.visit_expr(if_let_expr); + self.current = if_let_pat_pat; + self.visit_pat(if_let_pat); + }, } } self.current = format!("{}[{}].pat", arms_pat, i); @@ -730,6 +742,7 @@ fn desugaring_name(des: hir::MatchSource) -> String { "MatchSource::IfLetDesugar {{ contains_else_clause: {} }}", contains_else_clause ), + hir::MatchSource::IfLetGuardDesugar => "MatchSource::IfLetGuardDesugar".to_string(), hir::MatchSource::IfDesugar { contains_else_clause } => format!( "MatchSource::IfDesugar {{ contains_else_clause: {} }}", contains_else_clause diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 6403ff6dad18..32d7840a451c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports + /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs index d942d4e12b10..a8fbb2ffaf0b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs @@ -169,6 +169,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool { match (left, right) { (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r), + (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re), + _ => false, } } @@ -669,7 +671,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_guard(&mut self, g: &Guard<'_>) { match g { - Guard::If(ref expr) => { + Guard::If(ref expr) | Guard::IfLet(_, ref expr) => { self.hash_expr(expr); }, } diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs index 323d87455388..5d946e4bd495 100644 --- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs @@ -560,5 +560,10 @@ fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) { println!("{}If", ind); print_expr(cx, expr, indent + 1); }, + hir::Guard::IfLet(pat, expr) => { + println!("{}IfLet", ind); + print_pat(cx, pat, indent + 1); + print_expr(cx, expr, indent + 1); + }, } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 8b59a9541a73..9ba39f73ee88 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; @@ -247,6 +248,30 @@ declare_clippy_lint! { "invalid path" } +declare_clippy_lint! { + /// **What it does:** + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// **Why is this bad?** + /// It's faster and easier to use the symbol constant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -840,3 +865,56 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { } } } + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol value to the constant name. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item.ident.to_string()); + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + let value = Symbol::intern(&arg).as_u32(); + if let Some(symbol_const) = self.symbol_map.get(&value); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + format!("rustc_span::symbol::sym::{}", symbol_const), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 3a6b64c90e8f..424856090f26 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -41,7 +41,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind, @@ -603,6 +603,37 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { visitor.found } +struct FindMacroCalls<'a, 'b> { + names: &'a [&'b str], + result: Vec, +} + +impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) { + self.result.push(expr.span); + } + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Finds calls of the specified macros in a function body. +pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec { + let mut fmc = FindMacroCalls { + names, + result: Vec::new(), + }; + fmc.visit_expr(&body.value); + fmc.result +} + /// Converts a span to a code snippet if available, otherwise use default. /// /// This is useful if you want to provide suggestions for your lint or more generally, if you want @@ -1449,8 +1480,8 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { false }, ty::Dynamic(binder, _) => { - for predicate in binder.skip_binder().iter() { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate { + for predicate in binder.iter() { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() { return true; } @@ -1500,7 +1531,7 @@ pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. }) + matches!(item.kind, ItemKind::Impl { of_trait: Some(_), .. }) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs index 6fdc7b4587f0..2080a49a11cd 100644 --- a/src/tools/clippy/clippy_lints/src/utils/paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs @@ -146,6 +146,12 @@ pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; +#[cfg(feature = "internal-lints")] +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; +#[cfg(feature = "internal-lints")] +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; +#[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/src/tools/clippy/clippy_lints/src/utils/usage.rs b/src/tools/clippy/clippy_lints/src/utils/usage.rs index a7d0ea6ccfbb..fc0db7f64ec9 100644 --- a/src/tools/clippy/clippy_lints/src/utils/usage.rs +++ b/src/tools/clippy/clippy_lints/src/utils/usage.rs @@ -116,20 +116,27 @@ pub struct ParamBindingIdCollector { } impl<'tcx> ParamBindingIdCollector { fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { - let mut finder = ParamBindingIdCollector { - binding_hir_ids: Vec::new(), - }; - finder.visit_body(body); - finder.binding_hir_ids + let mut hir_ids: Vec = Vec::new(); + for param in body.params.iter() { + let mut finder = ParamBindingIdCollector { + binding_hir_ids: Vec::new(), + }; + finder.visit_param(param); + for hir_id in &finder.binding_hir_ids { + hir_ids.push(*hir_id); + } + } + hir_ids } } impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { type Map = Map<'tcx>; - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind { + fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind { self.binding_hir_ids.push(hir_id); } + intravisit::walk_pat(self, pat); } fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index ff414f748ef9..337f7a229b90 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -75,6 +75,24 @@ declare_clippy_lint! { "printing on stdout" } +declare_clippy_lint! { + /// **What it does:** Checks for printing on *stderr*. The purpose of this lint + /// is to catch debugging remnants. + /// + /// **Why is this bad?** People often print on *stderr* while debugging an + /// application and might forget to remove those prints afterward. + /// + /// **Known problems:** Only catches `eprint!` and `eprintln!` calls. + /// + /// **Example:** + /// ```rust + /// eprintln!("Hello world!"); + /// ``` + pub PRINT_STDERR, + restriction, + "printing on stderr" +} + declare_clippy_lint! { /// **What it does:** Checks for use of `Debug` formatting. The purpose of this /// lint is to catch debugging remnants. @@ -201,6 +219,7 @@ impl_lint_pass!(Write => [ PRINT_WITH_NEWLINE, PRINTLN_EMPTY_STRING, PRINT_STDOUT, + PRINT_STDERR, USE_DEBUG, PRINT_LITERAL, WRITE_WITH_NEWLINE, @@ -243,47 +262,22 @@ impl EarlyLintPass for Write { .map_or(false, |crate_name| crate_name == "build_script_build") } - if mac.path == sym!(println) { - if !is_build_script(cx) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - } - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if fmt_str.symbol == Symbol::intern("") { - span_lint_and_sugg( - cx, - PRINTLN_EMPTY_STRING, - mac.span(), - "using `println!(\"\")`", - "replace it with", - "println!()".to_string(), - Applicability::MachineApplicable, - ); - } - } - } else if mac.path == sym!(print) { + if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if check_newlines(&fmt_str) { - span_lint_and_then( - cx, - PRINT_WITH_NEWLINE, - mac.span(), - "using `print!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `println!` instead", - vec![ - (mac.path.span, String::from("println")), - (newline_span(&fmt_str), String::new()), - ], - Applicability::MachineApplicable, - ); - }, - ); - } + self.lint_print_with_newline(cx, mac); + } else if mac.path == sym!(println) { + if !is_build_script(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } + self.lint_println_empty_string(cx, mac); + } else if mac.path == sym!(eprint) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); + self.lint_print_with_newline(cx, mac); + } else if mac.path == sym!(eprintln) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(write) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { @@ -487,6 +481,45 @@ impl Write { } } } + + fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if fmt_str.symbol == Symbol::intern("") { + let name = mac.path.segments[0].ident.name; + span_lint_and_sugg( + cx, + PRINTLN_EMPTY_STRING, + mac.span(), + &format!("using `{}!(\"\")`", name), + "replace it with", + format!("{}!()", name), + Applicability::MachineApplicable, + ); + } + } + } + + fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if check_newlines(&fmt_str) { + let name = mac.path.segments[0].ident.name; + let suggested = format!("{}ln", name); + span_lint_and_then( + cx, + PRINT_WITH_NEWLINE, + mac.span(), + &format!("using `{}!()` with a format string that ends in a single newline", name), + |err| { + err.multipart_suggestion( + &format!("use `{}!` instead", suggested), + vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())], + Applicability::MachineApplicable, + ); + }, + ); + } + } + } } /// Checks if the format string contains a single newline that terminates it. diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs new file mode 100644 index 000000000000..1d5fa8d06a99 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -0,0 +1,82 @@ +use if_chain::if_chain; +use rustc_hir::{self as hir, HirId, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Adt, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_target::abi::LayoutOf as _; +use rustc_typeck::hir_ty_to_ty; + +use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. + /// + /// **Why is this bad?** Since there is only a single value for a zero-sized type, a map + /// containing zero sized values is effectively a set. Using a set in that case improves + /// readability and communicates intent more clearly. + /// + /// **Known problems:** + /// * A zero-sized type cannot be recovered later if it contains private fields. + /// * This lints the signature of public items + /// + /// **Example:** + /// + /// ```rust + /// # use std::collections::HashMap; + /// fn unique_words(text: &str) -> HashMap<&str, ()> { + /// todo!(); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::collections::HashSet; + /// fn unique_words(text: &str) -> HashSet<&str> { + /// todo!(); + /// } + /// ``` + pub ZERO_SIZED_MAP_VALUES, + pedantic, + "usage of map with zero-sized value type" +} + +declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]); + +impl LateLintPass<'_> for ZeroSizedMapValues { + fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { + if_chain! { + if !hir_ty.span.from_expansion(); + if !in_trait_impl(cx, hir_ty.hir_id); + let ty = ty_from_hir_ty(cx, hir_ty); + if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP); + if let Adt(_, ref substs) = ty.kind(); + let ty = substs.type_at(1); + if let Ok(layout) = cx.layout_of(ty); + if layout.is_zst(); + then { + span_lint_and_help(cx, ZERO_SIZED_MAP_VALUES, hir_ty.span, "map with zero-sized value type", None, "consider using a set instead"); + } + } + } +} + +fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { + let parent_id = cx.tcx.hir().get_parent_item(hir_id); + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(parent_id)) { + if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return true; + } + } + false +} + +fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { + cx.maybe_typeck_results() + .and_then(|results| { + if results.hir_owner == hir_ty.hir_id.owner { + results.node_type_opt(hir_ty.hir_id) + } else { + None + } + }) + .unwrap_or_else(|| hir_ty_to_ty(cx.tcx, hir_ty)) +} diff --git a/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml b/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml new file mode 100644 index 000000000000..85a91cd2decd --- /dev/null +++ b/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "path_dep" +version = "0.1.0" diff --git a/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs b/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs new file mode 100644 index 000000000000..35ce524f2b10 --- /dev/null +++ b/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs @@ -0,0 +1,6 @@ +#![deny(clippy::empty_loop)] + +#[cfg(feature = "primary_package_test")] +pub fn lint_me() { + loop {} +} diff --git a/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml b/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml index 83ea5868160b..45362c11b856 100644 --- a/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml +++ b/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml @@ -1,3 +1,6 @@ [package] name = "subcrate" version = "0.1.0" + +[dependencies] +path_dep = { path = "../path_dep" } diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md index b1dacfc9c6d2..60dfdb76650a 100644 --- a/src/tools/clippy/doc/adding_lints.md +++ b/src/tools/clippy/doc/adding_lints.md @@ -98,12 +98,12 @@ While we are working on implementing our lint, we can keep running the UI test. That allows us to check if the output is turning into what we want. Once we are satisfied with the output, we need to run -`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint. +`cargo dev bless` to update the `.stderr` file for our lint. Please note that, we should run `TESTNAME=foo_functions cargo uitest` -every time before running `tests/ui/update-all-references.sh`. +every time before running `cargo dev bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit our lint, we need to commit the generated `.stderr` files, too. In general, you -should only commit files changed by `tests/ui/update-all-references.sh` for the +should only commit files changed by `cargo dev bless` for the specific lint you are creating/editing. Note that if the generated files are empty, they should be removed. @@ -122,8 +122,7 @@ we will find by default two new crates, each with its manifest file: If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` -variable to `cargo uitest` works too, but the script to update the references -is in another path: `tests/ui-cargo/update-all-references.sh`. +variable to `cargo uitest` works too. ## Rustfix tests @@ -133,7 +132,7 @@ additionally run [rustfix] for that test. Rustfix will apply the suggestions from the lint to the code of the test file and compare that to the contents of a `.fixed` file. -Use `tests/ui/update-all-references.sh` to automatically generate the +Use `cargo dev bless` to automatically generate the `.fixed` file after running the tests. [rustfix]: https://github.com/rust-lang/rustfix @@ -226,13 +225,13 @@ store.register_early_pass(|| box foo_functions::FooFunctions); ``` As one may expect, there is a corresponding `register_late_pass` method -available as well. Without a call to one of `register_early_pass` or +available as well. Without a call to one of `register_early_pass` or `register_late_pass`, the lint pass in question will not be run. -One reason that `cargo dev` does not automate this step is that multiple lints +One reason that `cargo dev` does not automate this step is that multiple lints can use the same lint pass, so registering the lint pass may already be done when adding a new lint. Another reason that this step is not automated is that -the order that the passes are registered determines the order the passes +the order that the passes are registered determines the order the passes actually run, which in turn affects the order that any emitted lints are output in. @@ -368,7 +367,7 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { Now we should also run the full test suite with `cargo test`. At this point running `cargo test` should produce the expected output. Remember to run -`tests/ui/update-all-references.sh` to update the `.stderr` file. +`cargo dev bless` to update the `.stderr` file. `cargo test` (as opposed to `cargo uitest`) will also ensure that our lint implementation is not violating any Clippy lints itself. @@ -380,6 +379,57 @@ pass. [`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn [ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html +## Specifying the lint's minimum supported Rust version (msrv) + +Projects supporting older versions of Rust would need to disable a lint if it targets features +present in later versions. Support for this can be added by specifying an msrv in your lint like so, + +```rust +const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0); +``` + +The project's msrv will also have to be an attribute in the lint so you'll have to add a struct +and constructor for your lint. The project's msrv needs to be passed when the lint is registered +in `lib.rs` + +```rust +pub struct ManualStrip { + msrv: Option, +} + +impl ManualStrip { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} +``` + +The project's msrv can then be matched against the lint's msrv in the LintPass using the `meets_msrv` utility +function. + +``` rust +if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { + return; +} +``` + +The project's msrv can also be specified as an inner attribute, which overrides the value from +`clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing +LateContext/EarlyContext. + +```rust +impl<'tcx> LateLintPass<'tcx> for ManualStrip { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + ... + } + extract_msrv_attr!(LateContext); +} +``` + +Once the msrv is added to the lint, a relevant test case should be added to `tests/ui/min_rust_version_attr.rs` +which verifies that the lint isn't emitted if the project's msrv is lower. + ## Author lint If you have trouble implementing your lint, there is also the internal `author` diff --git a/src/tools/clippy/doc/basics.md b/src/tools/clippy/doc/basics.md index f25edb793e26..954474a17aa8 100644 --- a/src/tools/clippy/doc/basics.md +++ b/src/tools/clippy/doc/basics.md @@ -1,16 +1,14 @@ # Basics for hacking on Clippy This document explains the basics for hacking on Clippy. Besides others, this -includes how to set-up the development environment, how to build and how to test -Clippy. For a more in depth description on the codebase take a look at [Adding -Lints] or [Common Tools]. +includes how to build and test Clippy. For a more in depth description on +the codebase take a look at [Adding Lints] or [Common Tools]. [Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md [Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md - [Basics for hacking on Clippy](#basics-for-hacking-on-clippy) - - [Get the code](#get-the-code) - - [Setup](#setup) + - [Get the Code](#get-the-code) - [Building and Testing](#building-and-testing) - [`cargo dev`](#cargo-dev) - [PR](#pr) @@ -38,29 +36,9 @@ git rebase upstream/master git push ``` -## Setup - -Next we need to setup the toolchain to compile Clippy. Since Clippy heavily -relies on compiler internals it is build with the latest rustc master. To get -this toolchain, you can just use the `setup-toolchain.sh` script or use -`rustup-toolchain-install-master`: - -```bash -bash setup-toolchain.sh -# OR -cargo install rustup-toolchain-install-master -# For better IDE integration also add `-c rustfmt -c rust-src` (optional) -rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools -rustup override set master -``` - -_Note:_ Sometimes you may get compiler errors when building Clippy, even if you -didn't change anything. Normally those will be fixed by a maintainer in a few hours. - ## Building and Testing -Once the `master` toolchain is installed, you can build and test Clippy like -every other Rust project: +You can build and test Clippy like every other Rust project: ```bash cargo build # builds Clippy @@ -83,7 +61,7 @@ If the output of a [UI test] differs from the expected output, you can update th reference file with: ```bash -sh tests/ui/update-all-references.sh +cargo dev bless ``` For example, this is necessary, if you fix a typo in an error message of a lint @@ -109,7 +87,7 @@ cargo dev update_lints # create a new lint and register it cargo dev new_lint # (experimental) Setup Clippy to work with rust-analyzer -cargo dev ra-setup +cargo dev ra_setup ``` ## PR diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index bf867e0ae5b6..d2e84132f4ed 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1 +1,3 @@ -nightly +[toolchain] +channel = "nightly-2020-12-20" +components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/src/tools/clippy/setup-toolchain.sh b/src/tools/clippy/setup-toolchain.sh deleted file mode 100755 index 191ea4315a6b..000000000000 --- a/src/tools/clippy/setup-toolchain.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# Set up the appropriate rustc toolchain - -set -e - -cd "$(dirname "$0")" - -RTIM_PATH=$(command -v rustup-toolchain-install-master) || INSTALLED=false -CARGO_HOME=${CARGO_HOME:-$HOME/.cargo} - -# Check if RTIM is not installed or installed in other locations not in ~/.cargo/bin -if [[ "$INSTALLED" == false || "$RTIM_PATH" == $CARGO_HOME/bin/rustup-toolchain-install-master ]]; then - cargo +nightly install rustup-toolchain-install-master -else - VERSION=$(rustup-toolchain-install-master -V | grep -o "[0-9.]*") - REMOTE=$(cargo +nightly search rustup-toolchain-install-master | grep -o "[0-9.]*") - echo "info: skipping updating rustup-toolchain-install-master at $RTIM_PATH" - echo " current version : $VERSION" - echo " remote version : $REMOTE" -fi - -RUST_COMMIT=$(git ls-remote https://github.com/rust-lang/rust master | awk '{print $1}') - -if rustc +master -Vv 2>/dev/null | grep -q "$RUST_COMMIT"; then - echo "info: master toolchain is up-to-date" - exit 0 -fi - -if [[ -n "$HOST_TOOLCHAIN" ]]; then - TOOLCHAIN=('--host' "$HOST_TOOLCHAIN") -else - TOOLCHAIN=() -fi - -rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -c llvm-tools -- "$RUST_COMMIT" -rustup override set master diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index ef31c72481a2..e490ee54c0be 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -182,6 +182,7 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option None, + "--no-deps" => { + no_deps = true; + None + }, + _ => Some(s.to_string()), + }) + .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) + .collect::>(); + // We enable Clippy if one of the following conditions is met + // - IF Clippy is run on its test suite OR + // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN + // - IF `--no-deps` is not set (`!no_deps`) OR + // - IF `--no-deps` is set and Clippy is run on the specified primary package + let clippy_tests_set = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true"); + let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some(); + let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); + + let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package)); if clippy_enabled { - args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); - if let Ok(extra_args) = env::var("CLIPPY_ARGS") { - args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| { - if s.is_empty() { - None - } else { - Some(s.to_string()) - } - })); - } + args.extend(clippy_args); } + let mut clippy = ClippyCallbacks; let mut default = DefaultCallbacks; let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = if clippy_enabled { &mut clippy } else { &mut default }; + rustc_driver::RunCompiler::new(&args, callbacks).run() })) } diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index 6739a4cf2245..ea06743394d1 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -62,7 +62,7 @@ struct ClippyCmd { unstable_options: bool, cargo_subcommand: &'static str, args: Vec, - clippy_args: String, + clippy_args: Vec, } impl ClippyCmd { @@ -99,7 +99,10 @@ impl ClippyCmd { args.insert(0, "+nightly".to_string()); } - let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect(); + let mut clippy_args: Vec = old_args.collect(); + if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") { + clippy_args.push("--no-deps".into()); + } ClippyCmd { unstable_options, @@ -147,10 +150,15 @@ impl ClippyCmd { fn into_std_cmd(self) -> Command { let mut cmd = Command::new("cargo"); + let clippy_args: String = self + .clippy_args + .iter() + .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) + .collect(); cmd.env(self.path_env(), Self::path()) .envs(ClippyCmd::target_dir()) - .env("CLIPPY_ARGS", self.clippy_args) + .env("CLIPPY_ARGS", clippy_args) .arg(self.cargo_subcommand) .args(&self.args); @@ -201,6 +209,24 @@ mod tests { assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options"))); } + #[test] + fn fix_implies_no_deps() { + let args = "cargo clippy --fix -Zunstable-options" + .split_whitespace() + .map(ToString::to_string); + let cmd = ClippyCmd::new(args); + assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps")); + } + + #[test] + fn no_deps_not_duplicated_with_fix() { + let args = "cargo clippy --fix -Zunstable-options -- --no-deps" + .split_whitespace() + .map(ToString::to_string); + let cmd = ClippyCmd::new(args); + assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1); + } + #[test] fn check() { let args = "cargo clippy".split_whitespace().map(ToString::to_string); diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index a6163a83d768..052223d6d6ff 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -3,7 +3,7 @@ #![feature(once_cell)] use std::lazy::SyncLazy; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; mod cargo; @@ -47,12 +47,77 @@ fn dogfood_clippy() { #[test] fn dogfood_subprojects() { + fn test_no_deps_ignores_path_deps_in_workspaces() { + fn clean(cwd: &Path, target_dir: &Path) { + Command::new("cargo") + .current_dir(cwd) + .env("CARGO_TARGET_DIR", target_dir) + .arg("clean") + .args(&["-p", "subcrate"]) + .args(&["-p", "path_dep"]) + .output() + .unwrap(); + } + + if cargo::is_rustc_test_suite() { + return; + } + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = root.join("target").join("dogfood"); + let cwd = root.join("clippy_workspace_tests"); + + // Make sure we start with a clean state + clean(&cwd, &target_dir); + + // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. + // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("--no-deps") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + // Make sure we start with a clean state + clean(&cwd, &target_dir); + + // Test that without the `--no-deps` argument, `path_dep` is linted. + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(!output.status.success()); + } + // run clippy on remaining subprojects and fail the test if lint warnings are reported if cargo::is_rustc_test_suite() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + // NOTE: `path_dep` crate is omitted on purpose here for d in &[ "clippy_workspace_tests", "clippy_workspace_tests/src", @@ -78,4 +143,8 @@ fn dogfood_subprojects() { assert!(output.status.success()); } + + // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the + // same time, so we test this immediately after the dogfood for workspaces. + test_no_deps_ignores_path_deps_in_workspaces(); } diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs index a78273ce0da4..1718089e8bd2 100644 --- a/src/tools/clippy/tests/integration.rs +++ b/src/tools/clippy/tests/integration.rs @@ -72,6 +72,8 @@ fn integration_test() { panic!("incompatible crate versions"); } else if stderr.contains("failed to run `rustc` to learn about target-specific information") { panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`"); + } else if stderr.contains("toolchain") && stderr.contains("is not installed") { + panic!("missing required toolchain"); } match output.status.code() { diff --git a/src/tools/clippy/tests/ui-cargo/update-all-references.sh b/src/tools/clippy/tests/ui-cargo/update-all-references.sh index 7028b251ea03..4391499a1e1f 100755 --- a/src/tools/clippy/tests/ui-cargo/update-all-references.sh +++ b/src/tools/clippy/tests/ui-cargo/update-all-references.sh @@ -1,18 +1,3 @@ #!/bin/bash -# -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -BUILD_DIR=$PWD/target/debug/test_build_base -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/src/tools/clippy/tests/ui-cargo/update-references.sh b/src/tools/clippy/tests/ui-cargo/update-references.sh deleted file mode 100755 index 2ab51168bcaa..000000000000 --- a/src/tools/clippy/tests/ui-cargo/update-references.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a foo.stderr file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi -done diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed new file mode 100644 index 000000000000..c6b84d2ef650 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed @@ -0,0 +1,33 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::symbol::Symbol; + +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} + +fn main() { + // Direct use of Symbol::intern + let _ = rustc_span::symbol::sym::f32; + + // Using a sym macro + let _ = rustc_span::symbol::sym::f32; + + // Correct suggestion when symbol isn't stringified constant name + let _ = rustc_span::symbol::sym::proc_dash_macro; + + // Interning a symbol that is not defined + let _ = Symbol::intern("xyz123"); + let _ = sym!(xyz123); + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs new file mode 100644 index 000000000000..9ec82d4ad0ba --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs @@ -0,0 +1,33 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::symbol::Symbol; + +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} + +fn main() { + // Direct use of Symbol::intern + let _ = Symbol::intern("f32"); + + // Using a sym macro + let _ = sym!(f32); + + // Correct suggestion when symbol isn't stringified constant name + let _ = Symbol::intern("proc-macro"); + + // Interning a symbol that is not defined + let _ = Symbol::intern("xyz123"); + let _ = sym!(xyz123); + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr new file mode 100644 index 000000000000..74b906c8a579 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr @@ -0,0 +1,27 @@ +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:17:13 + | +LL | let _ = Symbol::intern("f32"); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | +note: the lint level is defined here + --> $DIR/interning_defined_symbol.rs:2:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` + +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:20:13 + | +LL | let _ = sym!(f32); + | ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:23:13 + | +LL | let _ = Symbol::intern("proc-macro"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/update-all-references.sh b/src/tools/clippy/tests/ui-toml/update-all-references.sh index 7028b251ea03..4391499a1e1f 100755 --- a/src/tools/clippy/tests/ui-toml/update-all-references.sh +++ b/src/tools/clippy/tests/ui-toml/update-all-references.sh @@ -1,18 +1,3 @@ #!/bin/bash -# -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -BUILD_DIR=$PWD/target/debug/test_build_base -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/src/tools/clippy/tests/ui-toml/update-references.sh b/src/tools/clippy/tests/ui-toml/update-references.sh deleted file mode 100755 index 2ab51168bcaa..000000000000 --- a/src/tools/clippy/tests/ui-toml/update-references.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a foo.stderr file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi -done diff --git a/src/tools/clippy/tests/ui/clone_on_copy.stderr b/src/tools/clippy/tests/ui/clone_on_copy.stderr index ec2faf4ab40d..14a700886a7b 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.stderr +++ b/src/tools/clippy/tests/ui/clone_on_copy.stderr @@ -1,4 +1,4 @@ -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:22:5 | LL | 42.clone(); @@ -6,25 +6,25 @@ LL | 42.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:26:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:29:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` -error: using `clone` on a `Copy` type +error: using `clone` on type `char` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:35:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:39:14 | LL | vec.push(42.clone()); diff --git a/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs b/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs index 6d2124c12fe9..c57a45dc7aab 100644 --- a/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs +++ b/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs @@ -1,7 +1,6 @@ -#![allow(clippy::useless_attribute)] //issue #2910 +// edition:2018 -#[macro_use] -extern crate serde_derive; +use serde::Deserialize; /// Tests that we do not lint for unused underscores in a `MacroAttribute` /// expansion diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.rs b/src/tools/clippy/tests/ui/eprint_with_newline.rs new file mode 100644 index 000000000000..8df32649ad94 --- /dev/null +++ b/src/tools/clippy/tests/ui/eprint_with_newline.rs @@ -0,0 +1,49 @@ +#![allow(clippy::print_literal)] +#![warn(clippy::print_with_newline)] + +fn main() { + eprint!("Hello\n"); + eprint!("Hello {}\n", "world"); + eprint!("Hello {} {}\n", "world", "#2"); + eprint!("{}\n", 1265); + eprint!("\n"); + + // these are all fine + eprint!(""); + eprint!("Hello"); + eprintln!("Hello"); + eprintln!("Hello\n"); + eprintln!("Hello {}\n", "world"); + eprint!("Issue\n{}", 1265); + eprint!("{}", 1265); + eprint!("\n{}", 1275); + eprint!("\n\n"); + eprint!("like eof\n\n"); + eprint!("Hello {} {}\n\n", "world", "#2"); + eprintln!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + eprintln!("\nbla\n\n"); // #3126 + + // Escaping + eprint!("\\n"); // #3514 + eprint!("\\\n"); // should fail + eprint!("\\\\n"); + + // Raw strings + eprint!(r"\n"); // #3778 + + // Literal newlines should also fail + eprint!( + " +" + ); + eprint!( + r" +" + ); + + // Don't warn on CRLF (#4208) + eprint!("\r\n"); + eprint!("foo\r\n"); + eprint!("\\r\n"); //~ ERROR + eprint!("foo\rbar\n") // ~ ERROR +} diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.stderr b/src/tools/clippy/tests/ui/eprint_with_newline.stderr new file mode 100644 index 000000000000..31811d1d92a0 --- /dev/null +++ b/src/tools/clippy/tests/ui/eprint_with_newline.stderr @@ -0,0 +1,121 @@ +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:5:5 + | +LL | eprint!("Hello/n"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-with-newline` implied by `-D warnings` +help: use `eprintln!` instead + | +LL | eprintln!("Hello"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:6:5 + | +LL | eprint!("Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("Hello {}", "world"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:7:5 + | +LL | eprint!("Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("Hello {} {}", "world", "#2"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:8:5 + | +LL | eprint!("{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("{}", 1265); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:9:5 + | +LL | eprint!("/n"); + | ^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!(); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:28:5 + | +LL | eprint!("//n"); // should fail + | ^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("/"); // should fail + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:35:5 + | +LL | / eprint!( +LL | | " +LL | | " +LL | | ); + | |_____^ + | +help: use `eprintln!` instead + | +LL | eprintln!( +LL | "" + | + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:39:5 + | +LL | / eprint!( +LL | | r" +LL | | " +LL | | ); + | |_____^ + | +help: use `eprintln!` instead + | +LL | eprintln!( +LL | r"" + | + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:47:5 + | +LL | eprint!("/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("/r"); //~ ERROR + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:48:5 + | +LL | eprint!("foo/rbar/n") // ~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("foo/rbar") // ~ ERROR + | ^^^^^^^^ -- + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index c4713ca8083d..16aa1b07733d 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -12,7 +12,7 @@ error: redundant closure found LL | meta(|a| foo(a)); | ^^^^^^^^^^ help: remove closure as shown: `foo` -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler --> $DIR/eta.rs:24:21 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted diff --git a/src/tools/clippy/tests/ui/formatting.rs b/src/tools/clippy/tests/ui/formatting.rs index f54b3f2bfe28..0d14807ff1cf 100644 --- a/src/tools/clippy/tests/ui/formatting.rs +++ b/src/tools/clippy/tests/ui/formatting.rs @@ -10,91 +10,6 @@ fn foo() -> bool { #[rustfmt::skip] fn main() { - // weird `else` formatting: - if foo() { - } { - } - - if foo() { - } if foo() { - } - - let _ = { // if as the last expression - let _ = 0; - - if foo() { - } if foo() { - } - else { - } - }; - - let _ = { // if in the middle of a block - if foo() { - } if foo() { - } - else { - } - - let _ = 0; - }; - - if foo() { - } else - { - } - - if foo() { - } - else - { - } - - if foo() { - } else - if foo() { // the span of the above error should continue here - } - - if foo() { - } - else - if foo() { // the span of the above error should continue here - } - - // those are ok: - if foo() { - } - { - } - - if foo() { - } else { - } - - if foo() { - } - else { - } - - if foo() { - } - if foo() { - } - - if foo() { - } else if foo() { - } - - if foo() { - } - else if foo() { - } - - if foo() { - } - else if - foo() {} - // weird op_eq formatting: let mut a = 42; a =- 35; @@ -146,7 +61,7 @@ fn main() { // don't lint if the indentation suggests not to let _ = &[ - 1 + 2, 3 + 1 + 2, 3 - 4, 5 ]; // lint if it doesn't diff --git a/src/tools/clippy/tests/ui/formatting.stderr b/src/tools/clippy/tests/ui/formatting.stderr index e2095cc125bb..bde434c7e2e7 100644 --- a/src/tools/clippy/tests/ui/formatting.stderr +++ b/src/tools/clippy/tests/ui/formatting.stderr @@ -1,80 +1,5 @@ -error: this looks like an `else {..}` but the `else` is missing - --> $DIR/formatting.rs:15:6 - | -LL | } { - | ^ - | - = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` - = note: to remove this lint, add the missing `else` or add a new line before the next block - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:19:6 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:26:10 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:34:10 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this is an `else {..}` but the formatting might hide it - --> $DIR/formatting.rs:43:6 - | -LL | } else - | ______^ -LL | | { - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` - -error: this is an `else {..}` but the formatting might hide it - --> $DIR/formatting.rs:48:6 - | -LL | } - | ______^ -LL | | else -LL | | { - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` - -error: this is an `else if` but the formatting might hide it - --> $DIR/formatting.rs:54:6 - | -LL | } else - | ______^ -LL | | if foo() { // the span of the above error should continue here - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` - -error: this is an `else if` but the formatting might hide it - --> $DIR/formatting.rs:59:6 - | -LL | } - | ______^ -LL | | else -LL | | if foo() { // the span of the above error should continue here - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` - error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)` - --> $DIR/formatting.rs:100:6 + --> $DIR/formatting.rs:15:6 | LL | a =- 35; | ^^^^ @@ -83,7 +8,7 @@ LL | a =- 35; = note: to remove this lint, use either `-=` or `= -` error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)` - --> $DIR/formatting.rs:101:6 + --> $DIR/formatting.rs:16:6 | LL | a =* &191; | ^^^^ @@ -91,7 +16,7 @@ LL | a =* &191; = note: to remove this lint, use either `*=` or `= *` error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)` - --> $DIR/formatting.rs:104:6 + --> $DIR/formatting.rs:19:6 | LL | b =! false; | ^^^^ @@ -99,7 +24,7 @@ LL | b =! false; = note: to remove this lint, use either `!=` or `= !` error: possibly missing a comma here - --> $DIR/formatting.rs:113:19 + --> $DIR/formatting.rs:28:19 | LL | -1, -2, -3 // <= no comma here | ^ @@ -108,7 +33,7 @@ LL | -1, -2, -3 // <= no comma here = note: to remove this lint, add a comma or write the expr in a single line error: possibly missing a comma here - --> $DIR/formatting.rs:117:19 + --> $DIR/formatting.rs:32:19 | LL | -1, -2, -3 // <= no comma here | ^ @@ -116,12 +41,12 @@ LL | -1, -2, -3 // <= no comma here = note: to remove this lint, add a comma or write the expr in a single line error: possibly missing a comma here - --> $DIR/formatting.rs:154:11 + --> $DIR/formatting.rs:69:11 | LL | -1 | ^ | = note: to remove this lint, add a comma or write the expr in a single line -error: aborting due to 14 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed index 7f4ebf566733..84981a525973 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed +++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed @@ -39,7 +39,7 @@ fn main() { B(i32), C, D, - }; + } let x = E::A(2); { // lint diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs index aee56dd4a5ef..94c7c3cadacf 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs +++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs @@ -51,7 +51,7 @@ fn main() { B(i32), C, D, - }; + } let x = E::A(2); { // lint diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed index f3627902eec9..526e94b10bd0 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.fixed +++ b/src/tools/clippy/tests/ui/match_single_binding.fixed @@ -87,4 +87,32 @@ fn main() { unwrapped }) .collect::>(); + // Ok + let x = 1; + match x { + #[cfg(disabled_feature)] + 0 => println!("Disabled branch"), + _ => println!("Enabled branch"), + } + // Lint + let x = 1; + let y = 1; + println!("Single branch"); + // Ok + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + #[cfg(disabled_feature)] + 0 => println!("Array index start"), + _ => println!("Not an array index start"), + } + // False negative + let x = 1; + match x { + // => + _ => println!("Not an array index start"), + } } diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs index 8c182148ae18..6a2ca7c5e934 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.rs +++ b/src/tools/clippy/tests/ui/match_single_binding.rs @@ -99,4 +99,37 @@ fn main() { unwrapped => unwrapped, }) .collect::>(); + // Ok + let x = 1; + match x { + #[cfg(disabled_feature)] + 0 => println!("Disabled branch"), + _ => println!("Enabled branch"), + } + // Lint + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + _ => println!("Single branch"), + } + // Ok + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + #[cfg(disabled_feature)] + 0 => println!("Array index start"), + _ => println!("Not an array index start"), + } + // False negative + let x = 1; + match x { + // => + _ => println!("Not an array index start"), + } } diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr index 795c8c3e24d7..cbbf5d29c024 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.stderr +++ b/src/tools/clippy/tests/ui/match_single_binding.stderr @@ -167,5 +167,16 @@ LL | unwrapped LL | }) | -error: aborting due to 11 previous errors +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:112:5 + | +LL | / match match y { +LL | | 0 => 1, +LL | | _ => 2, +LL | | } { +LL | | _ => println!("Single branch"), +LL | | } + | |_____^ help: consider using the match body instead: `println!("Single branch");` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs index 1026cc40d3b0..3848bca32075 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![clippy::msrv = "1.0.0"] -use std::ops::Deref; +use std::ops::{Deref, RangeFrom}; fn option_as_ref_deref() { let mut opt = Some(String::from("123")); @@ -42,12 +42,94 @@ pub fn manual_strip_msrv() { } } +pub fn redundant_fieldnames() { + let start = 0; + let _ = RangeFrom { start: start }; +} + +pub fn redundant_static_lifetime() { + const VAR_ONE: &'static str = "Test constant #1"; +} + +pub fn checked_conversion() { + let value: i64 = 42; + let _ = value <= (u32::max_value() as i64) && value >= 0; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +pub fn filter_map_next() { + let a = ["1", "lol", "3", "NaN", "5"]; + + #[rustfmt::skip] + let _: Option = vec![1, 2, 3, 4, 5, 6] + .into_iter() + .filter_map(|x| { + if x == 2 { + Some(x * 2) + } else { + None + } + }) + .next(); +} + +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] +pub fn manual_range_contains() { + let x = 5; + x >= 8 && x < 12; +} + +pub fn use_self() { + struct Foo {} + + impl Foo { + fn new() -> Foo { + Foo {} + } + fn test() -> Foo { + Foo::new() + } + } +} + +fn replace_with_default() { + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn map_unwrap_or() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt + .map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); +} + +// Could be const +fn missing_const_for_fn() -> i32 { + 1 +} + fn main() { + filter_map_next(); + checked_conversion(); + redundant_fieldnames(); + redundant_static_lifetime(); option_as_ref_deref(); match_like_matches(); match_same_arms(); match_same_arms2(); manual_strip_msrv(); + manual_range_contains(); + use_self(); + replace_with_default(); + map_unwrap_or(); + missing_const_for_fn(); } mod meets_msrv { diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr index 3e1af046e7a2..348052631049 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:60:24 + --> $DIR/min_rust_version_attr.rs:142:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:59:9 + --> $DIR/min_rust_version_attr.rs:141:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:72:24 + --> $DIR/min_rust_version_attr.rs:154:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:71:9 + --> $DIR/min_rust_version_attr.rs:153:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr index da46f9886366..d56c5cc4c3ae 100644 --- a/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr +++ b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr @@ -1,4 +1,4 @@ -error: missing documentation for crate +error: missing documentation for the crate --> $DIR/missing-doc-crate-missing.rs:1:1 | LL | / #![warn(clippy::missing_docs_in_private_items)] diff --git a/src/tools/clippy/tests/ui/missing-doc-impl.stderr b/src/tools/clippy/tests/ui/missing-doc-impl.stderr index 9656a39abceb..7e10404ca005 100644 --- a/src/tools/clippy/tests/ui/missing-doc-impl.stderr +++ b/src/tools/clippy/tests/ui/missing-doc-impl.stderr @@ -51,13 +51,13 @@ LL | | fn foo_with_impl(&self) {} LL | | } | |_^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:39:5 | LL | fn foo(&self); | ^^^^^^^^^^^^^^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:40:5 | LL | fn foo_with_impl(&self) {} @@ -75,25 +75,25 @@ error: missing documentation for an associated type LL | type AssociatedTypeDef = Self; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:62:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:63:5 | LL | fn bar() {} | ^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:67:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:70:5 | LL | fn foo2() {} diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr index 0bfeda7914db..bea4b41b803d 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.stderr +++ b/src/tools/clippy/tests/ui/needless_borrow.stderr @@ -1,4 +1,4 @@ -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:14:15 | LL | let c = x(&&a); @@ -12,7 +12,7 @@ error: this pattern creates a reference to a reference LL | if let Some(ref cake) = Some(&5) {} | ^^^^^^^^ help: change this to: `cake` -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:28:15 | LL | 46 => &&a, diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs index 883683e08a2a..83e9bbaa3af4 100644 --- a/src/tools/clippy/tests/ui/needless_doc_main.rs +++ b/src/tools/clippy/tests/ui/needless_doc_main.rs @@ -10,7 +10,7 @@ /// ``` /// /// With an explicit return type it should lint too -/// ``` +/// ```edition2015 /// fn main() -> () { /// unimplemented!(); /// } @@ -39,7 +39,7 @@ fn bad_doctests() {} /// ``` /// /// This shouldn't lint either, because main is async: -/// ``` +/// ```edition2018 /// async fn main() { /// assert_eq!(42, ANSWER); /// } @@ -128,6 +128,12 @@ fn bad_doctests() {} /// ``` fn no_false_positives() {} +/// Yields a parse error when interpreted as rust code: +/// ``` +/// r#"hi" +/// ``` +fn issue_6022() {} + fn main() { bad_doctests(); no_false_positives(); diff --git a/src/tools/clippy/tests/ui/needless_update.rs b/src/tools/clippy/tests/ui/needless_update.rs index bfa005a19f91..b93ff048a62f 100644 --- a/src/tools/clippy/tests/ui/needless_update.rs +++ b/src/tools/clippy/tests/ui/needless_update.rs @@ -6,9 +6,20 @@ struct S { pub b: i32, } +#[non_exhaustive] +struct T { + pub x: i32, + pub y: i32, +} + fn main() { let base = S { a: 0, b: 0 }; S { ..base }; // no error S { a: 1, ..base }; // no error S { a: 1, b: 1, ..base }; + + let base = T { x: 0, y: 0 }; + T { ..base }; // no error + T { x: 1, ..base }; // no error + T { x: 1, y: 1, ..base }; // no error } diff --git a/src/tools/clippy/tests/ui/needless_update.stderr b/src/tools/clippy/tests/ui/needless_update.stderr index 133c834880dd..b154b3b306dd 100644 --- a/src/tools/clippy/tests/ui/needless_update.stderr +++ b/src/tools/clippy/tests/ui/needless_update.stderr @@ -1,5 +1,5 @@ error: struct update has no effect, all the fields in the struct have already been specified - --> $DIR/needless_update.rs:13:23 + --> $DIR/needless_update.rs:19:23 | LL | S { a: 1, b: 1, ..base }; | ^^^^ diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr index ca73ac5a4111..eb744b0c198f 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr +++ b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr @@ -1,4 +1,4 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:7:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,7 +8,7 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:9:9 | @@ -16,7 +16,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:12:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,7 +25,7 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:14:9 | @@ -33,7 +33,7 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:17:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,7 +42,7 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:19:9 | @@ -50,7 +50,7 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:22:5 | LL | / fn result_with_todo() -> Result // should emit lint @@ -59,7 +59,7 @@ LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:24:9 | @@ -67,7 +67,7 @@ LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:53:1 | LL | / fn function_result_with_panic() -> Result // should emit lint @@ -76,7 +76,7 @@ LL | | panic!("error"); LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:55:5 | @@ -84,7 +84,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:68:1 | LL | / fn main() -> Result<(), String> { @@ -93,7 +93,7 @@ LL | | Ok(()) LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:69:5 | diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs new file mode 100644 index 000000000000..ffdf8288adc7 --- /dev/null +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs @@ -0,0 +1,48 @@ +#![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wraps)] + +struct A; + +impl A { + fn result_with_assert_with_message(x: i32) -> Result // should emit lint + { + assert!(x == 5, "wrong argument"); + Ok(true) + } + + fn result_with_assert_eq(x: i32) -> Result // should emit lint + { + assert_eq!(x, 5); + Ok(true) + } + + fn result_with_assert_ne(x: i32) -> Result // should emit lint + { + assert_ne!(x, 1); + Ok(true) + } + + fn other_with_assert_with_message(x: i32) // should not emit lint + { + assert!(x == 5, "wrong argument"); + } + + fn other_with_assert_eq(x: i32) // should not emit lint + { + assert_eq!(x, 5); + } + + fn other_with_assert_ne(x: i32) // should not emit lint + { + assert_ne!(x, 1); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + let assert = "assert!"; + println!("No {}", assert); + Ok(true) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr new file mode 100644 index 000000000000..86f61ad718a9 --- /dev/null +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr @@ -0,0 +1,57 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:7:5 + | +LL | / fn result_with_assert_with_message(x: i32) -> Result // should emit lint +LL | | { +LL | | assert!(x == 5, "wrong argument"); +LL | | Ok(true) +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:9:9 + | +LL | assert!(x == 5, "wrong argument"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:13:5 + | +LL | / fn result_with_assert_eq(x: i32) -> Result // should emit lint +LL | | { +LL | | assert_eq!(x, 5); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:15:9 + | +LL | assert_eq!(x, 5); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:19:5 + | +LL | / fn result_with_assert_ne(x: i32) -> Result // should emit lint +LL | | { +LL | | assert_ne!(x, 1); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_assertions.rs:21:9 + | +LL | assert_ne!(x, 1); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs new file mode 100644 index 000000000000..b60c79f97c86 --- /dev/null +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -0,0 +1,48 @@ +#![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wraps)] + +struct A; + +impl A { + fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint + { + debug_assert!(x == 5, "wrong argument"); + Ok(true) + } + + fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint + { + debug_assert_eq!(x, 5); + Ok(true) + } + + fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint + { + debug_assert_ne!(x, 1); + Ok(true) + } + + fn other_with_debug_assert_with_message(x: i32) // should not emit lint + { + debug_assert!(x == 5, "wrong argument"); + } + + fn other_with_debug_assert_eq(x: i32) // should not emit lint + { + debug_assert_eq!(x, 5); + } + + fn other_with_debug_assert_ne(x: i32) // should not emit lint + { + debug_assert_ne!(x, 1); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + let debug_assert = "debug_assert!"; + println!("No {}", debug_assert); + Ok(true) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr new file mode 100644 index 000000000000..ec18e89698c5 --- /dev/null +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr @@ -0,0 +1,57 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5 + | +LL | / fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert!(x == 5, "wrong argument"); +LL | | Ok(true) +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9 + | +LL | debug_assert!(x == 5, "wrong argument"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5 + | +LL | / fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert_eq!(x, 5); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9 + | +LL | debug_assert_eq!(x, 5); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5 + | +LL | / fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert_ne!(x, 1); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9 + | +LL | debug_assert_ne!(x, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/print_stderr.rs b/src/tools/clippy/tests/ui/print_stderr.rs new file mode 100644 index 000000000000..fa07e74a7be4 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_stderr.rs @@ -0,0 +1,8 @@ +#![warn(clippy::print_stderr)] + +fn main() { + eprintln!("Hello"); + println!("This should not do anything"); + eprint!("World"); + print!("Nor should this"); +} diff --git a/src/tools/clippy/tests/ui/print_stderr.stderr b/src/tools/clippy/tests/ui/print_stderr.stderr new file mode 100644 index 000000000000..5af735af6576 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_stderr.stderr @@ -0,0 +1,16 @@ +error: use of `eprintln!` + --> $DIR/print_stderr.rs:4:5 + | +LL | eprintln!("Hello"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stderr` implied by `-D warnings` + +error: use of `eprint!` + --> $DIR/print_stderr.rs:6:5 + | +LL | eprint!("World"); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/println_empty_string.fixed b/src/tools/clippy/tests/ui/println_empty_string.fixed index 2b889b62ea99..9760680927a6 100644 --- a/src/tools/clippy/tests/ui/println_empty_string.fixed +++ b/src/tools/clippy/tests/ui/println_empty_string.fixed @@ -8,4 +8,11 @@ fn main() { match "a" { _ => println!(), } + + eprintln!(); + eprintln!(); + + match "a" { + _ => eprintln!(), + } } diff --git a/src/tools/clippy/tests/ui/println_empty_string.rs b/src/tools/clippy/tests/ui/println_empty_string.rs index 890f5f684760..80fdb3e6e210 100644 --- a/src/tools/clippy/tests/ui/println_empty_string.rs +++ b/src/tools/clippy/tests/ui/println_empty_string.rs @@ -8,4 +8,11 @@ fn main() { match "a" { _ => println!(""), } + + eprintln!(); + eprintln!(""); + + match "a" { + _ => eprintln!(""), + } } diff --git a/src/tools/clippy/tests/ui/println_empty_string.stderr b/src/tools/clippy/tests/ui/println_empty_string.stderr index 23112b881689..17fe4ea74790 100644 --- a/src/tools/clippy/tests/ui/println_empty_string.stderr +++ b/src/tools/clippy/tests/ui/println_empty_string.stderr @@ -12,5 +12,17 @@ error: using `println!("")` LL | _ => println!(""), | ^^^^^^^^^^^^ help: replace it with: `println!()` -error: aborting due to 2 previous errors +error: using `eprintln!("")` + --> $DIR/println_empty_string.rs:13:5 + | +LL | eprintln!(""); + | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + +error: using `eprintln!("")` + --> $DIR/println_empty_string.rs:16:14 + | +LL | _ => eprintln!(""), + | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/range_contains.fixed b/src/tools/clippy/tests/ui/range_contains.fixed index 048874a7f829..47c974e614b9 100644 --- a/src/tools/clippy/tests/ui/range_contains.fixed +++ b/src/tools/clippy/tests/ui/range_contains.fixed @@ -44,3 +44,8 @@ fn main() { (0. ..1.).contains(&y); !(0. ..=1.).contains(&y); } + +// Fix #6373 +pub const fn in_range(a: i32) -> bool { + 3 <= a && a <= 20 +} diff --git a/src/tools/clippy/tests/ui/range_contains.rs b/src/tools/clippy/tests/ui/range_contains.rs index 60ad259f404d..835deced5e4c 100644 --- a/src/tools/clippy/tests/ui/range_contains.rs +++ b/src/tools/clippy/tests/ui/range_contains.rs @@ -44,3 +44,8 @@ fn main() { y >= 0. && y < 1.; y < 0. || y > 1.; } + +// Fix #6373 +pub const fn in_range(a: i32) -> bool { + 3 <= a && a <= 20 +} diff --git a/src/tools/clippy/tests/ui/redundant_else.rs b/src/tools/clippy/tests/ui/redundant_else.rs new file mode 100644 index 000000000000..737c8a9f8db4 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_else.rs @@ -0,0 +1,154 @@ +#![warn(clippy::redundant_else)] +#![allow(clippy::needless_return)] + +fn main() { + loop { + // break + if foo() { + println!("Love your neighbor;"); + break; + } else { + println!("yet don't pull down your hedge."); + } + // continue + if foo() { + println!("He that lies down with Dogs,"); + continue; + } else { + println!("shall rise up with fleas."); + } + // match block + if foo() { + match foo() { + 1 => break, + _ => return, + } + } else { + println!("You may delay, but time will not."); + } + } + // else if + if foo() { + return; + } else if foo() { + return; + } else { + println!("A fat kitchen makes a lean will."); + } + // let binding outside of block + let _ = { + if foo() { + return; + } else { + 1 + } + }; + // else if with let binding outside of block + let _ = { + if foo() { + return; + } else if foo() { + return; + } else { + 2 + } + }; + // inside if let + let _ = if let Some(1) = foo() { + let _ = 1; + if foo() { + return; + } else { + 1 + } + } else { + 1 + }; + + // + // non-lint cases + // + + // sanity check + if foo() { + let _ = 1; + } else { + println!("Who is wise? He that learns from every one."); + } + // else if without else + if foo() { + return; + } else if foo() { + foo() + }; + // nested if return + if foo() { + if foo() { + return; + } + } else { + foo() + }; + // match with non-breaking branch + if foo() { + match foo() { + 1 => foo(), + _ => return, + } + } else { + println!("Three may keep a secret, if two of them are dead."); + } + // let binding + let _ = if foo() { + return; + } else { + 1 + }; + // assign + let a; + a = if foo() { + return; + } else { + 1 + }; + // assign-op + a += if foo() { + return; + } else { + 1 + }; + // if return else if else + if foo() { + return; + } else if foo() { + 1 + } else { + 2 + }; + // if else if return else + if foo() { + 1 + } else if foo() { + return; + } else { + 2 + }; + // else if with let binding + let _ = if foo() { + return; + } else if foo() { + return; + } else { + 2 + }; + // inside function call + Box::new(if foo() { + return; + } else { + 1 + }); +} + +fn foo() -> T { + unimplemented!("I'm not Santa Claus") +} diff --git a/src/tools/clippy/tests/ui/redundant_else.stderr b/src/tools/clippy/tests/ui/redundant_else.stderr new file mode 100644 index 000000000000..9000cdc814b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_else.stderr @@ -0,0 +1,80 @@ +error: redundant else block + --> $DIR/redundant_else.rs:10:16 + | +LL | } else { + | ________________^ +LL | | println!("yet don't pull down your hedge."); +LL | | } + | |_________^ + | + = note: `-D clippy::redundant-else` implied by `-D warnings` + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:17:16 + | +LL | } else { + | ________________^ +LL | | println!("shall rise up with fleas."); +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:26:16 + | +LL | } else { + | ________________^ +LL | | println!("You may delay, but time will not."); +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:35:12 + | +LL | } else { + | ____________^ +LL | | println!("A fat kitchen makes a lean will."); +LL | | } + | |_____^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:42:16 + | +LL | } else { + | ________________^ +LL | | 1 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:52:16 + | +LL | } else { + | ________________^ +LL | | 2 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:61:16 + | +LL | } else { + | ________________^ +LL | | 1 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs new file mode 100644 index 000000000000..226010ec6df3 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs @@ -0,0 +1,79 @@ +#![warn(clippy::suspicious_else_formatting)] + +fn foo() -> bool { + true +} + +#[rustfmt::skip] +fn main() { + // weird `else` formatting: + if foo() { + } { + } + + if foo() { + } if foo() { + } + + let _ = { // if as the last expression + let _ = 0; + + if foo() { + } if foo() { + } + else { + } + }; + + let _ = { // if in the middle of a block + if foo() { + } if foo() { + } + else { + } + + let _ = 0; + }; + + if foo() { + } else + { + } + + if foo() { + } + else + { + } + + if foo() { + } else + if foo() { // the span of the above error should continue here + } + + if foo() { + } + else + if foo() { // the span of the above error should continue here + } + + // those are ok: + if foo() { + } + { + } + + if foo() { + } else { + } + + if foo() { + } + else { + } + + if foo() { + } + if foo() { + } +} diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr new file mode 100644 index 000000000000..bbc036d376fe --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr @@ -0,0 +1,77 @@ +error: this looks like an `else {..}` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:11:6 + | +LL | } { + | ^ + | + = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` + = note: to remove this lint, add the missing `else` or add a new line before the next block + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:15:6 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:22:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:30:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:39:6 + | +LL | } else + | ______^ +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:44:6 + | +LL | } + | ______^ +LL | | else +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:50:6 + | +LL | } else + | ______^ +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:55:6 + | +LL | } + | ______^ +LL | | else +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd061..9df1ae568673 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clone.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr @@ -30,7 +30,7 @@ error: using `.clone()` on a ref-counted pointer LL | let _: Arc = x.clone(); | ^^^^^^^^^ help: try this: `Arc::::clone(&x)` -error: using `clone` on a `Copy` type +error: using `clone` on type `T` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:40:5 | LL | t.clone(); @@ -38,13 +38,13 @@ LL | t.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on a `Copy` type +error: using `clone` on type `std::option::Option` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:42:5 | LL | Some(t).clone(); | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:48:22 | LL | let z: &Vec<_> = y.clone(); @@ -60,13 +60,13 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: using `clone` on a `Copy` type +error: using `clone` on type `many_derefs::E` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:84:20 | LL | let _: E = a.clone(); | ^^^^^^^^^ help: try dereferencing it: `*****a` -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:89:22 | LL | let _ = &mut encoded.clone(); @@ -81,7 +81,7 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &mut <&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:90:18 | LL | let _ = &encoded.clone(); diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs index 2e923bc97a2e..b05dd143bfd7 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -15,4 +15,8 @@ fn main() { } let _ = Ok(1).unwrap_or_else(|e::E| 2); let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); + + // Fix #6343 + let arr = [(Some(1),)]; + Some(&0).and_then(|&i| arr[i].0); } diff --git a/src/tools/clippy/tests/ui/update-all-references.sh b/src/tools/clippy/tests/ui/update-all-references.sh index 30ba9188db43..4391499a1e1f 100755 --- a/src/tools/clippy/tests/ui/update-all-references.sh +++ b/src/tools/clippy/tests/ui/update-all-references.sh @@ -1,21 +1,3 @@ #!/bin/bash -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. - -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-$PWD/target} -PROFILE=${PROFILE:-debug} -BUILD_DIR=${CARGO_TARGET_DIR}/${PROFILE}/test_build_base - -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/src/tools/clippy/tests/ui/update-references.sh b/src/tools/clippy/tests/ui/update-references.sh deleted file mode 100755 index e16ed600ef81..000000000000 --- a/src/tools/clippy/tests/ui/update-references.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a `foo.stderr` file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - FIXED_NAME="${1/%.rs/.fixed}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then - echo updating "$MYDIR"/"$FIXED_NAME" - cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME" - if [[ ! -s "$MYDIR"/"$FIXED_NAME" ]]; then - echo removing "$MYDIR"/"$FIXED_NAME" - rm "$MYDIR"/"$FIXED_NAME" - fi - fi -done diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index ebb3aa28daf3..d6a890014e68 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -71,6 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { + #[allow(clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index 8a182192ab34..b04d9ce75b2a 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -71,6 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { + #[allow(clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr index b33928597c14..80e1bfc75e80 100644 --- a/src/tools/clippy/tests/ui/use_self.stderr +++ b/src/tools/clippy/tests/ui/use_self.stderr @@ -37,19 +37,19 @@ LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:89:56 + --> $DIR/use_self.rs:90:56 | LL | fn bad(foos: &[Self]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:104:13 + --> $DIR/use_self.rs:105:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:25 + --> $DIR/use_self.rs:113:25 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -60,7 +60,7 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:17 + --> $DIR/use_self.rs:114:17 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` @@ -71,91 +71,91 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:149:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:13 + --> $DIR/use_self.rs:150:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:136:29 + --> $DIR/use_self.rs:137:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:137:21 + --> $DIR/use_self.rs:138:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:166:21 + --> $DIR/use_self.rs:167:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:167:21 + --> $DIR/use_self.rs:168:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:168:21 + --> $DIR/use_self.rs:169:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:199:13 + --> $DIR/use_self.rs:200:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:200:13 + --> $DIR/use_self.rs:201:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:202:13 + --> $DIR/use_self.rs:203:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:221:13 + --> $DIR/use_self.rs:222:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:235:25 + --> $DIR/use_self.rs:236:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:236:13 + --> $DIR/use_self.rs:237:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:240:16 + --> $DIR/use_self.rs:241:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:240:22 + --> $DIR/use_self.rs:241:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs index f44305d7e483..5282eba74fd1 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -88,3 +88,52 @@ mod issue4037 { } } } + +// Lint also in trait definition (see #6307) +mod issue6307 { + trait T: Sized { + fn as_i32(self) {} + fn as_u32(&self) {} + fn into_i32(&self) {} + fn into_u32(self) {} + fn is_i32(self) {} + fn is_u32(&self) {} + fn to_i32(self) {} + fn to_u32(&self) {} + fn from_i32(self) {} + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + fn from_cake(self) {} + + // test for false positives + fn as_(self) {} + fn into_(&self) {} + fn is_(self) {} + fn to_(self) {} + fn from_(self) {} + fn to_mut(&mut self) {} + } + + trait U { + fn as_i32(self); + fn as_u32(&self); + fn into_i32(&self); + fn into_u32(self); + fn is_i32(self); + fn is_u32(&self); + fn to_i32(self); + fn to_u32(&self); + fn from_i32(self); + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + fn from_cake(self); + + // test for false positives + fn as_(self); + fn into_(&self); + fn is_(self); + fn to_(self); + fn from_(self); + fn to_mut(&mut self); + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr index ef3ad73ebc7c..86467eb0fc73 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -72,5 +72,65 @@ error: methods called `from_*` usually take no self; consider choosing a less am LL | pub fn from_i64(self) {} | ^^^^ -error: aborting due to 12 previous errors +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:95:19 + | +LL | fn as_i32(self) {} + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:97:21 + | +LL | fn into_i32(&self) {} + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:99:19 + | +LL | fn is_i32(self) {} + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:101:19 + | +LL | fn to_i32(self) {} + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:103:21 + | +LL | fn from_i32(self) {} + | ^^^^ + +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:118:19 + | +LL | fn as_i32(self); + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:120:21 + | +LL | fn into_i32(&self); + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:122:19 + | +LL | fn is_i32(self); + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:124:19 + | +LL | fn to_i32(self); + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:126:21 + | +LL | fn from_i32(self); + | ^^^^ + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs new file mode 100644 index 000000000000..5cd254787d83 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs @@ -0,0 +1,68 @@ +#![warn(clippy::zero_sized_map_values)] +use std::collections::BTreeMap; + +const CONST_OK: Option> = None; +const CONST_NOT_OK: Option> = None; + +static STATIC_OK: Option> = None; +static STATIC_NOT_OK: Option> = None; + +type OkMap = BTreeMap; +type NotOkMap = BTreeMap; + +enum TestEnum { + Ok(BTreeMap), + NotOk(BTreeMap), +} + +struct Test { + ok: BTreeMap, + not_ok: BTreeMap, + + also_not_ok: Vec>, +} + +trait TestTrait { + type Output; + + fn produce_output() -> Self::Output; + + fn weird_map(&self, map: BTreeMap); +} + +impl Test { + fn ok(&self) -> BTreeMap { + todo!() + } + + fn not_ok(&self) -> BTreeMap { + todo!() + } +} + +impl TestTrait for Test { + type Output = BTreeMap; + + fn produce_output() -> Self::Output { + todo!(); + } + + fn weird_map(&self, map: BTreeMap) { + todo!(); + } +} + +fn test(map: BTreeMap, key: &str) -> BTreeMap { + todo!(); +} + +fn test2(map: BTreeMap, key: &str) -> BTreeMap { + todo!(); +} + +fn main() { + let _: BTreeMap = BTreeMap::new(); + let _: BTreeMap = BTreeMap::new(); + + let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect(); +} diff --git a/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr new file mode 100644 index 000000000000..334d921a9af3 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr @@ -0,0 +1,107 @@ +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:5:28 + | +LL | const CONST_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-sized-map-values` implied by `-D warnings` + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:8:30 + | +LL | static STATIC_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:11:17 + | +LL | type NotOkMap = BTreeMap; + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:15:11 + | +LL | NotOk(BTreeMap), + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:20:13 + | +LL | not_ok: BTreeMap, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:22:22 + | +LL | also_not_ok: Vec>, + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:30:30 + | +LL | fn weird_map(&self, map: BTreeMap); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:38:25 + | +LL | fn not_ok(&self) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:55:14 + | +LL | fn test(map: BTreeMap, key: &str) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:55:50 + | +LL | fn test(map: BTreeMap, key: &str) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:64:35 + | +LL | let _: BTreeMap = BTreeMap::new(); + | ^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:64:12 + | +LL | let _: BTreeMap = BTreeMap::new(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:67:12 + | +LL | let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect(); + | ^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs new file mode 100644 index 000000000000..a1608d863fb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs @@ -0,0 +1,68 @@ +#![warn(clippy::zero_sized_map_values)] +use std::collections::HashMap; + +const CONST_OK: Option> = None; +const CONST_NOT_OK: Option> = None; + +static STATIC_OK: Option> = None; +static STATIC_NOT_OK: Option> = None; + +type OkMap = HashMap; +type NotOkMap = HashMap; + +enum TestEnum { + Ok(HashMap), + NotOk(HashMap), +} + +struct Test { + ok: HashMap, + not_ok: HashMap, + + also_not_ok: Vec>, +} + +trait TestTrait { + type Output; + + fn produce_output() -> Self::Output; + + fn weird_map(&self, map: HashMap); +} + +impl Test { + fn ok(&self) -> HashMap { + todo!() + } + + fn not_ok(&self) -> HashMap { + todo!() + } +} + +impl TestTrait for Test { + type Output = HashMap; + + fn produce_output() -> Self::Output { + todo!(); + } + + fn weird_map(&self, map: HashMap) { + todo!(); + } +} + +fn test(map: HashMap, key: &str) -> HashMap { + todo!(); +} + +fn test2(map: HashMap, key: &str) -> HashMap { + todo!(); +} + +fn main() { + let _: HashMap = HashMap::new(); + let _: HashMap = HashMap::new(); + + let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); +} diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr new file mode 100644 index 000000000000..43987b3d01d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr @@ -0,0 +1,107 @@ +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:5:28 + | +LL | const CONST_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-sized-map-values` implied by `-D warnings` + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:8:30 + | +LL | static STATIC_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:11:17 + | +LL | type NotOkMap = HashMap; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:15:11 + | +LL | NotOk(HashMap), + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:20:13 + | +LL | not_ok: HashMap, + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:22:22 + | +LL | also_not_ok: Vec>, + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:30:30 + | +LL | fn weird_map(&self, map: HashMap); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:38:25 + | +LL | fn not_ok(&self) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:55:14 + | +LL | fn test(map: HashMap, key: &str) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:55:49 + | +LL | fn test(map: HashMap, key: &str) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:64:34 + | +LL | let _: HashMap = HashMap::new(); + | ^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:64:12 + | +LL | let _: HashMap = HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:67:12 + | +LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); + | ^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index b7b20b40e68a..b9549be3a8b6 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -1,7 +1,7 @@ [relabel] allow-unauthenticated = [ "A-*", "C-*", "E-*", "L-*", "M-*", "O-*", "P-*", "S-*", "T-*", - "good first issue" + "good-first-issue" ] [assign] diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html index e11f2eeba3b3..428708136cb6 100644 --- a/src/tools/clippy/util/gh-pages/index.html +++ b/src/tools/clippy/util/gh-pages/index.html @@ -89,7 +89,7 @@