diff options
560 files changed, 9293 insertions, 4241 deletions
diff --git a/.mailmap b/.mailmap index 2a53cbf9eff..90533e81b39 100644 --- a/.mailmap +++ b/.mailmap @@ -690,6 +690,7 @@ Xinye Tao <xy.tao@outlook.com> Xuefeng Wu <benewu@gmail.com> Xuefeng Wu <xfwu@thoughtworks.com> Xuefeng Wu <benewu@gmail.com> XuefengWu <benewu@gmail.com> York Xiang <bombless@126.com> +Yotam Ofek <yotam.ofek@gmail.com> <yotamofek@microsoft.com> Youngsoo Son <ysson83@gmail.com> <ysoo.son@samsung.com> Youngsuk Kim <joseph942010@gmail.com> Yuki Okushi <jtitor@2k36.org> diff --git a/Cargo.lock b/Cargo.lock index e1cf17e2c01..da3eb92ece8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "gimli", + "gimli 0.31.1", ] [[package]] @@ -367,6 +367,26 @@ dependencies = [ ] [[package]] +name = "capstone" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "015ef5d5ca1743e3f94af9509ba6bd2886523cfee46e48d15c2ef5216fd4ac9a" +dependencies = [ + "capstone-sys", + "libc", +] + +[[package]] +name = "capstone-sys" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0" +dependencies = [ + "cc", + "libc", +] + +[[package]] name = "cargo-miri" version = "0.1.0" dependencies = [ @@ -738,7 +758,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows", + "windows 0.61.3", ] [[package]] @@ -1464,6 +1484,17 @@ dependencies = [ ] [[package]] +name = "gimli" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] name = "glob" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1592,7 +1623,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -1917,6 +1948,25 @@ dependencies = [ ] [[package]] +name = "ipc-channel" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8251fb7bcd9ccd3725ed8deae9fe7db8e586495c9eb5b0c52e6233e5e75ea" +dependencies = [ + "bincode", + "crossbeam-channel", + "fnv", + "lazy_static", + "libc", + "mio", + "rand 0.8.5", + "serde", + "tempfile", + "uuid", + "windows 0.58.0", +] + +[[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2302,6 +2352,18 @@ dependencies = [ ] [[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "log", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] name = "miow" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2316,18 +2378,22 @@ version = "0.1.0" dependencies = [ "aes", "bitflags", + "capstone", "chrono", "chrono-tz", "colored", "directories", "getrandom 0.3.3", + "ipc-channel", "libc", "libffi", "libloading", "measureme", + "nix", "rand 0.9.1", "regex", "rustc_version", + "serde", "smallvec", "tempfile", "tikv-jemalloc-sys", @@ -2513,7 +2579,7 @@ dependencies = [ "hashbrown", "indexmap", "memchr", - "ruzstd", + "ruzstd 0.7.3", ] [[package]] @@ -2523,9 +2589,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a" dependencies = [ "crc32fast", + "flate2", "hashbrown", "indexmap", "memchr", + "ruzstd 0.8.1", "wasmparser 0.234.0", ] @@ -3139,9 +3207,9 @@ version = "0.2.0" dependencies = [ "bstr", "build_helper", - "gimli", + "gimli 0.32.0", "libc", - "object 0.36.7", + "object 0.37.1", "regex", "serde_json", "similar", @@ -3443,7 +3511,7 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags", - "gimli", + "gimli 0.31.1", "itertools", "libc", "measureme", @@ -3519,7 +3587,7 @@ dependencies = [ "thorin-dwp", "tracing", "wasm-encoder 0.219.2", - "windows", + "windows 0.61.3", ] [[package]] @@ -3578,7 +3646,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows", + "windows 0.61.3", ] [[package]] @@ -3641,7 +3709,7 @@ dependencies = [ "shlex", "stable_mir", "tracing", - "windows", + "windows 0.61.3", ] [[package]] @@ -3696,7 +3764,7 @@ dependencies = [ "termcolor", "termize", "tracing", - "windows", + "windows 0.61.3", ] [[package]] @@ -4418,7 +4486,7 @@ dependencies = [ "rustc_target", "rustc_trait_selection", "tracing", - "twox-hash", + "twox-hash 1.6.3", ] [[package]] @@ -4457,7 +4525,7 @@ dependencies = [ "rustc_target", "termize", "tracing", - "windows", + "windows 0.61.3", ] [[package]] @@ -4802,7 +4870,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" dependencies = [ - "twox-hash", + "twox-hash 1.6.3", +] + +[[package]] +name = "ruzstd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c" +dependencies = [ + "twox-hash 2.1.1", ] [[package]] @@ -5142,7 +5219,7 @@ dependencies = [ "libc", "objc2-core-foundation", "objc2-io-kit", - "windows", + "windows 0.61.3", ] [[package]] @@ -5283,7 +5360,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e9c1e705f82a260173f3eec93f2ff6d7807f23ad5a8cc2e7316a891733ea7a1" dependencies = [ - "gimli", + "gimli 0.31.1", "hashbrown", "object 0.36.7", "tracing", @@ -5526,6 +5603,12 @@ dependencies = [ ] [[package]] +name = "twox-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56" + +[[package]] name = "type-map" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6036,12 +6119,22 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.61.2", "windows-future", "windows-link", "windows-numerics", @@ -6064,7 +6157,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core", + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", ] [[package]] @@ -6073,11 +6179,11 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.0", + "windows-interface 0.59.1", "windows-link", - "windows-result", - "windows-strings", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -6086,13 +6192,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link", "windows-threading", ] [[package]] name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "windows-implement" version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" @@ -6104,6 +6221,17 @@ dependencies = [ [[package]] name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "windows-interface" version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" @@ -6125,12 +6253,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link", ] [[package]] name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" @@ -6140,6 +6277,16 @@ dependencies = [ [[package]] name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b2d8881e3f6..f5418402377 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -19,7 +19,6 @@ //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. use std::borrow::Cow; -use std::sync::Arc; use std::{cmp, fmt}; pub use GenericArgs::*; @@ -32,7 +31,7 @@ use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_span::{ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; pub use crate::format::*; @@ -1805,10 +1804,17 @@ pub enum ExprKind { Become(P<Expr>), /// Bytes included via `include_bytes!` + /// /// Added for optimization purposes to avoid the need to escape /// large binary blobs - should always behave like [`ExprKind::Lit`] /// with a `ByteStr` literal. - IncludedBytes(Arc<[u8]>), + /// + /// The value is stored as a `ByteSymbol`. It's unfortunate that we need to + /// intern (hash) the bytes because they're likely to be large and unique. + /// But it's necessary because this will eventually be lowered to + /// `LitKind::ByteStr`, which needs a `ByteSymbol` to impl `Copy` and avoid + /// arena allocation. + IncludedBytes(ByteSymbol), /// A `format_args!()` expression. FormatArgs(P<FormatArgs>), @@ -2066,7 +2072,7 @@ impl YieldKind { } /// A literal in a meta item. -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MetaItemLit { /// The original literal as written in the source code. pub symbol: Symbol, @@ -2129,16 +2135,18 @@ pub enum LitFloatType { /// deciding the `LitKind`. This means that float literals like `1f32` are /// classified by this type as `Float`. This is different to `token::LitKind` /// which does *not* consider the suffix. -#[derive(Clone, Encodable, Decodable, Debug, Hash, Eq, PartialEq, HashStable_Generic)] +#[derive(Clone, Copy, Encodable, Decodable, Debug, Hash, Eq, PartialEq, HashStable_Generic)] pub enum LitKind { /// A string literal (`"foo"`). The symbol is unescaped, and so may differ /// from the original token's symbol. Str(Symbol, StrStyle), - /// A byte string (`b"foo"`). Not stored as a symbol because it might be - /// non-utf8, and symbols only allow utf8 strings. - ByteStr(Arc<[u8]>, StrStyle), - /// A C String (`c"foo"`). Guaranteed to only have `\0` at the end. - CStr(Arc<[u8]>, StrStyle), + /// A byte string (`b"foo"`). The symbol is unescaped, and so may differ + /// from the original token's symbol. + ByteStr(ByteSymbol, StrStyle), + /// A C String (`c"foo"`). Guaranteed to only have `\0` at the end. The + /// symbol is unescaped, and so may differ from the original token's + /// symbol. + CStr(ByteSymbol, StrStyle), /// A byte char (`b'f'`). Byte(u8), /// A character literal (`'a'`). diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index ad9e5d1468b..fa7878873e5 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -5,7 +5,7 @@ use std::{ascii, fmt, str}; use rustc_literal_escaper::{ MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, }; -use rustc_span::{Span, Symbol, kw, sym}; +use rustc_span::{ByteSymbol, Span, Symbol, kw, sym}; use tracing::debug; use crate::ast::{self, LitKind, MetaItemLit, StrStyle}; @@ -116,13 +116,12 @@ impl LitKind { assert!(!err.is_fatal(), "failed to unescape string literal") } }); - LitKind::ByteStr(buf.into(), StrStyle::Cooked) + LitKind::ByteStr(ByteSymbol::intern(&buf), StrStyle::Cooked) } token::ByteStrRaw(n) => { - // Raw strings have no escapes so we can convert the symbol - // directly to a `Arc<u8>`. + // Raw byte strings have no escapes so no work is needed here. let buf = symbol.as_str().to_owned().into_bytes(); - LitKind::ByteStr(buf.into(), StrStyle::Raw(n)) + LitKind::ByteStr(ByteSymbol::intern(&buf), StrStyle::Raw(n)) } token::CStr => { let s = symbol.as_str(); @@ -137,7 +136,7 @@ impl LitKind { } }); buf.push(0); - LitKind::CStr(buf.into(), StrStyle::Cooked) + LitKind::CStr(ByteSymbol::intern(&buf), StrStyle::Cooked) } token::CStrRaw(n) => { // Raw strings have no escapes so we can convert the symbol @@ -145,7 +144,7 @@ impl LitKind { // char. let mut buf = symbol.as_str().to_owned().into_bytes(); buf.push(0); - LitKind::CStr(buf.into(), StrStyle::Raw(n)) + LitKind::CStr(ByteSymbol::intern(&buf), StrStyle::Raw(n)) } token::Err(guar) => LitKind::Err(guar), }) @@ -167,12 +166,12 @@ impl fmt::Display for LitKind { delim = "#".repeat(n as usize), string = sym )?, - LitKind::ByteStr(ref bytes, StrStyle::Cooked) => { - write!(f, "b\"{}\"", escape_byte_str_symbol(bytes))? + LitKind::ByteStr(ref byte_sym, StrStyle::Cooked) => { + write!(f, "b\"{}\"", escape_byte_str_symbol(byte_sym.as_byte_str()))? } - LitKind::ByteStr(ref bytes, StrStyle::Raw(n)) => { + LitKind::ByteStr(ref byte_sym, StrStyle::Raw(n)) => { // Unwrap because raw byte string literals can only contain ASCII. - let symbol = str::from_utf8(bytes).unwrap(); + let symbol = str::from_utf8(byte_sym.as_byte_str()).unwrap(); write!( f, "br{delim}\"{string}\"{delim}", @@ -181,11 +180,11 @@ impl fmt::Display for LitKind { )?; } LitKind::CStr(ref bytes, StrStyle::Cooked) => { - write!(f, "c\"{}\"", escape_byte_str_symbol(bytes))? + write!(f, "c\"{}\"", escape_byte_str_symbol(bytes.as_byte_str()))? } LitKind::CStr(ref bytes, StrStyle::Raw(n)) => { // This can only be valid UTF-8. - let symbol = str::from_utf8(bytes).unwrap(); + let symbol = str::from_utf8(bytes.as_byte_str()).unwrap(); write!(f, "cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize),)?; } LitKind::Int(n, ty) => { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c2140514e31..8747e624a4a 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -144,11 +144,11 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Unary(op, ohs) } ExprKind::Lit(token_lit) => hir::ExprKind::Lit(self.lower_lit(token_lit, e.span)), - ExprKind::IncludedBytes(bytes) => { - let lit = self.arena.alloc(respan( + ExprKind::IncludedBytes(byte_sym) => { + let lit = respan( self.lower_span(e.span), - LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked), - )); + LitKind::ByteStr(*byte_sym, StrStyle::Cooked), + ); hir::ExprKind::Lit(lit) } ExprKind::Cast(expr, ty) => { @@ -421,11 +421,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - pub(crate) fn lower_lit( - &mut self, - token_lit: &token::Lit, - span: Span, - ) -> &'hir Spanned<LitKind> { + pub(crate) fn lower_lit(&mut self, token_lit: &token::Lit, span: Span) -> hir::Lit { let lit_kind = match LitKind::from_token_lit(*token_lit) { Ok(lit_kind) => lit_kind, Err(err) => { @@ -433,7 +429,7 @@ impl<'hir> LoweringContext<'_, 'hir> { LitKind::Err(guar) } }; - self.arena.alloc(respan(self.lower_span(span), lit_kind)) + respan(self.lower_span(span), lit_kind) } fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { @@ -2141,10 +2137,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> { - let lit = self.arena.alloc(hir::Lit { + let lit = hir::Lit { span: sp, node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)), - }); + }; self.expr(sp, hir::ExprKind::Lit(lit)) } @@ -2161,9 +2157,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> { - let lit = self - .arena - .alloc(hir::Lit { span: sp, node: ast::LitKind::Str(value, ast::StrStyle::Cooked) }); + let lit = hir::Lit { span: sp, node: ast::LitKind::Str(value, ast::StrStyle::Cooked) }; self.expr(sp, hir::ExprKind::Lit(lit)) } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 58dea472f1d..e4440621048 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -390,19 +390,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { allow_paths: bool, ) -> &'hir hir::PatExpr<'hir> { let span = self.lower_span(expr.span); - let err = |guar| hir::PatExprKind::Lit { - lit: self.arena.alloc(respan(span, LitKind::Err(guar))), - negated: false, - }; + let err = + |guar| hir::PatExprKind::Lit { lit: respan(span, LitKind::Err(guar)), negated: false }; let kind = match &expr.kind { ExprKind::Lit(lit) => { hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false } } ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)), - ExprKind::IncludedBytes(bytes) => hir::PatExprKind::Lit { - lit: self - .arena - .alloc(respan(span, LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked))), + ExprKind::IncludedBytes(byte_sym) => hir::PatExprKind::Lit { + lit: respan(span, LitKind::ByteStr(*byte_sym, StrStyle::Cooked)), negated: false, }, ExprKind::Err(guar) => err(*guar), diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 7651e8365a2..8a2cb64b2a0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -469,8 +469,12 @@ impl<'a> State<'a> { ast::ExprKind::Lit(token_lit) => { self.print_token_literal(*token_lit, expr.span); } - ast::ExprKind::IncludedBytes(bytes) => { - let lit = token::Lit::new(token::ByteStr, escape_byte_str_symbol(bytes), None); + ast::ExprKind::IncludedBytes(byte_sym) => { + let lit = token::Lit::new( + token::ByteStr, + escape_byte_str_symbol(byte_sym.as_byte_str()), + None, + ); self.print_token_literal(lit, expr.span) } ast::ExprKind::Cast(expr, ty) => { diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 5eb1931152d..4651cf504fa 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -256,6 +256,9 @@ pub enum AttributeKind { /// Represents `#[link_name]`. LinkName { name: Symbol, span: Span }, + /// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute) + LinkSection { name: Symbol, span: Span }, + /// Represents `#[loop_match]`. LoopMatch(Span), @@ -287,6 +290,12 @@ pub enum AttributeKind { /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr(ThinVec<(ReprAttr, Span)>), + /// Represents `#[rustc_layout_scalar_valid_range_end]`. + RustcLayoutScalarValidRangeEnd(Box<u128>, Span), + + /// Represents `#[rustc_layout_scalar_valid_range_start]`. + RustcLayoutScalarValidRangeStart(Box<u128>, Span), + /// Represents `#[rustc_skip_during_method_dispatch]`. SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span }, diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 0d6ee77c718..17f7a7a2e6d 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -24,6 +24,7 @@ impl AttributeKind { DocComment { .. } => Yes, ExportName { .. } => Yes, Inline(..) => No, + LinkSection { .. } => No, MacroTransparency(..) => Yes, Repr(..) => No, Stability { .. } => Yes, @@ -37,6 +38,8 @@ impl AttributeKind { NoMangle(..) => No, Optimize(..) => No, PubTransparent(..) => Yes, + RustcLayoutScalarValidRangeEnd(..) => Yes, + RustcLayoutScalarValidRangeStart(..) => Yes, SkipDuringMethodDispatch { .. } => No, TrackCaller(..) => Yes, Used { .. } => No, diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index 86c73f0d9a0..8f8ce575a18 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -49,6 +49,16 @@ pub trait PrintAttribute { fn print_attribute(&self, p: &mut Printer); } +impl PrintAttribute for u128 { + fn should_render(&self) -> bool { + true + } + + fn print_attribute(&self, p: &mut Printer) { + p.word(self.to_string()) + } +} + impl<T: PrintAttribute> PrintAttribute for &T { fn should_render(&self) -> bool { T::should_render(self) diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 39652335f55..9ad46a83f50 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -99,6 +99,8 @@ attr_parsing_non_ident_feature = attr_parsing_null_on_export = `export_name` may not contain null characters +attr_parsing_null_on_link_section = `link_section` may not contain null characters + attr_parsing_repr_ident = meta item in `repr` must be an identifier diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 740222178ed..e298053ab76 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,11 +1,12 @@ use rustc_attr_data_structures::AttributeKind; -use rustc_attr_data_structures::AttributeKind::LinkName; +use rustc_attr_data_structures::AttributeKind::{LinkName, LinkSection}; use rustc_feature::{AttributeTemplate, template}; use rustc_span::{Symbol, sym}; use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; +use crate::session_diagnostics::NullOnLinkSection; pub(crate) struct LinkNameParser; @@ -28,3 +29,31 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser { Some(LinkName { name, span: cx.attr_span }) } } + +pub(crate) struct LinkSectionParser; + +impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser { + const PATH: &[Symbol] = &[sym::link_section]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(name) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + if name.as_str().contains('\0') { + // `#[link_section = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + cx.emit_err(NullOnLinkSection { span: cx.attr_span }); + return None; + } + + Some(LinkSection { name, span: cx.attr_span }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 584dadadcf7..b3a9c69c055 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -36,6 +36,7 @@ pub(crate) mod lint_helpers; pub(crate) mod loop_match; pub(crate) mod must_use; pub(crate) mod repr; +pub(crate) mod rustc_internal; pub(crate) mod semantics; pub(crate) mod stability; pub(crate) mod traits; diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index a672d956127..b5eb85f68b4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -21,7 +21,16 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser { span: cx.attr_span, reason: match args { ArgParser::NoArgs => None, - ArgParser::NameValue(name_value) => name_value.value_as_str(), + ArgParser::NameValue(name_value) => { + let Some(value_str) = name_value.value_as_str() else { + cx.expected_string_literal( + name_value.value_span, + Some(&name_value.value_as_lit()), + ); + return None; + }; + Some(value_str) + } ArgParser::List(_) => { let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use"); diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs new file mode 100644 index 00000000000..7364a3f20e2 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -0,0 +1,59 @@ +use rustc_ast::LitKind; +use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Symbol, sym}; + +use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct RustcLayoutScalarValidRangeStart; + +impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart { + const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(List: "start"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + parse_rustc_layout_scalar_valid_range(cx, args) + .map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(n, cx.attr_span)) + } +} + +pub(crate) struct RustcLayoutScalarValidRangeEnd; + +impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEnd { + const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(List: "end"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + parse_rustc_layout_scalar_valid_range(cx, args) + .map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(n, cx.attr_span)) + } +} + +fn parse_rustc_layout_scalar_valid_range<S: Stage>( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser<'_>, +) -> Option<Box<u128>> { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + let Some(lit) = single.lit() else { + cx.expected_integer_literal(single.span()); + return None; + }; + let LitKind::Int(num, _ty) = lit.kind else { + cx.expected_integer_literal(single.span()); + return None; + }; + Some(Box::new(num.0)) +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1ac41b9c7e8..6a03cde3a53 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -22,11 +22,14 @@ use crate::attributes::codegen_attrs::{ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::link_attrs::LinkNameParser; +use crate::attributes::link_attrs::{LinkNameParser, LinkSectionParser}; use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser}; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::must_use::MustUseParser; use crate::attributes::repr::{AlignParser, ReprParser}; +use crate::attributes::rustc_internal::{ + RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, +}; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, @@ -123,6 +126,7 @@ attribute_parsers!( Single<ExportNameParser>, Single<InlineParser>, Single<LinkNameParser>, + Single<LinkSectionParser>, Single<LoopMatchParser>, Single<MayDangleParser>, Single<MustUseParser>, @@ -130,6 +134,8 @@ attribute_parsers!( Single<OptimizeParser>, Single<PubTransparentParser>, Single<RustcForceInlineParser>, + Single<RustcLayoutScalarValidRangeEnd>, + Single<RustcLayoutScalarValidRangeStart>, Single<SkipDuringMethodDispatchParser>, Single<TrackCallerParser>, Single<TransparencyParser>, @@ -273,6 +279,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { }) } + pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedIntegerLiteral, + }) + } + pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 7cfce579979..6145f1e1d3e 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -453,6 +453,13 @@ pub(crate) struct NullOnExport { } #[derive(Diagnostic)] +#[diag(attr_parsing_null_on_link_section, code = E0648)] +pub(crate) struct NullOnLinkSection { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(attr_parsing_stability_outside_std, code = E0734)] pub(crate) struct StabilityOutsideStd { #[primary_span] @@ -503,6 +510,7 @@ pub(crate) struct NakedFunctionIncompatibleAttribute { pub(crate) enum AttributeParseErrorReason { ExpectedNoArgs, ExpectedStringLiteral { byte_string: Option<Span> }, + ExpectedIntegerLiteral, ExpectedAtLeastOneArgument, ExpectedSingleArgument, ExpectedList, @@ -543,6 +551,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag.span_label(self.span, "expected a string literal here"); } } + AttributeParseErrorReason::ExpectedIntegerLiteral => { + diag.span_label(self.span, "expected an integer literal here"); + } AttributeParseErrorReason::ExpectedSingleArgument => { diag.span_label(self.span, "expected a single argument here"); diag.code(E0805); @@ -566,10 +577,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag.code(E0565); } AttributeParseErrorReason::ExpectedNameValue(None) => { - diag.span_label( - self.span, - format!("expected this to be of the form `{name} = \"...\"`"), - ); + // The suggestion we add below this match already contains enough information } AttributeParseErrorReason::ExpectedNameValue(Some(name)) => { diag.span_label( diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index 92d011fb9d1..fd2d740c020 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -177,15 +177,15 @@ pub(crate) fn expand_concat_bytes( Ok(LitKind::Byte(val)) => { accumulator.push(val); } - Ok(LitKind::ByteStr(ref bytes, _)) => { - accumulator.extend_from_slice(bytes); + Ok(LitKind::ByteStr(ref byte_sym, _)) => { + accumulator.extend_from_slice(byte_sym.as_byte_str()); } _ => { guar.get_or_insert_with(|| invalid_type_err(cx, token_lit, e.span, false)); } }, - ExprKind::IncludedBytes(bytes) => { - accumulator.extend_from_slice(bytes); + ExprKind::IncludedBytes(byte_sym) => { + accumulator.extend_from_slice(byte_sym.as_byte_str()); } ExprKind::Err(guarantee) => { guar = Some(*guarantee); diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 8142f1518dd..cebfffa1e16 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -16,7 +16,7 @@ use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_span::source_map::SourceMap; -use rustc_span::{Pos, Span, Symbol}; +use rustc_span::{ByteSymbol, Pos, Span, Symbol}; use smallvec::SmallVec; use crate::errors; @@ -237,7 +237,7 @@ pub(crate) fn expand_include_bytes( Ok((bytes, _bsp)) => { // Don't care about getting the span for the raw bytes, // because the console can't really show them anyway. - let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); + let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(ByteSymbol::intern(&bytes))); MacEager::expr(expr) } Err(dummy) => dummy, diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 524ebde1c74..2f53bbf8b79 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -660,7 +660,7 @@ pub mod intrinsics { #[rustc_intrinsic] pub unsafe fn ctlz_nonzero<T>(x: T) -> u32; #[rustc_intrinsic] - pub fn needs_drop<T: ?::Sized>() -> bool; + pub const fn needs_drop<T: ?::Sized>() -> bool; #[rustc_intrinsic] pub fn bitreverse<T>(x: T) -> T; #[rustc_intrinsic] diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 1499f948deb..246bd3104ec 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,4 +1,13 @@ -#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)] +#![feature( + no_core, + lang_items, + never_type, + linkage, + extern_types, + thread_local, + repr_simd, + rustc_private +)] #![no_core] #![allow(dead_code, non_camel_case_types, internal_features)] @@ -207,10 +216,14 @@ fn main() { assert_eq!(intrinsics::align_of::<u16>() as u8, 2); assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8); - assert!(!intrinsics::needs_drop::<u8>()); - assert!(!intrinsics::needs_drop::<[u8]>()); - assert!(intrinsics::needs_drop::<NoisyDrop>()); - assert!(intrinsics::needs_drop::<NoisyDropUnsized>()); + let u8_needs_drop = const { intrinsics::needs_drop::<u8>() }; + assert!(!u8_needs_drop); + let slice_needs_drop = const { intrinsics::needs_drop::<[u8]>() }; + assert!(!slice_needs_drop); + let noisy_drop = const { intrinsics::needs_drop::<NoisyDrop>() }; + assert!(noisy_drop); + let noisy_unsized_drop = const { intrinsics::needs_drop::<NoisyDropUnsized>() }; + assert!(noisy_unsized_drop); Unique { pointer: NonNull(1 as *mut &str), _marker: PhantomData } as Unique<dyn SomeTrait>; diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 3a62cd52a9d..ee43eb736e6 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -133,7 +133,7 @@ pub(crate) fn codegen_const_value<'tcx>( } } Scalar::Ptr(ptr, _size) => { - let (prov, offset) = ptr.into_parts(); // we know the `offset` is relative + let (prov, offset) = ptr.prov_and_relative_offset(); let alloc_id = prov.alloc_id(); let base_addr = match fx.tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index df5748c34d1..4ff5773a06c 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -812,21 +812,6 @@ fn codegen_regular_intrinsic_call<'tcx>( dest.write_cvalue(fx, val); } - sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => { - intrinsic_args!(fx, args => (); intrinsic); - - let const_val = fx - .tcx - .const_eval_instance( - ty::TypingEnv::fully_monomorphized(), - instance, - source_info.span, - ) - .unwrap(); - let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty); - ret.write_cvalue(fx, val); - } - sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { intrinsic_args!(fx, args => (ptr, base); intrinsic); let ptr = ptr.load_scalar(fx); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 68ff0b622c8..6281089ee24 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -496,7 +496,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( | sym::simd_flog | sym::simd_flog10 | sym::simd_flog2 - | sym::simd_round => { + | sym::simd_round + | sym::simd_round_ties_even => { intrinsic_args!(fx, args => (a); intrinsic); if !a.layout().ty.is_simd() { @@ -527,6 +528,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( (sym::simd_flog2, types::F64) => "log2", (sym::simd_round, types::F32) => "roundf", (sym::simd_round, types::F64) => "round", + (sym::simd_round_ties_even, types::F32) => "rintf", + (sym::simd_round_ties_even, types::F64) => "rint", _ => unreachable!("{:?}", intrinsic), }; fx.lib_call( diff --git a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml index 931f6097abc..29a3bcec304 100644 --- a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml +++ b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml @@ -6,6 +6,7 @@ resolver = "2" [dependencies] core = { path = "./sysroot_src/library/core" } +compiler_builtins = { path = "./sysroot_src/library/compiler-builtins/compiler-builtins" } alloc = { path = "./sysroot_src/library/alloc" } std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] } test = { path = "./sysroot_src/library/test" } diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index f1f31f83ca2..cbb0f949383 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -9,8 +9,8 @@ use crate::build; use crate::config::{Channel, ConfigInfo}; use crate::utils::{ create_dir, get_sysroot_dir, get_toolchain, git_clone, git_clone_root_dir, remove_file, - run_command, run_command_with_env, run_command_with_output, run_command_with_output_and_env, - rustc_version_info, split_args, walk_dir, + run_command, run_command_with_env, run_command_with_output_and_env, rustc_version_info, + split_args, walk_dir, }; type Env = HashMap<String, String>; @@ -485,30 +485,6 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> { run_command_with_output_and_env(&[&"git", &"checkout"], rust_dir, Some(env))?; } - let mut patches = Vec::new(); - walk_dir( - "patches/tests", - &mut |_| Ok(()), - &mut |file_path: &Path| { - patches.push(file_path.to_path_buf()); - Ok(()) - }, - false, - )?; - patches.sort(); - // TODO: remove duplication with prepare.rs by creating a apply_patch function in the utils - // module. - for file_path in patches { - println!("[GIT] apply `{}`", file_path.display()); - let path = Path::new("../..").join(file_path); - run_command_with_output(&[&"git", &"apply", &path], rust_dir)?; - run_command_with_output(&[&"git", &"add", &"-A"], rust_dir)?; - run_command_with_output( - &[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())], - rust_dir, - )?; - } - let cargo = String::from_utf8( run_command_with_env(&[&"rustup", &"which", &"cargo"], rust_dir, Some(env))?.stdout, ) diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index c3bd62e5897..6b6f71edaf8 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -6,6 +6,7 @@ )] #![no_core] #![allow(dead_code, internal_features, non_camel_case_types)] +#![rustfmt::skip] extern crate mini_core; @@ -197,10 +198,10 @@ fn main() { assert_eq!(intrinsics::align_of::<u16>() as u8, 2); assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8); - assert!(!intrinsics::needs_drop::<u8>()); - assert!(!intrinsics::needs_drop::<[u8]>()); - assert!(intrinsics::needs_drop::<NoisyDrop>()); - assert!(intrinsics::needs_drop::<NoisyDropUnsized>()); + assert!(!const { intrinsics::needs_drop::<u8>() }); + assert!(!const { intrinsics::needs_drop::<[u8]>() }); + assert!(const { intrinsics::needs_drop::<NoisyDrop>() }); + assert!(const { intrinsics::needs_drop::<NoisyDropUnsized>() }); Unique { pointer: 0 as *const &str, diff --git a/compiler/rustc_codegen_gcc/patches/tests/0001-Workaround-to-make-a-run-make-test-pass.patch b/compiler/rustc_codegen_gcc/patches/tests/0001-Workaround-to-make-a-run-make-test-pass.patch deleted file mode 100644 index a329d09a95e..00000000000 --- a/compiler/rustc_codegen_gcc/patches/tests/0001-Workaround-to-make-a-run-make-test-pass.patch +++ /dev/null @@ -1,25 +0,0 @@ -From a131c69e54b5c02fe3b517e8f3ad23d4f784ffc8 Mon Sep 17 00:00:00 2001 -From: Antoni Boucher <bouanto@zoho.com> -Date: Fri, 13 Jun 2025 20:25:33 -0400 -Subject: [PATCH] Workaround to make a run-make test pass - ---- - tests/run-make/linker-warning/rmake.rs | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs -index bc21739fefc..0946a7e2a48 100644 ---- a/tests/run-make/linker-warning/rmake.rs -+++ b/tests/run-make/linker-warning/rmake.rs -@@ -55,7 +55,7 @@ fn main() { - diff() - .expected_file("short-error.txt") - .actual_text("(linker error)", out.stderr()) -- .normalize(r#"/rustc[^/]*/"#, "/rustc/") -+ .normalize(r#"/tmp/rustc[^/]*/"#, "/tmp/rustc/") - .normalize( - regex::escape(run_make_support::build_root().to_str().unwrap()), - "/build-root", --- -2.49.0 - diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index 8be204c1581..bccbc6cd2c5 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-06-02" +channel = "nightly-2025-06-28" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 08f3d281904..0b359f1c5c8 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -1,7 +1,9 @@ #[cfg(feature = "master")] use gccjit::FnAttribute; use gccjit::{ToLValue, ToRValue, Type}; -use rustc_abi::{ArmCall, CanonAbi, InterruptKind, Reg, RegKind, X86Call}; +#[cfg(feature = "master")] +use rustc_abi::{ArmCall, CanonAbi, InterruptKind, X86Call}; +use rustc_abi::{Reg, RegKind}; use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeCodegenMethods}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 1fce547ad1b..b1785af444a 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -538,11 +538,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn ret(&mut self, mut value: RValue<'gcc>) { - if self.structs_as_pointer.borrow().contains(&value) { - // NOTE: hack to workaround a limitation of the rustc API: see comment on - // CodegenCx.structs_as_pointer - value = value.dereference(self.location).to_rvalue(); - } let expected_return_type = self.current_func().get_return_type(); if !expected_return_type.is_compatible_with(value.get_type()) { // NOTE: due to opaque pointers now being used, we need to cast here. @@ -700,7 +695,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let a = self.gcc_int_cast(a, a_type); let b_type = b.get_type().to_unsigned(self); let b = self.gcc_int_cast(b, b_type); - a / b + self.gcc_udiv(a, b) } fn sdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { @@ -712,8 +707,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // FIXME(antoyo): rustc_codegen_ssa::mir::intrinsic uses different types for a and b but they // should be the same. let typ = a.get_type().to_signed(self); - let b = self.context.new_cast(self.location, b, typ); - a / b + let b = self.gcc_int_cast(b, typ); + self.gcc_sdiv(a, b) } fn fdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { @@ -1119,13 +1114,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // TODO(antoyo) } - fn store(&mut self, mut val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { - if self.structs_as_pointer.borrow().contains(&val) { - // NOTE: hack to workaround a limitation of the rustc API: see comment on - // CodegenCx.structs_as_pointer - val = val.dereference(self.location).to_rvalue(); - } - + fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { self.store_with_flags(val, ptr, align, MemFlags::empty()) } @@ -1508,16 +1497,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { element.get_address(self.location) } else if value_type.dyncast_vector().is_some() { panic!(); - } else if let Some(pointer_type) = value_type.get_pointee() { - if let Some(struct_type) = pointer_type.is_struct() { - // NOTE: hack to workaround a limitation of the rustc API: see comment on - // CodegenCx.structs_as_pointer - aggregate_value - .dereference_field(self.location, struct_type.get_field(idx as i32)) - .to_rvalue() - } else { - panic!("Unexpected type {:?}", value_type); - } } else if let Some(struct_type) = value_type.is_struct() { aggregate_value .access_field(self.location, struct_type.get_field(idx as i32)) @@ -1537,21 +1516,18 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { assert_eq!(idx as usize as u64, idx); let value_type = aggregate_value.get_type(); + let new_val = self.current_func().new_local(None, value_type, "aggregate_value"); + self.block.add_assignment(None, new_val, aggregate_value); + let lvalue = if value_type.dyncast_array().is_some() { let index = self .context .new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from")); - self.context.new_array_access(self.location, aggregate_value, index) + self.context.new_array_access(self.location, new_val, index) } else if value_type.dyncast_vector().is_some() { panic!(); - } else if let Some(pointer_type) = value_type.get_pointee() { - if let Some(struct_type) = pointer_type.is_struct() { - // NOTE: hack to workaround a limitation of the rustc API: see comment on - // CodegenCx.structs_as_pointer - aggregate_value.dereference_field(self.location, struct_type.get_field(idx as i32)) - } else { - panic!("Unexpected type {:?}", value_type); - } + } else if let Some(struct_type) = value_type.is_struct() { + new_val.access_field(None, struct_type.get_field(idx as i32)) } else { panic!("Unexpected type {:?}", value_type); }; @@ -1568,7 +1544,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { self.llbb().add_assignment(self.location, lvalue, value); - aggregate_value + new_val.to_rvalue() } fn set_personality_fn(&mut self, _personality: Function<'gcc>) { diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index fdd47821b51..dd582834fac 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -117,15 +117,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> { let local = self.current_func.borrow().expect("func").new_local(None, typ, "undefined"); - if typ.is_struct().is_some() { - // NOTE: hack to workaround a limitation of the rustc API: see comment on - // CodegenCx.structs_as_pointer - let pointer = local.get_address(None); - self.structs_as_pointer.borrow_mut().insert(pointer); - pointer - } else { - local.to_rvalue() - } + local.to_rvalue() } fn const_poison(&self, typ: Type<'gcc>) -> RValue<'gcc> { @@ -248,7 +240,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { } } Scalar::Ptr(ptr, _size) => { - let (prov, offset) = ptr.into_parts(); // we know the `offset` is relative + let (prov, offset) = ptr.prov_and_relative_offset(); let alloc_id = prov.alloc_id(); let base_addr = match self.tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 1d029811dfe..665cf22ddba 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -124,14 +124,6 @@ pub struct CodegenCx<'gcc, 'tcx> { pub pointee_infos: RefCell<FxHashMap<(Ty<'tcx>, Size), Option<PointeeInfo>>>, - /// NOTE: a hack is used because the rustc API is not suitable to libgccjit and as such, - /// `const_undef()` returns struct as pointer so that they can later be assigned a value (in - /// e.g. Builder::insert_value). - /// As such, this set remembers which of these pointers were returned by this function so that - /// they can be dereferenced later. - /// FIXME(antoyo): fix the rustc API to avoid having this hack. - pub structs_as_pointer: RefCell<FxHashSet<RValue<'gcc>>>, - #[cfg(feature = "master")] pub cleanup_blocks: RefCell<FxHashSet<Block<'gcc>>>, /// The alignment of a u128/i128 type. @@ -304,7 +296,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { #[cfg(feature = "master")] rust_try_fn: Cell::new(None), pointee_infos: Default::default(), - structs_as_pointer: Default::default(), #[cfg(feature = "master")] cleanup_blocks: Default::default(), }; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs index f0352c5e6e5..915ed875e32 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs @@ -1,9 +1,9 @@ // File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py` // DO NOT EDIT IT! /// Translate a given LLVM intrinsic name to an equivalent GCC one. -fn map_arch_intrinsic(name: &str) -> &str { - let Some(name) = name.strip_prefix("llvm.") else { - unimplemented!("***** unsupported LLVM intrinsic {}", name) +fn map_arch_intrinsic(full_name: &str) -> &'static str { + let Some(name) = full_name.strip_prefix("llvm.") else { + unimplemented!("***** unsupported LLVM intrinsic {}", full_name) }; let Some((arch, name)) = name.split_once('.') else { unimplemented!("***** unsupported LLVM intrinsic {}", name) @@ -11,7 +11,7 @@ fn map_arch_intrinsic(name: &str) -> &str { match arch { "AMDGPU" => { #[allow(non_snake_case)] - fn AMDGPU(name: &str) -> &str { + fn AMDGPU(name: &str, full_name: &str) -> &'static str { match name { // AMDGPU "div.fixup.f32" => "__builtin_amdgpu_div_fixup", @@ -42,14 +42,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "trig.preop.f64" => "__builtin_amdgpu_trig_preop", "trig.preop.v2f64" => "__builtin_amdgpu_trig_preop", "trig.preop.v4f32" => "__builtin_amdgpu_trig_preop", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - AMDGPU(name) + AMDGPU(name, full_name) } "aarch64" => { #[allow(non_snake_case)] - fn aarch64(name: &str) -> &str { + fn aarch64(name: &str, full_name: &str) -> &'static str { match name { // aarch64 "chkfeat" => "__builtin_arm_chkfeat", @@ -75,14 +75,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "tcommit" => "__builtin_arm_tcommit", "tstart" => "__builtin_arm_tstart", "ttest" => "__builtin_arm_ttest", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - aarch64(name) + aarch64(name, full_name) } "amdgcn" => { #[allow(non_snake_case)] - fn amdgcn(name: &str) -> &str { + fn amdgcn(name: &str, full_name: &str) -> &'static str { match name { // amdgcn "alignbyte" => "__builtin_amdgcn_alignbyte", @@ -99,6 +99,8 @@ fn map_arch_intrinsic(name: &str) -> &str { "cvt.f32.fp8" => "__builtin_amdgcn_cvt_f32_fp8", "cvt.off.f32.i4" => "__builtin_amdgcn_cvt_off_f32_i4", "cvt.pk.bf8.f32" => "__builtin_amdgcn_cvt_pk_bf8_f32", + "cvt.pk.f16.bf8" => "__builtin_amdgcn_cvt_pk_f16_bf8", + "cvt.pk.f16.fp8" => "__builtin_amdgcn_cvt_pk_f16_fp8", "cvt.pk.f32.bf8" => "__builtin_amdgcn_cvt_pk_f32_bf8", "cvt.pk.f32.fp8" => "__builtin_amdgcn_cvt_pk_f32_fp8", "cvt.pk.fp8.f32" => "__builtin_amdgcn_cvt_pk_fp8_f32", @@ -292,6 +294,7 @@ fn map_arch_intrinsic(name: &str) -> &str { "s.sendmsg" => "__builtin_amdgcn_s_sendmsg", "s.sendmsghalt" => "__builtin_amdgcn_s_sendmsghalt", "s.setprio" => "__builtin_amdgcn_s_setprio", + "s.setprio.inc.wg" => "__builtin_amdgcn_s_setprio_inc_wg", "s.setreg" => "__builtin_amdgcn_s_setreg", "s.sleep" => "__builtin_amdgcn_s_sleep", "s.sleep.var" => "__builtin_amdgcn_s_sleep_var", @@ -356,14 +359,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "workitem.id.x" => "__builtin_amdgcn_workitem_id_x", "workitem.id.y" => "__builtin_amdgcn_workitem_id_y", "workitem.id.z" => "__builtin_amdgcn_workitem_id_z", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - amdgcn(name) + amdgcn(name, full_name) } "arm" => { #[allow(non_snake_case)] - fn arm(name: &str) -> &str { + fn arm(name: &str, full_name: &str) -> &'static str { match name { // arm "cdp" => "__builtin_arm_cdp", @@ -465,14 +468,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "usub8" => "__builtin_arm_usub8", "uxtab16" => "__builtin_arm_uxtab16", "uxtb16" => "__builtin_arm_uxtb16", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - arm(name) + arm(name, full_name) } "bpf" => { #[allow(non_snake_case)] - fn bpf(name: &str) -> &str { + fn bpf(name: &str, full_name: &str) -> &'static str { match name { // bpf "btf.type.id" => "__builtin_bpf_btf_type_id", @@ -487,25 +490,25 @@ fn map_arch_intrinsic(name: &str) -> &str { "preserve.field.info" => "__builtin_bpf_preserve_field_info", "preserve.type.info" => "__builtin_bpf_preserve_type_info", "pseudo" => "__builtin_bpf_pseudo", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - bpf(name) + bpf(name, full_name) } "cuda" => { #[allow(non_snake_case)] - fn cuda(name: &str) -> &str { + fn cuda(name: &str, full_name: &str) -> &'static str { match name { // cuda "syncthreads" => "__syncthreads", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - cuda(name) + cuda(name, full_name) } "hexagon" => { #[allow(non_snake_case)] - fn hexagon(name: &str) -> &str { + fn hexagon(name: &str, full_name: &str) -> &'static str { match name { // hexagon "A2.abs" => "__builtin_HEXAGON_A2_abs", @@ -2479,14 +2482,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "prefetch" => "__builtin_HEXAGON_prefetch", "vmemcpy" => "__builtin_hexagon_vmemcpy", "vmemset" => "__builtin_hexagon_vmemset", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - hexagon(name) + hexagon(name, full_name) } "loongarch" => { #[allow(non_snake_case)] - fn loongarch(name: &str) -> &str { + fn loongarch(name: &str, full_name: &str) -> &'static str { match name { // loongarch "asrtgt.d" => "__builtin_loongarch_asrtgt_d", @@ -3988,14 +3991,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "movfcsr2gr" => "__builtin_loongarch_movfcsr2gr", "movgr2fcsr" => "__builtin_loongarch_movgr2fcsr", "syscall" => "__builtin_loongarch_syscall", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - loongarch(name) + loongarch(name, full_name) } "mips" => { #[allow(non_snake_case)] - fn mips(name: &str) -> &str { + fn mips(name: &str, full_name: &str) -> &'static str { match name { // mips "absq.s.ph" => "__builtin_mips_absq_s_ph", @@ -4669,14 +4672,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "wrdsp" => "__builtin_mips_wrdsp", "xor.v" => "__builtin_msa_xor_v", "xori.b" => "__builtin_msa_xori_b", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - mips(name) + mips(name, full_name) } "nvvm" => { #[allow(non_snake_case)] - fn nvvm(name: &str) -> &str { + fn nvvm(name: &str, full_name: &str) -> &'static str { match name { // nvvm "abs.i" => "__nvvm_abs_i", @@ -5024,6 +5027,7 @@ fn map_arch_intrinsic(name: &str) -> &str { "nanosleep" => "__nvvm_nanosleep", "neg.bf16" => "__nvvm_neg_bf16", "neg.bf16x2" => "__nvvm_neg_bf16x2", + "pm.event.mask" => "__nvvm_pm_event_mask", "popc.i" => "__nvvm_popc_i", "popc.ll" => "__nvvm_popc_ll", "prmt" => "__nvvm_prmt", @@ -5448,14 +5452,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "vote.ballot.sync" => "__nvvm_vote_ballot_sync", "vote.uni" => "__nvvm_vote_uni", "vote.uni.sync" => "__nvvm_vote_uni_sync", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - nvvm(name) + nvvm(name, full_name) } "ppc" => { #[allow(non_snake_case)] - fn ppc(name: &str) -> &str { + fn ppc(name: &str, full_name: &str) -> &'static str { match name { // ppc "addex" => "__builtin_ppc_addex", @@ -5842,7 +5846,10 @@ fn map_arch_intrinsic(name: &str) -> &str { "mulhdu" => "__builtin_ppc_mulhdu", "mulhw" => "__builtin_ppc_mulhw", "mulhwu" => "__builtin_ppc_mulhwu", + "national2packed" => "__builtin_ppc_national2packed", "pack.longdouble" => "__builtin_pack_longdouble", + "packed2national" => "__builtin_ppc_packed2national", + "packed2zoned" => "__builtin_ppc_packed2zoned", "pdepd" => "__builtin_pdepd", "pextd" => "__builtin_pextd", "qpx.qvfabs" => "__builtin_qpx_qvfabs", @@ -6035,14 +6042,15 @@ fn map_arch_intrinsic(name: &str) -> &str { "vsx.xxinsertw" => "__builtin_vsx_xxinsertw", "vsx.xxleqv" => "__builtin_vsx_xxleqv", "vsx.xxpermx" => "__builtin_vsx_xxpermx", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + "zoned2packed" => "__builtin_ppc_zoned2packed", + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - ppc(name) + ppc(name, full_name) } "ptx" => { #[allow(non_snake_case)] - fn ptx(name: &str) -> &str { + fn ptx(name: &str, full_name: &str) -> &'static str { match name { // ptx "bar.sync" => "__builtin_ptx_bar_sync", @@ -6063,14 +6071,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "read.pm3" => "__builtin_ptx_read_pm3", "read.smid" => "__builtin_ptx_read_smid", "read.warpid" => "__builtin_ptx_read_warpid", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - ptx(name) + ptx(name, full_name) } "r600" => { #[allow(non_snake_case)] - fn r600(name: &str) -> &str { + fn r600(name: &str, full_name: &str) -> &'static str { match name { // r600 "group.barrier" => "__builtin_r600_group_barrier", @@ -6088,14 +6096,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "read.tidig.x" => "__builtin_r600_read_tidig_x", "read.tidig.y" => "__builtin_r600_read_tidig_y", "read.tidig.z" => "__builtin_r600_read_tidig_z", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - r600(name) + r600(name, full_name) } "riscv" => { #[allow(non_snake_case)] - fn riscv(name: &str) -> &str { + fn riscv(name: &str, full_name: &str) -> &'static str { match name { // riscv "aes32dsi" => "__builtin_riscv_aes32dsi", @@ -6119,14 +6127,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "sha512sum0r" => "__builtin_riscv_sha512sum0r", "sha512sum1" => "__builtin_riscv_sha512sum1", "sha512sum1r" => "__builtin_riscv_sha512sum1r", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - riscv(name) + riscv(name, full_name) } "s390" => { #[allow(non_snake_case)] - fn s390(name: &str) -> &str { + fn s390(name: &str, full_name: &str) -> &'static str { match name { // s390 "bdepg" => "__builtin_s390_bdepg", @@ -6313,14 +6321,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "vupllf" => "__builtin_s390_vupllf", "vupllg" => "__builtin_s390_vupllg", "vupllh" => "__builtin_s390_vupllh", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - s390(name) + s390(name, full_name) } "ve" => { #[allow(non_snake_case)] - fn ve(name: &str) -> &str { + fn ve(name: &str, full_name: &str) -> &'static str { match name { // ve "vl.andm.MMM" => "__builtin_ve_vl_andm_MMM", @@ -7586,14 +7594,14 @@ fn map_arch_intrinsic(name: &str) -> &str { "vl.vxor.vvvvl" => "__builtin_ve_vl_vxor_vvvvl", "vl.xorm.MMM" => "__builtin_ve_vl_xorm_MMM", "vl.xorm.mmm" => "__builtin_ve_vl_xorm_mmm", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - ve(name) + ve(name, full_name) } "x86" => { #[allow(non_snake_case)] - fn x86(name: &str) -> &str { + fn x86(name: &str, full_name: &str) -> &'static str { match name { // x86 "aadd32" => "__builtin_ia32_aadd32", @@ -10154,25 +10162,25 @@ fn map_arch_intrinsic(name: &str) -> &str { "xresldtrk" => "__builtin_ia32_xresldtrk", "xsusldtrk" => "__builtin_ia32_xsusldtrk", "xtest" => "__builtin_ia32_xtest", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - x86(name) + x86(name, full_name) } "xcore" => { #[allow(non_snake_case)] - fn xcore(name: &str) -> &str { + fn xcore(name: &str, full_name: &str) -> &'static str { match name { // xcore "bitrev" => "__builtin_bitrev", "getid" => "__builtin_getid", "getps" => "__builtin_getps", "setps" => "__builtin_setps", - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } - xcore(name) + xcore(name, full_name) } - _ => unimplemented!("***** unsupported LLVM intrinsic {}", name), + _ => unimplemented!("***** unsupported LLVM architecture {arch}, intrinsic:{full_name}"), } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index 0b77694f115..39dba28b24c 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs @@ -648,6 +648,11 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( new_args.push(handle); args = new_args.into(); } + "__builtin_ia32_rdtscp" => { + let result = builder.current_func().new_local(None, builder.u32_type, "result"); + let new_args = vec![result.get_address(None).to_rvalue()]; + args = new_args.into(); + } _ => (), } } else { @@ -764,6 +769,14 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( new_args.swap(0, 1); args = new_args.into(); } + "__builtin_ia32_dpps256" => { + let mut new_args = args.to_vec(); + // NOTE: without this cast to u8 (and it needs to be a u8 to fix the issue), we + // would get the following error: + // the last argument must be an 8-bit immediate + new_args[2] = builder.context.new_cast(None, new_args[2], builder.cx.type_u8()); + args = new_args.into(); + } _ => (), } } @@ -935,6 +948,19 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>( ); return_value = result.to_rvalue(); } + "__builtin_ia32_rdtscp" => { + let field1 = builder.context.new_field(None, return_value.get_type(), "rdtscpField1"); + let return2 = args[0].dereference(None).to_rvalue(); + let field2 = builder.context.new_field(None, return2.get_type(), "rdtscpField2"); + let struct_type = + builder.context.new_struct_type(None, "rdtscpResult", &[field1, field2]); + return_value = builder.context.new_struct_constructor( + None, + struct_type.as_type(), + None, + &[return_value, return2], + ); + } _ => (), } @@ -1529,6 +1555,17 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.aesdecwide128kl" => "__builtin_ia32_aesdecwide128kl_u8", "llvm.x86.aesencwide256kl" => "__builtin_ia32_aesencwide256kl_u8", "llvm.x86.aesdecwide256kl" => "__builtin_ia32_aesdecwide256kl_u8", + "llvm.x86.avx512.uitofp.round.v8f16.v8i16" => "__builtin_ia32_vcvtuw2ph128_mask", + "llvm.x86.avx512.uitofp.round.v16f16.v16i16" => "__builtin_ia32_vcvtuw2ph256_mask", + "llvm.x86.avx512.uitofp.round.v32f16.v32i16" => "__builtin_ia32_vcvtuw2ph512_mask_round", + "llvm.x86.avx512.uitofp.round.v8f16.v8i32" => "__builtin_ia32_vcvtudq2ph256_mask", + "llvm.x86.avx512.uitofp.round.v16f16.v16i32" => "__builtin_ia32_vcvtudq2ph512_mask_round", + "llvm.x86.avx512.uitofp.round.v8f16.v8i64" => "__builtin_ia32_vcvtuqq2ph512_mask_round", + "llvm.x86.avx512.uitofp.round.v8f64.v8i64" => "__builtin_ia32_cvtuqq2pd512_mask", + "llvm.x86.avx512.uitofp.round.v2f64.v2i64" => "__builtin_ia32_cvtuqq2pd128_mask", + "llvm.x86.avx512.uitofp.round.v4f64.v4i64" => "__builtin_ia32_cvtuqq2pd256_mask", + "llvm.x86.avx512.uitofp.round.v8f32.v8i64" => "__builtin_ia32_cvtuqq2ps512_mask", + "llvm.x86.avx512.uitofp.round.v4f32.v4i64" => "__builtin_ia32_cvtuqq2ps256_mask", // TODO: support the tile builtins: "llvm.x86.ldtilecfg" => "__builtin_trap", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 4c10380fe74..497605978fe 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -114,7 +114,6 @@ fn get_simple_intrinsic<'gcc, 'tcx>( } sym::copysignf32 => "copysignf", sym::copysignf64 => "copysign", - sym::copysignf128 => "copysignl", sym::floorf32 => "floorf", sym::floorf64 => "floor", sym::ceilf32 => "ceilf", @@ -238,6 +237,7 @@ fn get_simple_function_f128_2args<'gcc, 'tcx>( let func_name = match name { sym::maxnumf128 => "fmaxf128", sym::minnumf128 => "fminf128", + sym::copysignf128 => "copysignf128", _ => return None, }; Some(cx.context.new_function( @@ -261,6 +261,7 @@ fn f16_builtin<'gcc, 'tcx>( let f32_type = cx.type_f32(); let builtin_name = match name { sym::ceilf16 => "__builtin_ceilf", + sym::copysignf16 => "__builtin_copysignf", sym::floorf16 => "__builtin_floorf", sym::fmaf16 => "fmaf", sym::maxnumf16 => "__builtin_fmaxf", @@ -330,6 +331,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc ) } sym::ceilf16 + | sym::copysignf16 | sym::floorf16 | sym::fmaf16 | sym::maxnumf16 diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index ac8b7f4ea48..2e508813fc3 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -780,6 +780,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( sym::simd_fsin => "sin", sym::simd_fsqrt => "sqrt", sym::simd_round => "round", + sym::simd_round_ties_even => "rint", sym::simd_trunc => "trunc", _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }), }; @@ -827,6 +828,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( | sym::simd_fsin | sym::simd_fsqrt | sym::simd_round + | sym::simd_round_ties_even | sym::simd_trunc ) { return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args); diff --git a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py index ed0ebf00719..88927f39b93 100644 --- a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py +++ b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py @@ -176,14 +176,14 @@ def update_intrinsics(llvm_path, llvmint, llvmint2): out.write("// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`\n") out.write("// DO NOT EDIT IT!\n") out.write("/// Translate a given LLVM intrinsic name to an equivalent GCC one.\n") - out.write("fn map_arch_intrinsic(name:&str)->&str{\n") - out.write('let Some(name) = name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", name) };\n') + out.write("fn map_arch_intrinsic(full_name:&str)->&'static str{\n") + out.write('let Some(name) = full_name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", full_name) };\n') out.write('let Some((arch, name)) = name.split_once(\'.\') else { unimplemented!("***** unsupported LLVM intrinsic {}", name) };\n') out.write("match arch {\n") for arch in archs: if len(intrinsics[arch]) == 0: continue - out.write("\"{}\" => {{ #[allow(non_snake_case)] fn {}(name: &str) -> &str {{ match name {{".format(arch,arch)) + out.write("\"{}\" => {{ #[allow(non_snake_case)] fn {}(name: &str,full_name:&str) -> &'static str {{ match name {{".format(arch,arch)) intrinsics[arch].sort(key=lambda x: (x[0], x[2])) out.write(' // {}\n'.format(arch)) for entry in intrinsics[arch]: @@ -196,9 +196,9 @@ def update_intrinsics(llvm_path, llvmint, llvmint2): out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(llvm_name, entry[1])) else: out.write(' "{}" => "{}",\n'.format(llvm_name, entry[1])) - out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {}", name),\n') - out.write("}} }} {}(name) }}\n,".format(arch)) - out.write(' _ => unimplemented!("***** unsupported LLVM architecture {}", name),\n') + out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"),\n') + out.write("}} }} {}(name,full_name) }}\n,".format(arch)) + out.write(' _ => unimplemented!("***** unsupported LLVM architecture {arch}, intrinsic:{full_name}"),\n') out.write("}\n}") subprocess.call(["rustfmt", output_file]) print("Done!") diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index ae5add59322..7cfab25bc50 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -268,7 +268,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { } } Scalar::Ptr(ptr, _size) => { - let (prov, offset) = ptr.into_parts(); + let (prov, offset) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); let base_addr = match global_alloc { GlobalAlloc::Memory(alloc) => { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index f7f062849a8..9930eae3fe7 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1537,6 +1537,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( sym::simd_fsin => "llvm.sin", sym::simd_fsqrt => "llvm.sqrt", sym::simd_round => "llvm.round", + sym::simd_round_ties_even => "llvm.rint", sym::simd_trunc => "llvm.trunc", _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }), }; @@ -1563,6 +1564,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( | sym::simd_fsqrt | sym::simd_relaxed_fma | sym::simd_round + | sym::simd_round_ties_even | sym::simd_trunc ) { return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args); @@ -2309,7 +2311,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // Unary integer intrinsics if matches!( name, - sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_ctpop | sym::simd_cttz + sym::simd_bswap + | sym::simd_bitreverse + | sym::simd_ctlz + | sym::simd_ctpop + | sym::simd_cttz + | sym::simd_funnel_shl + | sym::simd_funnel_shr ) { let vec_ty = bx.cx.type_vector( match *in_elem.kind() { @@ -2330,6 +2338,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( sym::simd_ctlz => "llvm.ctlz", sym::simd_ctpop => "llvm.ctpop", sym::simd_cttz => "llvm.cttz", + sym::simd_funnel_shl => "llvm.fshl", + sym::simd_funnel_shr => "llvm.fshr", _ => unreachable!(), }; let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits(); @@ -2350,6 +2360,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // simple unary argument cases Ok(bx.call_intrinsic(llvm_intrinsic, &[vec_ty], &[args[0].immediate()])) } + sym::simd_funnel_shl | sym::simd_funnel_shr => Ok(bx.call_intrinsic( + llvm_intrinsic, + &[vec_ty], + &[args[0].immediate(), args[1].immediate(), args[2].immediate()], + )), _ => unreachable!(), }; } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 6fd07d562af..0fb987bdf82 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -370,10 +370,18 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { let target_env = sess.target.options.env.as_ref(); let target_abi = sess.target.options.abi.as_ref(); let target_pointer_width = sess.target.pointer_width; + let version = get_version(); cfg.has_reliable_f16 = match (target_arch, target_os) { // Selection failure <https://github.com/llvm/llvm-project/issues/50374> ("s390x", _) => false, + // LLVM crash without neon <https://github.com/llvm/llvm-project/issues/129394> (now fixed) + ("aarch64", _) + if !cfg.target_features.iter().any(|f| f.as_str() == "neon") + && version < (20, 1, 1) => + { + false + } // Unsupported <https://github.com/llvm/llvm-project/issues/94434> ("arm64ec", _) => false, // MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054> diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index ede11495503..1896f63bd2d 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1794,7 +1794,10 @@ fn for_each_exported_symbols_include_dep<'tcx>( for (cnum, dep_format) in deps.iter_enumerated() { // For each dependency that we are linking to statically ... if *dep_format == Linkage::Static { - for &(symbol, info) in tcx.exported_symbols(cnum).iter() { + for &(symbol, info) in tcx.exported_non_generic_symbols(cnum).iter() { + callback(symbol, info, cnum); + } + for &(symbol, info) in tcx.exported_generic_symbols(cnum).iter() { callback(symbol, info, cnum); } } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 75f7a463556..2f5eca2d6b2 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -168,7 +168,7 @@ fn is_reachable_non_generic_provider_extern(tcx: TyCtxt<'_>, def_id: DefId) -> b tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) } -fn exported_symbols_provider_local<'tcx>( +fn exported_non_generic_symbols_provider_local<'tcx>( tcx: TyCtxt<'tcx>, _: LocalCrate, ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { @@ -306,6 +306,22 @@ fn exported_symbols_provider_local<'tcx>( )); } + // Sort so we get a stable incr. comp. hash. + symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx)); + + tcx.arena.alloc_from_iter(symbols) +} + +fn exported_generic_symbols_provider_local<'tcx>( + tcx: TyCtxt<'tcx>, + _: LocalCrate, +) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { + if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() { + return &[]; + } + + let mut symbols: Vec<_> = vec![]; + if tcx.local_crate_exports_generics() { use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; use rustc_middle::ty::InstanceKind; @@ -474,7 +490,7 @@ fn upstream_monomorphizations_provider( let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn(); for &cnum in cnums.iter() { - for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() { + for (exported_symbol, _) in tcx.exported_generic_symbols(cnum).iter() { let (def_id, args) = match *exported_symbol { ExportedSymbol::Generic(def_id, args) => (def_id, args), ExportedSymbol::DropGlue(ty) => { @@ -496,10 +512,7 @@ fn upstream_monomorphizations_provider( ExportedSymbol::AsyncDropGlue(def_id, ty) => (def_id, tcx.mk_args(&[ty.into()])), ExportedSymbol::NonGeneric(..) | ExportedSymbol::ThreadLocalShim(..) - | ExportedSymbol::NoDefId(..) => { - // These are no monomorphizations - continue; - } + | ExportedSymbol::NoDefId(..) => unreachable!("{exported_symbol:?}"), }; let args_map = instances.entry(def_id).or_default(); @@ -554,7 +567,8 @@ fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId) pub(crate) fn provide(providers: &mut Providers) { providers.reachable_non_generics = reachable_non_generics_provider; providers.is_reachable_non_generic = is_reachable_non_generic_provider_local; - providers.exported_symbols = exported_symbols_provider_local; + providers.exported_non_generic_symbols = exported_non_generic_symbols_provider_local; + providers.exported_generic_symbols = exported_generic_symbols_provider_local; providers.upstream_monomorphizations = upstream_monomorphizations_provider; providers.is_unreachable_local_definition = is_unreachable_local_definition_provider; providers.upstream_drop_glue_for = upstream_drop_glue_for_provider; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c3bfe4c13cd..8330e4f7af0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1124,8 +1124,9 @@ fn start_executing_work<B: ExtraBackendMethods>( let copy_symbols = |cnum| { let symbols = tcx - .exported_symbols(cnum) + .exported_non_generic_symbols(cnum) .iter() + .chain(tcx.exported_generic_symbols(cnum)) .map(|&(s, lvl)| (symbol_name_for_instance_in_crate(tcx, s, cnum), lvl)) .collect(); Arc::new(symbols) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 7680f8e1074..6e2143858de 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use rustc_abi::ExternAbi; +use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; use rustc_attr_data_structures::{ @@ -124,6 +124,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name), + AttributeKind::LinkSection { name, .. } => { + codegen_fn_attrs.link_section = Some(*name) + } AttributeKind::NoMangle(attr_span) => { if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; @@ -253,16 +256,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } } - sym::link_section => { - if let Some(val) = attr.value_str() { - if val.as_str().bytes().any(|b| b == 0) { - let msg = format!("illegal null byte in link_section value: `{val}`"); - tcx.dcx().span_err(attr.span(), msg); - } else { - codegen_fn_attrs.link_section = Some(val); - } - } - } sym::link_ordinal => { link_ordinal_span = Some(attr.span()); if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { @@ -402,6 +395,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment); + // On trait methods, inherit the `#[align]` of the trait's method prototype. + codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did)); + let inline_span; (codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) = find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span)) @@ -556,17 +552,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs } +/// If the provided DefId is a method in a trait impl, return the DefId of the method prototype. +fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { + let impl_item = tcx.opt_associated_item(def_id)?; + match impl_item.container { + ty::AssocItemContainer::Impl => impl_item.trait_item_def_id, + _ => None, + } +} + /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if let Some(impl_item) = tcx.opt_associated_item(def_id) - && let ty::AssocItemContainer::Impl = impl_item.container - && let Some(trait_item) = impl_item.trait_item_def_id - { - return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER); - } + let Some(trait_item) = opt_trait_item(tcx, def_id) else { return false }; + tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER) +} - false +/// If the provided DefId is a method in a trait impl, return the value of the `#[align]` +/// attribute on the method prototype (if any). +fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> { + tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment } fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> { @@ -734,5 +739,6 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> { } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; + *providers = + Providers { codegen_fn_attrs, should_inherit_track_caller, inherited_align, ..*providers }; } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 27fcab8ed2d..fc95f62b4a4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,7 +1,7 @@ use rustc_abi::WrappingRange; -use rustc_middle::bug; use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::sym; @@ -98,6 +98,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { discr.to_atomic_ordering() }; + if args.is_empty() { + match name { + sym::abort + | sym::unreachable + | sym::cold_path + | sym::breakpoint + | sym::assert_zero_valid + | sym::assert_mem_uninitialized_valid + | sym::assert_inhabited + | sym::ub_checks + | sym::contract_checks + | sym::atomic_fence + | sym::atomic_singlethreadfence + | sym::caller_location => {} + _ => { + span_bug!(span, "nullary intrinsic {name} must either be in a const block or explicitly opted out because it is inherently a runtime intrinsic +"); + } + } + } + let llval = match name { sym::abort => { bx.abort(); @@ -150,10 +171,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } value } - sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => { - let value = bx.tcx().const_eval_instance(bx.typing_env(), instance, span).unwrap(); - OperandRef::from_const(bx, value, result.layout.ty).immediate_or_packed_pair(bx) - } sym::arith_offset => { let ty = fn_args.type_at(0); let layout = bx.layout_of(ty); diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 66c4af4c935..10b44a1faf0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -354,15 +354,15 @@ fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let destination_block = target.unwrap(); - bb.statements.push(mir::Statement { - source_info: bb.terminator().source_info, - kind: mir::StatementKind::Assign(Box::new(( + bb.statements.push(mir::Statement::new( + bb.terminator().source_info, + mir::StatementKind::Assign(Box::new(( *destination, mir::Rvalue::Use(mir::Operand::Copy( arg_place.project_deeper(&[mir::ProjectionElem::Deref], tcx), )), ))), - }); + )); bb.terminator_mut().kind = mir::TerminatorKind::Goto { target: destination_block }; } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 2a2c3e6aee2..22a1894ee72 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -263,9 +263,6 @@ const_eval_non_const_try_block_from_output = const_eval_not_enough_caller_args = calling a function with fewer arguments than it requires -const_eval_nullary_intrinsic_fail = - could not evaluate nullary intrinsic - const_eval_offset_from_different_allocations = `{$name}` called on two different pointers that are not both derived from the same allocation const_eval_offset_from_out_of_bounds = diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 569a07c3a01..08e1877f0eb 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -20,7 +20,7 @@ use crate::const_eval::CheckAlignment; use crate::interpret::{ CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc, - eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust, + intern_const_alloc_recursive, interp_ok, throw_exhaust, }; use crate::{CTRL_C_RECEIVED, errors}; @@ -209,9 +209,9 @@ pub(super) fn op_to_const<'tcx>( match immediate { Left(ref mplace) => { - // We know `offset` is relative to the allocation, so we can use `into_parts`. - let (prov, offset) = mplace.ptr().into_parts(); - let alloc_id = prov.expect("cannot have `fake` place for non-ZST type").alloc_id(); + let (prov, offset) = + mplace.ptr().into_pointer_or_addr().unwrap().prov_and_relative_offset(); + let alloc_id = prov.alloc_id(); ConstValue::Indirect { alloc_id, offset } } // see comment on `let force_as_immediate` above @@ -232,9 +232,10 @@ pub(super) fn op_to_const<'tcx>( imm.layout.ty, ); let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to the beginning of an actual allocation"; - // We know `offset` is relative to the allocation, so we can use `into_parts`. - let (prov, offset) = a.to_pointer(ecx).expect(msg).into_parts(); - let alloc_id = prov.expect(msg).alloc_id(); + let ptr = a.to_pointer(ecx).expect(msg); + let (prov, offset) = + ptr.into_pointer_or_addr().expect(msg).prov_and_relative_offset(); + let alloc_id = prov.alloc_id(); let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory(); assert!(offset == abi::Size::ZERO, "{}", msg); let meta = b.to_target_usize(ecx).expect(msg); @@ -280,34 +281,6 @@ pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { - // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. - // Catch such calls and evaluate them instead of trying to load a constant's MIR. - if let ty::InstanceKind::Intrinsic(def_id) = key.value.instance.def { - let ty = key.value.instance.ty(tcx, key.typing_env); - let ty::FnDef(_, args) = ty.kind() else { - bug!("intrinsic with type {:?}", ty); - }; - return eval_nullary_intrinsic(tcx, key.typing_env, def_id, args).report_err().map_err( - |error| { - let span = tcx.def_span(def_id); - - // FIXME(oli-obk): why don't we have any tests for this code path? - super::report( - tcx, - error.into_kind(), - span, - || (span, vec![]), - |diag, span, _| { - diag.span_label( - span, - crate::fluent_generated::const_eval_nullary_intrinsic_fail, - ); - }, - ) - }, - ); - } - tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 14abdd8c98c..9133a5fc8ef 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -574,7 +574,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { if addr != 0 { diag.arg( "pointer", - Pointer::<Option<CtfeProvenance>>::from_addr_invalid(addr).to_string(), + Pointer::<Option<CtfeProvenance>>::without_provenance(addr).to_string(), ); } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b29c5c7c7d7..d7cede71293 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -6,10 +6,9 @@ use std::assert_matches::assert_matches; use rustc_abi::Size; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_hir::def_id::DefId; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::{TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_span::{Symbol, sym}; use tracing::trace; @@ -17,8 +16,8 @@ use tracing::trace; use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ - Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, Machine, - OpTy, PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, + Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy, + PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; @@ -30,73 +29,6 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll tcx.mk_const_alloc(alloc) } -/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated -/// inside an `InterpCx` and instead have their value computed directly from rustc internal info. -pub(crate) fn eval_nullary_intrinsic<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - def_id: DefId, - args: GenericArgsRef<'tcx>, -) -> InterpResult<'tcx, ConstValue<'tcx>> { - let tp_ty = args.type_at(0); - let name = tcx.item_name(def_id); - interp_ok(match name { - sym::type_name => { - ensure_monomorphic_enough(tcx, tp_ty)?; - let alloc = alloc_type_name(tcx, tp_ty); - ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() } - } - sym::needs_drop => { - ensure_monomorphic_enough(tcx, tp_ty)?; - ConstValue::from_bool(tp_ty.needs_drop(tcx, typing_env)) - } - sym::type_id => { - ensure_monomorphic_enough(tcx, tp_ty)?; - ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()) - } - sym::variant_count => match match tp_ty.kind() { - // Pattern types have the same number of variants as their base type. - // Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited. - // And `Result<(), !>` still has two variants according to `variant_count`. - ty::Pat(base, _) => *base, - _ => tp_ty, - } - .kind() - { - // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. - ty::Adt(adt, _) => ConstValue::from_target_usize(adt.variants().len() as u64, &tcx), - ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { - throw_inval!(TooGeneric) - } - ty::Pat(..) => unreachable!(), - ty::Bound(_, _) => bug!("bound ty during ctfe"), - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Slice(_) - | ty::RawPtr(_, _) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(..) - | ty::Dynamic(_, _, _) - | ty::Closure(_, _) - | ty::CoroutineClosure(_, _) - | ty::Coroutine(_, _) - | ty::CoroutineWitness(..) - | ty::UnsafeBinder(_) - | ty::Never - | ty::Tuple(_) - | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), - }, - other => bug!("`{}` is not a zero arg intrinsic", other), - }) -} - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own @@ -110,8 +42,77 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, bool> { let instance_args = instance.args; let intrinsic_name = self.tcx.item_name(instance.def_id()); + let tcx = self.tcx.tcx; match intrinsic_name { + sym::type_name => { + let tp_ty = instance.args.type_at(0); + ensure_monomorphic_enough(tcx, tp_ty)?; + let alloc = alloc_type_name(tcx, tp_ty); + let val = ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() }; + let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::needs_drop => { + let tp_ty = instance.args.type_at(0); + ensure_monomorphic_enough(tcx, tp_ty)?; + let val = ConstValue::from_bool(tp_ty.needs_drop(tcx, self.typing_env)); + let val = self.const_val_to_op(val, tcx.types.bool, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::type_id => { + let tp_ty = instance.args.type_at(0); + ensure_monomorphic_enough(tcx, tp_ty)?; + let val = ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()); + let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::variant_count => { + let tp_ty = instance.args.type_at(0); + let ty = match tp_ty.kind() { + // Pattern types have the same number of variants as their base type. + // Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited. + // And `Result<(), !>` still has two variants according to `variant_count`. + ty::Pat(base, _) => *base, + _ => tp_ty, + }; + let val = match ty.kind() { + // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. + ty::Adt(adt, _) => { + ConstValue::from_target_usize(adt.variants().len() as u64, &tcx) + } + ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { + throw_inval!(TooGeneric) + } + ty::Pat(..) => unreachable!(), + ty::Bound(_, _) => bug!("bound ty during ctfe"), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(..) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::UnsafeBinder(_) + | ty::Never + | ty::Tuple(_) + | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), + }; + let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::caller_location => { let span = self.find_closest_untracked_caller_location(); let val = self.tcx.span_as_caller_location(span); @@ -137,21 +138,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(result, self), dest)?; } - sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => { - let gid = GlobalId { instance, promoted: None }; - let ty = self - .tcx - .fn_sig(instance.def_id()) - .instantiate(self.tcx.tcx, instance.args) - .output() - .no_bound_vars() - .unwrap(); - let val = self - .ctfe_query(|tcx| tcx.const_eval_global_id(self.typing_env, gid, tcx.span))?; - let val = self.const_val_to_op(val, ty, Some(dest.layout))?; - self.copy_op(&val, dest)?; - } - sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index d6d230fbd17..35ec303f961 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -747,7 +747,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { // Allow these casts, but make the pointer not dereferenceable. // (I.e., they behave like transmutation.) // This is correct because no pointers can ever be exposed in compile-time evaluation. - interp_ok(Pointer::from_addr_invalid(addr)) + interp_ok(Pointer::without_provenance(addr)) } #[inline(always)] @@ -756,8 +756,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { ptr: Pointer<CtfeProvenance>, _size: i64, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { - // We know `offset` is relative to the allocation, so we can use `into_parts`. - let (prov, offset) = ptr.into_parts(); + let (prov, offset) = ptr.prov_and_relative_offset(); Some((prov.alloc_id(), offset, prov.immutable())) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 69fceb02ff9..3b36bb85985 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1596,7 +1596,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)), None => { assert!(M::Provenance::OFFSET_IS_ADDR); - let (_, addr) = ptr.into_parts(); + // Offset is absolute, as we just asserted. + let (_, addr) = ptr.into_raw_parts(); Err(addr.bytes()) } }, diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index f5792aba207..f8b3c92debb 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -29,7 +29,6 @@ pub use self::intern::{ HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop, intern_const_alloc_recursive, }; -pub(crate) use self::intrinsics::eval_nullary_intrinsic; pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine}; pub use self::memory::{AllocInfo, AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; use self::operand::Operand; diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 3028568dd8f..a3cd35ff0bb 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -118,7 +118,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { pub fn fake_alloc_zst(layout: TyAndLayout<'tcx>) -> Self { assert!(layout.is_zst()); let align = layout.align.abi; - let ptr = Pointer::from_addr_invalid(align.bytes()); // no provenance, absolute address + let ptr = Pointer::without_provenance(align.bytes()); // no provenance, absolute address MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None, misaligned: None }, layout } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 998ef3729ea..72680019380 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -518,7 +518,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance { ptr_kind, // FIXME this says "null pointer" when null but we need translate - pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(i)) + pointer: format!("{}", Pointer::<Option<AllocId>>::without_provenance(i)) }, Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds { ptr_kind @@ -868,7 +868,9 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { fn add_data_range(&mut self, ptr: Pointer<Option<M::Provenance>>, size: Size) { if let Some(data_bytes) = self.data_bytes.as_mut() { // We only have to store the offset, the rest is the same for all pointers here. - let (_prov, offset) = ptr.into_parts(); + // The logic is agnostic to wether the offset is relative or absolute as long as + // it is consistent. + let (_prov, offset) = ptr.into_raw_parts(); // Add this. data_bytes.add_range(offset, size); }; @@ -894,7 +896,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { .as_mplace_or_imm() .expect_left("place must be in memory") .ptr(); - let (_prov, offset) = ptr.into_parts(); + let (_prov, offset) = ptr.into_raw_parts(); offset } @@ -903,7 +905,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // Our value must be in memory, otherwise we would not have set up `data_bytes`. let mplace = self.ecx.force_allocation(place)?; // Determine starting offset and size. - let (_prov, start_offset) = mplace.ptr().into_parts(); + let (_prov, start_offset) = mplace.ptr().into_raw_parts(); let (size, _align) = self .ecx .size_and_align_of_val(&mplace)? diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 4855fc58d03..18490385455 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1112,7 +1112,9 @@ fn get_backend_from_raw_matches( matches: &Matches, ) -> Box<dyn CodegenBackend> { let debug_flags = matches.opt_strs("Z"); - let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); + let backend_name = debug_flags + .iter() + .find_map(|x| x.strip_prefix("codegen-backend=").or(x.strip_prefix("codegen_backend="))); let target = parse_target_triple(early_dcx, matches); let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from)); let target = config::build_target_config(early_dcx, &target, sysroot.path()); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 8da7cdd9358..fe9797026de 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1005,7 +1005,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// * may look like "to do xyz, use" or "to do xyz, use abc" /// * may contain a name of a function, variable, or type, but not whole expressions /// - /// See `CodeSuggestion` for more information. + /// See [`CodeSuggestion`] for more information. #[rustc_lint_diagnostics] pub fn span_suggestion( &mut self, @@ -1166,7 +1166,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Prints out a message with a suggested edit of the code. If the suggestion is presented /// inline, it will only show the message and not the suggestion. /// - /// See `CodeSuggestion` for more information. + /// See [`CodeSuggestion`] for more information. #[rustc_lint_diagnostics] pub fn span_suggestion_short( &mut self, diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index e333de4b660..3f5872f34a6 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2078,7 +2078,9 @@ impl HumanEmitter { // file name, saving in verbosity, but if it *isn't* we do need it, otherwise we're // telling users to make a change but not clarifying *where*. let loc = sm.lookup_char_pos(parts[0].span.lo()); - if loc.file.name != sm.span_to_filename(span) && loc.file.name.is_real() { + if (span.is_dummy() || loc.file.name != sm.span_to_filename(span)) + && loc.file.name.is_real() + { // --> file.rs:line:col // | let arrow = self.file_start(); diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index b7555bba28e..3fc0fa06191 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -109,9 +109,6 @@ expand_malformed_feature_attribute = expand_meta_var_dif_seq_matchers = {$msg} -expand_meta_var_expr_unrecognized_var = - variable `{$key}` is not recognized in meta-variable expression - expand_missing_fragment_specifier = missing fragment specifier .note = fragment specifiers must be provided .suggestion_add_fragspec = try adding a specifier here @@ -136,6 +133,9 @@ expand_module_multiple_candidates = expand_must_repeat_once = this must repeat at least once +expand_mve_unrecognized_var = + variable `{$key}` is not recognized in meta-variable expression + expand_non_inline_modules_in_proc_macro_input_are_unstable = non-inline modules in proc macro input are unstable diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index fe4d2af97a0..fdbc65aff68 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -28,14 +28,6 @@ pub(crate) struct CountRepetitionMisplaced { } #[derive(Diagnostic)] -#[diag(expand_meta_var_expr_unrecognized_var)] -pub(crate) struct MetaVarExprUnrecognizedVar { - #[primary_span] - pub span: Span, - pub key: MacroRulesNormalizedIdent, -} - -#[derive(Diagnostic)] #[diag(expand_var_still_repeating)] pub(crate) struct VarStillRepeating { #[primary_span] @@ -499,3 +491,16 @@ pub(crate) struct ProcMacroBackCompat { pub crate_name: String, pub fixed_version: String, } + +pub(crate) use metavar_exprs::*; +mod metavar_exprs { + use super::*; + + #[derive(Diagnostic)] + #[diag(expand_mve_unrecognized_var)] + pub(crate) struct MveUnrecognizedVar { + #[primary_span] + pub span: Span, + pub key: MacroRulesNormalizedIdent, + } +} diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 1ccb070f83a..ffd3548019a 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -47,46 +47,7 @@ impl MetaVarExpr { check_trailing_token(&mut iter, psess)?; let mut iter = args.iter(); let rslt = match ident.as_str() { - "concat" => { - let mut result = Vec::new(); - loop { - let is_var = try_eat_dollar(&mut iter); - let token = parse_token(&mut iter, psess, outer_span)?; - let element = if is_var { - MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?) - } else if let TokenKind::Literal(Lit { - kind: token::LitKind::Str, - symbol, - suffix: None, - }) = token.kind - { - MetaVarExprConcatElem::Literal(symbol) - } else { - match parse_ident_from_token(psess, token) { - Err(err) => { - err.cancel(); - return Err(psess - .dcx() - .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR)); - } - Ok(elem) => MetaVarExprConcatElem::Ident(elem), - } - }; - result.push(element); - if iter.peek().is_none() { - break; - } - if !try_eat_comma(&mut iter) { - return Err(psess.dcx().struct_span_err(outer_span, "expected comma")); - } - } - if result.len() < 2 { - return Err(psess - .dcx() - .struct_span_err(ident.span, "`concat` must have at least two elements")); - } - MetaVarExpr::Concat(result.into()) - } + "concat" => parse_concat(&mut iter, psess, outer_span, ident.span)?, "count" => parse_count(&mut iter, psess, ident.span)?, "ignore" => { eat_dollar(&mut iter, psess, ident.span)?; @@ -126,6 +87,22 @@ impl MetaVarExpr { } } +// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` +fn check_trailing_token<'psess>( + iter: &mut TokenStreamIter<'_>, + psess: &'psess ParseSess, +) -> PResult<'psess, ()> { + if let Some(tt) = iter.next() { + let mut diag = psess + .dcx() + .struct_span_err(tt.span(), format!("unexpected token: {}", pprust::tt_to_string(tt))); + diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens"); + Err(diag) + } else { + Ok(()) + } +} + /// Indicates what is placed in a `concat` parameter. For example, literals /// (`${concat("foo", "bar")}`) or adhoc identifiers (`${concat(foo, bar)}`). #[derive(Debug, Decodable, Encodable, PartialEq)] @@ -140,20 +117,48 @@ pub(crate) enum MetaVarExprConcatElem { Var(Ident), } -// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` -fn check_trailing_token<'psess>( +/// Parse a meta-variable `concat` expression: `concat($metavar, ident, ...)`. +fn parse_concat<'psess>( iter: &mut TokenStreamIter<'_>, psess: &'psess ParseSess, -) -> PResult<'psess, ()> { - if let Some(tt) = iter.next() { - let mut diag = psess + outer_span: Span, + expr_ident_span: Span, +) -> PResult<'psess, MetaVarExpr> { + let mut result = Vec::new(); + loop { + let is_var = try_eat_dollar(iter); + let token = parse_token(iter, psess, outer_span)?; + let element = if is_var { + MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?) + } else if let TokenKind::Literal(Lit { kind: token::LitKind::Str, symbol, suffix: None }) = + token.kind + { + MetaVarExprConcatElem::Literal(symbol) + } else { + match parse_ident_from_token(psess, token) { + Err(err) => { + err.cancel(); + return Err(psess + .dcx() + .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR)); + } + Ok(elem) => MetaVarExprConcatElem::Ident(elem), + } + }; + result.push(element); + if iter.peek().is_none() { + break; + } + if !try_eat_comma(iter) { + return Err(psess.dcx().struct_span_err(outer_span, "expected comma")); + } + } + if result.len() < 2 { + return Err(psess .dcx() - .struct_span_err(tt.span(), format!("unexpected token: {}", pprust::tt_to_string(tt))); - diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens"); - Err(diag) - } else { - Ok(()) + .struct_span_err(expr_ident_span, "`concat` must have at least two elements")); } + Ok(MetaVarExpr::Concat(result.into())) } /// Parse a meta-variable `count` expression: `count(ident[, depth])` diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index a8c4a9e4b1b..174844d6ad6 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -17,7 +17,7 @@ use rustc_span::{ use smallvec::{SmallVec, smallvec}; use crate::errors::{ - CountRepetitionMisplaced, MetaVarExprUnrecognizedVar, MetaVarsDifSeqMatchers, MustRepeatOnce, + CountRepetitionMisplaced, MetaVarsDifSeqMatchers, MustRepeatOnce, MveUnrecognizedVar, NoSyntaxVarsExprRepeat, VarStillRepeating, }; use crate::mbe::macro_parser::NamedMatch; @@ -879,7 +879,7 @@ where { let span = ident.span; let key = MacroRulesNormalizedIdent::new(ident); - interp.get(&key).ok_or_else(|| dcx.create_err(MetaVarExprUnrecognizedVar { span, key })) + interp.get(&key).ok_or_else(|| dcx.create_err(MveUnrecognizedVar { span, key })) } /// Used by meta-variable expressions when an user input is out of the actual declared bounds. For diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index fb5abaefb57..af91c8b8f00 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -599,8 +599,12 @@ impl server::TokenStream for Rustc<'_, '_> { ast::ExprKind::Lit(token_lit) => { Ok(tokenstream::TokenStream::token_alone(token::Literal(*token_lit), expr.span)) } - ast::ExprKind::IncludedBytes(bytes) => { - let lit = token::Lit::new(token::ByteStr, escape_byte_str_symbol(bytes), None); + ast::ExprKind::IncludedBytes(byte_sym) => { + let lit = token::Lit::new( + token::ByteStr, + escape_byte_str_symbol(byte_sym.as_byte_str()), + None, + ); Ok(tokenstream::TokenStream::token_alone(token::TokenKind::Literal(lit), expr.span)) } ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind { diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index b0dff635a9b..180cb6497e7 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -8,7 +8,6 @@ macro_rules! arena_types { [] asm_template: rustc_ast::InlineAsmTemplatePiece, [] attribute: rustc_hir::Attribute, [] owner_info: rustc_hir::OwnerInfo<'tcx>, - [] lit: rustc_hir::Lit, [] macro_def: rustc_ast::MacroDef, ]); ) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 75dff588669..4bb6a796be4 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1807,7 +1807,7 @@ pub struct PatExpr<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum PatExprKind<'hir> { Lit { - lit: &'hir Lit, + lit: Lit, // FIXME: move this into `Lit` and handle negated literal expressions // once instead of matching on unop neg expressions everywhere. negated: bool, @@ -2734,7 +2734,7 @@ pub enum ExprKind<'hir> { /// A unary operation (e.g., `!x`, `*x`). Unary(UnOp, &'hir Expr<'hir>), /// A literal (e.g., `1`, `"foo"`). - Lit(&'hir Lit), + Lit(Lit), /// A cast (e.g., `foo as f64`). Cast(&'hir Expr<'hir>, &'hir Ty<'hir>), /// A type ascription (e.g., `x: Foo`). See RFC 3307. @@ -3141,15 +3141,6 @@ pub enum TraitItemKind<'hir> { /// type. Type(GenericBounds<'hir>, Option<&'hir Ty<'hir>>), } -impl TraitItemKind<'_> { - pub fn descr(&self) -> &'static str { - match self { - TraitItemKind::Const(..) => "associated constant", - TraitItemKind::Fn(..) => "function", - TraitItemKind::Type(..) => "associated type", - } - } -} // The bodies for items are stored "out of line", in a separate // hashmap in the `Crate`. Here we just record the hir-id of the item @@ -3211,15 +3202,6 @@ pub enum ImplItemKind<'hir> { /// An associated type. Type(&'hir Ty<'hir>), } -impl ImplItemKind<'_> { - pub fn descr(&self) -> &'static str { - match self { - ImplItemKind::Const(..) => "associated constant", - ImplItemKind::Fn(..) => "function", - ImplItemKind::Type(..) => "associated type", - } - } -} /// A constraint on an associated item. /// @@ -4545,16 +4527,6 @@ pub enum ForeignItemKind<'hir> { Type, } -impl ForeignItemKind<'_> { - pub fn descr(&self) -> &'static str { - match self { - ForeignItemKind::Fn(..) => "function", - ForeignItemKind::Static(..) => "static variable", - ForeignItemKind::Type => "type", - } - } -} - /// A variable captured by a closure. #[derive(Debug, Copy, Clone, HashStable_Generic)] pub struct Upvar { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 57e49625148..a0bc318e2ca 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -347,7 +347,7 @@ pub trait Visitor<'v>: Sized { fn visit_pat_expr(&mut self, expr: &'v PatExpr<'v>) -> Self::Result { walk_pat_expr(self, expr) } - fn visit_lit(&mut self, _hir_id: HirId, _lit: &'v Lit, _negated: bool) -> Self::Result { + fn visit_lit(&mut self, _hir_id: HirId, _lit: Lit, _negated: bool) -> Self::Result { Self::Result::output() } fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { @@ -786,7 +786,7 @@ pub fn walk_pat_expr<'v, V: Visitor<'v>>(visitor: &mut V, expr: &'v PatExpr<'v>) let PatExpr { hir_id, span, kind } = expr; try_visit!(visitor.visit_id(*hir_id)); match kind { - PatExprKind::Lit { lit, negated } => visitor.visit_lit(*hir_id, lit, *negated), + PatExprKind::Lit { lit, negated } => visitor.visit_lit(*hir_id, *lit, *negated), PatExprKind::ConstBlock(c) => visitor.visit_inline_const(c), PatExprKind::Path(qpath) => visitor.visit_qpath(qpath, *hir_id, *span), } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 060fc51b7bd..cebf7d1b532 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -594,8 +594,9 @@ pub(crate) fn check_intrinsic_type( | sym::simd_ceil | sym::simd_floor | sym::simd_round + | sym::simd_round_ties_even | sym::simd_trunc => (1, 0, vec![param(0)], param(0)), - sym::simd_fma | sym::simd_relaxed_fma => { + sym::simd_fma | sym::simd_relaxed_fma | sym::simd_funnel_shl | sym::simd_funnel_shr => { (1, 0, vec![param(0), param(0), param(0)], param(0)) } sym::simd_gather => (3, 0, vec![param(0), param(1), param(2)], param(0)), diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index d7568554669..3ee39fb71b2 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -277,17 +277,7 @@ impl<'tcx> ItemCtxt<'tcx> { } _ => self.item_def_id, }; - // FIXME: just invoke `tcx.def_descr` instead of going through the HIR - // Can also remove most `descr` methods then. - let kind = match self.tcx.hir_node_by_def_id(kind_id) { - Node::Item(it) => it.kind.descr(), - Node::ImplItem(it) => it.kind.descr(), - Node::TraitItem(it) => it.kind.descr(), - Node::ForeignItem(it) => it.kind.descr(), - Node::OpaqueTy(_) => "opaque type", - Node::Synthetic => self.tcx.def_descr(kind_id.into()), - node => todo!("{node:#?}"), - }; + let kind = self.tcx.def_descr(kind_id.into()); let mut diag = placeholder_type_error_diag( self, generics, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 74739355e1f..55bf2ab6b50 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2364,9 +2364,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let lit_input = match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), + hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: false }), hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: true }), + hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: true }), _ => None, }, _ => None, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 087167dfd90..e00c22c47aa 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1480,7 +1480,7 @@ impl<'a> State<'a> { self.print_expr_addr_of(k, m, expr); } hir::ExprKind::Lit(lit) => { - self.print_literal(lit); + self.print_literal(&lit); } hir::ExprKind::Cast(expr, ty) => { self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Cast); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 6fa473d177d..57d3c5da873 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -278,8 +278,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// fall back to subtyping (`unify_and`). fn coerce_from_inference_variable(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); - assert!(a.is_ty_var() && self.shallow_resolve(a) == a); - assert!(self.shallow_resolve(b) == b); + debug_assert!(a.is_ty_var() && self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); if b.is_ty_var() { // Two unresolved type variables: create a `Coerce` predicate. @@ -323,6 +323,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { mutbl_b: hir::Mutability, ) -> CoerceResult<'tcx> { debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); // If we have a parameter of type `&M T_a` and the value // provided is `expr`, we will be adding an implicit borrow, @@ -514,10 +516,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// /// [unsized coercion](https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions) #[instrument(skip(self), level = "debug")] - fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> { - source = self.shallow_resolve(source); - target = self.shallow_resolve(target); + fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tcx> { debug!(?source, ?target); + debug_assert!(self.shallow_resolve(source) == source); + debug_assert!(self.shallow_resolve(target) == target); // We don't apply any coercions incase either the source or target // aren't sufficiently well known but tend to instead just equate @@ -531,6 +533,54 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return Err(TypeError::Mismatch); } + // This is an optimization because coercion is one of the most common + // operations that we do in typeck, since it happens at every assignment + // and call arg (among other positions). + // + // These targets are known to never be RHS in `LHS: CoerceUnsized<RHS>`. + // That's because these are built-in types for which a core-provided impl + // doesn't exist, and for which a user-written impl is invalid. + // + // This is technically incomplete when users write impossible bounds like + // `where T: CoerceUnsized<usize>`, for example, but that trait is unstable + // and coercion is allowed to be incomplete. The only case where this matters + // is impossible bounds. + // + // Note that some of these types implement `LHS: Unsize<RHS>`, but they + // do not implement *`CoerceUnsized`* which is the root obligation of the + // check below. + match target.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Tuple(_) => return Err(TypeError::Mismatch), + _ => {} + } + // Additionally, we ignore `&str -> &str` coercions, which happen very + // commonly since strings are one of the most used argument types in Rust, + // we do coercions when type checking call expressions. + if let ty::Ref(_, source_pointee, ty::Mutability::Not) = *source.kind() + && source_pointee.is_str() + && let ty::Ref(_, target_pointee, ty::Mutability::Not) = *target.kind() + && target_pointee.is_str() + { + return Err(TypeError::Mismatch); + } + let traits = (self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait()); let (Some(unsize_did), Some(coerce_unsized_did)) = traits else { @@ -800,6 +850,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// - `Pin<Box<T>>` as `Pin<&mut T>` #[instrument(skip(self), level = "trace")] fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + // We need to make sure the two types are compatible for coercion. // Then we will build a ReborrowPin adjustment and return that as an InferOk. @@ -848,6 +901,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { b: Ty<'tcx>, adjustment: Option<Adjust>, ) -> CoerceResult<'tcx> { + debug_assert!(self.shallow_resolve(b) == b); + self.commit_if_ok(|snapshot| { let outer_universe = self.infcx.universe(); @@ -888,24 +943,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn_ty_a: ty::PolyFnSig<'tcx>, b: Ty<'tcx>, ) -> CoerceResult<'tcx> { - //! Attempts to coerce from the type of a Rust function item - //! into a closure or a `proc`. - //! - - let b = self.shallow_resolve(b); debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer"); + debug_assert!(self.shallow_resolve(b) == b); self.coerce_from_safe_fn(fn_ty_a, b, None) } fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { - //! Attempts to coerce from the type of a Rust function item - //! into a closure or a `proc`. + debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); - let b = self.shallow_resolve(b); let InferOk { value: b, mut obligations } = self.at(&self.cause, self.param_env).normalize(b); - debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); match b.kind() { ty::FnPtr(_, b_hdr) => { @@ -955,6 +1005,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } + /// Attempts to coerce from the type of a non-capturing closure + /// into a function pointer. fn coerce_closure_to_fn( &self, a: Ty<'tcx>, @@ -962,11 +1014,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { args_a: GenericArgsRef<'tcx>, b: Ty<'tcx>, ) -> CoerceResult<'tcx> { - //! Attempts to coerce from the type of a non-capturing closure - //! into a function pointer. - //! - - let b = self.shallow_resolve(b); + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); match b.kind() { // At this point we haven't done capture analysis, which means @@ -1010,6 +1059,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { mutbl_b: hir::Mutability, ) -> CoerceResult<'tcx> { debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b); + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); let (is_ref, mt_a) = match *a.kind() { ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c7b9cb47091..3c53a060f7f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1637,7 +1637,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast::LitKind::ByteStr(ref v, _) => Ty::new_imm_ref( tcx, tcx.lifetimes.re_static, - Ty::new_array(tcx, tcx.types.u8, v.len() as u64), + Ty::new_array(tcx, tcx.types.u8, v.as_byte_str().len() as u64), ), ast::LitKind::Byte(_) => tcx.types.u8, ast::LitKind::Char(_) => tcx.types.char, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 7e5f1d97a8b..dd6eb73a3a0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1624,7 +1624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed), span, }) => { - let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(*span) else { + let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) else { return false; }; if !(snippet.starts_with("0x") || snippet.starts_with("0X")) { @@ -1683,7 +1683,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We have satisfied all requirements to provide a suggestion. Emit it. err.span_suggestion( - *span, + span, format!("if you meant to create a null pointer, use `{null_path_str}()`"), null_path_str + "()", Applicability::MachineApplicable, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index d0a48872f75..df1ee0e79c2 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -3055,7 +3055,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn note_unmet_impls_on_type( &self, err: &mut Diag<'_>, - errors: Vec<FulfillmentError<'tcx>>, + errors: &[FulfillmentError<'tcx>], suggest_derive: bool, ) { let preds: Vec<_> = errors diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index b9d24506986..87a7cd62ae5 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -322,7 +322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr.span, format!("cannot use `{}` on type `{}`", s, lhs_ty_str), ); - self.note_unmet_impls_on_type(&mut err, errors, false); + self.note_unmet_impls_on_type(&mut err, &errors, false); (err, None) } Op::BinOp(bin_op) => { @@ -382,7 +382,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(rhs_expr.span, rhs_ty_str); } let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty); - self.note_unmet_impls_on_type(&mut err, errors, suggest_derive); + self.note_unmet_impls_on_type(&mut err, &errors, suggest_derive); (err, output_def_id) } }; @@ -582,22 +582,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted } else if lhs_ty.has_non_region_param() { - // Look for a TraitPredicate in the Fulfillment errors, - // and use it to generate a suggestion. - // - // Note that lookup_op_method must be called again but - // with a specific rhs_ty instead of a placeholder so - // the resulting predicate generates a more specific - // suggestion for the user. - let errors = self - .lookup_op_method( - (lhs_expr, lhs_ty), - Some((rhs_expr, rhs_ty)), - lang_item_for_binop(self.tcx, op), - op.span(), - expected, - ) - .unwrap_err(); if !errors.is_empty() { for error in errors { if let Some(trait_pred) = @@ -946,7 +930,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Str | ty::Never | ty::Char | ty::Tuple(_) | ty::Array(_, _) => {} ty::Ref(_, lty, _) if *lty.kind() == ty::Str => {} _ => { - self.note_unmet_impls_on_type(&mut err, errors, true); + self.note_unmet_impls_on_type(&mut err, &errors, true); } } } diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index cae674165f0..ddc0b116806 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -110,17 +110,16 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match r.kind() { - ty::ReBound(..) => { - // leave bound regions alone - r - } + // Leave bound regions alone, since they affect selection via the leak check. + ty::ReBound(..) => r, + // Leave error regions alone, since they affect selection b/c of incompleteness. + ty::ReError(_) => r, ty::ReEarlyParam(..) | ty::ReLateParam(_) | ty::ReVar(_) | ty::RePlaceholder(..) | ty::ReStatic - | ty::ReError(_) | ty::ReErased => self.cx().lifetimes.re_erased, } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index edfb05e2ccd..3fe7c5e4286 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -298,6 +298,16 @@ fn configure_and_expand( fn print_macro_stats(ecx: &ExtCtxt<'_>) { use std::fmt::Write; + let crate_name = ecx.ecfg.crate_name.as_str(); + let crate_name = if crate_name == "build_script_build" { + // This is a build script. Get the package name from the environment. + let pkg_name = + std::env::var("CARGO_PKG_NAME").unwrap_or_else(|_| "<unknown crate>".to_string()); + format!("{pkg_name} build script") + } else { + crate_name.to_string() + }; + // No instability because we immediately sort the produced vector. #[allow(rustc::potential_query_instability)] let mut macro_stats: Vec<_> = ecx @@ -327,7 +337,7 @@ fn print_macro_stats(ecx: &ExtCtxt<'_>) { // non-interleaving, though. let mut s = String::new(); _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w)); - _ = writeln!(s, "{prefix} MACRO EXPANSION STATS: {}", ecx.ecfg.crate_name); + _ = writeln!(s, "{prefix} MACRO EXPANSION STATS: {}", crate_name); _ = writeln!( s, "{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}", @@ -341,20 +351,30 @@ fn print_macro_stats(ecx: &ExtCtxt<'_>) { } for (bytes, lines, uses, name, kind) in macro_stats { let mut name = ExpnKind::Macro(kind, *name).descr(); + let uses_with_underscores = thousands::usize_with_underscores(uses); let avg_lines = lines as f64 / uses as f64; let avg_bytes = bytes as f64 / uses as f64; - if name.len() >= name_w { - // If the name is long, print it on a line by itself, then - // set the name to empty and print things normally, to show the - // stats on the next line. + + // Ensure the "Macro Name" and "Uses" columns are as compact as possible. + let mut uses_w = uses_w; + if name.len() + uses_with_underscores.len() >= name_w + uses_w { + // The name would abut or overlap the uses value. Print the name + // on a line by itself, then set the name to empty and print things + // normally, to show the stats on the next line. _ = writeln!(s, "{prefix} {:<name_w$}", name); name = String::new(); - } + } else if name.len() >= name_w { + // The name won't abut or overlap with the uses value, but it does + // overlap with the empty part of the uses column. Shrink the width + // of the uses column to account for the excess name length. + uses_w = uses_with_underscores.len() + 1 + }; + _ = writeln!( s, "{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}", name, - thousands::usize_with_underscores(uses), + uses_with_underscores, thousands::usize_with_underscores(lines), thousands::f64p1_with_underscores(avg_lines), thousands::usize_with_underscores(bytes), diff --git a/compiler/rustc_lint/src/invalid_from_utf8.rs b/compiler/rustc_lint/src/invalid_from_utf8.rs index 11eb079ddc0..41b670c92c4 100644 --- a/compiler/rustc_lint/src/invalid_from_utf8.rs +++ b/compiler/rustc_lint/src/invalid_from_utf8.rs @@ -108,8 +108,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidFromUtf8 { } match init.kind { ExprKind::Lit(Spanned { node: lit, .. }) => { - if let LitKind::ByteStr(bytes, _) = &lit - && let Err(utf8_error) = std::str::from_utf8(bytes) + if let LitKind::ByteStr(byte_sym, _) = &lit + && let Err(utf8_error) = std::str::from_utf8(byte_sym.as_byte_str()) { lint(init.span, utf8_error); } diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 852bb01c096..c681deea779 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -152,7 +152,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_pat(self, p); } - fn visit_lit(&mut self, hir_id: HirId, lit: &'tcx hir::Lit, negated: bool) { + fn visit_lit(&mut self, hir_id: HirId, lit: hir::Lit, negated: bool) { lint_callback!(self, check_lit, hir_id, lit, negated); } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 409a23d1da0..affea1b80ec 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -23,7 +23,7 @@ macro_rules! late_lint_methods { fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>); fn check_arm(a: &'tcx rustc_hir::Arm<'tcx>); fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>); - fn check_lit(hir_id: rustc_hir::HirId, a: &'tcx rustc_hir::Lit, negated: bool); + fn check_lit(hir_id: rustc_hir::HirId, a: rustc_hir::Lit, negated: bool); fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>); fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>); fn check_ty(a: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index aaba0c14b1c..ea5485d8e5d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -547,18 +547,12 @@ fn lint_fn_pointer<'tcx>( } impl<'tcx> LateLintPass<'tcx> for TypeLimits { - fn check_lit( - &mut self, - cx: &LateContext<'tcx>, - hir_id: HirId, - lit: &'tcx hir::Lit, - negated: bool, - ) { + fn check_lit(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, lit: hir::Lit, negated: bool) { if negated { self.negated_expr_id = Some(hir_id); self.negated_expr_span = Some(lit.span); } - lint_literal(cx, self, hir_id, lit.span, lit, negated); + lint_literal(cx, self, hir_id, lit.span, &lit, negated); } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index a868c887493..a206f71e153 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -183,6 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let mut op_warned = false; if let Some(must_use_op) = must_use_op { + let span = expr.span.find_oldest_ancestor_in_same_ctxt(); cx.emit_span_lint( UNUSED_MUST_USE, expr.span, @@ -191,11 +192,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { label: expr.span, suggestion: if expr_is_from_block { UnusedOpSuggestion::BlockTailExpr { - before_span: expr.span.shrink_to_lo(), - after_span: expr.span.shrink_to_hi(), + before_span: span.shrink_to_lo(), + after_span: span.shrink_to_hi(), } } else { - UnusedOpSuggestion::NormalExpr { span: expr.span.shrink_to_lo() } + UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() } }, }, ); @@ -508,9 +509,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } MustUsePath::Def(span, def_id, reason) => { + let span = span.find_oldest_ancestor_in_same_ctxt(); cx.emit_span_lint( UNUSED_MUST_USE, - *span, + span, UnusedDef { pre: descr_pre, post: descr_post, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 2696c47c62c..e6aedc61338 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -32,7 +32,9 @@ use rustc_session::Session; use rustc_session::config::TargetModifier; use rustc_session::cstore::{CrateSource, ExternCrate}; use rustc_span::hygiene::HygieneDecodeContext; -use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext, kw}; +use rustc_span::{ + BytePos, ByteSymbol, DUMMY_SP, Pos, SpanData, SpanDecoder, Symbol, SyntaxContext, kw, +}; use tracing::debug; use crate::creader::CStore; @@ -384,6 +386,28 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> { fn read_raw_bytes(&mut self, len: usize) -> &[u8] { self.opaque.read_raw_bytes(len) } + + fn decode_symbol_or_byte_symbol<S>( + &mut self, + new_from_index: impl Fn(u32) -> S, + read_and_intern_str_or_byte_str_this: impl Fn(&mut Self) -> S, + read_and_intern_str_or_byte_str_opaque: impl Fn(&mut MemDecoder<'a>) -> S, + ) -> S { + let tag = self.read_u8(); + + match tag { + SYMBOL_STR => read_and_intern_str_or_byte_str_this(self), + SYMBOL_OFFSET => { + // read str offset + let pos = self.read_usize(); + + // move to str offset and read + self.opaque.with_position(pos, |d| read_and_intern_str_or_byte_str_opaque(d)) + } + SYMBOL_PREDEFINED => new_from_index(self.read_u32()), + _ => unreachable!(), + } + } } impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { @@ -545,29 +569,19 @@ impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { } fn decode_symbol(&mut self) -> Symbol { - let tag = self.read_u8(); - - match tag { - SYMBOL_STR => { - let s = self.read_str(); - Symbol::intern(s) - } - SYMBOL_OFFSET => { - // read str offset - let pos = self.read_usize(); + self.decode_symbol_or_byte_symbol( + Symbol::new, + |this| Symbol::intern(this.read_str()), + |opaque| Symbol::intern(opaque.read_str()), + ) + } - // move to str offset and read - self.opaque.with_position(pos, |d| { - let s = d.read_str(); - Symbol::intern(s) - }) - } - SYMBOL_PREDEFINED => { - let symbol_index = self.read_u32(); - Symbol::new(symbol_index) - } - _ => unreachable!(), - } + fn decode_byte_symbol(&mut self) -> ByteSymbol { + self.decode_symbol_or_byte_symbol( + ByteSymbol::new, + |this| ByteSymbol::intern(this.read_byte_str()), + |opaque| ByteSymbol::intern(opaque.read_byte_str()), + ) } } @@ -1496,11 +1510,18 @@ impl<'a> CrateMetadataRef<'a> { .map(move |v| (self.local_def_id(v.0), v.1)) } - fn exported_symbols<'tcx>( + fn exported_non_generic_symbols<'tcx>( + self, + tcx: TyCtxt<'tcx>, + ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { + tcx.arena.alloc_from_iter(self.root.exported_non_generic_symbols.decode((self, tcx))) + } + + fn exported_generic_symbols<'tcx>( self, tcx: TyCtxt<'tcx>, ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { - tcx.arena.alloc_from_iter(self.root.exported_symbols.decode((self, tcx))) + tcx.arena.alloc_from_iter(self.root.exported_generic_symbols.decode((self, tcx))) } fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 375928c2245..6943d4198df 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -358,7 +358,7 @@ provide! { tcx, def_id, other, cdata, specialization_enabled_in => { cdata.root.specialization_enabled_in } reachable_non_generics => { let reachable_non_generics = tcx - .exported_symbols(cdata.cnum) + .exported_non_generic_symbols(cdata.cnum) .iter() .filter_map(|&(exported_symbol, export_info)| { if let ExportedSymbol::NonGeneric(def_id) = exported_symbol { @@ -408,15 +408,8 @@ provide! { tcx, def_id, other, cdata, exportable_items => { tcx.arena.alloc_from_iter(cdata.get_exportable_items()) } stable_order_of_exportable_impls => { tcx.arena.alloc(cdata.get_stable_order_of_exportable_impls().collect()) } - exported_symbols => { - let syms = cdata.exported_symbols(tcx); - - // FIXME rust-lang/rust#64319, rust-lang/rust#64872: We want - // to block export of generics from dylibs, but we must fix - // rust-lang/rust#65890 before we can do that robustly. - - syms - } + exported_non_generic_symbols => { cdata.exported_non_generic_symbols(tcx) } + exported_generic_symbols => { cdata.exported_generic_symbols(tcx) } crate_extern_paths => { cdata.source().paths().cloned().collect() } expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d74918235b6..90bc427a19a 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -29,8 +29,8 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; use rustc_session::config::{CrateType, OptLevel, TargetModifier}; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ - ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext, - sym, + ByteSymbol, ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, + Symbol, SyntaxContext, sym, }; use tracing::{debug, instrument, trace}; @@ -63,7 +63,8 @@ pub(super) struct EncodeContext<'a, 'tcx> { required_source_files: Option<FxIndexSet<usize>>, is_proc_macro: bool, hygiene_ctxt: &'a HygieneEncodeContext, - symbol_table: FxHashMap<Symbol, usize>, + // Used for both `Symbol`s and `ByteSymbol`s. + symbol_index_table: FxHashMap<u32, usize>, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -200,27 +201,14 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } } - fn encode_symbol(&mut self, symbol: Symbol) { - // if symbol predefined, emit tag and symbol index - if symbol.is_predefined() { - self.opaque.emit_u8(SYMBOL_PREDEFINED); - self.opaque.emit_u32(symbol.as_u32()); - } else { - // otherwise write it as string or as offset to it - match self.symbol_table.entry(symbol) { - Entry::Vacant(o) => { - self.opaque.emit_u8(SYMBOL_STR); - let pos = self.opaque.position(); - o.insert(pos); - self.emit_str(symbol.as_str()); - } - Entry::Occupied(o) => { - let x = *o.get(); - self.emit_u8(SYMBOL_OFFSET); - self.emit_usize(x); - } - } - } + fn encode_symbol(&mut self, sym: Symbol) { + self.encode_symbol_or_byte_symbol(sym.as_u32(), |this| this.emit_str(sym.as_str())); + } + + fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol) { + self.encode_symbol_or_byte_symbol(byte_sym.as_u32(), |this| { + this.emit_byte_str(byte_sym.as_byte_str()) + }); } } @@ -492,6 +480,33 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { LazyArray::from_position_and_num_elems(pos, len) } + fn encode_symbol_or_byte_symbol( + &mut self, + index: u32, + emit_str_or_byte_str: impl Fn(&mut Self), + ) { + // if symbol/byte symbol is predefined, emit tag and symbol index + if Symbol::is_predefined(index) { + self.opaque.emit_u8(SYMBOL_PREDEFINED); + self.opaque.emit_u32(index); + } else { + // otherwise write it as string or as offset to it + match self.symbol_index_table.entry(index) { + Entry::Vacant(o) => { + self.opaque.emit_u8(SYMBOL_STR); + let pos = self.opaque.position(); + o.insert(pos); + emit_str_or_byte_str(self); + } + Entry::Occupied(o) => { + let x = *o.get(); + self.emit_u8(SYMBOL_OFFSET); + self.emit_usize(x); + } + } + } + } + fn encode_def_path_table(&mut self) { let table = self.tcx.def_path_table(); if self.is_proc_macro { @@ -677,9 +692,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { stat!("exportable-items", || self.encode_stable_order_of_exportable_impls()); // Encode exported symbols info. This is prefetched in `encode_metadata`. - let exported_symbols = stat!("exported-symbols", || { - self.encode_exported_symbols(tcx.exported_symbols(LOCAL_CRATE)) - }); + let (exported_non_generic_symbols, exported_generic_symbols) = + stat!("exported-symbols", || { + ( + self.encode_exported_symbols(tcx.exported_non_generic_symbols(LOCAL_CRATE)), + self.encode_exported_symbols(tcx.exported_generic_symbols(LOCAL_CRATE)), + ) + }); // Encode the hygiene data. // IMPORTANT: this *must* be the last thing that we encode (other than `SourceMap`). The @@ -745,7 +764,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { incoherent_impls, exportable_items, stable_order_of_exportable_impls, - exported_symbols, + exported_non_generic_symbols, + exported_generic_symbols, interpret_alloc_index, tables, syntax_contexts, @@ -2360,7 +2380,13 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { // Prefetch some queries used by metadata encoding. // This is not necessary for correctness, but is only done for performance reasons. // It can be removed if it turns out to cause trouble or be detrimental to performance. - join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE)); + join( + || prefetch_mir(tcx), + || { + let _ = tcx.exported_non_generic_symbols(LOCAL_CRATE); + let _ = tcx.exported_generic_symbols(LOCAL_CRATE); + }, + ); } with_encode_metadata_header(tcx, path, |ecx| { @@ -2427,7 +2453,7 @@ fn with_encode_metadata_header( required_source_files, is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: &hygiene_ctxt, - symbol_table: Default::default(), + symbol_index_table: Default::default(), }; // Encode the rustc version string in a predictable location. diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 077835283e9..a962a787a42 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -282,7 +282,8 @@ pub(crate) struct CrateRoot { exportable_items: LazyArray<DefIndex>, stable_order_of_exportable_impls: LazyArray<(DefIndex, usize)>, - exported_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>, + exported_non_generic_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>, + exported_generic_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>, syntax_contexts: SyntaxContextTable, expn_data: ExpnDataTable, diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 2b2ffa71628..16edc240544 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -168,8 +168,9 @@ impl<'tcx> ConstValue<'tcx> { return Some(&[]); } // Non-empty slice, must have memory. We know this is a relative pointer. - let (inner_prov, offset) = ptr.into_parts(); - let data = tcx.global_alloc(inner_prov?.alloc_id()).unwrap_memory(); + let (inner_prov, offset) = + ptr.into_pointer_or_addr().ok()?.prov_and_relative_offset(); + let data = tcx.global_alloc(inner_prov.alloc_id()).unwrap_memory(); (data, offset.bytes(), offset.bytes() + len) } }; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index dd55d039794..4198b198ab1 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -526,7 +526,7 @@ impl Allocation { let ptr_bytes = &mut bytes[idx..idx + ptr_size]; let bits = read_target_uint(endian, ptr_bytes).unwrap(); let (ptr_prov, ptr_offset) = - adjust_ptr(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_parts(); + adjust_ptr(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_raw_parts(); write_target_uint(endian, ptr_bytes, ptr_offset.bytes().into()).unwrap(); new_provenance.push((offset, ptr_prov)); } @@ -769,7 +769,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> // as-is into memory. This also double-checks that `val.size()` matches `range.size`. let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size)? { Right(ptr) => { - let (provenance, offset) = ptr.into_parts(); + let (provenance, offset) = ptr.into_raw_parts(); (u128::from(offset.bytes()), Some(provenance)) } Left(data) => (data, None), diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index ea2f84d46d7..da9e5bdbadd 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -77,7 +77,7 @@ impl<'tcx> GlobalId<'tcx> { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)] pub struct LitToConstInput<'tcx> { /// The absolute value of the resultant constant. - pub lit: &'tcx LitKind, + pub lit: LitKind, /// The type of the constant. pub ty: Ty<'tcx>, /// If the constant is negative. diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 25c7c26ddd9..0ff14f15c13 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -288,7 +288,7 @@ impl From<CtfeProvenance> for Pointer { impl<Prov> From<Pointer<Prov>> for Pointer<Option<Prov>> { #[inline(always)] fn from(ptr: Pointer<Prov>) -> Self { - let (prov, offset) = ptr.into_parts(); + let (prov, offset) = ptr.into_raw_parts(); Pointer::new(Some(prov), offset) } } @@ -314,19 +314,17 @@ impl<Prov> Pointer<Option<Prov>> { assert!(Prov::OFFSET_IS_ADDR); self.offset } -} -impl<Prov> Pointer<Option<Prov>> { /// Creates a pointer to the given address, with invalid provenance (i.e., cannot be used for /// any memory access). #[inline(always)] - pub fn from_addr_invalid(addr: u64) -> Self { + pub fn without_provenance(addr: u64) -> Self { Pointer { provenance: None, offset: Size::from_bytes(addr) } } #[inline(always)] pub fn null() -> Self { - Pointer::from_addr_invalid(0) + Pointer::without_provenance(0) } } @@ -336,11 +334,11 @@ impl<Prov> Pointer<Prov> { Pointer { provenance, offset } } - /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Prov`! - /// This function must only be used in the implementation of `Machine::ptr_get_alloc`, - /// and when a `Pointer` is taken apart to be stored efficiently in an `Allocation`. + /// Obtain the constituents of this pointer. Note that the meaning of the offset depends on the + /// type `Prov`! This is a low-level function that should only be used when absolutely + /// necessary. Prefer `prov_and_relative_offset` if possible. #[inline(always)] - pub fn into_parts(self) -> (Prov, Size) { + pub fn into_raw_parts(self) -> (Prov, Size) { (self.provenance, self.offset) } @@ -361,3 +359,12 @@ impl<Prov> Pointer<Prov> { self.wrapping_offset(Size::from_bytes(i as u64), cx) } } + +impl Pointer<CtfeProvenance> { + /// Return the provenance and relative offset stored in this pointer. Safer alternative to + /// `into_raw_parts` since the type ensures that the offset is indeed relative. + #[inline(always)] + pub fn prov_and_relative_offset(self) -> (CtfeProvenance, Size) { + (self.provenance, self.offset) + } +} diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 7ba0e5b5e07..8092f634dc8 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -109,7 +109,7 @@ impl<Prov> Scalar<Prov> { /// Create a Scalar from a pointer with an `Option<_>` provenance (where `None` represents a /// plain integer / "invalid" pointer). pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self { - match ptr.into_parts() { + match ptr.into_raw_parts() { (Some(prov), offset) => Scalar::from_pointer(Pointer::new(prov, offset), cx), (None, offset) => { Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap()) @@ -276,7 +276,7 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> { Right(ptr) => interp_ok(ptr.into()), Left(bits) => { let addr = u64::try_from(bits).unwrap(); - interp_ok(Pointer::from_addr_invalid(addr)) + interp_ok(Pointer::without_provenance(addr)) } } } @@ -299,7 +299,7 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> { Ok(ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap()) } else { // We know `offset` is relative, since `OFFSET_IS_ADDR == false`. - let (prov, offset) = ptr.into_parts(); + let (prov, offset) = ptr.into_raw_parts(); // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail. Err(Scalar::Ptr(Pointer::new(prov.get_alloc_id().unwrap(), offset), sz)) } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index adc100941a3..9f39908c3b2 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1359,7 +1359,15 @@ pub struct BasicBlockData<'tcx> { impl<'tcx> BasicBlockData<'tcx> { pub fn new(terminator: Option<Terminator<'tcx>>, is_cleanup: bool) -> BasicBlockData<'tcx> { - BasicBlockData { statements: vec![], terminator, is_cleanup } + BasicBlockData::new_stmts(Vec::new(), terminator, is_cleanup) + } + + pub fn new_stmts( + statements: Vec<Statement<'tcx>>, + terminator: Option<Terminator<'tcx>>, + is_cleanup: bool, + ) -> BasicBlockData<'tcx> { + BasicBlockData { statements, terminator, is_cleanup } } /// Accessor for terminator. diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index d98b40f0fcf..d16477adb77 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -16,12 +16,16 @@ pub struct Statement<'tcx> { pub kind: StatementKind<'tcx>, } -impl Statement<'_> { +impl<'tcx> Statement<'tcx> { /// Changes a statement to a nop. This is both faster than deleting instructions and avoids /// invalidating statement indices in `Location`s. pub fn make_nop(&mut self) { self.kind = StatementKind::Nop } + + pub fn new(source_info: SourceInfo, kind: StatementKind<'tcx>) -> Self { + Statement { source_info, kind } + } } impl<'tcx> StatementKind<'tcx> { diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 26a31cb055e..f138c5ca039 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -266,6 +266,7 @@ trivial! { Option<rustc_target::spec::PanicStrategy>, Option<usize>, Option<rustc_middle::ty::IntrinsicDef>, + Option<rustc_abi::Align>, Result<(), rustc_errors::ErrorGuaranteed>, Result<(), rustc_middle::traits::query::NoSolution>, Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 8a3d26e1b03..3e68f0f784e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -67,6 +67,7 @@ use std::mem; use std::path::PathBuf; use std::sync::Arc; +use rustc_abi::Align; use rustc_arena::TypedArena; use rustc_ast::expand::StrippedCfgItem; use rustc_ast::expand::allocator::AllocatorKind; @@ -1481,6 +1482,10 @@ rustc_queries! { desc { |tcx| "computing should_inherit_track_caller of `{}`", tcx.def_path_str(def_id) } } + query inherited_align(def_id: DefId) -> Option<Align> { + desc { |tcx| "computing inherited_align of `{}`", tcx.def_path_str(def_id) } + } + query lookup_deprecation_entry(def_id: DefId) -> Option<DeprecationEntry> { desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } @@ -2312,13 +2317,32 @@ rustc_queries! { separate_provide_extern } - /// The list of symbols exported from the given crate. + /// The list of non-generic symbols exported from the given crate. + /// + /// This is separate from exported_generic_symbols to avoid having + /// to deserialize all non-generic symbols too for upstream crates + /// in the upstream_monomorphizations query. + /// + /// - All names contained in `exported_non_generic_symbols(cnum)` are + /// guaranteed to correspond to a publicly visible symbol in `cnum` + /// machine code. + /// - The `exported_non_generic_symbols` and `exported_generic_symbols` + /// sets of different crates do not intersect. + query exported_non_generic_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { + desc { "collecting exported non-generic symbols for crate `{}`", cnum} + cache_on_disk_if { *cnum == LOCAL_CRATE } + separate_provide_extern + } + + /// The list of generic symbols exported from the given crate. /// - /// - All names contained in `exported_symbols(cnum)` are guaranteed to - /// correspond to a publicly visible symbol in `cnum` machine code. - /// - The `exported_symbols` sets of different crates do not intersect. - query exported_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { - desc { "collecting exported symbols for crate `{}`", cnum} + /// - All names contained in `exported_generic_symbols(cnum)` are + /// guaranteed to correspond to a publicly visible symbol in `cnum` + /// machine code. + /// - The `exported_non_generic_symbols` and `exported_generic_symbols` + /// sets of different crates do not intersect. + query exported_generic_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { + desc { "collecting exported generic symbols for crate `{}`", cnum} cache_on_disk_if { *cnum == LOCAL_CRATE } separate_provide_extern } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index e1876f8f0f9..a7ac3442898 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -20,8 +20,8 @@ use rustc_span::hygiene::{ }; use rustc_span::source_map::Spanned; use rustc_span::{ - BytePos, CachingSourceMapView, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span, - SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, + BytePos, ByteSymbol, CachingSourceMapView, ExpnData, ExpnHash, Pos, RelativeBytePos, + SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, }; use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; @@ -42,7 +42,7 @@ const TAG_RELATIVE_SPAN: u8 = 2; const TAG_SYNTAX_CONTEXT: u8 = 0; const TAG_EXPN_DATA: u8 = 1; -// Tags for encoding Symbol's +// Tags for encoding Symbols and ByteSymbols const SYMBOL_STR: u8 = 0; const SYMBOL_OFFSET: u8 = 1; const SYMBOL_PREDEFINED: u8 = 2; @@ -253,7 +253,7 @@ impl OnDiskCache { source_map: CachingSourceMapView::new(tcx.sess.source_map()), file_to_file_index, hygiene_context: &hygiene_encode_context, - symbol_table: Default::default(), + symbol_index_table: Default::default(), }; // Encode query results. @@ -479,6 +479,30 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { .expect("failed to lookup `SourceFile` in new context") })) } + + // copy&paste impl from rustc_metadata + #[inline] + fn decode_symbol_or_byte_symbol<S>( + &mut self, + new_from_index: impl Fn(u32) -> S, + read_and_intern_str_or_byte_str_this: impl Fn(&mut Self) -> S, + read_and_intern_str_or_byte_str_opaque: impl Fn(&mut MemDecoder<'a>) -> S, + ) -> S { + let tag = self.read_u8(); + + match tag { + SYMBOL_STR => read_and_intern_str_or_byte_str_this(self), + SYMBOL_OFFSET => { + // read str offset + let pos = self.read_usize(); + + // move to str offset and read + self.opaque.with_position(pos, |d| read_and_intern_str_or_byte_str_opaque(d)) + } + SYMBOL_PREDEFINED => new_from_index(self.read_u32()), + _ => unreachable!(), + } + } } // Decodes something that was encoded with `encode_tagged()` and verify that the @@ -653,32 +677,20 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { Span::new(lo, hi, ctxt, parent) } - // copy&paste impl from rustc_metadata - #[inline] fn decode_symbol(&mut self) -> Symbol { - let tag = self.read_u8(); - - match tag { - SYMBOL_STR => { - let s = self.read_str(); - Symbol::intern(s) - } - SYMBOL_OFFSET => { - // read str offset - let pos = self.read_usize(); + self.decode_symbol_or_byte_symbol( + Symbol::new, + |this| Symbol::intern(this.read_str()), + |opaque| Symbol::intern(opaque.read_str()), + ) + } - // move to str offset and read - self.opaque.with_position(pos, |d| { - let s = d.read_str(); - Symbol::intern(s) - }) - } - SYMBOL_PREDEFINED => { - let symbol_index = self.read_u32(); - Symbol::new(symbol_index) - } - _ => unreachable!(), - } + fn decode_byte_symbol(&mut self) -> ByteSymbol { + self.decode_symbol_or_byte_symbol( + ByteSymbol::new, + |this| ByteSymbol::intern(this.read_byte_str()), + |opaque| ByteSymbol::intern(opaque.read_byte_str()), + ) } fn decode_crate_num(&mut self) -> CrateNum { @@ -807,7 +819,8 @@ pub struct CacheEncoder<'a, 'tcx> { source_map: CachingSourceMapView<'tcx>, file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>, hygiene_context: &'a HygieneEncodeContext, - symbol_table: FxHashMap<Symbol, usize>, + // Used for both `Symbol`s and `ByteSymbol`s. + symbol_index_table: FxHashMap<u32, usize>, } impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { @@ -831,6 +844,34 @@ impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { ((end_pos - start_pos) as u64).encode(self); } + // copy&paste impl from rustc_metadata + fn encode_symbol_or_byte_symbol( + &mut self, + index: u32, + emit_str_or_byte_str: impl Fn(&mut Self), + ) { + // if symbol/byte symbol is predefined, emit tag and symbol index + if Symbol::is_predefined(index) { + self.encoder.emit_u8(SYMBOL_PREDEFINED); + self.encoder.emit_u32(index); + } else { + // otherwise write it as string or as offset to it + match self.symbol_index_table.entry(index) { + Entry::Vacant(o) => { + self.encoder.emit_u8(SYMBOL_STR); + let pos = self.encoder.position(); + o.insert(pos); + emit_str_or_byte_str(self); + } + Entry::Occupied(o) => { + let x = *o.get(); + self.emit_u8(SYMBOL_OFFSET); + self.emit_usize(x); + } + } + } + } + #[inline] fn finish(mut self) -> FileEncodeResult { self.encoder.finish() @@ -889,28 +930,14 @@ impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> { len.encode(self); } - // copy&paste impl from rustc_metadata - fn encode_symbol(&mut self, symbol: Symbol) { - // if symbol predefined, emit tag and symbol index - if symbol.is_predefined() { - self.encoder.emit_u8(SYMBOL_PREDEFINED); - self.encoder.emit_u32(symbol.as_u32()); - } else { - // otherwise write it as string or as offset to it - match self.symbol_table.entry(symbol) { - Entry::Vacant(o) => { - self.encoder.emit_u8(SYMBOL_STR); - let pos = self.encoder.position(); - o.insert(pos); - self.emit_str(symbol.as_str()); - } - Entry::Occupied(o) => { - let x = *o.get(); - self.emit_u8(SYMBOL_OFFSET); - self.emit_usize(x); - } - } - } + fn encode_symbol(&mut self, sym: Symbol) { + self.encode_symbol_or_byte_symbol(sym.as_u32(), |this| this.emit_str(sym.as_str())); + } + + fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol) { + self.encode_symbol_or_byte_symbol(byte_sym.as_u32(), |this| { + this.emit_byte_str(byte_sym.as_byte_str()) + }); } fn encode_crate_num(&mut self, crate_num: CrateNum) { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index d0e72a86d8a..bda8dcadbce 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -526,7 +526,7 @@ pub enum ExprKind<'tcx> { Closure(Box<ClosureExpr<'tcx>>), /// A literal. Literal { - lit: &'tcx hir::Lit, + lit: hir::Lit, neg: bool, }, /// For literals that don't correspond to anything in the HIR diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 457a4f4d502..72810491214 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -17,6 +17,7 @@ use std::{fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; use rustc_ast as ast; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -1648,32 +1649,9 @@ impl<'tcx> TyCtxt<'tcx> { /// `rustc_layout_scalar_valid_range` attribute. // FIXME(eddyb) this is an awkward spot for this method, maybe move it? pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound<u128>, Bound<u128>) { - let get = |name| { - let Some(attr) = self.get_attr(def_id, name) else { - return Bound::Unbounded; - }; - debug!("layout_scalar_valid_range: attr={:?}", attr); - if let Some( - &[ - ast::MetaItemInner::Lit(ast::MetaItemLit { - kind: ast::LitKind::Int(a, _), .. - }), - ], - ) = attr.meta_item_list().as_deref() - { - Bound::Included(a.get()) - } else { - self.dcx().span_delayed_bug( - attr.span(), - "invalid rustc_layout_scalar_valid_range attribute", - ); - Bound::Unbounded - } - }; - ( - get(sym::rustc_layout_scalar_valid_range_start), - get(sym::rustc_layout_scalar_valid_range_end), - ) + let start = find_attr!(self.get_all_attrs(def_id), AttributeKind::RustcLayoutScalarValidRangeStart(n, _) => Bound::Included(**n)).unwrap_or(Bound::Unbounded); + let end = find_attr!(self.get_all_attrs(def_id), AttributeKind::RustcLayoutScalarValidRangeEnd(n, _) => Bound::Included(**n)).unwrap_or(Bound::Unbounded); + (start, end) } pub fn lift<T: Lift<TyCtxt<'tcx>>>(self, value: T) -> Option<T::Lifted> { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 1392d1d08fc..bee490b3648 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1755,7 +1755,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ) -> Result<(), PrintError> { define_scoped_cx!(self); - let (prov, offset) = ptr.into_parts(); + let (prov, offset) = ptr.prov_and_relative_offset(); match ty.kind() { // Byte strings (&[u8; N]) ty::Ref(_, inner, _) => { diff --git a/compiler/rustc_mir_build/src/builder/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs index 082cdc2e2a4..2faccc43247 100644 --- a/compiler/rustc_mir_build/src/builder/cfg.rs +++ b/compiler/rustc_mir_build/src/builder/cfg.rs @@ -42,7 +42,7 @@ impl<'tcx> CFG<'tcx> { ) { self.push( block, - Statement { source_info, kind: StatementKind::Assign(Box::new((place, rvalue))) }, + Statement::new(source_info, StatementKind::Assign(Box::new((place, rvalue)))), ); } @@ -88,7 +88,7 @@ impl<'tcx> CFG<'tcx> { place: Place<'tcx>, ) { let kind = StatementKind::FakeRead(Box::new((cause, place))); - let stmt = Statement { source_info, kind }; + let stmt = Statement::new(source_info, kind); self.push(block, stmt); } @@ -99,7 +99,7 @@ impl<'tcx> CFG<'tcx> { place: Place<'tcx>, ) { let kind = StatementKind::PlaceMention(Box::new(place)); - let stmt = Statement { source_info, kind }; + let stmt = Statement::new(source_info, kind); self.push(block, stmt); } @@ -110,7 +110,7 @@ impl<'tcx> CFG<'tcx> { /// syntax (e.g. `continue` or `if !`) that would otherwise not appear in MIR. pub(crate) fn push_coverage_span_marker(&mut self, block: BasicBlock, source_info: SourceInfo) { let kind = StatementKind::Coverage(coverage::CoverageKind::SpanMarker); - let stmt = Statement { source_info, kind }; + let stmt = Statement::new(source_info, kind); self.push(block, stmt); } diff --git a/compiler/rustc_mir_build/src/builder/coverageinfo.rs b/compiler/rustc_mir_build/src/builder/coverageinfo.rs index a80bd4f3c80..aa43b273cff 100644 --- a/compiler/rustc_mir_build/src/builder/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/builder/coverageinfo.rs @@ -61,10 +61,10 @@ impl BlockMarkerGen { block: BasicBlock, ) -> BlockMarkerId { let id = self.next_block_marker_id(); - let marker_statement = mir::Statement { + let marker_statement = mir::Statement::new( source_info, - kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), - }; + mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), + ); cfg.push(block, marker_statement); id diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index 91e284604b6..10154461c33 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs @@ -315,10 +315,8 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let stmt = self.statement_as_expr(*stmt_id)?; let span = self.thir[stmt].span; let statement = self.parse_statement(stmt)?; - data.statements.push(Statement { - source_info: SourceInfo { span, scope: self.source_scope }, - kind: statement, - }); + data.statements + .push(Statement::new(SourceInfo { span, scope: self.source_scope }, statement)); } let Some(trailing) = block.expr else { return Err(self.expr_error(expr_id, "terminator")) }; diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index eb8e98ec364..d0d0c21463f 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -49,7 +49,7 @@ pub(crate) fn as_constant_inner<'tcx>( let Expr { ty, temp_lifetime: _, span, ref kind } = *expr; match *kind { ExprKind::Literal { lit, neg } => { - let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }); + let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty, neg }); ConstOperand { span, user_ty: None, const_ } } @@ -128,34 +128,35 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_)) => { - let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8], ()); + let allocation = Allocation::from_bytes_byte_aligned_immutable(data.as_byte_str(), ()); let allocation = tcx.mk_const_alloc(allocation); ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } } - (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { - let id = tcx.allocate_bytes_dedup(data, CTFE_ALLOC_SALT); + (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { + let id = tcx.allocate_bytes_dedup(byte_sym.as_byte_str(), CTFE_ALLOC_SALT); ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) } - (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => + (ast::LitKind::CStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => { - let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8], ()); + let allocation = + Allocation::from_bytes_byte_aligned_immutable(byte_sym.as_byte_str(), ()); let allocation = tcx.mk_const_alloc(allocation); ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } } (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { - ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) + ConstValue::Scalar(Scalar::from_uint(n, Size::from_bytes(1))) } (ast::LitKind::Int(n, _), ty::Uint(_)) if !neg => trunc(n.get()), (ast::LitKind::Int(n, _), ty::Int(_)) => { trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }) } (ast::LitKind::Float(n, _), ty::Float(fty)) => { - parse_float_into_constval(*n, *fty, neg).unwrap() + parse_float_into_constval(n, *fty, neg).unwrap() } - (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), - (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), + (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(b)), + (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(c)), (ast::LitKind::Err(guar), _) => { - return Const::Ty(Ty::new_error(tcx, *guar), ty::Const::new_error(tcx, *guar)); + return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)); } _ => bug!("invalid lit/ty combination in `lit_to_mir_constant`: {lit:?}: {ty:?}"), }; diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 99148504a87..7c851ec465b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -489,16 +489,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let place = place_builder.to_place(this); this.cfg.push( block, - Statement { - source_info: ty_source_info, - kind: StatementKind::AscribeUserType( + Statement::new( + ty_source_info, + StatementKind::AscribeUserType( Box::new(( place, UserTypeProjection { base: annotation_index, projs: vec![] }, )), Variance::Invariant, ), - }, + ), ); } block.and(place_builder) @@ -518,16 +518,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }); this.cfg.push( block, - Statement { - source_info: ty_source_info, - kind: StatementKind::AscribeUserType( + Statement::new( + ty_source_info, + StatementKind::AscribeUserType( Box::new(( Place::from(temp), UserTypeProjection { base: annotation_index, projs: vec![] }, )), Variance::Invariant, ), - }, + ), ); } block.and(PlaceBuilder::from(temp)) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 9e07dd5da7e..975226bb642 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -175,10 +175,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // and therefore is not considered during coroutine auto-trait // determination. See the comment about `box` at `yield_in_scope`. let result = this.local_decls.push(LocalDecl::new(expr.ty, expr_span)); - this.cfg.push( - block, - Statement { source_info, kind: StatementKind::StorageLive(result) }, - ); + this.cfg + .push(block, Statement::new(source_info, StatementKind::StorageLive(result))); if let Some(scope) = scope.temp_lifetime { // schedule a shallow free of that memory, lest we unwind: this.schedule_drop_storage_and_value(expr_span, scope, result); @@ -278,12 +276,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; this.cfg.push( block, - Statement { + Statement::new( source_info, - kind: StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::Assume(Operand::Move(assert_place)), - )), - }, + StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume( + Operand::Move(assert_place), + ))), + ), ); } @@ -789,7 +787,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = this.source_info(upvar_span); let temp = this.local_decls.push(LocalDecl::new(upvar_ty, upvar_span)); - this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) }); + this.cfg.push(block, Statement::new(source_info, StatementKind::StorageLive(temp))); let arg_place_builder = unpack!(block = this.as_place_builder(block, arg)); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_temp.rs b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs index 0bd61168fba..b0ce3527d8b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs @@ -102,8 +102,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Block { expr: None, targeted_by_break: false, .. } = this.thir[block] && expr_ty.is_never() => {} _ => { - this.cfg - .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) }); + this.cfg.push(block, Statement::new(source_info, StatementKind::StorageLive(temp))); // In constants, `temp_lifetime` is `None` for temporaries that // live for the `'static` lifetime. Thus we do not drop these diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 270a7d4b154..9600067a85f 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -646,9 +646,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let base = self.canonical_user_type_annotations.push(annotation.clone()); self.cfg.push( block, - Statement { - source_info: ty_source_info, - kind: StatementKind::AscribeUserType( + Statement::new( + ty_source_info, + StatementKind::AscribeUserType( Box::new((place, UserTypeProjection { base, projs: Vec::new() })), // We always use invariant as the variance here. This is because the // variance field from the ascription refers to the variance to use @@ -666,7 +666,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // `<expr>`. ty::Invariant, ), - }, + ), ); self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); @@ -828,7 +828,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> Place<'tcx> { let local_id = self.var_local_id(var, for_guard); let source_info = self.source_info(span); - self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); + self.cfg.push(block, Statement::new(source_info, StatementKind::StorageLive(local_id))); // Although there is almost always scope for given variable in corner cases // like #92893 we might get variable with no scope. if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) @@ -2578,16 +2578,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let base = self.canonical_user_type_annotations.push(ascription.annotation); self.cfg.push( block, - Statement { + Statement::new( source_info, - kind: StatementKind::AscribeUserType( + StatementKind::AscribeUserType( Box::new(( ascription.source, UserTypeProjection { base, projs: Vec::new() }, )), ascription.variance, ), - }, + ), ); } } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 1d15e7e126f..405d47c7c79 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -431,13 +431,13 @@ impl DropTree { cfg.terminate(block, drop_node.data.source_info, terminator); } DropKind::ForLint => { - let stmt = Statement { - source_info: drop_node.data.source_info, - kind: StatementKind::BackwardIncompatibleDropHint { + let stmt = Statement::new( + drop_node.data.source_info, + StatementKind::BackwardIncompatibleDropHint { place: Box::new(drop_node.data.local.into()), reason: BackwardIncompatibleDropReason::Edition2024, }, - }; + ); cfg.push(block, stmt); let target = blocks[drop_node.next].unwrap(); if target != block { @@ -454,10 +454,10 @@ impl DropTree { // Root nodes don't correspond to a drop. DropKind::Storage if drop_idx == ROOT_NODE => {} DropKind::Storage => { - let stmt = Statement { - source_info: drop_node.data.source_info, - kind: StatementKind::StorageDead(drop_node.data.local), - }; + let stmt = Statement::new( + drop_node.data.source_info, + StatementKind::StorageDead(drop_node.data.local), + ); cfg.push(block, stmt); let target = blocks[drop_node.next].unwrap(); if target != block { @@ -1124,13 +1124,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { DropKind::ForLint => { self.cfg.push( block, - Statement { + Statement::new( source_info, - kind: StatementKind::BackwardIncompatibleDropHint { + StatementKind::BackwardIncompatibleDropHint { place: Box::new(local.into()), reason: BackwardIncompatibleDropReason::Edition2024, }, - }, + ), ); } DropKind::Storage => { @@ -1138,7 +1138,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { assert!(local.index() > self.arg_count); self.cfg.push( block, - Statement { source_info, kind: StatementKind::StorageDead(local) }, + Statement::new(source_info, StatementKind::StorageDead(local)), ); } } @@ -1880,13 +1880,13 @@ where cfg.push( block, - Statement { + Statement::new( source_info, - kind: StatementKind::BackwardIncompatibleDropHint { + StatementKind::BackwardIncompatibleDropHint { place: Box::new(local.into()), reason: BackwardIncompatibleDropReason::Edition2024, }, - }, + ), ); } DropKind::Storage => { @@ -1910,7 +1910,7 @@ where } // Only temps and vars need their storage dead. assert!(local.index() > arg_count); - cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) }); + cfg.push(block, Statement::new(source_info, StatementKind::StorageDead(local))); } } } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index b4fa55e1c1f..8e218a380e9 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -43,27 +43,23 @@ pub(crate) fn lit_to_const<'tcx>( let str_bytes = s.as_str().as_bytes(); ty::ValTree::from_raw_bytes(tcx, str_bytes) } - (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) + (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_) | ty::Array(..)) => { - let bytes = data as &[u8]; - ty::ValTree::from_raw_bytes(tcx, bytes) + ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()) } - (ast::LitKind::ByteStr(data, _), ty::Slice(_) | ty::Array(..)) + (ast::LitKind::ByteStr(byte_sym, _), ty::Slice(_) | ty::Array(..)) if tcx.features().deref_patterns() => { // Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is // enabled, in order to allow, e.g., `deref!(b"..."): Vec<u8>`. - let bytes = data as &[u8]; - ty::ValTree::from_raw_bytes(tcx, bytes) + ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()) } (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { - ty::ValTree::from_scalar_int(tcx, (*n).into()) + ty::ValTree::from_scalar_int(tcx, n.into()) } - (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => - { - let bytes = data as &[u8]; - ty::ValTree::from_raw_bytes(tcx, bytes) + (ast::LitKind::CStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => { + ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()) } (ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => { let scalar_int = trunc(n.get(), *ui); @@ -76,15 +72,15 @@ pub(crate) fn lit_to_const<'tcx>( ); ty::ValTree::from_scalar_int(tcx, scalar_int) } - (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int(tcx, (*b).into()), + (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int(tcx, b.into()), (ast::LitKind::Float(n, _), ty::Float(fty)) => { - let bits = parse_float_into_scalar(*n, *fty, neg).unwrap_or_else(|| { + let bits = parse_float_into_scalar(n, *fty, neg).unwrap_or_else(|| { tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit)) }); ty::ValTree::from_scalar_int(tcx, bits) } - (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int(tcx, (*c).into()), - (ast::LitKind::Err(guar), _) => return ty::Const::new_error(tcx, *guar), + (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int(tcx, c.into()), + (ast::LitKind::Err(guar), _) => return ty::Const::new_error(tcx, guar), _ => return ty::Const::new_misc_error(tcx), }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index fcd106d78e2..e44a440b5c1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -680,7 +680,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { Some(pat_ty) => pat_ty, None => self.typeck_results.node_type(expr.hir_id), }; - let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg: *negated }; + let lit_input = LitToConstInput { lit: lit.node, ty: ct_ty, neg: *negated }; let constant = self.tcx.at(expr.span).lit_to_const(lit_input); self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind } diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 8602bb55765..23e28a11a45 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -17,13 +17,13 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { let mut blocks = IndexVec::new(); let mut block = |n, kind| { - let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop }; + let nop = mir::Statement::new(source_info, mir::StatementKind::Nop); - blocks.push(mir::BasicBlockData { - statements: std::iter::repeat(&nop).cloned().take(n).collect(), - terminator: Some(mir::Terminator { source_info, kind }), - is_cleanup: false, - }) + blocks.push(mir::BasicBlockData::new_stmts( + std::iter::repeat(&nop).cloned().take(n).collect(), + Some(mir::Terminator { source_info, kind }), + false, + )) }; let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() }; diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index bacff287859..26839ebf61e 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -44,11 +44,10 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { let cur_len = body.basic_blocks.len(); let mut new_block = |source_info: SourceInfo, is_cleanup: bool, target: BasicBlock| { - let block = BasicBlockData { - statements: vec![], + let block = BasicBlockData::new( + Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), is_cleanup, - terminator: Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), - }; + ); let idx = cur_len + new_blocks.len(); new_blocks.push(block); BasicBlock::new(idx) diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index a414d120e68..7ae2ebaf4ff 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -93,11 +93,11 @@ fn add_move_for_packed_drop<'tcx>( let ty = place.ty(body, tcx).ty; let temp = patch.new_temp(ty, source_info.span); - let storage_dead_block = patch.new_block(BasicBlockData { - statements: vec![Statement { source_info, kind: StatementKind::StorageDead(temp) }], - terminator: Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), + let storage_dead_block = patch.new_block(BasicBlockData::new_stmts( + vec![Statement::new(source_info, StatementKind::StorageDead(temp))], + Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), is_cleanup, - }); + )); patch.add_statement(loc, StatementKind::StorageLive(temp)); patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*place))); diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index e5a28d1b66c..3c29d4624b7 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -81,9 +81,11 @@ impl<'tcx> crate::MirPass<'tcx> for AddRetag { // Emit their retags. basic_blocks[START_BLOCK].statements.splice( 0..0, - places.map(|(place, source_info)| Statement { - source_info, - kind: StatementKind::Retag(RetagKind::FnEntry, Box::new(place)), + places.map(|(place, source_info)| { + Statement::new( + source_info, + StatementKind::Retag(RetagKind::FnEntry, Box::new(place)), + ) }), ); } @@ -113,10 +115,10 @@ impl<'tcx> crate::MirPass<'tcx> for AddRetag { for (source_info, dest_place, dest_block) in returns { basic_blocks[dest_block].statements.insert( 0, - Statement { + Statement::new( source_info, - kind: StatementKind::Retag(RetagKind::Default, Box::new(dest_place)), - }, + StatementKind::Retag(RetagKind::Default, Box::new(dest_place)), + ), ); } @@ -174,10 +176,7 @@ impl<'tcx> crate::MirPass<'tcx> for AddRetag { let source_info = block_data.statements[i].source_info; block_data.statements.insert( i + 1, - Statement { - source_info, - kind: StatementKind::Retag(retag_kind, Box::new(place)), - }, + Statement::new(source_info, StatementKind::Retag(retag_kind, Box::new(place))), ); } } diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index 8f88613b79f..989787504b7 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -51,22 +51,18 @@ fn insert_alignment_check<'tcx>( let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr); let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into(); - stmts - .push(Statement { source_info, kind: StatementKind::Assign(Box::new((thin_ptr, rvalue))) }); + stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((thin_ptr, rvalue))))); // Transmute the pointer to a usize (equivalent to `ptr.addr()`). let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize); let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); - stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) }); + stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((addr, rvalue))))); // Get the alignment of the pointee let alignment = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); let rvalue = Rvalue::NullaryOp(NullOp::AlignOf, pointee_ty); - stmts.push(Statement { - source_info, - kind: StatementKind::Assign(Box::new((alignment, rvalue))), - }); + stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((alignment, rvalue))))); // Subtract 1 from the alignment to get the alignment mask let alignment_mask = @@ -76,13 +72,13 @@ fn insert_alignment_check<'tcx>( user_ty: None, const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(1, &tcx)), tcx.types.usize), })); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( alignment_mask, Rvalue::BinaryOp(BinOp::Sub, Box::new((Operand::Copy(alignment), one))), ))), - }); + )); // If this target does not have reliable alignment, further limit the mask by anding it with // the mask for the highest reliable alignment. @@ -99,31 +95,31 @@ fn insert_alignment_check<'tcx>( tcx.types.usize, ), })); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( alignment_mask, Rvalue::BinaryOp( BinOp::BitAnd, Box::new((Operand::Copy(alignment_mask), max_mask)), ), ))), - }); + )); } // BitAnd the alignment mask with the pointer let alignment_bits = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( alignment_bits, Rvalue::BinaryOp( BinOp::BitAnd, Box::new((Operand::Copy(addr), Operand::Copy(alignment_mask))), ), ))), - }); + )); // Check if the alignment bits are all zero let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); @@ -132,13 +128,13 @@ fn insert_alignment_check<'tcx>( user_ty: None, const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)), tcx.types.usize), })); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( is_ok, Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(alignment_bits), zero.clone()))), ))), - }); + )); // Emit a check that asserts on the alignment and otherwise triggers a // AssertKind::MisalignedPointerDereference. diff --git a/compiler/rustc_mir_transform/src/check_null.rs b/compiler/rustc_mir_transform/src/check_null.rs index ad74e335bd9..e365d36580a 100644 --- a/compiler/rustc_mir_transform/src/check_null.rs +++ b/compiler/rustc_mir_transform/src/check_null.rs @@ -41,13 +41,12 @@ fn insert_null_check<'tcx>( let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr); let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into(); - stmts - .push(Statement { source_info, kind: StatementKind::Assign(Box::new((thin_ptr, rvalue))) }); + stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((thin_ptr, rvalue))))); // Transmute the pointer to a usize (equivalent to `ptr.addr()`). let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize); let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); - stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) }); + stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((addr, rvalue))))); let zero = Operand::Constant(Box::new(ConstOperand { span: source_info.span, @@ -71,24 +70,24 @@ fn insert_null_check<'tcx>( let rvalue = Rvalue::NullaryOp(NullOp::SizeOf, pointee_ty); let sizeof_pointee = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new((sizeof_pointee, rvalue))), - }); + StatementKind::Assign(Box::new((sizeof_pointee, rvalue))), + )); // Check that the pointee is not a ZST. let is_pointee_not_zst = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( is_pointee_not_zst, Rvalue::BinaryOp( BinOp::Ne, Box::new((Operand::Copy(sizeof_pointee), zero.clone())), ), ))), - }); + )); // Pointer needs to be checked only if pointee is not a ZST. Operand::Copy(is_pointee_not_zst) @@ -97,38 +96,38 @@ fn insert_null_check<'tcx>( // Check whether the pointer is null. let is_null = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( is_null, Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(addr), zero))), ))), - }); + )); // We want to throw an exception if the pointer is null and the pointee is not unconditionally // allowed (which for all non-borrow place uses, is when the pointee is ZST). let should_throw_exception = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( should_throw_exception, Rvalue::BinaryOp( BinOp::BitAnd, Box::new((Operand::Copy(is_null), pointee_should_be_checked)), ), ))), - }); + )); // The final condition whether this pointer usage is ok or not. let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); - stmts.push(Statement { + stmts.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( is_ok, Rvalue::UnaryOp(UnOp::Not, Operand::Copy(should_throw_exception)), ))), - }); + )); // Emit a PointerCheck that asserts on the condition and otherwise triggers // a AssertKind::NullPointerDereference. diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs index bf94f1aad24..4f913c1fca0 100644 --- a/compiler/rustc_mir_transform/src/check_pointers.rs +++ b/compiler/rustc_mir_transform/src/check_pointers.rs @@ -235,11 +235,11 @@ fn split_block( let block_data = &mut basic_blocks[location.block]; // Drain every statement after this one and move the current terminator to a new basic block. - let new_block = BasicBlockData { - statements: block_data.statements.split_off(location.statement_index), - terminator: block_data.terminator.take(), - is_cleanup: block_data.is_cleanup, - }; + let new_block = BasicBlockData::new_stmts( + block_data.statements.split_off(location.statement_index), + block_data.terminator.take(), + block_data.is_cleanup, + ); basic_blocks.push(new_block) } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 06c6b46a9c2..761d5461a99 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -252,16 +252,16 @@ impl<'tcx> TransformVisitor<'tcx> { } }; - let statements = vec![Statement { - kind: StatementKind::Assign(Box::new((Place::return_place(), none_value))), + let statements = vec![Statement::new( source_info, - }]; + StatementKind::Assign(Box::new((Place::return_place(), none_value))), + )]; - body.basic_blocks_mut().push(BasicBlockData { + body.basic_blocks_mut().push(BasicBlockData::new_stmts( statements, - terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), - is_cleanup: false, - }); + Some(Terminator { source_info, kind: TerminatorKind::Return }), + false, + )); block } @@ -342,10 +342,10 @@ impl<'tcx> TransformVisitor<'tcx> { } }; - statements.push(Statement { - kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))), + statements.push(Statement::new( source_info, - }); + StatementKind::Assign(Box::new((Place::return_place(), rvalue))), + )); } // Create a Place referencing a coroutine struct field @@ -361,13 +361,13 @@ impl<'tcx> TransformVisitor<'tcx> { // Create a statement which changes the discriminant fn set_discr(&self, state_disc: VariantIdx, source_info: SourceInfo) -> Statement<'tcx> { let self_place = Place::from(SELF_ARG); - Statement { + Statement::new( source_info, - kind: StatementKind::SetDiscriminant { + StatementKind::SetDiscriminant { place: Box::new(self_place), variant_index: state_disc, }, - } + ) } // Create a statement which reads the discriminant into a temporary @@ -377,10 +377,10 @@ impl<'tcx> TransformVisitor<'tcx> { let temp = Place::from(local_decls_len); let self_place = Place::from(SELF_ARG); - let assign = Statement { - source_info: SourceInfo::outermost(body.span), - kind: StatementKind::Assign(Box::new((temp, Rvalue::Discriminant(self_place)))), - }; + let assign = Statement::new( + SourceInfo::outermost(body.span), + StatementKind::Assign(Box::new((temp, Rvalue::Discriminant(self_place)))), + ); (assign, temp) } } @@ -450,7 +450,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { && !self.always_live_locals.contains(l); if needs_storage_dead { data.statements - .push(Statement { source_info, kind: StatementKind::StorageDead(l) }); + .push(Statement::new(source_info, StatementKind::StorageDead(l))); } } @@ -596,10 +596,8 @@ fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local let local = arg.node.place().unwrap().local; let arg = Rvalue::Use(arg.node); - let assign = Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new((destination, arg))), - }; + let assign = + Statement::new(terminator.source_info, StatementKind::Assign(Box::new((destination, arg)))); bb_data.statements.push(assign); bb_data.terminator = Some(Terminator { source_info: terminator.source_info, @@ -1075,11 +1073,11 @@ fn insert_switch<'tcx>( let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().raw.insert( 0, - BasicBlockData { - statements: vec![assign], - terminator: Some(Terminator { source_info, kind: switch }), - is_cleanup: false, - }, + BasicBlockData::new_stmts( + vec![assign], + Some(Terminator { source_info, kind: switch }), + false, + ), ); for b in body.basic_blocks_mut().iter_mut() { @@ -1089,11 +1087,7 @@ fn insert_switch<'tcx>( fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock { let source_info = SourceInfo::outermost(body.span); - body.basic_blocks_mut().push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { source_info, kind }), - is_cleanup: false, - }) + body.basic_blocks_mut().push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) } fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) -> Statement<'tcx> { @@ -1109,19 +1103,16 @@ fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) -> Box::new(AggregateKind::Adt(poll_def_id, VariantIdx::from_usize(0), args, None, None)), IndexVec::from_raw(vec![val]), ); - Statement { - kind: StatementKind::Assign(Box::new((Place::return_place(), ready_val))), - source_info, - } + Statement::new(source_info, StatementKind::Assign(Box::new((Place::return_place(), ready_val)))) } fn insert_poll_ready_block<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> BasicBlock { let source_info = SourceInfo::outermost(body.span); - body.basic_blocks_mut().push(BasicBlockData { - statements: [return_poll_ready_assign(tcx, source_info)].to_vec(), - terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), - is_cleanup: false, - }) + body.basic_blocks_mut().push(BasicBlockData::new_stmts( + [return_poll_ready_assign(tcx, source_info)].to_vec(), + Some(Terminator { source_info, kind: TerminatorKind::Return }), + false, + )) } fn insert_panic_block<'tcx>( @@ -1205,13 +1196,11 @@ fn generate_poison_block_and_redirect_unwinds_there<'tcx>( body: &mut Body<'tcx>, ) { let source_info = SourceInfo::outermost(body.span); - let poison_block = body.basic_blocks_mut().push(BasicBlockData { - statements: vec![ - transform.set_discr(VariantIdx::new(CoroutineArgs::POISONED), source_info), - ], - terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), - is_cleanup: true, - }); + let poison_block = body.basic_blocks_mut().push(BasicBlockData::new_stmts( + vec![transform.set_discr(VariantIdx::new(CoroutineArgs::POISONED), source_info)], + Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), + true, + )); for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() { let source_info = block.terminator().source_info; @@ -1345,32 +1334,28 @@ fn create_cases<'tcx>( && !transform.remap.contains(l) && !transform.always_live_locals.contains(l); if needs_storage_live { - statements - .push(Statement { source_info, kind: StatementKind::StorageLive(l) }); + statements.push(Statement::new(source_info, StatementKind::StorageLive(l))); } } if operation == Operation::Resume { // Move the resume argument to the destination place of the `Yield` terminator let resume_arg = CTX_ARG; - statements.push(Statement { + statements.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( point.resume_arg, Rvalue::Use(Operand::Move(resume_arg.into())), ))), - }); + )); } // Then jump to the real target - let block = body.basic_blocks_mut().push(BasicBlockData { + let block = body.basic_blocks_mut().push(BasicBlockData::new_stmts( statements, - terminator: Some(Terminator { - source_info, - kind: TerminatorKind::Goto { target }, - }), - is_cleanup: false, - }); + Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), + false, + )); (point.state, block) }) @@ -1540,13 +1525,13 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { let stmts = &mut body.basic_blocks_mut()[START_BLOCK].statements; stmts.insert( 0, - Statement { + Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( old_resume_local.into(), Rvalue::Use(Operand::Move(resume_local.into())), ))), - }, + ), ); let always_live_locals = always_storage_live_locals(body); diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index dc68629ec0d..406575c4f43 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -87,12 +87,11 @@ fn build_pin_fut<'tcx>( const_: Const::zero_sized(pin_fut_new_unchecked_fn), })); - let storage_live = - Statement { source_info, kind: StatementKind::StorageLive(fut_pin_place.local) }; + let storage_live = Statement::new(source_info, StatementKind::StorageLive(fut_pin_place.local)); - let fut_ref_assign = Statement { + let fut_ref_assign = Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( fut_ref_place, Rvalue::Ref( tcx.lifetimes.re_erased, @@ -100,12 +99,12 @@ fn build_pin_fut<'tcx>( fut_place, ), ))), - }; + ); // call Pin<FutTy>::new_unchecked(&mut fut) - let pin_fut_bb = body.basic_blocks_mut().push(BasicBlockData { - statements: [storage_live, fut_ref_assign].to_vec(), - terminator: Some(Terminator { + let pin_fut_bb = body.basic_blocks_mut().push(BasicBlockData::new_stmts( + [storage_live, fut_ref_assign].to_vec(), + Some(Terminator { source_info, kind: TerminatorKind::Call { func: pin_fut_new_unchecked_fn, @@ -117,8 +116,8 @@ fn build_pin_fut<'tcx>( fn_span: span, }, }), - is_cleanup: false, - }); + false, + )); (pin_fut_bb, fut_pin_place) } @@ -156,19 +155,15 @@ fn build_poll_switch<'tcx>( let source_info = SourceInfo::outermost(body.span); let poll_discr_place = Place::from(body.local_decls.push(LocalDecl::new(poll_discr_ty, source_info.span))); - let discr_assign = Statement { + let discr_assign = Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( - poll_discr_place, - Rvalue::Discriminant(*poll_unit_place), - ))), - }; - let storage_dead = - Statement { source_info, kind: StatementKind::StorageDead(fut_pin_place.local) }; + StatementKind::Assign(Box::new((poll_discr_place, Rvalue::Discriminant(*poll_unit_place)))), + ); + let storage_dead = Statement::new(source_info, StatementKind::StorageDead(fut_pin_place.local)); let unreachable_block = insert_term_block(body, TerminatorKind::Unreachable); - body.basic_blocks_mut().push(BasicBlockData { - statements: [storage_dead, discr_assign].to_vec(), - terminator: Some(Terminator { + body.basic_blocks_mut().push(BasicBlockData::new_stmts( + [storage_dead, discr_assign].to_vec(), + Some(Terminator { source_info, kind: TerminatorKind::SwitchInt { discr: Operand::Move(poll_discr_place), @@ -179,8 +174,8 @@ fn build_poll_switch<'tcx>( ), }, }), - is_cleanup: false, - }) + false, + )) } // Gather blocks, reachable through 'drop' targets of Yield and Drop terminators (chained) @@ -330,10 +325,10 @@ pub(super) fn expand_async_drops<'tcx>( let context_ref_place = Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span))); let arg = Rvalue::Use(Operand::Move(Place::from(CTX_ARG))); - body[bb].statements.push(Statement { + body[bb].statements.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new((context_ref_place, arg))), - }); + StatementKind::Assign(Box::new((context_ref_place, arg))), + )); let yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield let (pin_bb, fut_pin_place) = build_pin_fut(tcx, body, fut_place.clone(), UnwindAction::Continue); @@ -551,11 +546,8 @@ pub(super) fn insert_clean_drop<'tcx>( }; // Create a block to destroy an unresumed coroutines. This can only destroy upvars. - body.basic_blocks_mut().push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { source_info, kind: term }), - is_cleanup: false, - }) + body.basic_blocks_mut() + .push(BasicBlockData::new(Some(Terminator { source_info, kind: term }), false)) } pub(super) fn create_coroutine_drop_shim<'tcx>( @@ -734,11 +726,7 @@ pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>( body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info); // call coroutine_drop() - let call_bb = body.basic_blocks_mut().push(BasicBlockData { - statements: Vec::new(), - terminator: None, - is_cleanup: false, - }); + let call_bb = body.basic_blocks_mut().push(BasicBlockData::new(None, false)); // return Poll::Ready() let ret_bb = insert_poll_ready_block(tcx, &mut body); diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 702c62eddc7..f253d1662ca 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -259,7 +259,7 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb debug!(" injecting statement {counter_kind:?} for {bb:?}"); let data = &mut mir_body[bb]; let source_info = data.terminator().source_info; - let statement = Statement { source_info, kind: StatementKind::Coverage(counter_kind) }; + let statement = Statement::new(source_info, StatementKind::Coverage(counter_kind)); data.statements.insert(0, statement); } diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index 3c0053c610d..b0fc5e90f07 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -68,14 +68,13 @@ impl<'tcx> MockBlocks<'tcx> { BytePos(1) }; let next_hi = next_lo + BytePos(1); - self.blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + self.blocks.push(BasicBlockData::new( + Some(Terminator { source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)), kind, }), - is_cleanup: false, - }) + false, + )) } fn link(&mut self, from_block: BasicBlock, to_block: BasicBlock) { diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index d0b313e149a..fb17cca30f4 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -55,8 +55,8 @@ fn has_back_edge( } fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) { - basic_block_data.statements.push(Statement { - source_info: basic_block_data.terminator().source_info, - kind: StatementKind::ConstEvalCounter, - }); + basic_block_data.statements.push(Statement::new( + basic_block_data.terminator().source_info, + StatementKind::ConstEvalCounter, + )); } diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index c9bc52c6c7e..de96b1f255a 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -222,15 +222,14 @@ where let span = self.source_info.span; let pin_obj_bb = bb.unwrap_or_else(|| { - self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + self.elaborator.patch().new_block(BasicBlockData::new( + Some(Terminator { // Temporary terminator, will be replaced by patch source_info: self.source_info, kind: TerminatorKind::Return, }), - is_cleanup: false, - }) + false, + )) }); let (fut_ty, drop_fn_def_id, trait_args) = if call_destructor_only { @@ -366,10 +365,8 @@ where call_statements.push(self.assign(obj_ptr_place, addr)); obj_ptr_place }; - call_statements.push(Statement { - source_info: self.source_info, - kind: StatementKind::StorageLive(fut.local), - }); + call_statements + .push(Statement::new(self.source_info, StatementKind::StorageLive(fut.local))); let call_drop_bb = self.new_block_with_statements( unwind, @@ -732,17 +729,17 @@ where let do_drop_bb = self.drop_subpath(interior, interior_path, succ, unwind, dropline); - let setup_bbd = BasicBlockData { - statements: vec![self.assign( + let setup_bbd = BasicBlockData::new_stmts( + vec![self.assign( Place::from(ptr_local), Rvalue::Cast(CastKind::Transmute, Operand::Copy(nonnull_place), ptr_ty), )], - terminator: Some(Terminator { + Some(Terminator { kind: TerminatorKind::Goto { target: do_drop_bb }, source_info: self.source_info, }), - is_cleanup: unwind.is_cleanup(), - }; + unwind.is_cleanup(), + ); self.elaborator.patch().new_block(setup_bbd) } @@ -753,14 +750,13 @@ where args: GenericArgsRef<'tcx>, ) -> BasicBlock { if adt.variants().is_empty() { - return self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + return self.elaborator.patch().new_block(BasicBlockData::new( + Some(Terminator { source_info: self.source_info, kind: TerminatorKind::Unreachable, }), - is_cleanup: self.unwind.is_cleanup(), - }); + self.unwind.is_cleanup(), + )); } let skip_contents = adt.is_union() || adt.is_manually_drop(); @@ -927,9 +923,9 @@ where let discr_ty = adt.repr().discr_type().to_ty(self.tcx()); let discr = Place::from(self.new_temp(discr_ty)); let discr_rv = Rvalue::Discriminant(self.place); - let switch_block = BasicBlockData { - statements: vec![self.assign(discr, discr_rv)], - terminator: Some(Terminator { + let switch_block = BasicBlockData::new_stmts( + vec![self.assign(discr, discr_rv)], + Some(Terminator { source_info: self.source_info, kind: TerminatorKind::SwitchInt { discr: Operand::Move(discr), @@ -939,8 +935,8 @@ where ), }, }), - is_cleanup: unwind.is_cleanup(), - }; + unwind.is_cleanup(), + ); let switch_block = self.elaborator.patch().new_block(switch_block); self.drop_flag_test_block(switch_block, succ, unwind) } @@ -956,8 +952,8 @@ where let ref_place = self.new_temp(ref_ty); let unit_temp = Place::from(self.new_temp(tcx.types.unit)); - let result = BasicBlockData { - statements: vec![self.assign( + let result = BasicBlockData::new_stmts( + vec![self.assign( Place::from(ref_place), Rvalue::Ref( tcx.lifetimes.re_erased, @@ -965,7 +961,7 @@ where self.place, ), )], - terminator: Some(Terminator { + Some(Terminator { kind: TerminatorKind::Call { func: Operand::function_handle( tcx, @@ -983,8 +979,8 @@ where }, source_info: self.source_info, }), - is_cleanup: unwind.is_cleanup(), - }; + unwind.is_cleanup(), + ); let destructor_block = self.elaborator.patch().new_block(result); @@ -1047,8 +1043,8 @@ where let can_go = Place::from(self.new_temp(tcx.types.bool)); let one = self.constant_usize(1); - let drop_block = BasicBlockData { - statements: vec![ + let drop_block = BasicBlockData::new_stmts( + vec![ self.assign( ptr, Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_index(self.place, cur)), @@ -1058,26 +1054,26 @@ where Rvalue::BinaryOp(BinOp::Add, Box::new((move_(cur.into()), one))), ), ], - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { + Some(Terminator { source_info: self.source_info, // this gets overwritten by drop elaboration. kind: TerminatorKind::Unreachable, }), - }; + unwind.is_cleanup(), + ); let drop_block = self.elaborator.patch().new_block(drop_block); - let loop_block = BasicBlockData { - statements: vec![self.assign( + let loop_block = BasicBlockData::new_stmts( + vec![self.assign( can_go, Rvalue::BinaryOp(BinOp::Eq, Box::new((copy(Place::from(cur)), copy(len.into())))), )], - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { + Some(Terminator { source_info: self.source_info, kind: TerminatorKind::if_(move_(can_go), succ, drop_block), }), - }; + unwind.is_cleanup(), + ); let loop_block = self.elaborator.patch().new_block(loop_block); let place = tcx.mk_place_deref(ptr); @@ -1187,8 +1183,8 @@ where let slice_ptr_ty = Ty::new_mut_ptr(tcx, slice_ty); let slice_ptr = self.new_temp(slice_ptr_ty); - let mut delegate_block = BasicBlockData { - statements: vec![ + let mut delegate_block = BasicBlockData::new_stmts( + vec![ self.assign(Place::from(array_ptr), Rvalue::RawPtr(RawPtrKind::Mut, self.place)), self.assign( Place::from(slice_ptr), @@ -1202,9 +1198,9 @@ where ), ), ], - is_cleanup: self.unwind.is_cleanup(), - terminator: None, - }; + None, + self.unwind.is_cleanup(), + ); let array_place = mem::replace( &mut self.place, @@ -1246,8 +1242,8 @@ where }; let zero = self.constant_usize(0); - let block = BasicBlockData { - statements: vec![ + let block = BasicBlockData::new_stmts( + vec![ self.assign( len.into(), Rvalue::UnaryOp( @@ -1257,12 +1253,12 @@ where ), self.assign(cur.into(), Rvalue::Use(zero)), ], - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { + Some(Terminator { source_info: self.source_info, kind: TerminatorKind::Goto { target: loop_block }, }), - }; + unwind.is_cleanup(), + ); let drop_block = self.elaborator.patch().new_block(block); // FIXME(#34708): handle partially-dropped array/slice elements. @@ -1308,14 +1304,13 @@ where self.source_info.span, "open drop for unsafe binder shouldn't be encountered", ); - self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + self.elaborator.patch().new_block(BasicBlockData::new( + Some(Terminator { source_info: self.source_info, kind: TerminatorKind::Unreachable, }), - is_cleanup: self.unwind.is_cleanup(), - }) + self.unwind.is_cleanup(), + )) } _ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty), @@ -1434,11 +1429,10 @@ where } fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock { - self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { source_info: self.source_info, kind: k }), - is_cleanup: unwind.is_cleanup(), - }) + self.elaborator.patch().new_block(BasicBlockData::new( + Some(Terminator { source_info: self.source_info, kind: k }), + unwind.is_cleanup(), + )) } fn new_block_with_statements( @@ -1447,11 +1441,11 @@ where statements: Vec<Statement<'tcx>>, k: TerminatorKind<'tcx>, ) -> BasicBlock { - self.elaborator.patch().new_block(BasicBlockData { + self.elaborator.patch().new_block(BasicBlockData::new_stmts( statements, - terminator: Some(Terminator { source_info: self.source_info, kind: k }), - is_cleanup: unwind.is_cleanup(), - }) + Some(Terminator { source_info: self.source_info, kind: k }), + unwind.is_cleanup(), + )) } fn new_temp(&mut self, ty: Ty<'tcx>) -> Local { @@ -1467,9 +1461,6 @@ where } fn assign(&self, lhs: Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { - Statement { - source_info: self.source_info, - kind: StatementKind::Assign(Box::new((lhs, rhs))), - } + Statement::new(self.source_info, StatementKind::Assign(Box::new((lhs, rhs)))) } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index b17b7f45000..335354c23b6 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1636,7 +1636,7 @@ fn op_to_prop_const<'tcx>( } let pointer = mplace.ptr().into_pointer_or_addr().ok()?; - let (prov, offset) = pointer.into_parts(); + let (prov, offset) = pointer.prov_and_relative_offset(); let alloc_id = prov.alloc_id(); intern_const_alloc_for_constprop(ecx, alloc_id).discard_err()?; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index c27087fea11..7852bb7ae2f 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -900,10 +900,10 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( ); let dest_ty = dest.ty(caller_body, tcx); let temp = Place::from(new_call_temp(caller_body, callsite, dest_ty, return_block)); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new((temp, dest))), - }); + caller_body[callsite.block].statements.push(Statement::new( + callsite.source_info, + StatementKind::Assign(Box::new((temp, dest))), + )); tcx.mk_place_deref(temp) } else { destination @@ -947,10 +947,9 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( for local in callee_body.vars_and_temps_iter() { if integrator.always_live_locals.contains(local) { let new_local = integrator.map_local(local); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageLive(new_local), - }); + caller_body[callsite.block] + .statements + .push(Statement::new(callsite.source_info, StatementKind::StorageLive(new_local))); } } if let Some(block) = return_block { @@ -958,22 +957,22 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( // the slice once. let mut n = 0; if remap_destination { - caller_body[block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new(( + caller_body[block].statements.push(Statement::new( + callsite.source_info, + StatementKind::Assign(Box::new(( dest, Rvalue::Use(Operand::Move(destination_local.into())), ))), - }); + )); n += 1; } for local in callee_body.vars_and_temps_iter().rev() { if integrator.always_live_locals.contains(local) { let new_local = integrator.map_local(local); - caller_body[block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageDead(new_local), - }); + caller_body[block].statements.push(Statement::new( + callsite.source_info, + StatementKind::StorageDead(new_local), + )); n += 1; } } @@ -1126,10 +1125,10 @@ fn create_temp_if_necessary<'tcx, I: Inliner<'tcx>>( trace!("creating temp for argument {:?}", arg); let arg_ty = arg.ty(caller_body, inliner.tcx()); let local = new_call_temp(caller_body, callsite, arg_ty, return_block); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg)))), - }); + caller_body[callsite.block].statements.push(Statement::new( + callsite.source_info, + StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg)))), + )); local } @@ -1142,19 +1141,14 @@ fn new_call_temp<'tcx>( ) -> Local { let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span)); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageLive(local), - }); + caller_body[callsite.block] + .statements + .push(Statement::new(callsite.source_info, StatementKind::StorageLive(local))); if let Some(block) = return_block { - caller_body[block].statements.insert( - 0, - Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageDead(local), - }, - ); + caller_body[block] + .statements + .insert(0, Statement::new(callsite.source_info, StatementKind::StorageDead(local))); } local diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 5f0c55ddc09..dbcaed20953 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -240,15 +240,15 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { let Some(arg_place) = arg.node.place() else { return }; - statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::Use(Operand::Copy( arg_place.project_deeper(&[ProjectionElem::Deref], self.tcx), )), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target: *destination_block }; } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index c4415294264..08f25276cec 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -259,13 +259,13 @@ fn remap_mir_for_const_eval_select<'tcx>( // (const generic stuff) so we just create a temporary and deconstruct // that. let local = body.local_decls.push(LocalDecl::new(ty, fn_span)); - bb.statements.push(Statement { - source_info: SourceInfo::outermost(fn_span), - kind: StatementKind::Assign(Box::new(( + bb.statements.push(Statement::new( + SourceInfo::outermost(fn_span), + StatementKind::Assign(Box::new(( local.into(), Rvalue::Use(tupled_args.node.clone()), ))), - }); + )); (Operand::Move, local.into()) } Operand::Move(place) => (Operand::Move, place), diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index fa29ab985b7..8dadce0d448 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -25,31 +25,31 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { } sym::ub_checks => { let target = target.unwrap(); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::contract_checks => { let target = target.unwrap(); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::forget => { let target = target.unwrap(); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::Use(Operand::Constant(Box::new(ConstOperand { span: terminator.source_info.span, @@ -57,7 +57,7 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { const_: Const::zero_sized(tcx.types.unit), }))), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::copy_nonoverlapping => { @@ -65,9 +65,9 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { let Ok([src, dst, count]) = take_array(args) else { bug!("Wrong arguments for copy_non_overlapping intrinsic"); }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Intrinsic(Box::new( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Intrinsic(Box::new( NonDivergingIntrinsic::CopyNonOverlapping( rustc_middle::mir::CopyNonOverlapping { src: src.node, @@ -76,7 +76,7 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { }, ), )), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::assume => { @@ -84,12 +84,12 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { let Ok([arg]) = take_array(args) else { bug!("Wrong arguments for assume intrinsic"); }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::Assume(arg.node), - )), - }); + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume( + arg.node, + ))), + )); terminator.kind = TerminatorKind::Goto { target }; } sym::wrapping_add @@ -121,13 +121,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { sym::unchecked_shr => BinOp::ShrUnchecked, _ => bug!("unexpected intrinsic"), }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { @@ -141,13 +141,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { sym::mul_with_overflow => BinOp::MulWithOverflow, _ => bug!("unexpected intrinsic"), }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::size_of | sym::align_of => { @@ -158,13 +158,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { sym::align_of => NullOp::AlignOf, _ => bug!("unexpected intrinsic"), }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::NullaryOp(null_op, tp_ty), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::read_via_copy => { @@ -183,13 +183,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { }; // Add new statement at the end of the block that does the read, and patch // up the terminator. - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::Use(Operand::Copy(derefed_place)), ))), - }); + )); terminator.kind = match *target { None => { // No target means this read something uninhabited, @@ -217,13 +217,10 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { "Only passing a local is supported" ); }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - derefed_place, - Rvalue::Use(val.node), - ))), - }); + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new((derefed_place, Rvalue::Use(val.node)))), + )); terminator.kind = TerminatorKind::Goto { target }; } sym::discriminant_value => { @@ -236,13 +233,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { }; let arg = arg.node.place().unwrap(); let arg = tcx.mk_place_deref(arg); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::Discriminant(arg), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::offset => { @@ -253,13 +250,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { "Wrong number of arguments for offset intrinsic", ); }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr.node, delta.node))), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::slice_get_unchecked => { @@ -302,10 +299,10 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { _ => bug!("Unknown return type {ret_ty:?}"), }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new((*destination, rvalue))), - }); + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new((*destination, rvalue))), + )); terminator.kind = TerminatorKind::Goto { target }; } sym::transmute | sym::transmute_unchecked => { @@ -320,13 +317,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { // Always emit the cast, even if we transmute to an uninhabited type, // because that lets CTFE and codegen generate better error messages // when such a transmute actually ends up reachable. - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::Cast(CastKind::Transmute, arg.node, dst_ty), ))), - }); + )); if let Some(target) = *target { terminator.kind = TerminatorKind::Goto { target }; } else { @@ -351,13 +348,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { ); }; let fields = [data.node, meta.node]; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::Aggregate(Box::new(kind), fields.into()), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } sym::ptr_metadata => { @@ -368,13 +365,13 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { ); }; let target = target.unwrap(); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( *destination, Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node), ))), - }); + )); terminator.kind = TerminatorKind::Goto { target }; } _ => {} diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index aca80e36e33..79a9017de3e 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -56,8 +56,7 @@ fn lower_slice_len_call<'tcx>(block: &mut BasicBlockData<'tcx>, slice_len_fn_ite // make new RValue for Len let r_value = Rvalue::UnaryOp(UnOp::PtrMetadata, arg.node.clone()); let len_statement_kind = StatementKind::Assign(Box::new((*destination, r_value))); - let add_statement = - Statement { kind: len_statement_kind, source_info: terminator.source_info }; + let add_statement = Statement::new(terminator.source_info, len_statement_kind); // modify terminator into simple Goto let new_terminator_kind = TerminatorKind::Goto { target: *bb }; diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index a872eae15f1..c781d1a5324 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -78,14 +78,13 @@ impl<'tcx> MirPatch<'tcx> { return bb; } - let bb = self.new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + let bb = self.new_block(BasicBlockData::new( + Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::UnwindResume, }), - is_cleanup: true, - }); + true, + )); self.resume_block = Some(bb); bb } @@ -95,14 +94,13 @@ impl<'tcx> MirPatch<'tcx> { return bb; } - let bb = self.new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + let bb = self.new_block(BasicBlockData::new( + Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::Unreachable, }), - is_cleanup: true, - }); + true, + )); self.unreachable_cleanup_block = Some(bb); bb } @@ -112,14 +110,13 @@ impl<'tcx> MirPatch<'tcx> { return bb; } - let bb = self.new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + let bb = self.new_block(BasicBlockData::new( + Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::Unreachable, }), - is_cleanup: false, - }); + false, + )); self.unreachable_no_cleanup_block = Some(bb); bb } @@ -131,14 +128,13 @@ impl<'tcx> MirPatch<'tcx> { return cached_bb; } - let bb = self.new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + let bb = self.new_block(BasicBlockData::new( + Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::UnwindTerminate(reason), }), - is_cleanup: true, - }); + true, + )); self.terminate_block = Some((bb, reason)); bb } @@ -280,7 +276,7 @@ impl<'tcx> MirPatch<'tcx> { let source_info = Self::source_info_for_index(&body[loc.block], loc); body[loc.block] .statements - .insert(loc.statement_index, Statement { source_info, kind: stmt }); + .insert(loc.statement_index, Statement::new(source_info, stmt)); delta += 1; } } diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 47d43830970..4e8f30e077b 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -731,23 +731,22 @@ struct Promoter<'a, 'tcx> { impl<'a, 'tcx> Promoter<'a, 'tcx> { fn new_block(&mut self) -> BasicBlock { let span = self.promoted.span; - self.promoted.basic_blocks_mut().push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + self.promoted.basic_blocks_mut().push(BasicBlockData::new( + Some(Terminator { source_info: SourceInfo::outermost(span), kind: TerminatorKind::Return, }), - is_cleanup: false, - }) + false, + )) } fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) { let last = self.promoted.basic_blocks.last_index().unwrap(); let data = &mut self.promoted[last]; - data.statements.push(Statement { - source_info: SourceInfo::outermost(span), - kind: StatementKind::Assign(Box::new((Place::from(dest), rvalue))), - }); + data.statements.push(Statement::new( + SourceInfo::outermost(span), + StatementKind::Assign(Box::new((Place::from(dest), rvalue))), + )); } fn is_temp_kind(&self, local: Local) -> bool { @@ -914,13 +913,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref); let promoted_operand = promoted_operand(ref_ty, span); - let promoted_ref_statement = Statement { - source_info: statement.source_info, - kind: StatementKind::Assign(Box::new(( + let promoted_ref_statement = Statement::new( + statement.source_info, + StatementKind::Assign(Box::new(( Place::from(promoted_ref), Rvalue::Use(Operand::Constant(Box::new(promoted_operand))), ))), - }; + ); self.extra_statements.push((loc, promoted_ref_statement)); ( diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 6d45bbc6e16..4083038cbb6 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -323,9 +323,7 @@ fn dropee_emit_retag<'tcx>( StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)), ]; for s in new_statements { - body.basic_blocks_mut()[START_BLOCK] - .statements - .push(Statement { source_info, kind: s }); + body.basic_blocks_mut()[START_BLOCK].statements.push(Statement::new(source_info, s)); } } dropee_ptr @@ -350,11 +348,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) let return_block = BasicBlock::new(1); let mut blocks = IndexVec::with_capacity(2); let block = |blocks: &mut IndexVec<_, _>, kind| { - blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { source_info, kind }), - is_cleanup: false, - }) + blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) }; block(&mut blocks, TerminatorKind::Goto { target: return_block }); block(&mut blocks, TerminatorKind::Return); @@ -515,17 +509,17 @@ fn build_thread_local_shim<'tcx>( let span = tcx.def_span(def_id); let source_info = SourceInfo::outermost(span); - let blocks = IndexVec::from_raw(vec![BasicBlockData { - statements: vec![Statement { + let blocks = IndexVec::from_raw(vec![BasicBlockData::new_stmts( + vec![Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( Place::return_place(), Rvalue::ThreadLocalRef(def_id), ))), - }], - terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), - is_cleanup: false, - }]); + )], + Some(Terminator { source_info, kind: TerminatorKind::Return }), + false, + )]); new_body( MirSource::from_instance(instance), @@ -609,11 +603,11 @@ impl<'tcx> CloneShimBuilder<'tcx> { is_cleanup: bool, ) -> BasicBlock { let source_info = self.source_info(); - self.blocks.push(BasicBlockData { + self.blocks.push(BasicBlockData::new_stmts( statements, - terminator: Some(Terminator { source_info, kind }), + Some(Terminator { source_info, kind }), is_cleanup, - }) + )) } /// Gives the index of an upcoming BasicBlock, with an offset. @@ -625,7 +619,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { } fn make_statement(&self, kind: StatementKind<'tcx>) -> Statement<'tcx> { - Statement { source_info: self.source_info(), kind } + Statement::new(self.source_info(), kind) } fn copy_shim(&mut self) { @@ -901,13 +895,13 @@ fn build_call_shim<'tcx>( .immutable(), ); let borrow_kind = BorrowKind::Mut { kind: MutBorrowKind::Default }; - statements.push(Statement { + statements.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( Place::from(ref_rcvr), Rvalue::Ref(tcx.lifetimes.re_erased, borrow_kind, rcvr_place()), ))), - }); + )); Operand::Move(Place::from(ref_rcvr)) } }); @@ -956,11 +950,11 @@ fn build_call_shim<'tcx>( let n_blocks = if let Some(Adjustment::RefMut) = rcvr_adjustment { 5 } else { 2 }; let mut blocks = IndexVec::with_capacity(n_blocks); let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| { - blocks.push(BasicBlockData { + blocks.push(BasicBlockData::new_stmts( statements, - terminator: Some(Terminator { source_info, kind }), + Some(Terminator { source_info, kind }), is_cleanup, - }) + )) }; // BB #0 @@ -1071,8 +1065,9 @@ pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { let kind = AggregateKind::Adt(adt_def.did(), variant_index, args, None, None); let variant = adt_def.variant(variant_index); - let statement = Statement { - kind: StatementKind::Assign(Box::new(( + let statement = Statement::new( + source_info, + StatementKind::Assign(Box::new(( Place::return_place(), Rvalue::Aggregate( Box::new(kind), @@ -1081,14 +1076,13 @@ pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { .collect(), ), ))), - source_info, - }; + ); - let start_block = BasicBlockData { - statements: vec![statement], - terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), - is_cleanup: false, - }; + let start_block = BasicBlockData::new_stmts( + vec![statement], + Some(Terminator { source_info, kind: TerminatorKind::Return }), + false, + ); let source = MirSource::item(ctor_id); let mut body = new_body( @@ -1130,16 +1124,16 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t Operand::Move(Place::from(Local::new(1))), Ty::new_imm_ptr(tcx, tcx.types.unit), ); - let stmt = Statement { + let stmt = Statement::new( source_info, - kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))), - }; + StatementKind::Assign(Box::new((Place::return_place(), rvalue))), + ); let statements = vec![stmt]; - let start_block = BasicBlockData { + let start_block = BasicBlockData::new_stmts( statements, - terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), - is_cleanup: false, - }; + Some(Terminator { source_info, kind: TerminatorKind::Return }), + false, + ); let source = MirSource::from_instance(ty::InstanceKind::FnPtrAddrShim(def_id, self_ty)); new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span) } @@ -1230,16 +1224,16 @@ fn build_construct_coroutine_by_move_shim<'tcx>( Box::new(AggregateKind::Coroutine(coroutine_def_id, coroutine_args)), IndexVec::from_raw(fields), ); - let stmt = Statement { + let stmt = Statement::new( source_info, - kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))), - }; + StatementKind::Assign(Box::new((Place::return_place(), rvalue))), + ); let statements = vec![stmt]; - let start_block = BasicBlockData { + let start_block = BasicBlockData::new_stmts( statements, - terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), - is_cleanup: false, - }; + Some(Terminator { source_info, kind: TerminatorKind::Return }), + false, + ); let source = MirSource::from_instance(ty::InstanceKind::ConstructCoroutineInClosureShim { coroutine_closure_def_id, diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index fd7b7362cd9..18d09473c19 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -88,11 +88,7 @@ pub(super) fn build_async_drop_shim<'tcx>( let return_block = BasicBlock::new(1); let mut blocks = IndexVec::with_capacity(2); let block = |blocks: &mut IndexVec<_, _>, kind| { - blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { source_info, kind }), - is_cleanup: false, - }) + blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) }; block( &mut blocks, @@ -133,7 +129,7 @@ pub(super) fn build_async_drop_shim<'tcx>( dropee_ptr, Rvalue::Use(Operand::Move(coroutine_layout_dropee)), ))); - body.basic_blocks_mut()[START_BLOCK].statements.push(Statement { source_info, kind: st_kind }); + body.basic_blocks_mut()[START_BLOCK].statements.push(Statement::new(source_info, st_kind)); dropee_ptr = dropee_emit_retag(tcx, &mut body, dropee_ptr, span); let dropline = body.basic_blocks.last_index(); @@ -240,13 +236,13 @@ fn build_adrop_for_coroutine_shim<'tcx>( .project_deeper(&[PlaceElem::Field(FieldIdx::ZERO, proxy_ref)], tcx); body.basic_blocks_mut()[START_BLOCK].statements.insert( idx, - Statement { + Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( Place::from(proxy_ref_local), Rvalue::CopyForDeref(proxy_ref_place), ))), - }, + ), ); idx += 1; let mut cor_ptr_local = proxy_ref_local; @@ -261,13 +257,13 @@ fn build_adrop_for_coroutine_shim<'tcx>( // _cor_ptr = _proxy.0.0 (... .0) body.basic_blocks_mut()[START_BLOCK].statements.insert( idx, - Statement { + Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( Place::from(cor_ptr_local), Rvalue::CopyForDeref(impl_ptr_place), ))), - }, + ), ); idx += 1; } @@ -281,10 +277,10 @@ fn build_adrop_for_coroutine_shim<'tcx>( ); body.basic_blocks_mut()[START_BLOCK].statements.insert( idx, - Statement { + Statement::new( source_info, - kind: StatementKind::Assign(Box::new((Place::from(cor_ref_local), reborrow))), - }, + StatementKind::Assign(Box::new((Place::from(cor_ref_local), reborrow))), + ), ); } body @@ -334,13 +330,13 @@ fn build_adrop_for_adrop_shim<'tcx>( let mut statements = Vec::new(); - statements.push(Statement { + statements.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( Place::from(proxy_ref_local), Rvalue::CopyForDeref(proxy_ref_place), ))), - }); + )); let mut cor_ptr_local = proxy_ref_local; proxy_ty.find_async_drop_impl_coroutine(tcx, |ty| { @@ -350,13 +346,13 @@ fn build_adrop_for_adrop_shim<'tcx>( .project_deeper(&[PlaceElem::Deref, PlaceElem::Field(FieldIdx::ZERO, ty_ptr)], tcx); cor_ptr_local = locals.push(LocalDecl::new(ty_ptr, span)); // _cor_ptr = _proxy.0.0 (... .0) - statements.push(Statement { + statements.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new(( + StatementKind::Assign(Box::new(( Place::from(cor_ptr_local), Rvalue::CopyForDeref(impl_ptr_place), ))), - }); + )); } }); @@ -367,10 +363,10 @@ fn build_adrop_for_adrop_shim<'tcx>( tcx.mk_place_deref(Place::from(cor_ptr_local)), ); let cor_ref_place = Place::from(locals.push(LocalDecl::new(cor_ref, span))); - statements.push(Statement { + statements.push(Statement::new( source_info, - kind: StatementKind::Assign(Box::new((cor_ref_place, reborrow))), - }); + StatementKind::Assign(Box::new((cor_ref_place, reborrow))), + )); // cor_pin_ty = `Pin<&mut cor_ref>` let cor_pin_ty = Ty::new_adt(tcx, pin_adt_ref, tcx.mk_args(&[cor_ref.into()])); @@ -378,9 +374,9 @@ fn build_adrop_for_adrop_shim<'tcx>( let pin_fn = tcx.require_lang_item(LangItem::PinNewUnchecked, span); // call Pin<FutTy>::new_unchecked(&mut impl_cor) - blocks.push(BasicBlockData { + blocks.push(BasicBlockData::new_stmts( statements, - terminator: Some(Terminator { + Some(Terminator { source_info, kind: TerminatorKind::Call { func: Operand::function_handle(tcx, pin_fn, [cor_ref.into()], span), @@ -392,15 +388,14 @@ fn build_adrop_for_adrop_shim<'tcx>( fn_span: span, }, }), - is_cleanup: false, - }); + false, + )); // When dropping async drop coroutine, we continue its execution: // we call impl::poll (impl_layout, ctx) let poll_fn = tcx.require_lang_item(LangItem::FuturePoll, span); let resume_ctx = Place::from(Local::new(2)); - blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { + blocks.push(BasicBlockData::new( + Some(Terminator { source_info, kind: TerminatorKind::Call { func: Operand::function_handle(tcx, poll_fn, [impl_ty.into()], span), @@ -416,13 +411,12 @@ fn build_adrop_for_adrop_shim<'tcx>( fn_span: span, }, }), - is_cleanup: false, - }); - blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), - is_cleanup: false, - }); + false, + )); + blocks.push(BasicBlockData::new( + Some(Terminator { source_info, kind: TerminatorKind::Return }), + false, + )); let source = MirSource::from_instance(instance); let mut body = new_body(source, blocks, locals, sig.inputs().len(), span); diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index bd008230731..c60eb566521 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -115,10 +115,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { for bb_idx in new_targets.all_targets() { storage_deads_to_insert.push(( *bb_idx, - Statement { - source_info: terminator.source_info, - kind: StatementKind::StorageDead(opt.to_switch_on.local), - }, + Statement::new( + terminator.source_info, + StatementKind::StorageDead(opt.to_switch_on.local), + ), )); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index a300558c0c9..c281ef216c7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -383,10 +383,10 @@ where let mut candidates = vec![]; - if let TypingMode::Coherence = self.typing_mode() { - if let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) { - return vec![candidate]; - } + if let TypingMode::Coherence = self.typing_mode() + && let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) + { + return vec![candidate]; } self.assemble_alias_bound_candidates(goal, &mut candidates); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 5b6f9785272..73bb1508de9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -997,12 +997,12 @@ where } fn try_fold_ty(&mut self, ty: I::Ty) -> Result<I::Ty, Ambiguous> { - if let ty::Alias(ty::Projection, alias_ty) = ty.kind() { - if let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())? { - return Ok(term.expect_ty()); - } + if let ty::Alias(ty::Projection, alias_ty) = ty.kind() + && let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())? + { + Ok(term.expect_ty()) + } else { + ty.try_super_fold_with(self) } - - ty.try_super_fold_with(self) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 0547eb77f8d..42264f58bcd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -42,20 +42,18 @@ where goal: Goal<I, Self>, assumption: I::Clause, ) -> Result<(), NoSolution> { - if let Some(host_clause) = assumption.as_host_effect_clause() { - if host_clause.def_id() == goal.predicate.def_id() - && host_clause.constness().satisfies(goal.predicate.constness) - { - if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( - goal.predicate.trait_ref.args, - host_clause.skip_binder().trait_ref.args, - ) { - return Ok(()); - } - } + if let Some(host_clause) = assumption.as_host_effect_clause() + && host_clause.def_id() == goal.predicate.def_id() + && host_clause.constness().satisfies(goal.predicate.constness) + && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.trait_ref.args, + host_clause.skip_binder().trait_ref.args, + ) + { + Ok(()) + } else { + Err(NoSolution) } - - Err(NoSolution) } fn match_assumption( diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index dd9ccadf6cf..b42587618b5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -429,22 +429,21 @@ where // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. - if let Some(stalled_on) = stalled_on { - if !stalled_on.stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) - && !self - .delegate - .opaque_types_storage_num_entries() - .needs_reevaluation(stalled_on.num_opaques) - { - return Ok(( - NestedNormalizationGoals::empty(), - GoalEvaluation { - certainty: Certainty::Maybe(stalled_on.stalled_cause), - has_changed: HasChanged::No, - stalled_on: Some(stalled_on), - }, - )); - } + if let Some(stalled_on) = stalled_on + && !stalled_on.stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) + && !self + .delegate + .opaque_types_storage_num_entries() + .needs_reevaluation(stalled_on.num_opaques) + { + return Ok(( + NestedNormalizationGoals::empty(), + GoalEvaluation { + certainty: Certainty::Maybe(stalled_on.stalled_cause), + has_changed: HasChanged::No, + stalled_on: Some(stalled_on), + }, + )); } let (orig_values, canonical_goal) = self.canonicalize_goal(goal); @@ -833,14 +832,11 @@ where match t.kind() { ty::Infer(ty::TyVar(vid)) => { - if let ty::TermKind::Ty(term) = self.term.kind() { - if let ty::Infer(ty::TyVar(term_vid)) = term.kind() { - if self.delegate.root_ty_var(vid) - == self.delegate.root_ty_var(term_vid) - { - return ControlFlow::Break(()); - } - } + if let ty::TermKind::Ty(term) = self.term.kind() + && let ty::Infer(ty::TyVar(term_vid)) = term.kind() + && self.delegate.root_ty_var(vid) == self.delegate.root_ty_var(term_vid) + { + return ControlFlow::Break(()); } self.check_nameable(self.delegate.universe_of_ty(vid).unwrap())?; @@ -860,15 +856,12 @@ where fn visit_const(&mut self, c: I::Const) -> Self::Result { match c.kind() { ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - if let ty::TermKind::Const(term) = self.term.kind() { - if let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() - { - if self.delegate.root_const_var(vid) - == self.delegate.root_const_var(term_vid) - { - return ControlFlow::Break(()); - } - } + if let ty::TermKind::Const(term) = self.term.kind() + && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() + && self.delegate.root_const_var(vid) + == self.delegate.root_const_var(term_vid) + { + return ControlFlow::Break(()); } self.check_nameable(self.delegate.universe_of_ct(vid).unwrap()) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index d51c87fe68e..edb354b4a84 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -112,18 +112,17 @@ where goal: Goal<I, Self>, assumption: I::Clause, ) -> Result<(), NoSolution> { - if let Some(projection_pred) = assumption.as_projection_clause() { - if projection_pred.item_def_id() == goal.predicate.def_id() { - if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( - goal.predicate.alias.args, - projection_pred.skip_binder().projection_term.args, - ) { - return Ok(()); - } - } + if let Some(projection_pred) = assumption.as_projection_clause() + && projection_pred.item_def_id() == goal.predicate.def_id() + && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.alias.args, + projection_pred.skip_binder().projection_term.args, + ) + { + Ok(()) + } else { + Err(NoSolution) } - - Err(NoSolution) } fn match_assumption( diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 8aaa8e9ca87..6c9fb63b579 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -127,33 +127,32 @@ where goal: Goal<I, Self>, assumption: I::Clause, ) -> Result<(), NoSolution> { - if let Some(trait_clause) = assumption.as_trait_clause() { - if trait_clause.polarity() != goal.predicate.polarity { - return Err(NoSolution); - } - - if trait_clause.def_id() == goal.predicate.def_id() { - if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( - goal.predicate.trait_ref.args, - trait_clause.skip_binder().trait_ref.args, - ) { - return Ok(()); - } - } - + fn trait_def_id_matches<I: Interner>( + cx: I, + clause_def_id: I::DefId, + goal_def_id: I::DefId, + ) -> bool { + clause_def_id == goal_def_id // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so - // check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds - // are syntactic sugar for a lack of bounds so don't need this. - if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized) - && ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized) - { - let meta_sized_clause = - trait_predicate_with_def_id(ecx.cx(), trait_clause, goal.predicate.def_id()); - return Self::fast_reject_assumption(ecx, goal, meta_sized_clause); - } + // check for a `MetaSized` supertrait being matched against a `Sized` assumption. + // + // `PointeeSized` bounds are syntactic sugar for a lack of bounds so don't need this. + || (cx.is_lang_item(clause_def_id, TraitSolverLangItem::Sized) + && cx.is_lang_item(goal_def_id, TraitSolverLangItem::MetaSized)) } - Err(NoSolution) + if let Some(trait_clause) = assumption.as_trait_clause() + && trait_clause.polarity() == goal.predicate.polarity + && trait_def_id_matches(ecx.cx(), trait_clause.def_id(), goal.predicate.def_id()) + && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.trait_ref.args, + trait_clause.skip_binder().trait_ref.args, + ) + { + return Ok(()); + } else { + Err(NoSolution) + } } fn match_assumption( diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 8b1d864c995..6c2c571d240 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -303,6 +303,11 @@ fn emit_malformed_attribute( | sym::must_use | sym::track_caller | sym::link_name + | sym::export_name + | sym::rustc_macro_transparency + | sym::link_section + | sym::rustc_layout_scalar_valid_range_start + | sym::rustc_layout_scalar_valid_range_end ) { return; } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index b1c7b0fcd6c..9a60c151034 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -618,9 +618,6 @@ passes_rustc_force_inline_coro = attribute cannot be applied to a `async`, `gen` or `async gen` function .label = `async`, `gen` or `async gen` function -passes_rustc_layout_scalar_valid_range_arg = - expected exactly one integer literal argument - passes_rustc_layout_scalar_valid_range_not_struct = attribute should be applied to a struct .label = not a struct diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ae486a58062..4bc48cb6aca 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -9,7 +9,7 @@ use std::cell::Cell; use std::collections::hash_map::Entry; use rustc_abi::{Align, ExternAbi, Size}; -use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast}; +use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast}; use rustc_attr_data_structures::{AttributeKind, InlineAttr, ReprAttr, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; @@ -177,6 +177,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => { self.check_align(span, target, *align, *repr_span) } + Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => { + self.check_link_section(hir_id, *attr_span, span, target) + } Attribute::Parsed(AttributeKind::Naked(attr_span)) => { self.check_naked(hir_id, *attr_span, span, target) } @@ -184,6 +187,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_track_caller(hir_id, *attr_span, attrs, span, target) } Attribute::Parsed( + AttributeKind::RustcLayoutScalarValidRangeStart(_num, attr_span) + | AttributeKind::RustcLayoutScalarValidRangeEnd(_num, attr_span), + ) => self.check_rustc_layout_scalar_valid_range(*attr_span, span, target), + Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect | AttributeKind::MacroTransparency(_), @@ -234,10 +241,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &mut doc_aliases, ), [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), - [sym::rustc_layout_scalar_valid_range_start, ..] - | [sym::rustc_layout_scalar_valid_range_end, ..] => { - self.check_rustc_layout_scalar_valid_range(attr, span, target) - } [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), [sym::rustc_std_internal_symbol, ..] => { self.check_rustc_std_internal_symbol(attr, span, target) @@ -286,7 +289,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), [sym::link, ..] => self.check_link(hir_id, attr, span, target), - [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target), [sym::macro_use, ..] | [sym::macro_escape, ..] => { self.check_macro_use(hir_id, attr, target) } @@ -1676,23 +1678,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_rustc_layout_scalar_valid_range(&self, attr: &Attribute, span: Span, target: Target) { + fn check_rustc_layout_scalar_valid_range(&self, attr_span: Span, span: Span, target: Target) { if target != Target::Struct { - self.dcx().emit_err(errors::RustcLayoutScalarValidRangeNotStruct { - attr_span: attr.span(), - span, - }); - return; - } - - let Some(list) = attr.meta_item_list() else { + self.dcx().emit_err(errors::RustcLayoutScalarValidRangeNotStruct { attr_span, span }); return; - }; - - if !matches!(&list[..], &[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) { - self.tcx - .dcx() - .emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span() }); } } @@ -1831,7 +1820,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[link_section]` is applied to a function or static. - fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_link_section(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { match target { Target::Static | Target::Fn | Target::Method(..) => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an @@ -1839,7 +1828,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "link_section"); + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "link_section"); } _ => { // FIXME: #[link_section] was previously allowed on non-functions/statics and some @@ -1847,7 +1836,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::LinkSection { span }, ); } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index eaff1cc65de..d4988277073 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -536,13 +536,6 @@ pub(crate) struct RustcLayoutScalarValidRangeNotStruct { } #[derive(Diagnostic)] -#[diag(passes_rustc_layout_scalar_valid_range_arg)] -pub(crate) struct RustcLayoutScalarValidRangeArg { - #[primary_span] - pub attr_span: Span, -} - -#[derive(Diagnostic)] #[diag(passes_rustc_legacy_const_generics_only)] pub(crate) struct RustcLegacyConstGenericsOnly { #[primary_span] diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 8940d10696d..846710c3398 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -21,6 +21,11 @@ use thin_vec::ThinVec; /// [utf8]: https://en.wikipedia.org/w/index.php?title=UTF-8&oldid=1058865525#Codepage_layout const STR_SENTINEL: u8 = 0xC1; +/// For byte strings there are no bytes that canot occur. Just use this value +/// as a best-effort sentinel. There is no validation skipped so the potential +/// for badness is lower than in the `STR_SENTINEL` case. +const BYTE_STR_SENTINEL: u8 = 0xC2; + /// A note about error handling. /// /// Encoders may be fallible, but in practice failure is rare and there are so @@ -72,6 +77,13 @@ pub trait Encoder { self.emit_u8(STR_SENTINEL); } + #[inline] + fn emit_byte_str(&mut self, v: &[u8]) { + self.emit_usize(v.len()); + self.emit_raw_bytes(v); + self.emit_u8(BYTE_STR_SENTINEL); + } + fn emit_raw_bytes(&mut self, s: &[u8]); } @@ -122,9 +134,19 @@ pub trait Decoder { let len = self.read_usize(); let bytes = self.read_raw_bytes(len + 1); assert!(bytes[len] == STR_SENTINEL); + // SAFETY: the presence of `STR_SENTINEL` gives us high (but not + // perfect) confidence that the bytes we just read truly are UTF-8. unsafe { std::str::from_utf8_unchecked(&bytes[..len]) } } + #[inline] + fn read_byte_str(&mut self) -> &[u8] { + let len = self.read_usize(); + let bytes = self.read_raw_bytes(len + 1); + assert!(bytes[len] == BYTE_STR_SENTINEL); + &bytes[..len] + } + fn read_raw_bytes(&mut self, len: usize) -> &[u8]; fn peek_byte(&self) -> u8; @@ -239,7 +261,7 @@ impl<S: Encoder> Encodable<S> for str { impl<S: Encoder> Encodable<S> for String { fn encode(&self, s: &mut S) { - s.emit_str(&self[..]); + s.emit_str(&self); } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index c8a29a2f68f..3d3a681c798 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -66,7 +66,9 @@ mod span_encoding; pub use span_encoding::{DUMMY_SP, Span}; pub mod symbol; -pub use symbol::{Ident, MacroRulesNormalizedIdent, STDLIB_STABLE_CRATES, Symbol, kw, sym}; +pub use symbol::{ + ByteSymbol, Ident, MacroRulesNormalizedIdent, STDLIB_STABLE_CRATES, Symbol, kw, sym, +}; mod analyze_source_file; pub mod fatal_error; @@ -1184,11 +1186,12 @@ rustc_index::newtype_index! { /// It is similar to rustc_type_ir's TyEncoder. pub trait SpanEncoder: Encoder { fn encode_span(&mut self, span: Span); - fn encode_symbol(&mut self, symbol: Symbol); + fn encode_symbol(&mut self, sym: Symbol); + fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol); fn encode_expn_id(&mut self, expn_id: ExpnId); fn encode_syntax_context(&mut self, syntax_context: SyntaxContext); - /// As a local identifier, a `CrateNum` is only meaningful within its context, e.g. within a tcx. - /// Therefore, make sure to include the context when encode a `CrateNum`. + /// As a local identifier, a `CrateNum` is only meaningful within its context, e.g. within a + /// tcx. Therefore, make sure to include the context when encode a `CrateNum`. fn encode_crate_num(&mut self, crate_num: CrateNum); fn encode_def_index(&mut self, def_index: DefIndex); fn encode_def_id(&mut self, def_id: DefId); @@ -1201,8 +1204,12 @@ impl SpanEncoder for FileEncoder { span.hi.encode(self); } - fn encode_symbol(&mut self, symbol: Symbol) { - self.emit_str(symbol.as_str()); + fn encode_symbol(&mut self, sym: Symbol) { + self.emit_str(sym.as_str()); + } + + fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol) { + self.emit_byte_str(byte_sym.as_byte_str()); } fn encode_expn_id(&mut self, _expn_id: ExpnId) { @@ -1239,6 +1246,12 @@ impl<E: SpanEncoder> Encodable<E> for Symbol { } } +impl<E: SpanEncoder> Encodable<E> for ByteSymbol { + fn encode(&self, s: &mut E) { + s.encode_byte_symbol(*self); + } +} + impl<E: SpanEncoder> Encodable<E> for ExpnId { fn encode(&self, s: &mut E) { s.encode_expn_id(*self) @@ -1280,6 +1293,7 @@ impl<E: SpanEncoder> Encodable<E> for AttrId { pub trait SpanDecoder: Decoder { fn decode_span(&mut self) -> Span; fn decode_symbol(&mut self) -> Symbol; + fn decode_byte_symbol(&mut self) -> ByteSymbol; fn decode_expn_id(&mut self) -> ExpnId; fn decode_syntax_context(&mut self) -> SyntaxContext; fn decode_crate_num(&mut self) -> CrateNum; @@ -1300,6 +1314,10 @@ impl SpanDecoder for MemDecoder<'_> { Symbol::intern(self.read_str()) } + fn decode_byte_symbol(&mut self) -> ByteSymbol { + ByteSymbol::intern(self.read_byte_str()) + } + fn decode_expn_id(&mut self) -> ExpnId { panic!("cannot decode `ExpnId` with `MemDecoder`"); } @@ -1337,6 +1355,12 @@ impl<D: SpanDecoder> Decodable<D> for Symbol { } } +impl<D: SpanDecoder> Decodable<D> for ByteSymbol { + fn decode(s: &mut D) -> ByteSymbol { + s.decode_byte_symbol() + } +} + impl<D: SpanDecoder> Decodable<D> for ExpnId { fn decode(s: &mut D) -> ExpnId { s.decode_expn_id() diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a51f4e3dc07..34869a38bb4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1977,6 +1977,8 @@ symbols! { simd_fmin, simd_fsin, simd_fsqrt, + simd_funnel_shl, + simd_funnel_shr, simd_gather, simd_ge, simd_gt, @@ -2004,6 +2006,7 @@ symbols! { simd_relaxed_fma, simd_rem, simd_round, + simd_round_ties_even, simd_saturating_add, simd_saturating_sub, simd_scatter, @@ -2580,7 +2583,7 @@ impl fmt::Display for MacroRulesNormalizedIdent { } } -/// An interned string. +/// An interned UTF-8 string. /// /// Internally, a `Symbol` is implemented as an index, and all operations /// (including hashing, equality, and ordering) operate on that index. The use @@ -2592,20 +2595,23 @@ impl fmt::Display for MacroRulesNormalizedIdent { #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Symbol(SymbolIndex); +// Used within both `Symbol` and `ByteSymbol`. rustc_index::newtype_index! { #[orderable] struct SymbolIndex {} } impl Symbol { + /// Avoid this except for things like deserialization of previously + /// serialized symbols, and testing. Use `intern` instead. pub const fn new(n: u32) -> Self { Symbol(SymbolIndex::from_u32(n)) } /// Maps a string to its interned representation. #[rustc_diagnostic_item = "SymbolIntern"] - pub fn intern(string: &str) -> Self { - with_session_globals(|session_globals| session_globals.symbol_interner.intern(string)) + pub fn intern(str: &str) -> Self { + with_session_globals(|session_globals| session_globals.symbol_interner.intern_str(str)) } /// Access the underlying string. This is a slowish operation because it @@ -2618,7 +2624,7 @@ impl Symbol { /// it works out ok. pub fn as_str(&self) -> &str { with_session_globals(|session_globals| unsafe { - std::mem::transmute::<&str, &str>(session_globals.symbol_interner.get(*self)) + std::mem::transmute::<&str, &str>(session_globals.symbol_interner.get_str(*self)) }) } @@ -2675,56 +2681,130 @@ impl StableCompare for Symbol { } } +/// Like `Symbol`, but for byte strings. `ByteSymbol` is used less widely, so +/// it has fewer operations defined than `Symbol`. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ByteSymbol(SymbolIndex); + +impl ByteSymbol { + /// Avoid this except for things like deserialization of previously + /// serialized symbols, and testing. Use `intern` instead. + pub const fn new(n: u32) -> Self { + ByteSymbol(SymbolIndex::from_u32(n)) + } + + /// Maps a string to its interned representation. + pub fn intern(byte_str: &[u8]) -> Self { + with_session_globals(|session_globals| { + session_globals.symbol_interner.intern_byte_str(byte_str) + }) + } + + /// Like `Symbol::as_str`. + pub fn as_byte_str(&self) -> &[u8] { + with_session_globals(|session_globals| unsafe { + std::mem::transmute::<&[u8], &[u8]>(session_globals.symbol_interner.get_byte_str(*self)) + }) + } + + pub fn as_u32(self) -> u32 { + self.0.as_u32() + } +} + +impl fmt::Debug for ByteSymbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_byte_str(), f) + } +} + +impl<CTX> HashStable<CTX> for ByteSymbol { + #[inline] + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.as_byte_str().hash_stable(hcx, hasher); + } +} + +// Interner used for both `Symbol`s and `ByteSymbol`s. If a string and a byte +// string with identical contents (e.g. "foo" and b"foo") are both interned, +// only one copy will be stored and the resulting `Symbol` and `ByteSymbol` +// will have the same index. pub(crate) struct Interner(Lock<InternerInner>); -// The `&'static str`s in this type actually point into the arena. +// The `&'static [u8]`s in this type actually point into the arena. // // This type is private to prevent accidentally constructing more than one // `Interner` on the same thread, which makes it easy to mix up `Symbol`s // between `Interner`s. struct InternerInner { arena: DroplessArena, - strings: FxIndexSet<&'static str>, + byte_strs: FxIndexSet<&'static [u8]>, } impl Interner { + // These arguments are `&str`, but because of the sharing, we are + // effectively pre-interning all these strings for both `Symbol` and + // `ByteSymbol`. fn prefill(init: &[&'static str], extra: &[&'static str]) -> Self { - let strings = FxIndexSet::from_iter(init.iter().copied().chain(extra.iter().copied())); + let byte_strs = FxIndexSet::from_iter( + init.iter().copied().chain(extra.iter().copied()).map(|str| str.as_bytes()), + ); assert_eq!( - strings.len(), + byte_strs.len(), init.len() + extra.len(), - "there are duplicate symbols in the rustc symbol list and the extra symbols added by the driver", + "duplicate symbols in the rustc symbol list and the extra symbols added by the driver", ); - Interner(Lock::new(InternerInner { arena: Default::default(), strings })) + Interner(Lock::new(InternerInner { arena: Default::default(), byte_strs })) + } + + fn intern_str(&self, str: &str) -> Symbol { + Symbol::new(self.intern_inner(str.as_bytes())) + } + + fn intern_byte_str(&self, byte_str: &[u8]) -> ByteSymbol { + ByteSymbol::new(self.intern_inner(byte_str)) } #[inline] - fn intern(&self, string: &str) -> Symbol { + fn intern_inner(&self, byte_str: &[u8]) -> u32 { let mut inner = self.0.lock(); - if let Some(idx) = inner.strings.get_index_of(string) { - return Symbol::new(idx as u32); + if let Some(idx) = inner.byte_strs.get_index_of(byte_str) { + return idx as u32; } - let string: &str = inner.arena.alloc_str(string); + let byte_str: &[u8] = inner.arena.alloc_slice(byte_str); // SAFETY: we can extend the arena allocation to `'static` because we // only access these while the arena is still alive. - let string: &'static str = unsafe { &*(string as *const str) }; + let byte_str: &'static [u8] = unsafe { &*(byte_str as *const [u8]) }; // This second hash table lookup can be avoided by using `RawEntryMut`, // but this code path isn't hot enough for it to be worth it. See // #91445 for details. - let (idx, is_new) = inner.strings.insert_full(string); + let (idx, is_new) = inner.byte_strs.insert_full(byte_str); debug_assert!(is_new); // due to the get_index_of check above - Symbol::new(idx as u32) + idx as u32 } /// Get the symbol as a string. /// /// [`Symbol::as_str()`] should be used in preference to this function. - fn get(&self, symbol: Symbol) -> &str { - self.0.lock().strings.get_index(symbol.0.as_usize()).unwrap() + fn get_str(&self, symbol: Symbol) -> &str { + let byte_str = self.get_inner(symbol.0.as_usize()); + // SAFETY: known to be a UTF8 string because it's a `Symbol`. + unsafe { str::from_utf8_unchecked(byte_str) } + } + + /// Get the symbol as a string. + /// + /// [`ByteSymbol::as_byte_str()`] should be used in preference to this function. + fn get_byte_str(&self, symbol: ByteSymbol) -> &[u8] { + self.get_inner(symbol.0.as_usize()) + } + + fn get_inner(&self, index: usize) -> &[u8] { + self.0.lock().byte_strs.get_index(index).unwrap() } } @@ -2819,9 +2899,11 @@ impl Symbol { self != sym::empty && self != kw::Underscore && !self.is_path_segment_keyword() } - /// Was this symbol predefined in the compiler's `symbols!` macro - pub fn is_predefined(self) -> bool { - self.as_u32() < PREDEFINED_SYMBOLS_COUNT + /// Was this symbol index predefined in the compiler's `symbols!` macro? + /// Note: this applies to both `Symbol`s and `ByteSymbol`s, which is why it + /// takes a `u32` argument instead of a `&self` argument. Use with care. + pub fn is_predefined(index: u32) -> bool { + index < PREDEFINED_SYMBOLS_COUNT } } diff --git a/compiler/rustc_span/src/symbol/tests.rs b/compiler/rustc_span/src/symbol/tests.rs index 660d0d7179a..bf0660aa510 100644 --- a/compiler/rustc_span/src/symbol/tests.rs +++ b/compiler/rustc_span/src/symbol/tests.rs @@ -5,14 +5,14 @@ use crate::create_default_session_globals_then; fn interner_tests() { let i = Interner::prefill(&[], &[]); // first one is zero: - assert_eq!(i.intern("dog"), Symbol::new(0)); - // re-use gets the same entry: - assert_eq!(i.intern("dog"), Symbol::new(0)); + assert_eq!(i.intern_str("dog"), Symbol::new(0)); + // re-use gets the same entry, even with a `ByteSymbol` + assert_eq!(i.intern_byte_str(b"dog"), ByteSymbol::new(0)); // different string gets a different #: - assert_eq!(i.intern("cat"), Symbol::new(1)); - assert_eq!(i.intern("cat"), Symbol::new(1)); + assert_eq!(i.intern_byte_str(b"cat"), ByteSymbol::new(1)); + assert_eq!(i.intern_str("cat"), Symbol::new(1)); // dog is still at zero - assert_eq!(i.intern("dog"), Symbol::new(0)); + assert_eq!(i.intern_str("dog"), Symbol::new(0)); } #[test] diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 60f8bd9d83a..eb751da7c73 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -120,7 +120,7 @@ fn recurse_build<'tcx>( } &ExprKind::Literal { lit, neg } => { let sp = node.span; - tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) + tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: node.ty, neg }) } &ExprKind::NonHirLiteral { lit, user_ty: _ } => { let val = ty::ValTree::from_scalar_int(tcx, lit); diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 177bad96595..7ffcf7b5d96 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -320,10 +320,10 @@ pub fn supertrait_def_ids<I: Interner>( let trait_def_id = stack.pop()?; for (predicate, _) in cx.explicit_super_predicates_of(trait_def_id).iter_identity() { - if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() { - if set.insert(data.def_id()) { - stack.push(data.def_id()); - } + if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() + && set.insert(data.def_id()) + { + stack.push(data.def_id()); } } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 2bc12d0a23b..8ba9e7105d6 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -117,12 +117,20 @@ impl<I: Interner> TypingMode<I> { } pub fn borrowck(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> { - TypingMode::Borrowck { defining_opaque_types: cx.opaque_types_defined_by(body_def_id) } + let defining_opaque_types = cx.opaque_types_defined_by(body_def_id); + if defining_opaque_types.is_empty() { + TypingMode::non_body_analysis() + } else { + TypingMode::Borrowck { defining_opaque_types } + } } pub fn post_borrowck_analysis(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> { - TypingMode::PostBorrowckAnalysis { - defined_opaque_types: cx.opaque_types_defined_by(body_def_id), + let defined_opaque_types = cx.opaque_types_defined_by(body_def_id); + if defined_opaque_types.is_empty() { + TypingMode::non_body_analysis() + } else { + TypingMode::PostBorrowckAnalysis { defined_opaque_types } } } } diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index e42639c6807..79f6bc5dabb 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -284,12 +284,12 @@ where } // If they have no bound vars, relate normally. - if let Some(a_inner) = a.no_bound_vars() { - if let Some(b_inner) = b.no_bound_vars() { - self.relate(a_inner, b_inner)?; - return Ok(a); - } - }; + if let Some(a_inner) = a.no_bound_vars() + && let Some(b_inner) = b.no_bound_vars() + { + self.relate(a_inner, b_inner)?; + return Ok(a); + } match self.ambient_variance { // Checks whether `for<..> sub <: for<..> sup` holds. diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index 1b99cc820f1..eb56c1af408 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -80,31 +80,29 @@ impl<X: Cx> GlobalCache<X> { mut candidate_is_applicable: impl FnMut(&NestedGoals<X>) -> bool, ) -> Option<CacheData<'a, X>> { let entry = self.map.get(&input)?; - if let Some(Success { required_depth, ref nested_goals, ref result }) = entry.success { - if available_depth.cache_entry_is_applicable(required_depth) - && candidate_is_applicable(nested_goals) - { - return Some(CacheData { - result: cx.get_tracked(&result), - required_depth, - encountered_overflow: false, - nested_goals, - }); - } + if let Some(Success { required_depth, ref nested_goals, ref result }) = entry.success + && available_depth.cache_entry_is_applicable(required_depth) + && candidate_is_applicable(nested_goals) + { + return Some(CacheData { + result: cx.get_tracked(&result), + required_depth, + encountered_overflow: false, + nested_goals, + }); } let additional_depth = available_depth.0; if let Some(WithOverflow { nested_goals, result }) = entry.with_overflow.get(&additional_depth) + && candidate_is_applicable(nested_goals) { - if candidate_is_applicable(nested_goals) { - return Some(CacheData { - result: cx.get_tracked(result), - required_depth: additional_depth, - encountered_overflow: true, - nested_goals, - }); - } + return Some(CacheData { + result: cx.get_tracked(result), + required_depth: additional_depth, + encountered_overflow: true, + nested_goals, + }); } None diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7aa3f3c6d74..01dce114592 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -742,7 +742,7 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of<T: ?Sized + 'static>() -> TypeId { - let t: u128 = intrinsics::type_id::<T>(); + let t: u128 = const { intrinsics::type_id::<T>() }; let t1 = (t >> 64) as u64; let t2 = t as u64; @@ -824,7 +824,7 @@ impl fmt::Debug for TypeId { #[stable(feature = "type_name", since = "1.38.0")] #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub const fn type_name<T: ?Sized>() -> &'static str { - intrinsics::type_name::<T>() + const { intrinsics::type_name::<T>() } } /// Returns the type name of the pointed-to value as a string slice. diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index cfbd88bbe79..fab13bb7c85 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -4,7 +4,8 @@ use crate::ops; use crate::pin::Pin; use crate::task::{Context, Poll}; -/// A future represents an asynchronous computation obtained by use of [`async`]. +/// A future represents an asynchronous computation, commonly obtained by use of +/// [`async`]. /// /// A future is a value that might not have finished computing yet. This kind of /// "asynchronous value" makes it possible for a thread to continue doing useful @@ -68,13 +69,21 @@ pub trait Future { /// /// # Runtime characteristics /// - /// Futures alone are *inert*; they must be *actively* `poll`ed to make - /// progress, meaning that each time the current task is woken up, it should - /// actively re-`poll` pending futures that it still has an interest in. + /// Futures alone are *inert*; they must be *actively* `poll`ed for the + /// underlying computation to make progress, meaning that each time the + /// current task is woken up, it should actively re-`poll` pending futures + /// that it still has an interest in. /// - /// The `poll` function is not called repeatedly in a tight loop -- instead, - /// it should only be called when the future indicates that it is ready to - /// make progress (by calling `wake()`). If you're familiar with the + /// Having said that, some Futures may represent a value that is being + /// computed in a different task. In this case, the future's underlying + /// computation is simply acting as a conduit for a value being computed + /// by that other task, which will proceed independently of the Future. + /// Futures of this kind are typically obtained when spawning a new task into an + /// async runtime. + /// + /// The `poll` function should not be called repeatedly in a tight loop -- + /// instead, it should only be called when the future indicates that it is + /// ready to make progress (by calling `wake()`). If you're familiar with the /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures /// typically do *not* suffer the same problems of "all wakeups must poll /// all events"; they are more like `epoll(4)`. diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index b5c3e91d046..4250de9fb2b 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -839,10 +839,10 @@ pub const unsafe fn transmute_unchecked<Src, Dst>(src: Src) -> Dst; /// If the actual type neither requires drop glue nor implements /// `Copy`, then the return value of this function is unspecified. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`mem::needs_drop`](crate::mem::needs_drop). #[rustc_intrinsic_const_stable_indirect] @@ -2655,10 +2655,10 @@ pub const fn align_of<T>() -> usize; /// Returns the number of variants of the type `T` cast to a `usize`; /// if `T` has no variants, returns `0`. Uninhabited variants will be counted. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. /// /// The to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`]. #[rustc_nounwind] @@ -2694,10 +2694,10 @@ pub const unsafe fn align_of_val<T: ?Sized>(ptr: *const T) -> usize; /// Gets a static string slice containing the name of a type. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`core::any::type_name`]. #[rustc_nounwind] @@ -2709,10 +2709,10 @@ pub const fn type_name<T: ?Sized>() -> &'static str; /// function will return the same value for a type regardless of whichever /// crate it is invoked in. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. #[rustc_nounwind] diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 40efc263068..11533ab6aa4 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -126,6 +126,40 @@ pub unsafe fn simd_shl<T>(lhs: T, rhs: T) -> T; #[rustc_nounwind] pub unsafe fn simd_shr<T>(lhs: T, rhs: T) -> T; +/// Funnel Shifts vector left elementwise, with UB on overflow. +/// +/// Concatenates `a` and `b` elementwise (with `a` in the most significant half), +/// creating a vector of the same length, but with each element being twice as +/// wide. Then shift this vector left elementwise by `shift`, shifting in zeros, +/// and extract the most significant half of each of the elements. If `a` and `b` +/// are the same, this is equivalent to an elementwise rotate left operation. +/// +/// `T` must be a vector of integers. +/// +/// # Safety +/// +/// Each element of `shift` must be less than `<int>::BITS`. +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn simd_funnel_shl<T>(a: T, b: T, shift: T) -> T; + +/// Funnel Shifts vector right elementwise, with UB on overflow. +/// +/// Concatenates `a` and `b` elementwise (with `a` in the most significant half), +/// creating a vector of the same length, but with each element being twice as +/// wide. Then shift this vector right elementwise by `shift`, shifting in zeros, +/// and extract the least significant half of each of the elements. If `a` and `b` +/// are the same, this is equivalent to an elementwise rotate right operation. +/// +/// `T` must be a vector of integers. +/// +/// # Safety +/// +/// Each element of `shift` must be less than `<int>::BITS`. +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn simd_funnel_shr<T>(a: T, b: T, shift: T) -> T; + /// "Ands" vectors elementwise. /// /// `T` must be a vector of integers. @@ -678,6 +712,14 @@ pub unsafe fn simd_floor<T>(x: T) -> T; #[rustc_nounwind] pub unsafe fn simd_round<T>(x: T) -> T; +/// Rounds each element to the closest integer-valued float. +/// Ties are resolved by rounding to the number with an even least significant digit +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn simd_round_ties_even<T>(x: T) -> T; + /// Returns the integer part of each element as an integer-valued float. /// In other words, non-integer values are truncated towards zero. /// diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index b93f854b9dc..c00585de064 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -616,7 +616,7 @@ pub const unsafe fn align_of_val_raw<T: ?Sized>(val: *const T) -> usize { #[rustc_const_stable(feature = "const_mem_needs_drop", since = "1.36.0")] #[rustc_diagnostic_item = "needs_drop"] pub const fn needs_drop<T: ?Sized>() -> bool { - intrinsics::needs_drop::<T>() + const { intrinsics::needs_drop::<T>() } } /// Returns the value of type `T` represented by the all-zero byte-pattern. @@ -1215,7 +1215,7 @@ pub const fn discriminant<T>(v: &T) -> Discriminant<T> { #[rustc_const_unstable(feature = "variant_count", issue = "73662")] #[rustc_diagnostic_item = "mem_variant_count"] pub const fn variant_count<T>() -> usize { - intrinsics::variant_count::<T>() + const { intrinsics::variant_count::<T>() } } /// Provides associated constants for various useful properties of types, diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 0ac887f99dc..f55bdfeb354 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -316,6 +316,11 @@ mod prim_bool {} #[unstable(feature = "never_type", issue = "35121")] mod prim_never {} +// Required to make auto trait impls render. +// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls +#[doc(hidden)] +impl ! {} + #[rustc_doc_primitive = "char"] #[allow(rustdoc::invalid_rust_codeblocks)] /// A character type. @@ -1078,11 +1083,13 @@ mod prim_str {} /// * [`Debug`] /// * [`Default`] /// * [`Hash`] +/// * [`Random`] /// * [`From<[T; N]>`][from] /// /// [from]: convert::From /// [`Debug`]: fmt::Debug /// [`Hash`]: hash::Hash +/// [`Random`]: random::Random /// /// The following traits are implemented for tuples of any length. These traits have /// implementations that are automatically generated by the compiler, so are not limited by diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 3ff55792431..6327c41f052 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -3,6 +3,7 @@ use crate::cmp::Ordering::{self, *}; use crate::marker::{ConstParamTy_, PointeeSized, StructuralPartialEq, UnsizedConstParamTy}; use crate::ops::ControlFlow::{self, Break, Continue}; +use crate::random::{Random, RandomSource}; // Recursive macro for implementing n-ary tuple functions and operations // @@ -141,6 +142,16 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ + #[unstable(feature = "random", issue = "130703")] + impl<$($T: Random),+> Random for ($($T,)+) { + fn random(source: &mut (impl RandomSource + ?Sized)) -> Self { + ($({ let x: $T = Random::random(source); x},)+) + } + } + } + + maybe_tuple_doc! { + $($T)+ @ #[stable(feature = "array_tuple_conv", since = "1.71.0")] impl<T> From<[T; ${count($T)}]> for ($(${ignore($T)} T,)+) { #[inline] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 865ea620a28..ea342ec9c12 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -682,11 +682,11 @@ impl File { /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not /// cause non-lockholders to block. /// - /// If this file handle/descriptor, or a clone of it, already holds an lock the exact behavior + /// If this file handle/descriptor, or a clone of it, already holds a lock the exact behavior /// is unspecified and platform dependent, including the possibility that it will deadlock. /// However, if this method returns, then an exclusive lock is held. /// - /// If the file not open for writing, it is unspecified whether this function returns an error. + /// If the file is not open for writing, it is unspecified whether this function returns an error. /// /// The lock will be released when this file (along with any other file descriptors/handles /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. @@ -736,7 +736,7 @@ impl File { /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not /// cause non-lockholders to block. /// - /// If this file handle/descriptor, or a clone of it, already holds an lock, the exact behavior + /// If this file handle/descriptor, or a clone of it, already holds a lock, the exact behavior /// is unspecified and platform dependent, including the possibility that it will deadlock. /// However, if this method returns, then a shared lock is held. /// @@ -790,11 +790,11 @@ impl File { /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not /// cause non-lockholders to block. /// - /// If this file handle/descriptor, or a clone of it, already holds an lock, the exact behavior + /// If this file handle/descriptor, or a clone of it, already holds a lock, the exact behavior /// is unspecified and platform dependent, including the possibility that it will deadlock. /// However, if this method returns `Ok(true)`, then it has acquired an exclusive lock. /// - /// If the file not open for writing, it is unspecified whether this function returns an error. + /// If the file is not open for writing, it is unspecified whether this function returns an error. /// /// The lock will be released when this file (along with any other file descriptors/handles /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. @@ -855,7 +855,7 @@ impl File { /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not /// cause non-lockholders to block. /// - /// If this file handle, or a clone of it, already holds an lock, the exact behavior is + /// If this file handle, or a clone of it, already holds a lock, the exact behavior is /// unspecified and platform dependent, including the possibility that it will deadlock. /// However, if this method returns `Ok(true)`, then it has acquired a shared lock. /// diff --git a/library/std/src/sys/fs/windows/remove_dir_all.rs b/library/std/src/sys/fs/windows/remove_dir_all.rs index 06734f9e309..c8b1a076768 100644 --- a/library/std/src/sys/fs/windows/remove_dir_all.rs +++ b/library/std/src/sys/fs/windows/remove_dir_all.rs @@ -33,7 +33,7 @@ use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use super::{AsRawHandle, DirBuff, File, FromRawHandle}; use crate::sys::c; -use crate::sys::pal::api::WinError; +use crate::sys::pal::api::{UnicodeStrRef, WinError, unicode_str}; use crate::thread; // The maximum number of times to spin when waiting for deletes to complete. @@ -74,7 +74,7 @@ unsafe fn nt_open_file( /// `options` will be OR'd with `FILE_OPEN_REPARSE_POINT`. fn open_link_no_reparse( parent: &File, - path: &[u16], + path: UnicodeStrRef<'_>, access: u32, options: u32, ) -> Result<Option<File>, WinError> { @@ -90,9 +90,8 @@ fn open_link_no_reparse( static ATTRIBUTES: Atomic<u32> = AtomicU32::new(c::OBJ_DONT_REPARSE); let result = unsafe { - let mut path_str = c::UNICODE_STRING::from_ref(path); let mut object = c::OBJECT_ATTRIBUTES { - ObjectName: &mut path_str, + ObjectName: path.as_ptr(), RootDirectory: parent.as_raw_handle(), Attributes: ATTRIBUTES.load(Ordering::Relaxed), ..c::OBJECT_ATTRIBUTES::with_length() @@ -129,7 +128,7 @@ fn open_link_no_reparse( } } -fn open_dir(parent: &File, name: &[u16]) -> Result<Option<File>, WinError> { +fn open_dir(parent: &File, name: UnicodeStrRef<'_>) -> Result<Option<File>, WinError> { // Open the directory for synchronous directory listing. open_link_no_reparse( parent, @@ -140,7 +139,7 @@ fn open_dir(parent: &File, name: &[u16]) -> Result<Option<File>, WinError> { ) } -fn delete(parent: &File, name: &[u16]) -> Result<(), WinError> { +fn delete(parent: &File, name: UnicodeStrRef<'_>) -> Result<(), WinError> { // Note that the `delete` function consumes the opened file to ensure it's // dropped immediately. See module comments for why this is important. match open_link_no_reparse(parent, name, c::DELETE, 0) { @@ -179,8 +178,9 @@ pub fn remove_dir_all_iterative(dir: File) -> Result<(), WinError> { 'outer: while let Some(dir) = dirlist.pop() { let more_data = dir.fill_dir_buff(&mut buffer, restart)?; for (name, is_directory) in buffer.iter() { + let name = unicode_str!(&name); if is_directory { - let Some(subdir) = open_dir(&dir, &name)? else { continue }; + let Some(subdir) = open_dir(&dir, name)? else { continue }; dirlist.push(dir); dirlist.push(subdir); continue 'outer; @@ -188,7 +188,7 @@ pub fn remove_dir_all_iterative(dir: File) -> Result<(), WinError> { // Attempt to delete, retrying on sharing violation errors as these // can often be very temporary. E.g. if something takes just a // bit longer than expected to release a file handle. - retry(|| delete(&dir, &name), WinError::SHARING_VIOLATION)?; + retry(|| delete(&dir, name), WinError::SHARING_VIOLATION)?; } } if more_data { @@ -197,7 +197,8 @@ pub fn remove_dir_all_iterative(dir: File) -> Result<(), WinError> { } else { // Attempt to delete, retrying on not empty errors because we may // need to wait some time for files to be removed from the filesystem. - retry(|| delete(&dir, &[]), WinError::DIR_NOT_EMPTY)?; + let name = unicode_str!(""); + retry(|| delete(&dir, name), WinError::DIR_NOT_EMPTY)?; restart = true; } } diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 773455c572f..25a6c2d7d8e 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -30,6 +30,7 @@ //! should go in sys/pal/windows/mod.rs rather than here. See `IoResult` as an example. use core::ffi::c_void; +use core::marker::PhantomData; use super::c; @@ -291,3 +292,75 @@ impl WinError { pub const TIMEOUT: Self = Self::new(c::ERROR_TIMEOUT); // tidy-alphabetical-end } + +/// A wrapper around a UNICODE_STRING that is equivalent to `&[u16]`. +/// +/// It is preferable to use the `unicode_str!` macro as that contains mitigations for #143078. +/// +/// If the MaximumLength field of the underlying UNICODE_STRING is greater than +/// the Length field then you can test if the string is null terminated by inspecting +/// the u16 directly after the string. You cannot otherwise depend on nul termination. +#[derive(Copy, Clone)] +pub struct UnicodeStrRef<'a> { + s: c::UNICODE_STRING, + lifetime: PhantomData<&'a [u16]>, +} + +static EMPTY_STRING_NULL_TERMINATED: &[u16] = &[0]; + +impl UnicodeStrRef<'_> { + const fn new(slice: &[u16], is_null_terminated: bool) -> Self { + let (len, max_len, ptr) = if slice.is_empty() { + (0, 2, EMPTY_STRING_NULL_TERMINATED.as_ptr().cast_mut()) + } else { + let len = slice.len() - (is_null_terminated as usize); + (len * 2, size_of_val(slice), slice.as_ptr().cast_mut()) + }; + Self { + s: c::UNICODE_STRING { Length: len as _, MaximumLength: max_len as _, Buffer: ptr }, + lifetime: PhantomData, + } + } + + pub const fn from_slice_with_nul(slice: &[u16]) -> Self { + if !slice.is_empty() { + debug_assert!(slice[slice.len() - 1] == 0); + } + Self::new(slice, true) + } + + pub const fn from_slice(slice: &[u16]) -> Self { + Self::new(slice, false) + } + + /// Returns a pointer to the underlying UNICODE_STRING + pub const fn as_ptr(&self) -> *const c::UNICODE_STRING { + &self.s + } +} + +/// Create a UnicodeStringRef from a literal str or a u16 array. +/// +/// To mitigate #143078, when using a literal str the created UNICODE_STRING +/// will be nul terminated. The MaximumLength field of the UNICODE_STRING will +/// be set greater than the Length field to indicate that a nul may be present. +/// +/// If using a u16 array, the array is used exactly as provided and you cannot +/// count on the string being nul terminated. +/// This should generally be used for strings that come from the OS. +/// +/// **NOTE:** we lack a UNICODE_STRING builder type as we don't currently have +/// a use for it. If needing to dynamically build a UNICODE_STRING, the builder +/// should try to ensure there's a nul one past the end of the string. +pub macro unicode_str { + ($str:literal) => {const { + crate::sys::pal::windows::api::UnicodeStrRef::from_slice_with_nul( + crate::sys::pal::windows::api::wide_str!($str), + ) + }}, + ($array:expr) => { + crate::sys::pal::windows::api::UnicodeStrRef::from_slice( + $array, + ) + } +} diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 53dec105d0c..eee169d410a 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -37,13 +37,6 @@ pub fn nt_success(status: NTSTATUS) -> bool { status >= 0 } -impl UNICODE_STRING { - pub fn from_ref(slice: &[u16]) -> Self { - let len = size_of_val(slice); - Self { Length: len as _, MaximumLength: len as _, Buffer: slice.as_ptr() as _ } - } -} - impl OBJECT_ATTRIBUTES { pub fn with_length() -> Self { Self { diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index bc5d05c4505..b5ccf037a4f 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -1,9 +1,8 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::ops::Neg; use crate::os::windows::prelude::*; -use crate::sys::api::utf16; -use crate::sys::c; use crate::sys::handle::Handle; +use crate::sys::{api, c}; use crate::sys_common::{FromInner, IntoInner}; use crate::{mem, ptr}; @@ -73,8 +72,8 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res // Open a handle to the pipe filesystem (`\??\PIPE\`). // This will be used when creating a new annon pipe. let pipe_fs = { - let path = c::UNICODE_STRING::from_ref(utf16!(r"\??\PIPE\")); - object_attributes.ObjectName = &path; + let path = api::unicode_str!(r"\??\PIPE\"); + object_attributes.ObjectName = path.as_ptr(); let mut pipe_fs = ptr::null_mut(); let status = c::NtOpenFile( &mut pipe_fs, @@ -93,8 +92,12 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res // From now on we're using handles instead of paths to create and open pipes. // So set the `ObjectName` to a zero length string. + // As a (perhaps overzealous) mitigation for #143078, we use the null pointer + // for empty.Buffer instead of unicode_str!(""). + // There's no difference to the OS itself but it's possible that third party + // DLLs which hook in to processes could be relying on the exact form of this string. let empty = c::UNICODE_STRING::default(); - object_attributes.ObjectName = ∅ + object_attributes.ObjectName = &raw const empty; // Create our side of the pipe for async access. let ours = { diff --git a/library/stdarch/crates/intrinsic-test/README.md b/library/stdarch/crates/intrinsic-test/README.md index 260d59fca80..bea95f91e9e 100644 --- a/library/stdarch/crates/intrinsic-test/README.md +++ b/library/stdarch/crates/intrinsic-test/README.md @@ -7,7 +7,7 @@ USAGE: intrinsic-test [FLAGS] [OPTIONS] <INPUT> FLAGS: - --a32 Run tests for A32 instrinsics instead of A64 + --a32 Run tests for A32 intrinsics instead of A64 --generate-only Regenerate test programs, but don't build or run them -h, --help Prints help information -V, --version Prints version information diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index a6ca699e282..2434a278d55 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -12,6 +12,15 @@ dependencies = [ ] [[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -62,8 +71,8 @@ dependencies = [ "toml", "tracing", "tracing-chrome", + "tracing-forest", "tracing-subscriber", - "tracing-tree", "walkdir", "windows", "xz2", @@ -450,15 +459,6 @@ dependencies = [ ] [[package]] -name = "nu-ansi-term" -version = "0.50.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] name = "objc2-core-foundation" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -776,6 +776,26 @@ dependencies = [ ] [[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -838,6 +858,19 @@ dependencies = [ ] [[package]] +name = "tracing-forest" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee40835db14ddd1e3ba414292272eddde9dad04d3d4b65509656414d1c42592f" +dependencies = [ + "ansi_term", + "smallvec", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -855,7 +888,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", - "nu-ansi-term 0.46.0", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", @@ -867,18 +900,6 @@ dependencies = [ ] [[package]] -name = "tracing-tree" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f459ca79f1b0d5f71c54ddfde6debfc59c8b6eeb46808ae492077f739dc7b49c" -dependencies = [ - "nu-ansi-term 0.50.1", - "tracing-core", - "tracing-log", - "tracing-subscriber", -] - -[[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 9785a306c9b..073cebdcae2 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -7,7 +7,7 @@ default-run = "bootstrap" [features] build-metrics = ["sysinfo"] -tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:tracing-tree"] +tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:tracing-forest"] [lib] path = "src/lib.rs" @@ -64,7 +64,7 @@ sysinfo = { version = "0.35.0", default-features = false, optional = true, featu tracing = { version = "0.1", optional = true, features = ["attributes"] } tracing-chrome = { version = "0.7", optional = true } tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] } -tracing-tree = { version = "0.4.0", optional = true } +tracing-forest = { version = "0.1.6", optional = true, default-features = false, features = ["smallvec", "ansi", "env-filter"] } [target.'cfg(windows)'.dependencies.junction] version = "1.0.0" diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 833f8027951..e1862a451f2 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -217,12 +217,11 @@ fn check_version(config: &Config) -> Option<String> { // "tracing", instrument(..))]`. #[cfg(feature = "tracing")] fn setup_tracing() -> impl Drop { + use tracing_forest::ForestLayer; use tracing_subscriber::EnvFilter; use tracing_subscriber::layer::SubscriberExt; let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); - // cf. <https://docs.rs/tracing-tree/latest/tracing_tree/struct.HierarchicalLayer.html>. - let layer = tracing_tree::HierarchicalLayer::default().with_targets(true).with_indent_amount(2); let mut chrome_layer = tracing_chrome::ChromeLayerBuilder::new().include_args(true); @@ -233,7 +232,8 @@ fn setup_tracing() -> impl Drop { let (chrome_layer, _guard) = chrome_layer.build(); - let registry = tracing_subscriber::registry().with(filter).with(layer).with(chrome_layer); + let registry = + tracing_subscriber::registry().with(filter).with(ForestLayer::default()).with(chrome_layer); tracing::subscriber::set_global_default(registry).unwrap(); _guard diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 8200e154169..84064150738 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2241,7 +2241,7 @@ impl Step for Assemble { debug!("copying codegen backends to sysroot"); copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler); - if builder.config.lld_enabled && !builder.config.is_system_llvm(target_compiler.host) { + if builder.config.lld_enabled { builder.ensure(crate::core::build_steps::tool::LldWrapper { build_compiler, target_compiler, diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index d1ffdf24acd..0cdfbbdaf75 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1003,9 +1003,7 @@ impl Config { } if config.lld_enabled && config.is_system_llvm(config.host_target) { - eprintln!( - "Warning: LLD is enabled when using external llvm-config. LLD will not be built and copied to the sysroot." - ); + panic!("Cannot enable LLD with `rust.lld = true` when using external llvm-config."); } config.optimized_compiler_builtins = diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 7c588cfea8c..006c294d445 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -431,4 +431,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "It is no longer possible to `x build` with stage 0. All build commands have to be on stage 1+.", }, + ChangeInfo { + change_id: 143175, + severity: ChangeSeverity::Info, + summary: "It is no longer possible to combine `rust.lld = true` with configuring external LLVM using `llvm.llvm-config`.", + }, ]; diff --git a/src/doc/book b/src/doc/book -Subproject 8a6d44e45b7b564eeb6bae30507e1fbac439d72 +Subproject ef1ce8f87a8b18feb1b6a9cf9a4939a79bde679 diff --git a/src/doc/embedded-book b/src/doc/embedded-book -Subproject 10fa1e084365f23f24ad0000df541923385b73b +Subproject 41f688a598a5022b749e23d37f3c524f6a0b28e diff --git a/src/doc/reference b/src/doc/reference -Subproject 50fc1628f36563958399123829c73755fa7a842 +Subproject e9fc99f107840813916f62e16b3f6d9556e1f2d diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject 05c7d8bae65f23a1837430c5a19be129d414f5e +Subproject 288b4e4948add43f387cad35adc7b1c54ca6fe1 diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 09dc476d68e..f7e62e1eccf 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -499,7 +499,7 @@ This directive takes comma-separated issue numbers as arguments, or `"unknown"`: - `//@ known-bug: rust-lang/chalk#123456` (allows arbitrary text before the `#`, which is useful when the issue is on another repo) - `//@ known-bug: unknown` - (when there is no known issue yet; preferrably open one if it does not already exist) + (when there is no known issue yet; preferably open one if it does not already exist) Do not include [error annotations](#error-annotations) in a test with `known-bug`. The test should still include other normal directives and diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md index 65e6b417427..4d7f1a4aafc 100644 --- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md +++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md @@ -62,7 +62,7 @@ This form of the `doc` attribute lets you control the favicon of your docs. This will put `<link rel="icon" href="{}">` into your docs, where the string for the attribute goes into the `{}`. -If you don't use this attribute, there will be no favicon. +If you don't use this attribute, a default favicon will be used. ### `html_logo_url` diff --git a/src/doc/unstable-book/src/language-features/type-alias-impl-trait.md b/src/doc/unstable-book/src/language-features/type-alias-impl-trait.md index a6fb25a55be..10464e500ec 100644 --- a/src/doc/unstable-book/src/language-features/type-alias-impl-trait.md +++ b/src/doc/unstable-book/src/language-features/type-alias-impl-trait.md @@ -64,7 +64,7 @@ struct HaveAlias { In this example, the concrete type referred to by `Alias` is guaranteed to be the same wherever `Alias` occurs. -> Orginally this feature included type aliases as an associated type of a trait. In [#110237] this was split off to [`impl_trait_in_assoc_type`]. +> Originally this feature included type aliases as an associated type of a trait. In [#110237] this was split off to [`impl_trait_in_assoc_type`]. ### `type_alias_impl_trait` in argument position. diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 3cac55493f0..600c564d714 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -753,9 +753,12 @@ impl Item { .other_attrs .iter() .filter_map(|attr| { + if let hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) = attr { + Some(format!("#[link_section = \"{name}\"]")) + } // NoMangle is special cased, as it appears in HTML output, and we want to show it in source form, not HIR printing. // It is also used by cargo-semver-checks. - if let hir::Attribute::Parsed(AttributeKind::NoMangle(..)) = attr { + else if let hir::Attribute::Parsed(AttributeKind::NoMangle(..)) = attr { Some("#[no_mangle]".to_string()) } else if let hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) = attr { diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 852e48cbcae..5ed4c82634a 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -74,7 +74,7 @@ impl ApproxConstant { } impl LateLintPass<'_> for ApproxConstant { - fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: &Lit, _negated: bool) { + fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: Lit, _negated: bool) { match lit.node { LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { FloatTy::F16 => self.check_known_consts(cx, lit.span, s, "f16"), diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index 8f95e44bf85..581fe33ea0b 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -42,7 +42,7 @@ fn extract_bool_lit(e: &Expr<'_>) -> Option<bool> { }) = e.kind && !e.span.from_expansion() { - Some(*b) + Some(b) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs index d9e88d6a401..92910cf8adf 100644 --- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -46,7 +46,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool { match expr.kind { ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to), - ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to), + ExprKind::Lit(lit) => is_literal_aligned(cx, &lit, to), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 010f09d4c1d..c88a0539d70 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -243,7 +243,7 @@ fn lint_unnecessary_cast( ); } -fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<Lit> { match expr.kind { ExprKind::Lit(lit) => Some(lit), ExprKind::Unary(UnOp::Neg, e) => { diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 784214c29af..1507f1ed305 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -83,7 +83,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } /// Check whether a passed literal has potential to cause fallback or not. - fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { + fn check_lit(&self, lit: Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if !lit.span.in_external_macro(self.cx.sess().source_map()) && matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))) && matches!( @@ -210,7 +210,7 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { ExprKind::Lit(lit) => { let ty = self.cx.typeck_results().expr_ty(expr); - self.check_lit(lit, ty, expr.hir_id); + self.check_lit(*lit, ty, expr.hir_id); return; }, diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 621a2af1d32..8707612fbdd 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -57,7 +57,7 @@ impl LateLintPass<'_> for LargeIncludeFile { if let ExprKind::Lit(lit) = &expr.kind && let len = match &lit.node { // include_bytes - LitKind::ByteStr(bstr, _) => bstr.len(), + LitKind::ByteStr(bstr, _) => bstr.as_byte_str().len(), // include_str LitKind::Str(sym, _) => sym.as_str().len(), _ => return, diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs index 57c03fbb2ed..f7d9ec1fae8 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -41,12 +41,12 @@ declare_clippy_lint! { declare_lint_pass!(ManualIgnoreCaseCmp => [MANUAL_IGNORE_CASE_CMP]); -enum MatchType<'a, 'b> { +enum MatchType<'a> { ToAscii(bool, Ty<'a>), - Literal(&'b LitKind), + Literal(LitKind), } -fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> { +fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Option<(Span, MatchType<'a>)> { if let MethodCall(path, expr, _, _) = kind { let is_lower = match path.ident.name { sym::to_ascii_lowercase => true, @@ -63,7 +63,7 @@ fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) - return Some((expr.span, ToAscii(is_lower, ty_raw))); } } else if let Lit(expr) = kind { - return Some((expr.span, Literal(&expr.node))); + return Some((expr.span, Literal(expr.node))); } None } diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 9e911e61f19..6bf43a1c6d4 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -184,7 +184,7 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t .. }) = expr.kind { - constant_length(cx, pattern).is_some_and(|length| *n == length) + constant_length(cx, pattern).is_some_and(|length| n == length) } else { len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg)) } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index f14b69d91ce..5816da5695e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -159,7 +159,7 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> { node: LitKind::Bool(b), .. }) = exp.kind { - Some(*b) + Some(b) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index dbb29ee776b..ede68f30941 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -12,7 +12,7 @@ use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExp use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol}; use super::MATCH_SAME_ARMS; @@ -193,7 +193,7 @@ enum NormalizedPat<'a> { Or(&'a [Self]), Path(Option<DefId>), LitStr(Symbol), - LitBytes(&'a [u8]), + LitBytes(ByteSymbol), LitInt(u128), LitBool(bool), Range(PatRange), @@ -332,7 +332,9 @@ impl<'a> NormalizedPat<'a> { // TODO: Handle negative integers. They're currently treated as a wild match. PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), - LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), + LitKind::ByteStr(byte_sym, _) | LitKind::CStr(byte_sym, _) => { + Self::LitBytes(byte_sym) + } LitKind::Byte(val) => Self::LitInt(val.into()), LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val.get()), diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index fd368024177..9b5f138295c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -76,7 +76,7 @@ fn get_open_options( .. } = span { - Argument::Set(*lit) + Argument::Set(lit) } else { // The function is called with a literal which is not a boolean literal. // This is theoretically possible, but not very likely. diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index c8e3462b24e..cf0c85990b1 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -104,7 +104,7 @@ fn len_comparison<'hir>( ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { macro_rules! int_lit_pat { ($id:ident) => { - ExprKind::Lit(&Spanned { + ExprKind::Lit(Spanned { node: LitKind::Int(Pu128($id), _), .. }) diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 3a08531cf1c..ac92ab5a245 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -324,7 +324,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } } - fn lit(&self, lit: &Binding<&Lit>) { + fn lit(&self, lit: &Binding<Lit>) { let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index aaa071fd5c9..09299c869dc 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -4,8 +4,6 @@ //! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] -use std::sync::Arc; - use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; @@ -38,7 +36,7 @@ pub enum Constant<'tcx> { /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). - Binary(Arc<[u8]>), + Binary(Vec<u8>), /// A single `char` (e.g., `'a'`). Char(char), /// An integer's bit representation. @@ -306,7 +304,9 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constan match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => Constant::Binary(Arc::clone(s)), + LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => { + Constant::Binary(s.as_byte_str().to_vec()) + } LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n.get()), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { @@ -568,7 +568,9 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } else { match &lit.node { LitKind::Str(is, _) => Some(is.is_empty()), - LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => Some(s.is_empty()), + LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => { + Some(s.as_byte_str().is_empty()) + } _ => None, } } diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 3b544d8b828..cdada5a2230 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -6,6 +6,10 @@ edition = "2024" [lib] doctest = false +[[bin]] +name = "compiletest" +path = "src/bin/main.rs" + [dependencies] # tidy-alphabetical-start anstyle-svg = "0.1.3" diff --git a/src/tools/compiletest/src/bin/main.rs b/src/tools/compiletest/src/bin/main.rs new file mode 100644 index 00000000000..1f777e71cf9 --- /dev/null +++ b/src/tools/compiletest/src/bin/main.rs @@ -0,0 +1,24 @@ +use std::env; +use std::io::IsTerminal; +use std::sync::Arc; + +use compiletest::{early_config_check, log_config, parse_config, run_tests}; + +fn main() { + tracing_subscriber::fmt::init(); + + // colored checks stdout by default, but for some reason only stderr is a terminal. + // compiletest *does* print many things to stdout, but it doesn't really matter. + if std::io::stderr().is_terminal() + && matches!(std::env::var("NO_COLOR").as_deref(), Err(_) | Ok("0")) + { + colored::control::set_override(true); + } + + let config = Arc::new(parse_config(env::args().collect())); + + early_config_check(&config); + + log_config(&config); + run_tests(config); +} diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 0db4d3f6a41..23a4dd73796 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -51,12 +51,6 @@ use crate::util::logv; /// some code here that inspects environment variables or even runs executables /// (e.g. when discovering debugger versions). pub fn parse_config(args: Vec<String>) -> Config { - if env::var("RUST_TEST_NOCAPTURE").is_ok() { - eprintln!( - "WARNING: RUST_TEST_NOCAPTURE is not supported. Use the `--no-capture` flag instead." - ); - } - let mut opts = Options::new(); opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH") .reqopt("", "run-lib-path", "path to target shared libraries", "PATH") @@ -1111,3 +1105,25 @@ fn check_for_overlapping_test_paths(found_path_stems: &HashSet<Utf8PathBuf>) { ); } } + +pub fn early_config_check(config: &Config) { + if !config.has_html_tidy && config.mode == Mode::Rustdoc { + eprintln!("warning: `tidy` (html-tidy.org) is not installed; diffs will not be generated"); + } + + if !config.profiler_runtime && config.mode == Mode::CoverageRun { + let actioned = if config.bless { "blessed" } else { "checked" }; + eprintln!( + r#" +WARNING: profiler runtime is not available, so `.coverage` files won't be {actioned} +help: try setting `profiler = true` in the `[build]` section of `bootstrap.toml`"# + ); + } + + // `RUST_TEST_NOCAPTURE` is a libtest env var, but we don't callout to libtest. + if env::var("RUST_TEST_NOCAPTURE").is_ok() { + eprintln!( + "WARNING: RUST_TEST_NOCAPTURE is not supported. Use the `--no-capture` flag instead." + ); + } +} diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs deleted file mode 100644 index b9ae583581e..00000000000 --- a/src/tools/compiletest/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::env; -use std::io::IsTerminal; -use std::sync::Arc; - -use compiletest::common::Mode; -use compiletest::{log_config, parse_config, run_tests}; - -fn main() { - tracing_subscriber::fmt::init(); - - // colored checks stdout by default, but for some reason only stderr is a terminal. - // compiletest *does* print many things to stdout, but it doesn't really matter. - if std::io::stderr().is_terminal() - && matches!(std::env::var("NO_COLOR").as_deref(), Err(_) | Ok("0")) - { - colored::control::set_override(true); - } - - let config = Arc::new(parse_config(env::args().collect())); - - if !config.has_html_tidy && config.mode == Mode::Rustdoc { - eprintln!("warning: `tidy` (html-tidy.org) is not installed; diffs will not be generated"); - } - - if !config.profiler_runtime && config.mode == Mode::CoverageRun { - let actioned = if config.bless { "blessed" } else { "checked" }; - eprintln!( - r#" -WARNING: profiler runtime is not available, so `.coverage` files won't be {actioned} -help: try setting `profiler = true` in the `[build]` section of `bootstrap.toml`"# - ); - } - - log_config(&config); - run_tests(config); -} diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 192d4f444c2..d3123caaa47 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -81,6 +81,15 @@ dependencies = [ ] [[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] name = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -113,6 +122,26 @@ dependencies = [ ] [[package]] +name = "capstone" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "015ef5d5ca1743e3f94af9509ba6bd2886523cfee46e48d15c2ef5216fd4ac9a" +dependencies = [ + "capstone-sys", + "libc", +] + +[[package]] +name = "capstone-sys" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0" +dependencies = [ + "cc", + "libc", +] + +[[package]] name = "cargo-platform" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -151,6 +180,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] name = "chrono" version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -224,7 +259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -243,7 +278,7 @@ dependencies = [ "libc", "once_cell", "unicode-width 0.2.0", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -298,7 +333,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -314,7 +349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -334,6 +369,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -401,6 +442,25 @@ dependencies = [ ] [[package]] +name = "ipc-channel" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8251fb7bcd9ccd3725ed8deae9fe7db8e586495c9eb5b0c52e6233e5e75ea" +dependencies = [ + "bincode", + "crossbeam-channel", + "fnv", + "lazy_static", + "libc", + "mio", + "rand 0.8.5", + "serde", + "tempfile", + "uuid", + "windows", +] + +[[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -534,23 +594,39 @@ dependencies = [ ] [[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] name = "miri" version = "0.1.0" dependencies = [ "aes", "bitflags", + "capstone", "chrono", "chrono-tz", "colored", "directories", "getrandom 0.3.2", + "ipc-channel", "libc", "libffi", "libloading", "measureme", + "nix", "rand 0.9.0", "regex", "rustc_version", + "serde", "smallvec", "tempfile", "tikv-jemalloc-sys", @@ -558,6 +634,18 @@ dependencies = [ ] [[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -748,6 +836,8 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -757,13 +847,23 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.3", "zerocopy", ] [[package]] name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" @@ -777,6 +877,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] [[package]] name = "rand_core" @@ -879,7 +982,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -993,7 +1096,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1148,6 +1251,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom 0.3.2", +] + +[[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1242,6 +1354,79 @@ dependencies = [ ] [[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index a314488bb25..0b4b8e26bb8 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -40,6 +40,12 @@ libc = "0.2" libffi = "4.0.0" libloading = "0.8" +[target.'cfg(target_os = "linux")'.dependencies] +nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"] } +ipc-channel = "0.19.0" +serde = { version = "1.0.219", features = ["derive"] } +capstone = "0.13" + [dev-dependencies] ui_test = "0.29.1" colored = "2" diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 3031d2c744a..e609fb9b6f9 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -419,6 +419,9 @@ to Miri failing to detect cases of undefined behavior in a program. Finally, the flag is **unsound** in the sense that Miri stops tracking details such as initialization and provenance on memory shared with native code, so it is easily possible to write code that has UB which is missed by Miri. +* `-Zmiri-native-lib-enable-tracing` enables the WIP detailed tracing mode for invoking native code. + Note that this flag is only meaningful on Linux systems; other Unixes (currently) do not support + tracing mode. * `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program. This can be used to find which parts of your program are executing slowly under Miri. The profile is written out to a file inside a directory called `<name>`, and can be processed diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 86362145d47..d18e9c7791f 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -32,6 +32,7 @@ impl MiriEnv { &mut self, quiet: bool, target: Option<impl AsRef<OsStr>>, + features: &[String], ) -> Result<PathBuf> { if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") { // Sysroot already set, use that. @@ -39,8 +40,8 @@ impl MiriEnv { } // Make sure everything is built. Also Miri itself. - self.build(".", &[], quiet)?; - self.build("cargo-miri", &[], quiet)?; + self.build(".", features, &[], quiet)?; + self.build("cargo-miri", &[], &[], quiet)?; let target_flag = if let Some(target) = &target { vec![OsStr::new("--target"), target.as_ref()] @@ -58,7 +59,7 @@ impl MiriEnv { } let mut cmd = self - .cargo_cmd("cargo-miri", "run") + .cargo_cmd("cargo-miri", "run", &[]) .arg("--quiet") .arg("--") .args(&["miri", "setup", "--print-sysroot"]) @@ -90,7 +91,9 @@ impl Command { Self::fmt(vec![])?; } if auto_clippy { - Self::clippy(vec![])?; + // no features for auto actions, see + // https://github.com/rust-lang/miri/pull/4396#discussion_r2149654845 + Self::clippy(vec![], vec![])?; } Ok(()) @@ -175,16 +178,16 @@ impl Command { } // Then run the actual command. match self { - Command::Install { flags } => Self::install(flags), - Command::Build { flags } => Self::build(flags), - Command::Check { flags } => Self::check(flags), - Command::Test { bless, flags, target, coverage } => - Self::test(bless, flags, target, coverage), - Command::Run { dep, verbose, target, edition, flags } => - Self::run(dep, verbose, target, edition, flags), - Command::Doc { flags } => Self::doc(flags), + Command::Install { features, flags } => Self::install(features, flags), + Command::Build { features, flags } => Self::build(features, flags), + Command::Check { features, flags } => Self::check(features, flags), + Command::Test { bless, target, coverage, features, flags } => + Self::test(bless, target, coverage, features, flags), + Command::Run { dep, verbose, target, edition, features, flags } => + Self::run(dep, verbose, target, edition, features, flags), + Command::Doc { features, flags } => Self::doc(features, flags), Command::Fmt { flags } => Self::fmt(flags), - Command::Clippy { flags } => Self::clippy(flags), + Command::Clippy { features, flags } => Self::clippy(features, flags), Command::Bench { target, no_install, save_baseline, load_baseline, benches } => Self::bench(target, no_install, save_baseline, load_baseline, benches), Command::Toolchain { flags } => Self::toolchain(flags), @@ -494,7 +497,7 @@ impl Command { if !no_install { // Make sure we have an up-to-date Miri installed and selected the right toolchain. - Self::install(vec![])?; + Self::install(vec![], vec![])?; } let results_json_dir = if save_baseline.is_some() || load_baseline.is_some() { Some(TempDir::new()?) @@ -601,47 +604,48 @@ impl Command { Ok(()) } - fn install(flags: Vec<String>) -> Result<()> { + fn install(features: Vec<String>, flags: Vec<String>) -> Result<()> { let e = MiriEnv::new()?; - e.install_to_sysroot(e.miri_dir.clone(), &flags)?; - e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?; + e.install_to_sysroot(".", &features, &flags)?; + e.install_to_sysroot("cargo-miri", &[], &flags)?; Ok(()) } - fn build(flags: Vec<String>) -> Result<()> { + fn build(features: Vec<String>, flags: Vec<String>) -> Result<()> { let e = MiriEnv::new()?; - e.build(".", &flags, /* quiet */ false)?; - e.build("cargo-miri", &flags, /* quiet */ false)?; + e.build(".", &features, &flags, /* quiet */ false)?; + e.build("cargo-miri", &[], &flags, /* quiet */ false)?; Ok(()) } - fn check(flags: Vec<String>) -> Result<()> { + fn check(features: Vec<String>, flags: Vec<String>) -> Result<()> { let e = MiriEnv::new()?; - e.check(".", &flags)?; - e.check("cargo-miri", &flags)?; + e.check(".", &features, &flags)?; + e.check("cargo-miri", &[], &flags)?; Ok(()) } - fn doc(flags: Vec<String>) -> Result<()> { + fn doc(features: Vec<String>, flags: Vec<String>) -> Result<()> { let e = MiriEnv::new()?; - e.doc(".", &flags)?; - e.doc("cargo-miri", &flags)?; + e.doc(".", &features, &flags)?; + e.doc("cargo-miri", &[], &flags)?; Ok(()) } - fn clippy(flags: Vec<String>) -> Result<()> { + fn clippy(features: Vec<String>, flags: Vec<String>) -> Result<()> { let e = MiriEnv::new()?; - e.clippy(".", &flags)?; - e.clippy("cargo-miri", &flags)?; - e.clippy("miri-script", &flags)?; + e.clippy(".", &features, &flags)?; + e.clippy("cargo-miri", &[], &flags)?; + e.clippy("miri-script", &[], &flags)?; Ok(()) } fn test( bless: bool, - mut flags: Vec<String>, target: Option<String>, coverage: bool, + features: Vec<String>, + mut flags: Vec<String>, ) -> Result<()> { let mut e = MiriEnv::new()?; @@ -652,7 +656,7 @@ impl Command { } // Prepare a sysroot. (Also builds cargo-miri, which we need.) - e.build_miri_sysroot(/* quiet */ false, target.as_deref())?; + e.build_miri_sysroot(/* quiet */ false, target.as_deref(), &features)?; // Forward information to test harness. if bless { @@ -672,10 +676,10 @@ impl Command { // Then test, and let caller control flags. // Only in root project as `cargo-miri` has no tests. - e.test(".", &flags)?; + e.test(".", &features, &flags)?; if let Some(coverage) = &coverage { - coverage.show_coverage_report(&e)?; + coverage.show_coverage_report(&e, &features)?; } Ok(()) @@ -686,14 +690,17 @@ impl Command { verbose: bool, target: Option<String>, edition: Option<String>, + features: Vec<String>, flags: Vec<String>, ) -> Result<()> { let mut e = MiriEnv::new()?; // Preparation: get a sysroot, and get the miri binary. - let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?; - let miri_bin = - e.build_get_binary(".").context("failed to get filename of miri executable")?; + let miri_sysroot = + e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref(), &features)?; + let miri_bin = e + .build_get_binary(".", &features) + .context("failed to get filename of miri executable")?; // More flags that we will pass before `flags` // (because `flags` may contain `--`). @@ -718,7 +725,7 @@ impl Command { // The basic command that executes the Miri driver. let mut cmd = if dep { // We invoke the test suite as that has all the logic for running with dependencies. - e.cargo_cmd(".", "test") + e.cargo_cmd(".", "test", &features) .args(&["--test", "ui"]) .args(quiet_flag) .arg("--") diff --git a/src/tools/miri/miri-script/src/coverage.rs b/src/tools/miri/miri-script/src/coverage.rs index 8cafcea0d16..cdf2bbb9330 100644 --- a/src/tools/miri/miri-script/src/coverage.rs +++ b/src/tools/miri/miri-script/src/coverage.rs @@ -49,7 +49,7 @@ impl CoverageReport { /// show_coverage_report will print coverage information using the artifact /// files in `self.path`. - pub fn show_coverage_report(&self, e: &MiriEnv) -> Result<()> { + pub fn show_coverage_report(&self, e: &MiriEnv, features: &[String]) -> Result<()> { let profraw_files = self.profraw_files()?; let profdata_bin = path!(e.libdir / ".." / "bin" / "llvm-profdata"); @@ -63,8 +63,9 @@ impl CoverageReport { // Create the coverage report. let cov_bin = path!(e.libdir / ".." / "bin" / "llvm-cov"); - let miri_bin = - e.build_get_binary(".").context("failed to get filename of miri executable")?; + let miri_bin = e + .build_get_binary(".", features) + .context("failed to get filename of miri executable")?; cmd!( e.sh, "{cov_bin} report --instr-profile={merged_file} --object {miri_bin} --sources src/" diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index 6aab2f79bd7..673d658cf1d 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -14,24 +14,40 @@ pub enum Command { /// Sets up the rpath such that the installed binary should work in any /// working directory. Install { + /// Pass features to cargo invocations on the "miri" crate in the root. This option does + /// **not** apply to other crates, so e.g. these features won't be used on "cargo-miri". + #[arg(long, value_delimiter = ',', action = clap::ArgAction::Append)] + features: Vec<String>, /// Flags that are passed through to `cargo install`. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec<String>, }, /// Build Miri. Build { + /// Pass features to cargo invocations on the "miri" crate in the root. This option does + /// **not** apply to other crates, so e.g. these features won't be used on "cargo-miri". + #[arg(long, value_delimiter = ',', action = clap::ArgAction::Append)] + features: Vec<String>, /// Flags that are passed through to `cargo build`. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec<String>, }, /// Check Miri. Check { + /// Pass features to cargo invocations on the "miri" crate in the root. This option does + /// **not** apply to other crates, so e.g. these features won't be used on "cargo-miri". + #[arg(long, value_delimiter = ',', action = clap::ArgAction::Append)] + features: Vec<String>, /// Flags that are passed through to `cargo check`. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec<String>, }, /// Check Miri with Clippy. Clippy { + /// Pass features to cargo invocations on the "miri" crate in the root. This option does + /// **not** apply to other crates, so e.g. these features won't be used on "cargo-miri". + #[arg(long, value_delimiter = ',', action = clap::ArgAction::Append)] + features: Vec<String>, /// Flags that are passed through to `cargo clippy`. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec<String>, @@ -47,6 +63,10 @@ pub enum Command { /// Produce coverage report. #[arg(long)] coverage: bool, + /// Pass features to cargo invocations on the "miri" crate in the root. This option does + /// **not** apply to other crates, so e.g. these features won't be used on "cargo-miri". + #[arg(long, value_delimiter = ',', action = clap::ArgAction::Append)] + features: Vec<String>, /// Flags that are passed through to the test harness. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec<String>, @@ -67,6 +87,10 @@ pub enum Command { /// The Rust edition. #[arg(long)] edition: Option<String>, + /// Pass features to cargo invocations on the "miri" crate in the root. This option does + /// **not** apply to other crates, so e.g. these features won't be used on "cargo-miri". + #[arg(long, value_delimiter = ',', action = clap::ArgAction::Append)] + features: Vec<String>, /// Flags that are passed through to `miri`. /// /// The flags set in `MIRIFLAGS` are added in front of these flags. @@ -75,6 +99,10 @@ pub enum Command { }, /// Build documentation. Doc { + /// Pass features to cargo invocations on the "miri" crate in the root. This option does + /// **not** apply to other crates, so e.g. these features won't be used on "cargo-miri". + #[arg(long, value_delimiter = ',', action = clap::ArgAction::Append)] + features: Vec<String>, /// Flags that are passed through to `cargo doc`. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec<String>, @@ -144,13 +172,13 @@ impl Command { } match self { - Self::Install { flags } - | Self::Build { flags } - | Self::Check { flags } - | Self::Doc { flags } + Self::Install { flags, .. } + | Self::Build { flags, .. } + | Self::Check { flags, .. } + | Self::Doc { flags, .. } | Self::Fmt { flags } | Self::Toolchain { flags } - | Self::Clippy { flags } + | Self::Clippy { flags, .. } | Self::Run { flags, .. } | Self::Test { flags, .. } => { flags.extend(remainder); diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs index 5c2a055990f..c100cf195ba 100644 --- a/src/tools/miri/miri-script/src/util.rs +++ b/src/tools/miri/miri-script/src/util.rs @@ -26,6 +26,12 @@ pub fn flagsplit(flags: &str) -> Vec<String> { flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() } +/// Turns a list of features into a list of arguments to pass to cargo invocations. +/// Each feature will go in its own argument, e.g. "--features feat1 --features feat2". +fn features_to_args(features: &[String]) -> impl IntoIterator<Item = &str> { + features.iter().flat_map(|feat| ["--features", feat]) +} + /// Some extra state we track for building Miri, such as the right RUSTFLAGS. pub struct MiriEnv { /// miri_dir is the root of the miri repository checkout we are working in. @@ -116,44 +122,70 @@ impl MiriEnv { Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags, libdir }) } - pub fn cargo_cmd(&self, crate_dir: impl AsRef<OsStr>, cmd: &str) -> Cmd<'_> { + /// Make sure the `features` you pass here exist for the specified `crate_dir`. For example, the + /// "--features" parameter of [crate::Command]s is intended only for the "miri" root crate. + pub fn cargo_cmd( + &self, + crate_dir: impl AsRef<OsStr>, + cmd: &str, + features: &[String], + ) -> Cmd<'_> { let MiriEnv { toolchain, cargo_extra_flags, .. } = self; let manifest_path = path!(self.miri_dir / crate_dir.as_ref() / "Cargo.toml"); + let features = features_to_args(features); cmd!( self.sh, - "cargo +{toolchain} {cmd} {cargo_extra_flags...} --manifest-path {manifest_path}" + "cargo +{toolchain} {cmd} {cargo_extra_flags...} --manifest-path {manifest_path} {features...}" ) } + /// Make sure the `features` you pass here exist for the specified `crate_dir`. For example, the + /// "--features" parameter of [crate::Command]s is intended only for the "miri" root crate. pub fn install_to_sysroot( &self, - path: impl AsRef<OsStr>, + crate_dir: impl AsRef<OsStr>, + features: &[String], args: impl IntoIterator<Item = impl AsRef<OsStr>>, ) -> Result<()> { let MiriEnv { sysroot, toolchain, cargo_extra_flags, .. } = self; - let path = path!(self.miri_dir / path.as_ref()); + let path = path!(self.miri_dir / crate_dir.as_ref()); + let features = features_to_args(features); // Install binaries to the miri toolchain's `sysroot` so they do not interact with other toolchains. // (Not using `cargo_cmd` as `install` is special and doesn't use `--manifest-path`.) - cmd!(self.sh, "cargo +{toolchain} install {cargo_extra_flags...} --path {path} --force --root {sysroot} {args...}").run()?; + cmd!(self.sh, "cargo +{toolchain} install {cargo_extra_flags...} --path {path} --force --root {sysroot} {features...} {args...}").run()?; Ok(()) } - pub fn build(&self, crate_dir: impl AsRef<OsStr>, args: &[String], quiet: bool) -> Result<()> { + pub fn build( + &self, + crate_dir: impl AsRef<OsStr>, + features: &[String], + args: &[String], + quiet: bool, + ) -> Result<()> { let quiet_flag = if quiet { Some("--quiet") } else { None }; // We build all targets, since building *just* the bin target doesnot include // `dev-dependencies` and that changes feature resolution. This also gets us more // parallelism in `./miri test` as we build Miri and its tests together. - let mut cmd = - self.cargo_cmd(crate_dir, "build").args(&["--all-targets"]).args(quiet_flag).args(args); + let mut cmd = self + .cargo_cmd(crate_dir, "build", features) + .args(&["--all-targets"]) + .args(quiet_flag) + .args(args); cmd.set_quiet(quiet); cmd.run()?; Ok(()) } /// Returns the path to the main crate binary. Assumes that `build` has been called before. - pub fn build_get_binary(&self, crate_dir: impl AsRef<OsStr>) -> Result<PathBuf> { - let cmd = - self.cargo_cmd(crate_dir, "build").args(&["--all-targets", "--message-format=json"]); + pub fn build_get_binary( + &self, + crate_dir: impl AsRef<OsStr>, + features: &[String], + ) -> Result<PathBuf> { + let cmd = self + .cargo_cmd(crate_dir, "build", features) + .args(&["--all-targets", "--message-format=json"]); let output = cmd.output()?; let mut bin = None; for line in output.stdout.lines() { @@ -174,23 +206,43 @@ impl MiriEnv { bin.ok_or_else(|| anyhow!("found no binary in cargo output")) } - pub fn check(&self, crate_dir: impl AsRef<OsStr>, args: &[String]) -> Result<()> { - self.cargo_cmd(crate_dir, "check").arg("--all-targets").args(args).run()?; + pub fn check( + &self, + crate_dir: impl AsRef<OsStr>, + features: &[String], + args: &[String], + ) -> Result<()> { + self.cargo_cmd(crate_dir, "check", features).arg("--all-targets").args(args).run()?; Ok(()) } - pub fn doc(&self, crate_dir: impl AsRef<OsStr>, args: &[String]) -> Result<()> { - self.cargo_cmd(crate_dir, "doc").args(args).run()?; + pub fn doc( + &self, + crate_dir: impl AsRef<OsStr>, + features: &[String], + args: &[String], + ) -> Result<()> { + self.cargo_cmd(crate_dir, "doc", features).args(args).run()?; Ok(()) } - pub fn clippy(&self, crate_dir: impl AsRef<OsStr>, args: &[String]) -> Result<()> { - self.cargo_cmd(crate_dir, "clippy").arg("--all-targets").args(args).run()?; + pub fn clippy( + &self, + crate_dir: impl AsRef<OsStr>, + features: &[String], + args: &[String], + ) -> Result<()> { + self.cargo_cmd(crate_dir, "clippy", features).arg("--all-targets").args(args).run()?; Ok(()) } - pub fn test(&self, crate_dir: impl AsRef<OsStr>, args: &[String]) -> Result<()> { - self.cargo_cmd(crate_dir, "test").args(args).run()?; + pub fn test( + &self, + crate_dir: impl AsRef<OsStr>, + features: &[String], + args: &[String], + ) -> Result<()> { + self.cargo_cmd(crate_dir, "test", features).args(args).run()?; Ok(()) } diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 90c6e31e9d1..b2ab17fd76a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -0cbc0764380630780a275c437260e4d4d5f28c92 +d41e12f1f4e4884c356f319b881921aa37040de5 diff --git a/src/tools/miri/src/alloc/isolated_alloc.rs b/src/tools/miri/src/alloc/isolated_alloc.rs index 3a7879f372a..a7bb9b4da75 100644 --- a/src/tools/miri/src/alloc/isolated_alloc.rs +++ b/src/tools/miri/src/alloc/isolated_alloc.rs @@ -1,5 +1,7 @@ -use std::alloc::{self, Layout}; +use std::alloc::Layout; +use std::ptr::NonNull; +use nix::sys::mman; use rustc_index::bit_set::DenseBitSet; /// How many bytes of memory each bit in the bitset represents. @@ -11,8 +13,8 @@ const COMPRESSION_FACTOR: usize = 4; pub struct IsolatedAlloc { /// Pointers to page-aligned memory that has been claimed by the allocator. /// Every pointer here must point to a page-sized allocation claimed via - /// the global allocator. These pointers are used for "small" allocations. - page_ptrs: Vec<*mut u8>, + /// mmap. These pointers are used for "small" allocations. + page_ptrs: Vec<NonNull<u8>>, /// Metadata about which bytes have been allocated on each page. The length /// of this vector must be the same as that of `page_ptrs`, and the domain /// size of the bitset must be exactly `page_size / COMPRESSION_FACTOR`. @@ -24,7 +26,7 @@ pub struct IsolatedAlloc { page_infos: Vec<DenseBitSet<usize>>, /// Pointers to multiple-page-sized allocations. These must also be page-aligned, /// with their size stored as the second element of the vector. - huge_ptrs: Vec<(*mut u8, usize)>, + huge_ptrs: Vec<(NonNull<u8>, usize)>, /// The host (not emulated) page size. page_size: usize, } @@ -52,20 +54,26 @@ impl IsolatedAlloc { Layout::from_size_align(size, align).unwrap() } - /// Returns the layout used to allocate the pages that hold small allocations. + /// For greater-than-page-sized allocations, returns the allocation size we need to request + /// including the slack we need to satisfy the alignment request. #[inline] - fn page_layout(&self) -> Layout { - Layout::from_size_align(self.page_size, self.page_size).unwrap() - } - - /// If the allocation is greater than a page, then round to the nearest page #. - #[inline] - fn huge_normalized_layout(layout: Layout, page_size: usize) -> Layout { + fn huge_normalized_layout(&self, layout: Layout) -> usize { // Allocate in page-sized chunks - let size = layout.size().next_multiple_of(page_size); + let size = layout.size().next_multiple_of(self.page_size); // And make sure the align is at least one page - let align = std::cmp::max(layout.align(), page_size); - Layout::from_size_align(size, align).unwrap() + let align = std::cmp::max(layout.align(), self.page_size); + // pg_count gives us the # of pages needed to satisfy the size. For + // align > page_size where align = n * page_size, a sufficently-aligned + // address must exist somewhere in the range of + // some_page_aligned_address..some_page_aligned_address + (n-1) * page_size + // (since if some_page_aligned_address + n * page_size is sufficently aligned, + // then so is some_page_aligned_address itself per the definition of n, so we + // can avoid using that 1 extra page). + // Thus we allocate n-1 extra pages + let pg_count = size.div_ceil(self.page_size); + let extra_pages = align.strict_div(self.page_size).saturating_sub(1); + + pg_count.strict_add(extra_pages).strict_mul(self.page_size) } /// Determined whether a given normalized (size, align) should be sent to @@ -78,7 +86,7 @@ impl IsolatedAlloc { /// Allocates memory as described in `Layout`. This memory should be deallocated /// by calling `dealloc` on this same allocator. /// - /// SAFETY: See `alloc::alloc()` + /// SAFETY: See `alloc::alloc()`. pub unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 { // SAFETY: Upheld by caller unsafe { self.allocate(layout, false) } @@ -86,7 +94,7 @@ impl IsolatedAlloc { /// Same as `alloc`, but zeroes out the memory. /// - /// SAFETY: See `alloc::alloc_zeroed()` + /// SAFETY: See `alloc::alloc_zeroed()`. pub unsafe fn alloc_zeroed(&mut self, layout: Layout) -> *mut u8 { // SAFETY: Upheld by caller unsafe { self.allocate(layout, true) } @@ -95,14 +103,13 @@ impl IsolatedAlloc { /// Abstracts over the logic of `alloc_zeroed` vs `alloc`, as determined by /// the `zeroed` argument. /// - /// SAFETY: See `alloc::alloc()`, with the added restriction that `page_size` - /// corresponds to the host pagesize. + /// SAFETY: See `alloc::alloc()`. unsafe fn allocate(&mut self, layout: Layout, zeroed: bool) -> *mut u8 { let layout = IsolatedAlloc::normalized_layout(layout); if self.is_huge_alloc(&layout) { // SAFETY: Validity of `layout` upheld by caller; we checked that // the size and alignment are appropriate for being a huge alloc - unsafe { self.alloc_huge(layout, zeroed) } + unsafe { self.alloc_huge(layout) } } else { for (&mut page, pinfo) in std::iter::zip(&mut self.page_ptrs, &mut self.page_infos) { // SAFETY: The value in `self.page_size` is used to allocate @@ -132,7 +139,7 @@ impl IsolatedAlloc { unsafe fn alloc_small( page_size: usize, layout: Layout, - page: *mut u8, + page: NonNull<u8>, pinfo: &mut DenseBitSet<usize>, zeroed: bool, ) -> Option<*mut u8> { @@ -159,7 +166,7 @@ impl IsolatedAlloc { // zero out, even if we allocated more ptr.write_bytes(0, layout.size()); } - return Some(ptr); + return Some(ptr.as_ptr()); } } } @@ -167,26 +174,50 @@ impl IsolatedAlloc { } /// Expands the available memory pool by adding one page. - fn add_page(&mut self) -> (*mut u8, &mut DenseBitSet<usize>) { - // SAFETY: The system page size, which is the layout size, cannot be 0 - let page_ptr = unsafe { alloc::alloc(self.page_layout()) }; + fn add_page(&mut self) -> (NonNull<u8>, &mut DenseBitSet<usize>) { + // SAFETY: mmap is always safe to call when requesting anonymous memory + let page_ptr = unsafe { + libc::mmap( + std::ptr::null_mut(), + self.page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + .cast::<u8>() + }; + assert_ne!(page_ptr.addr(), usize::MAX, "mmap failed"); // `page_infos` has to have one bit for each `COMPRESSION_FACTOR`-sized chunk of bytes in the page. - assert!(self.page_size % COMPRESSION_FACTOR == 0); + assert!(self.page_size.is_multiple_of(COMPRESSION_FACTOR)); self.page_infos.push(DenseBitSet::new_empty(self.page_size / COMPRESSION_FACTOR)); - self.page_ptrs.push(page_ptr); - (page_ptr, self.page_infos.last_mut().unwrap()) + self.page_ptrs.push(NonNull::new(page_ptr).unwrap()); + (NonNull::new(page_ptr).unwrap(), self.page_infos.last_mut().unwrap()) } /// Allocates in multiples of one page on the host system. + /// Will always be zeroed. /// /// SAFETY: Same as `alloc()`. - unsafe fn alloc_huge(&mut self, layout: Layout, zeroed: bool) -> *mut u8 { - let layout = IsolatedAlloc::huge_normalized_layout(layout, self.page_size); - // SAFETY: Upheld by caller - let ret = - unsafe { if zeroed { alloc::alloc_zeroed(layout) } else { alloc::alloc(layout) } }; - self.huge_ptrs.push((ret, layout.size())); - ret + unsafe fn alloc_huge(&mut self, layout: Layout) -> *mut u8 { + let size = self.huge_normalized_layout(layout); + // SAFETY: mmap is always safe to call when requesting anonymous memory + let ret = unsafe { + libc::mmap( + std::ptr::null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + .cast::<u8>() + }; + assert_ne!(ret.addr(), usize::MAX, "mmap failed"); + self.huge_ptrs.push((NonNull::new(ret).unwrap(), size)); + // huge_normalized_layout ensures that we've overallocated enough space + // for this to be valid. + ret.map_addr(|a| a.next_multiple_of(layout.align())) } /// Deallocates a pointer from this allocator. @@ -215,15 +246,15 @@ impl IsolatedAlloc { let page_ptr = self.page_ptrs.remove(idx); // SAFETY: We checked that there are no outstanding allocations // from us pointing to this page, and we know it was allocated - // with this layout + // in add_page as exactly a single page. unsafe { - alloc::dealloc(page_ptr, self.page_layout()); + assert_eq!(libc::munmap(page_ptr.as_ptr().cast(), self.page_size), 0); } } } } - /// Returns the index of the page that this was deallocated from + /// Returns the index of the page that this was deallocated from. /// /// SAFETY: the pointer must have been allocated with `alloc_small`. unsafe fn dealloc_small(&mut self, ptr: *mut u8, layout: Layout) -> usize { @@ -236,7 +267,7 @@ impl IsolatedAlloc { // This could be made faster if the list was sorted -- the allocator isn't fully optimized at the moment. let pinfo = std::iter::zip(&mut self.page_ptrs, &mut self.page_infos) .enumerate() - .find(|(_, (page, _))| page.addr() == page_addr); + .find(|(_, (page, _))| page.addr().get() == page_addr); let Some((idx_of_pinfo, (_, pinfo))) = pinfo else { panic!("Freeing in an unallocated page: {ptr:?}\nHolding pages {:?}", self.page_ptrs) }; @@ -252,19 +283,72 @@ impl IsolatedAlloc { /// SAFETY: Same as `dealloc()` with the added requirement that `layout` /// must ask for a size larger than the host pagesize. unsafe fn dealloc_huge(&mut self, ptr: *mut u8, layout: Layout) { - let layout = IsolatedAlloc::huge_normalized_layout(layout, self.page_size); - // Find the pointer matching in address with the one we got + let size = self.huge_normalized_layout(layout); + // Find the huge allocation containing `ptr`. let idx = self .huge_ptrs .iter() - .position(|pg| ptr.addr() == pg.0.addr()) + .position(|&(pg, size)| { + pg.addr().get() <= ptr.addr() && ptr.addr() < pg.addr().get().strict_add(size) + }) .expect("Freeing unallocated pages"); // And kick it from the list - self.huge_ptrs.remove(idx); - // SAFETY: Caller ensures validity of the layout + let (un_offset_ptr, size2) = self.huge_ptrs.remove(idx); + assert_eq!(size, size2, "got wrong layout in dealloc"); + // SAFETY: huge_ptrs contains allocations made with mmap with the size recorded there. unsafe { - alloc::dealloc(ptr, layout); + let ret = libc::munmap(un_offset_ptr.as_ptr().cast(), size); + assert_eq!(ret, 0); + } + } + + /// Returns a vector of page addresses managed by the allocator. + pub fn pages(&self) -> Vec<usize> { + let mut pages: Vec<usize> = + self.page_ptrs.iter().map(|p| p.expose_provenance().get()).collect(); + for (ptr, size) in self.huge_ptrs.iter() { + for i in 0..size / self.page_size { + pages.push(ptr.expose_provenance().get().strict_add(i * self.page_size)); + } + } + pages + } + + /// Protects all owned memory as `PROT_NONE`, preventing accesses. + /// + /// SAFETY: Accessing memory after this point will result in a segfault + /// unless it is first unprotected. + pub unsafe fn prepare_ffi(&mut self) -> Result<(), nix::errno::Errno> { + let prot = mman::ProtFlags::PROT_NONE; + unsafe { self.mprotect(prot) } + } + + /// Deprotects all owned memory by setting it to RW. Erroring here is very + /// likely unrecoverable, so it may panic if applying those permissions + /// fails. + pub fn unprep_ffi(&mut self) { + let prot = mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE; + unsafe { + self.mprotect(prot).unwrap(); + } + } + + /// Applies `prot` to every page managed by the allocator. + /// + /// SAFETY: Accessing memory in violation of the protection flags will + /// trigger a segfault. + unsafe fn mprotect(&mut self, prot: mman::ProtFlags) -> Result<(), nix::errno::Errno> { + for &pg in &self.page_ptrs { + unsafe { + mman::mprotect(pg.cast(), self.page_size, prot)?; + } + } + for &(hpg, size) in &self.huge_ptrs { + unsafe { + mman::mprotect(hpg.cast(), size.next_multiple_of(self.page_size), prot)?; + } } + Ok(()) } } @@ -347,12 +431,15 @@ mod tests { sizes.append(&mut vec![256; 12]); // Give it some multi-page ones too sizes.append(&mut vec![32 * 1024; 4]); + sizes.push(4 * 1024); // Matching aligns for the sizes let mut aligns = vec![16; 12]; aligns.append(&mut vec![256; 2]); aligns.append(&mut vec![64; 12]); aligns.append(&mut vec![4096; 4]); + // And one that requests align > page_size + aligns.push(64 * 1024); // Make sure we didn't mess up in the test itself! assert_eq!(sizes.len(), aligns.len()); diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 4a038fe6487..1796120cf8a 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -390,7 +390,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> { let this = self.eval_context_ref(); - let (prov, offset) = ptr.into_parts(); // offset is relative (AllocId provenance) + let (prov, offset) = ptr.prov_and_relative_offset(); let alloc_id = prov.alloc_id(); // Get a pointer to the beginning of this allocation. @@ -447,7 +447,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> Option<(AllocId, Size)> { let this = self.eval_context_ref(); - let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance) + let (tag, addr) = ptr.into_raw_parts(); // addr is absolute (Miri provenance) let alloc_id = if let Provenance::Concrete { alloc_id, .. } = tag { alloc_id diff --git a/src/tools/miri/src/alloc_addresses/reuse_pool.rs b/src/tools/miri/src/alloc_addresses/reuse_pool.rs index ab6aaed5e3e..b6cc017f772 100644 --- a/src/tools/miri/src/alloc_addresses/reuse_pool.rs +++ b/src/tools/miri/src/alloc_addresses/reuse_pool.rs @@ -129,7 +129,7 @@ impl ReusePool { let idx = rng.random_range(begin..end); // Remove it from the pool and return. let (chosen_addr, chosen_size, chosen_thread, clock) = subpool.remove(idx); - debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0); + debug_assert!(chosen_size >= size && chosen_addr.is_multiple_of(align.bytes())); debug_assert!(cross_thread_reuse || chosen_thread == thread); // No synchronization needed if we reused from the current thread. Some((chosen_addr, if chosen_thread == thread { None } else { Some(clock) })) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 72057f812f9..56be370d5da 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -85,7 +85,7 @@ fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) { return (def_id, MiriEntryFnType::Rustc(entry_type)); } // Look for a symbol in the local crate named `miri_start`, and treat that as the entry point. - let sym = tcx.exported_symbols(LOCAL_CRATE).iter().find_map(|(sym, _)| { + let sym = tcx.exported_non_generic_symbols(LOCAL_CRATE).iter().find_map(|(sym, _)| { if sym.symbol_name_for_local_instance(tcx).name == "miri_start" { Some(sym) } else { None } }); if let Some(ExportedSymbol::NonGeneric(id)) = sym { @@ -227,10 +227,11 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { } else { let return_code = miri::eval_entry(tcx, entry_def_id, entry_type, &config, None) .unwrap_or_else(|| { + //#[cfg(target_os = "linux")] + //miri::native_lib::register_retcode_sv(rustc_driver::EXIT_FAILURE); tcx.dcx().abort_if_errors(); rustc_driver::EXIT_FAILURE }); - std::process::exit(return_code); } @@ -249,10 +250,10 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { // Queries overridden here affect the data stored in `rmeta` files of dependencies, // which will be used later in non-`MIRI_BE_RUSTC` mode. config.override_queries = Some(|_, local_providers| { - // `exported_symbols` and `reachable_non_generics` provided by rustc always returns + // `exported_non_generic_symbols` and `reachable_non_generics` provided by rustc always returns // an empty result if `tcx.sess.opts.output_types.should_codegen()` is false. // In addition we need to add #[used] symbols to exported_symbols for `lookup_link_section`. - local_providers.exported_symbols = |tcx, LocalCrate| { + local_providers.exported_non_generic_symbols = |tcx, LocalCrate| { let reachable_set = tcx.with_stable_hashing_context(|hcx| { tcx.reachable_set(()).to_sorted(&hcx, true) }); @@ -723,6 +724,8 @@ fn main() { } else { show_error!("-Zmiri-native-lib `{}` does not exist", filename); } + } else if arg == "-Zmiri-native-lib-enable-tracing" { + miri_config.native_lib_enable_tracing = true; } else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") { let num_cpus = param .parse::<u32>() @@ -793,6 +796,16 @@ fn main() { debug!("rustc arguments: {:?}", rustc_args); debug!("crate arguments: {:?}", miri_config.args); + #[cfg(target_os = "linux")] + if !miri_config.native_lib.is_empty() && miri_config.native_lib_enable_tracing { + // FIXME: This should display a diagnostic / warning on error + // SAFETY: If any other threads exist at this point (namely for the ctrlc + // handler), they will not interact with anything on the main rustc/Miri + // thread in an async-signal-unsafe way such as by accessing shared + // semaphores, etc.; the handler only calls `sleep()` and `exit()`, which + // are async-signal-safe, as is accessing atomics + //let _ = unsafe { miri::native_lib::init_sv() }; + } run_compiler_and_exit( &rustc_args, &mut MiriCompilerCalls::new(miri_config, many_seeds, genmc_config), diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 99171b0349e..a0761cb07a1 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -468,10 +468,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // - when `extern type` is involved we use the size of the known prefix, // - if the pointer is not reborrowed (raw pointer) then we override the size // to do a zero-length reborrow. - let reborrow_size = this - .size_and_align_of_val(place)? - .map(|(size, _)| size) - .unwrap_or(place.layout.size); + let reborrow_size = + this.size_and_align_of_val(place)?.map(|(size, _)| size).unwrap_or(place.layout.size); trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size); // This new tag is not guaranteed to actually be used. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs index dcd5a6cb023..ad0a565dfd8 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs @@ -327,7 +327,7 @@ mod tests { for i in 0..1000 { i.hash(&mut hasher); let rng = hasher.finish(); - let op = rng % 3 == 0; + let op = rng.is_multiple_of(3); let key = (rng / 2) % 50; let val = (rng / 100) % 1000; if op { diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 38b5d4c0f06..c8a408fd8ac 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -582,88 +582,6 @@ impl<'tcx> ThreadManager<'tcx> { interp_ok(()) } - /// Mark that the active thread tries to join the thread with `joined_thread_id`. - fn join_thread( - &mut self, - joined_thread_id: ThreadId, - data_race_handler: &mut GlobalDataRaceHandler, - ) -> InterpResult<'tcx> { - if self.threads[joined_thread_id].join_status == ThreadJoinStatus::Detached { - // On Windows this corresponds to joining on a closed handle. - throw_ub_format!("trying to join a detached thread"); - } - - fn after_join<'tcx>( - threads: &mut ThreadManager<'_>, - joined_thread_id: ThreadId, - data_race_handler: &mut GlobalDataRaceHandler, - ) -> InterpResult<'tcx> { - match data_race_handler { - GlobalDataRaceHandler::None => {} - GlobalDataRaceHandler::Vclocks(data_race) => - data_race.thread_joined(threads, joined_thread_id), - GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_thread_join(threads.active_thread, joined_thread_id)?, - } - interp_ok(()) - } - - // Mark the joined thread as being joined so that we detect if other - // threads try to join it. - self.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined; - if !self.threads[joined_thread_id].state.is_terminated() { - trace!( - "{:?} blocked on {:?} when trying to join", - self.active_thread, joined_thread_id - ); - // The joined thread is still running, we need to wait for it. - // Unce we get unblocked, perform the appropriate synchronization. - self.block_thread( - BlockReason::Join(joined_thread_id), - None, - callback!( - @capture<'tcx> { - joined_thread_id: ThreadId, - } - |this, unblock: UnblockKind| { - assert_eq!(unblock, UnblockKind::Ready); - after_join(&mut this.machine.threads, joined_thread_id, &mut this.machine.data_race) - } - ), - ); - } else { - // The thread has already terminated - establish happens-before - after_join(self, joined_thread_id, data_race_handler)?; - } - interp_ok(()) - } - - /// Mark that the active thread tries to exclusively join the thread with `joined_thread_id`. - /// If the thread is already joined by another thread, it will throw UB - fn join_thread_exclusive( - &mut self, - joined_thread_id: ThreadId, - data_race_handler: &mut GlobalDataRaceHandler, - ) -> InterpResult<'tcx> { - if self.threads[joined_thread_id].join_status == ThreadJoinStatus::Joined { - throw_ub_format!("trying to join an already joined thread"); - } - - if joined_thread_id == self.active_thread { - throw_ub_format!("trying to join itself"); - } - - // Sanity check `join_status`. - assert!( - self.threads - .iter() - .all(|thread| { !thread.state.is_blocked_on(BlockReason::Join(joined_thread_id)) }), - "this thread already has threads waiting for its termination" - ); - - self.join_thread(joined_thread_id, data_race_handler) - } - /// Set the name of the given thread. pub fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) { self.threads[thread].thread_name = Some(new_thread_name); @@ -1114,20 +1032,102 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine.threads.detach_thread(thread_id, allow_terminated_joined) } - #[inline] - fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> { + /// Mark that the active thread tries to join the thread with `joined_thread_id`. + /// + /// When the join is successful (immediately, or as soon as the joined thread finishes), `success_retval` will be written to `return_dest`. + fn join_thread( + &mut self, + joined_thread_id: ThreadId, + success_retval: Scalar, + return_dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - this.machine.threads.join_thread(joined_thread_id, &mut this.machine.data_race)?; + let thread_mgr = &mut this.machine.threads; + if thread_mgr.threads[joined_thread_id].join_status == ThreadJoinStatus::Detached { + // On Windows this corresponds to joining on a closed handle. + throw_ub_format!("trying to join a detached thread"); + } + + fn after_join<'tcx>( + this: &mut InterpCx<'tcx, MiriMachine<'tcx>>, + joined_thread_id: ThreadId, + success_retval: Scalar, + return_dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let threads = &this.machine.threads; + match &mut this.machine.data_race { + GlobalDataRaceHandler::None => {} + GlobalDataRaceHandler::Vclocks(data_race) => + data_race.thread_joined(threads, joined_thread_id), + GlobalDataRaceHandler::Genmc(genmc_ctx) => + genmc_ctx.handle_thread_join(threads.active_thread, joined_thread_id)?, + } + this.write_scalar(success_retval, return_dest)?; + interp_ok(()) + } + + // Mark the joined thread as being joined so that we detect if other + // threads try to join it. + thread_mgr.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined; + if !thread_mgr.threads[joined_thread_id].state.is_terminated() { + trace!( + "{:?} blocked on {:?} when trying to join", + thread_mgr.active_thread, joined_thread_id + ); + // The joined thread is still running, we need to wait for it. + // Once we get unblocked, perform the appropriate synchronization and write the return value. + let dest = return_dest.clone(); + thread_mgr.block_thread( + BlockReason::Join(joined_thread_id), + None, + callback!( + @capture<'tcx> { + joined_thread_id: ThreadId, + dest: MPlaceTy<'tcx>, + success_retval: Scalar, + } + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + after_join(this, joined_thread_id, success_retval, &dest) + } + ), + ); + } else { + // The thread has already terminated - establish happens-before and write the return value. + after_join(this, joined_thread_id, success_retval, return_dest)?; + } interp_ok(()) } - #[inline] - fn join_thread_exclusive(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> { + /// Mark that the active thread tries to exclusively join the thread with `joined_thread_id`. + /// If the thread is already joined by another thread, it will throw UB. + /// + /// When the join is successful (immediately, or as soon as the joined thread finishes), `success_retval` will be written to `return_dest`. + fn join_thread_exclusive( + &mut self, + joined_thread_id: ThreadId, + success_retval: Scalar, + return_dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - this.machine - .threads - .join_thread_exclusive(joined_thread_id, &mut this.machine.data_race)?; - interp_ok(()) + let threads = &this.machine.threads.threads; + if threads[joined_thread_id].join_status == ThreadJoinStatus::Joined { + throw_ub_format!("trying to join an already joined thread"); + } + + if joined_thread_id == this.machine.threads.active_thread { + throw_ub_format!("trying to join itself"); + } + + // Sanity check `join_status`. + assert!( + threads + .iter() + .all(|thread| { !thread.state.is_blocked_on(BlockReason::Join(joined_thread_id)) }), + "this thread already has threads waiting for its termination" + ); + + this.join_thread(joined_thread_id, success_retval, return_dest) } #[inline] diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 54a7c1407ea..9ecbd31c5b9 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -132,7 +132,9 @@ pub enum NonHaltingDiagnostic { Int2Ptr { details: bool, }, - NativeCallSharedMem, + NativeCallSharedMem { + tracing: bool, + }, WeakMemoryOutdatedLoad { ptr: Pointer, }, @@ -627,7 +629,7 @@ impl<'tcx> MiriMachine<'tcx> { RejectedIsolatedOp(_) => ("operation rejected by isolation".to_string(), DiagLevel::Warning), Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning), - NativeCallSharedMem => + NativeCallSharedMem { .. } => ("sharing memory with a native function".to_string(), DiagLevel::Warning), ExternTypeReborrow => ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning), @@ -663,7 +665,8 @@ impl<'tcx> MiriMachine<'tcx> { ProgressReport { .. } => format!("progress report: current operation being executed is here"), Int2Ptr { .. } => format!("integer-to-pointer cast"), - NativeCallSharedMem => format!("sharing memory with a native function called via FFI"), + NativeCallSharedMem { .. } => + format!("sharing memory with a native function called via FFI"), WeakMemoryOutdatedLoad { ptr } => format!("weak memory emulation: outdated value returned from load at {ptr}"), ExternTypeReborrow => @@ -709,22 +712,41 @@ impl<'tcx> MiriMachine<'tcx> { } v } - NativeCallSharedMem => { - vec![ - note!( - "when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory" - ), - note!( - "in particular, Miri assumes that the native call initializes all memory it has access to" - ), - note!( - "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory" - ), - note!( - "what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free" - ), - ] - } + NativeCallSharedMem { tracing } => + if *tracing { + vec![ + note!( + "when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis" + ), + note!( + "in particular, Miri assumes that the native call initializes all memory it has written to" + ), + note!( + "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory" + ), + note!( + "what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free" + ), + note!( + "tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here" + ), + ] + } else { + vec![ + note!( + "when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory" + ), + note!( + "in particular, Miri assumes that the native call initializes all memory it has access to" + ), + note!( + "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory" + ), + note!( + "what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free" + ), + ] + }, ExternTypeReborrow => { assert!(self.borrow_tracker.as_ref().is_some_and(|b| { matches!( diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 04fe08b30ee..44612dae1ee 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -150,6 +150,8 @@ pub struct MiriConfig { pub retag_fields: RetagFields, /// The location of the shared object files to load when calling external functions pub native_lib: Vec<PathBuf>, + /// Whether to enable the new native lib tracing system. + pub native_lib_enable_tracing: bool, /// Run a garbage collector for BorTags every N basic blocks. pub gc_interval: u32, /// The number of CPUs to be reported by miri. @@ -199,6 +201,7 @@ impl Default for MiriConfig { report_progress: None, retag_fields: RetagFields::Yes, native_lib: vec![], + native_lib_enable_tracing: false, gc_interval: 10_000, num_cpus: 1, page_size: None, diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index fb34600fa37..ce4668c36c0 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -162,7 +162,7 @@ pub fn iter_exported_symbols<'tcx>( // We can ignore `_export_info` here: we are a Rust crate, and everything is exported // from a Rust crate. - for &(symbol, _export_info) in tcx.exported_symbols(cnum) { + for &(symbol, _export_info) in tcx.exported_non_generic_symbols(cnum) { if let ExportedSymbol::NonGeneric(def_id) = symbol { f(cnum, def_id)?; } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index ec77a162cdb..ed1851a19ae 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -191,7 +191,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; - let res = fixed_float_value(intrinsic_name, &[f]).unwrap_or_else(||{ + let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { // Using host floats (but it's fine, these operations do not have // guaranteed precision). let host = f.to_host(); @@ -235,7 +235,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; - let res = fixed_float_value(intrinsic_name, &[f]).unwrap_or_else(||{ + let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { // Using host floats (but it's fine, these operations do not have // guaranteed precision). let host = f.to_host(); @@ -312,7 +312,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; - let res = fixed_float_value(intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f1.to_host().powf(f2.to_host()).to_soft(); @@ -330,7 +330,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; - let res = fixed_float_value(intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f1.to_host().powf(f2.to_host()).to_soft(); @@ -349,7 +349,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f = this.read_scalar(f)?.to_f32()?; let i = this.read_scalar(i)?.to_i32()?; - let res = fixed_powi_float_value(f, i).unwrap_or_else(|| { + let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| { // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f.to_host().powi(i).to_soft(); @@ -367,7 +367,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f = this.read_scalar(f)?.to_f64()?; let i = this.read_scalar(i)?.to_i32()?; - let res = fixed_powi_float_value(f, i).unwrap_or_else(|| { + let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| { // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f.to_host().powi(i).to_soft(); @@ -496,52 +496,88 @@ fn apply_random_float_error_to_imm<'tcx>( /// - logf32, logf64, log2f32, log2f64, log10f32, log10f64 /// - powf32, powf64 /// +/// # Return +/// /// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard /// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error /// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying /// implementation. Returns `None` if no specific value is guaranteed. +/// +/// # Note +/// +/// For `powf*` operations of the form: +/// +/// - `(SNaN)^(±0)` +/// - `1^(SNaN)` +/// +/// The result is implementation-defined: +/// - musl returns for both `1.0` +/// - glibc returns for both `NaN` +/// +/// This discrepancy exists because SNaN handling is not consistently defined across platforms, +/// and the C standard leaves behavior for SNaNs unspecified. +/// +/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically. fn fixed_float_value<S: Semantics>( + ecx: &mut MiriInterpCx<'_>, intrinsic_name: &str, args: &[IeeeFloat<S>], ) -> Option<IeeeFloat<S>> { let one = IeeeFloat::<S>::one(); - match (intrinsic_name, args) { + Some(match (intrinsic_name, args) { // cos(+- 0) = 1 - ("cosf32" | "cosf64", [input]) if input.is_zero() => Some(one), + ("cosf32" | "cosf64", [input]) if input.is_zero() => one, // e^0 = 1 - ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => Some(one), - - // 1^y = 1 for any y, even a NaN. - ("powf32" | "powf64", [base, _]) if *base == one => Some(one), + ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one, // (-1)^(±INF) = 1 - ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => Some(one), + ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, + + // 1^y = 1 for any y, even a NaN + ("powf32" | "powf64", [base, exp]) if *base == one => { + let rng = ecx.machine.rng.get_mut(); + // SNaN exponents get special treatment: they might return 1, or a NaN. + let return_nan = exp.is_signaling() && ecx.machine.float_nondet && rng.random(); + // Handle both the musl and glibc cases non-deterministically. + if return_nan { ecx.generate_nan(args) } else { one } + } - // FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate - // the NaN. We should return either 1 or the NaN non-deterministically here. - // But for now, just handle them all the same. // x^(±0) = 1 for any x, even a NaN - ("powf32" | "powf64", [_, exp]) if exp.is_zero() => Some(one), + ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { + let rng = ecx.machine.rng.get_mut(); + // SNaN bases get special treatment: they might return 1, or a NaN. + let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random(); + // Handle both the musl and glibc cases non-deterministically. + if return_nan { ecx.generate_nan(args) } else { one } + } - // There are a lot of cases for fixed outputs according to the C Standard, but these are mainly INF or zero - // which are not affected by the applied error. - _ => None, - } + // There are a lot of cases for fixed outputs according to the C Standard, but these are + // mainly INF or zero which are not affected by the applied error. + _ => return None, + }) } -/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the C standard -/// (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`. -fn fixed_powi_float_value<S: Semantics>(base: IeeeFloat<S>, exp: i32) -> Option<IeeeFloat<S>> { - match (base.category(), exp) { - // x^0 = 1, if x is not a Signaling NaN - // FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate - // the NaN. We should return either 1 or the NaN non-deterministically here. - // But for now, just handle them all the same. - (_, 0) => Some(IeeeFloat::<S>::one()), - - _ => None, - } +/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the +/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`. +fn fixed_powi_float_value<S: Semantics>( + ecx: &mut MiriInterpCx<'_>, + base: IeeeFloat<S>, + exp: i32, +) -> Option<IeeeFloat<S>> { + Some(match exp { + 0 => { + let one = IeeeFloat::<S>::one(); + let rng = ecx.machine.rng.get_mut(); + let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); + // For SNaN treatment, we are consistent with `powf`above. + // (We wouldn't have two, unlike powf all implementations seem to agree for powi, + // but for now we are maximally conservative.) + if return_nan { ecx.generate_nan(&[base]) } else { one } + } + + _ => return None, + }) } /// Given an floating-point operation and a floating-point value, clamps the result to the output diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 52224f33c73..e63992aa95f 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -36,6 +36,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "ceil" | "floor" | "round" + | "round_ties_even" | "trunc" | "fsqrt" | "fsin" @@ -71,6 +72,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "ceil" => Op::Round(rustc_apfloat::Round::TowardPositive), "floor" => Op::Round(rustc_apfloat::Round::TowardNegative), "round" => Op::Round(rustc_apfloat::Round::NearestTiesToAway), + "round_ties_even" => Op::Round(rustc_apfloat::Round::NearestTiesToEven), "trunc" => Op::Round(rustc_apfloat::Round::TowardZero), "ctlz" => Op::Numeric(sym::ctlz), "ctpop" => Op::Numeric(sym::ctpop), diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index ea56582ee48..00f26a37f71 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -10,6 +10,7 @@ #![feature(variant_count)] #![feature(yeet_expr)] #![feature(nonzero_ops)] +#![cfg_attr(bootstrap, feature(nonnull_provenance))] #![feature(strict_overflow_ops)] #![feature(pointer_is_aligned_to)] #![feature(ptr_metadata)] @@ -99,6 +100,11 @@ pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _}; use rustc_middle::{bug, span_bug}; use tracing::{info, trace}; +//#[cfg(target_os = "linux")] +//pub mod native_lib { +// pub use crate::shims::{init_sv, register_retcode_sv}; +//} + // Type aliases that set the provenance parameter. pub type Pointer = interpret::Pointer<Option<machine::Provenance>>; pub type StrictPointer = interpret::Pointer<machine::Provenance>; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index b4d7db34efa..17c13a9e33c 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -285,7 +285,7 @@ impl interpret::Provenance for Provenance { } fn fmt(ptr: &interpret::Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (prov, addr) = ptr.into_parts(); // address is absolute + let (prov, addr) = ptr.into_raw_parts(); // offset is absolute address write!(f, "{:#x}", addr.bytes())?; if f.alternate() { write!(f, "{prov:#?}")?; @@ -1056,7 +1056,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // What's the offset between us and the promised alignment? let distance = offset.bytes().wrapping_sub(promised_offset.bytes()); // That must also be aligned. - if distance % align.bytes() == 0 { + if distance.is_multiple_of(align.bytes()) { // All looking good! None } else { @@ -1612,7 +1612,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx.machine.since_gc += 1; // Possibly report our progress. This will point at the terminator we are about to execute. if let Some(report_progress) = ecx.machine.report_progress { - if ecx.machine.basic_block_count % u64::from(report_progress) == 0 { + if ecx.machine.basic_block_count.is_multiple_of(u64::from(report_progress)) { ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport { block_count: ecx.machine.basic_block_count, }); diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs index b3d715db9cd..6adf1448648 100644 --- a/src/tools/miri/src/provenance_gc.rs +++ b/src/tools/miri/src/provenance_gc.rs @@ -68,15 +68,13 @@ impl VisitProvenance for Provenance { impl VisitProvenance for StrictPointer { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let (prov, _offset) = self.into_parts(); - prov.visit_provenance(visit); + self.provenance.visit_provenance(visit); } } impl VisitProvenance for Pointer { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let (prov, _offset) = self.into_parts(); - prov.visit_provenance(visit); + self.provenance.visit_provenance(visit); } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 416cb1ab55e..97070eb742f 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -411,7 +411,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { AlignFromBytesError::TooLarge(_) => Align::MAX, } }); - let (_, addr) = ptr.into_parts(); // we know the offset is absolute + let addr = ptr.addr(); // Cannot panic since `align` is a power of 2 and hence non-zero. if addr.bytes().strict_rem(align.bytes()) != 0 { throw_unsup_format!( diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index b498551ace3..f09fc546b3e 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -21,6 +21,8 @@ pub mod time; pub mod tls; pub use self::files::FdTable; +//#[cfg(target_os = "linux")] +//pub use self::native_lib::trace::{init_sv, register_retcode_sv}; pub use self::unix::{DirTable, EpollInterestTable}; /// What needs to be done after emulating an item (a shim or an intrinsic) is done. diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 030c2e36721..9c659f65e50 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -1,4 +1,9 @@ //! Implements calling functions from a native library. + +// FIXME: disabled since it fails to build on many targets. +//#[cfg(target_os = "linux")] +//pub mod trace; + use std::ops::Deref; use libffi::high::call as ffi; @@ -8,8 +13,15 @@ use rustc_middle::mir::interpret::Pointer; use rustc_middle::ty::{self as ty, IntTy, UintTy}; use rustc_span::Symbol; +//#[cfg(target_os = "linux")] +//use self::trace::Supervisor; use crate::*; +//#[cfg(target_os = "linux")] +//type CallResult<'tcx> = InterpResult<'tcx, (ImmTy<'tcx>, Option<self::trace::messages::MemEvents>)>; +//#[cfg(not(target_os = "linux"))] +type CallResult<'tcx> = InterpResult<'tcx, (ImmTy<'tcx>, Option<!>)>; + impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Call native host function and return the output as an immediate. @@ -19,71 +31,93 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ptr: CodePtr, libffi_args: Vec<libffi::high::Arg<'a>>, - ) -> InterpResult<'tcx, ImmTy<'tcx>> { + ) -> CallResult<'tcx> { let this = self.eval_context_mut(); + //#[cfg(target_os = "linux")] + //let alloc = this.machine.allocator.as_ref().unwrap(); + + // SAFETY: We don't touch the machine memory past this point. + //#[cfg(target_os = "linux")] + //let (guard, stack_ptr) = unsafe { Supervisor::start_ffi(alloc) }; // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value // as the specified primitive integer type - let scalar = match dest.layout.ty.kind() { - // ints - ty::Int(IntTy::I8) => { - // Unsafe because of the call to native code. - // Because this is calling a C function it is not necessarily sound, - // but there is no way around this and we've checked as much as we can. - let x = unsafe { ffi::call::<i8>(ptr, libffi_args.as_slice()) }; - Scalar::from_i8(x) - } - ty::Int(IntTy::I16) => { - let x = unsafe { ffi::call::<i16>(ptr, libffi_args.as_slice()) }; - Scalar::from_i16(x) - } - ty::Int(IntTy::I32) => { - let x = unsafe { ffi::call::<i32>(ptr, libffi_args.as_slice()) }; - Scalar::from_i32(x) - } - ty::Int(IntTy::I64) => { - let x = unsafe { ffi::call::<i64>(ptr, libffi_args.as_slice()) }; - Scalar::from_i64(x) - } - ty::Int(IntTy::Isize) => { - let x = unsafe { ffi::call::<isize>(ptr, libffi_args.as_slice()) }; - Scalar::from_target_isize(x.try_into().unwrap(), this) - } - // uints - ty::Uint(UintTy::U8) => { - let x = unsafe { ffi::call::<u8>(ptr, libffi_args.as_slice()) }; - Scalar::from_u8(x) - } - ty::Uint(UintTy::U16) => { - let x = unsafe { ffi::call::<u16>(ptr, libffi_args.as_slice()) }; - Scalar::from_u16(x) - } - ty::Uint(UintTy::U32) => { - let x = unsafe { ffi::call::<u32>(ptr, libffi_args.as_slice()) }; - Scalar::from_u32(x) - } - ty::Uint(UintTy::U64) => { - let x = unsafe { ffi::call::<u64>(ptr, libffi_args.as_slice()) }; - Scalar::from_u64(x) - } - ty::Uint(UintTy::Usize) => { - let x = unsafe { ffi::call::<usize>(ptr, libffi_args.as_slice()) }; - Scalar::from_target_usize(x.try_into().unwrap(), this) - } - // Functions with no declared return type (i.e., the default return) - // have the output_type `Tuple([])`. - ty::Tuple(t_list) if (*t_list).deref().is_empty() => { - unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) }; - return interp_ok(ImmTy::uninit(dest.layout)); - } - ty::RawPtr(..) => { - let x = unsafe { ffi::call::<*const ()>(ptr, libffi_args.as_slice()) }; - let ptr = Pointer::new(Provenance::Wildcard, Size::from_bytes(x.addr())); - Scalar::from_pointer(ptr, this) - } - _ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name), + let res = 'res: { + let scalar = match dest.layout.ty.kind() { + // ints + ty::Int(IntTy::I8) => { + // Unsafe because of the call to native code. + // Because this is calling a C function it is not necessarily sound, + // but there is no way around this and we've checked as much as we can. + let x = unsafe { ffi::call::<i8>(ptr, libffi_args.as_slice()) }; + Scalar::from_i8(x) + } + ty::Int(IntTy::I16) => { + let x = unsafe { ffi::call::<i16>(ptr, libffi_args.as_slice()) }; + Scalar::from_i16(x) + } + ty::Int(IntTy::I32) => { + let x = unsafe { ffi::call::<i32>(ptr, libffi_args.as_slice()) }; + Scalar::from_i32(x) + } + ty::Int(IntTy::I64) => { + let x = unsafe { ffi::call::<i64>(ptr, libffi_args.as_slice()) }; + Scalar::from_i64(x) + } + ty::Int(IntTy::Isize) => { + let x = unsafe { ffi::call::<isize>(ptr, libffi_args.as_slice()) }; + Scalar::from_target_isize(x.try_into().unwrap(), this) + } + // uints + ty::Uint(UintTy::U8) => { + let x = unsafe { ffi::call::<u8>(ptr, libffi_args.as_slice()) }; + Scalar::from_u8(x) + } + ty::Uint(UintTy::U16) => { + let x = unsafe { ffi::call::<u16>(ptr, libffi_args.as_slice()) }; + Scalar::from_u16(x) + } + ty::Uint(UintTy::U32) => { + let x = unsafe { ffi::call::<u32>(ptr, libffi_args.as_slice()) }; + Scalar::from_u32(x) + } + ty::Uint(UintTy::U64) => { + let x = unsafe { ffi::call::<u64>(ptr, libffi_args.as_slice()) }; + Scalar::from_u64(x) + } + ty::Uint(UintTy::Usize) => { + let x = unsafe { ffi::call::<usize>(ptr, libffi_args.as_slice()) }; + Scalar::from_target_usize(x.try_into().unwrap(), this) + } + // Functions with no declared return type (i.e., the default return) + // have the output_type `Tuple([])`. + ty::Tuple(t_list) if (*t_list).deref().is_empty() => { + unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) }; + break 'res interp_ok(ImmTy::uninit(dest.layout)); + } + ty::RawPtr(..) => { + let x = unsafe { ffi::call::<*const ()>(ptr, libffi_args.as_slice()) }; + let ptr = Pointer::new(Provenance::Wildcard, Size::from_bytes(x.addr())); + Scalar::from_pointer(ptr, this) + } + _ => + break 'res Err(err_unsup_format!( + "unsupported return type for native call: {:?}", + link_name + )) + .into(), + }; + interp_ok(ImmTy::from_scalar(scalar, dest.layout)) }; - interp_ok(ImmTy::from_scalar(scalar, dest.layout)) + + // SAFETY: We got the guard and stack pointer from start_ffi, and + // the allocator is the same + //#[cfg(target_os = "linux")] + //let events = unsafe { Supervisor::end_ffi(alloc, guard, stack_ptr) }; + //#[cfg(not(target_os = "linux"))] + let events = None; + + interp_ok((res?, events)) } /// Get the pointer to the function of the specified name in the shared object file, @@ -179,7 +213,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The first time this happens, print a warning. if !this.machine.native_call_mem_warned.replace(true) { // Newly set, so first time we get here. - this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem); + this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { + //#[cfg(target_os = "linux")] + //tracing: self::trace::Supervisor::is_enabled(), + //#[cfg(not(target_os = "linux"))] + tracing: false, + }); } this.expose_provenance(prov)?; @@ -196,7 +235,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .collect::<Vec<libffi::high::Arg<'_>>>(); // Call the function and store output, depending on return type in the function signature. - let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?; + let (ret, maybe_memevents) = + this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?; + + if cfg!(target_os = "linux") + && let Some(events) = maybe_memevents + { + trace!("Registered FFI events:\n{events:#0x?}"); + } + this.write_immediate(*ret, dest)?; interp_ok(true) } diff --git a/src/tools/miri/src/shims/native_lib/trace/child.rs b/src/tools/miri/src/shims/native_lib/trace/child.rs new file mode 100644 index 00000000000..4961e875c77 --- /dev/null +++ b/src/tools/miri/src/shims/native_lib/trace/child.rs @@ -0,0 +1,251 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use ipc_channel::ipc; +use nix::sys::{ptrace, signal}; +use nix::unistd; + +use super::CALLBACK_STACK_SIZE; +use super::messages::{Confirmation, MemEvents, StartFfiInfo, TraceRequest}; +use super::parent::{ChildListener, sv_loop}; +use crate::alloc::isolated_alloc::IsolatedAlloc; + +static SUPERVISOR: std::sync::Mutex<Option<Supervisor>> = std::sync::Mutex::new(None); + +/// The main means of communication between the child and parent process, +/// allowing the former to send requests and get info from the latter. +pub struct Supervisor { + /// Sender for FFI-mode-related requests. + message_tx: ipc::IpcSender<TraceRequest>, + /// Used for synchronisation, allowing us to receive confirmation that the + /// parent process has handled the request from `message_tx`. + confirm_rx: ipc::IpcReceiver<Confirmation>, + /// Receiver for memory acceses that ocurred during the FFI call. + event_rx: ipc::IpcReceiver<MemEvents>, +} + +/// Marker representing that an error occurred during creation of the supervisor. +#[derive(Debug)] +pub struct SvInitError; + +impl Supervisor { + /// Returns `true` if the supervisor process exists, and `false` otherwise. + pub fn is_enabled() -> bool { + SUPERVISOR.lock().unwrap().is_some() + } + + /// Begins preparations for doing an FFI call. This should be called at + /// the last possible moment before entering said call. `alloc` points to + /// the allocator which handed out the memory used for this machine. + /// + /// As this locks the supervisor via a mutex, no other threads may enter FFI + /// until this one returns and its guard is dropped via `end_ffi`. The + /// pointer returned should be passed to `end_ffi` to avoid a memory leak. + /// + /// SAFETY: The resulting guard must be dropped *via `end_ffi`* immediately + /// after the desired call has concluded. + pub unsafe fn start_ffi( + alloc: &Rc<RefCell<IsolatedAlloc>>, + ) -> (std::sync::MutexGuard<'static, Option<Supervisor>>, Option<*mut [u8; CALLBACK_STACK_SIZE]>) + { + let mut sv_guard = SUPERVISOR.lock().unwrap(); + // If the supervisor is not initialised for whatever reason, fast-fail. + // This might be desired behaviour, as even on platforms where ptracing + // is not implemented it enables us to enforce that only one FFI call + // happens at a time. + let Some(sv) = sv_guard.take() else { + return (sv_guard, None); + }; + + // Get pointers to all the pages the supervisor must allow accesses in + // and prepare the callback stack. + let page_ptrs = alloc.borrow().pages(); + let raw_stack_ptr: *mut [u8; CALLBACK_STACK_SIZE] = + Box::leak(Box::new([0u8; CALLBACK_STACK_SIZE])).as_mut_ptr().cast(); + let stack_ptr = raw_stack_ptr.expose_provenance(); + let start_info = StartFfiInfo { page_ptrs, stack_ptr }; + + // SAFETY: We do not access machine memory past this point until the + // supervisor is ready to allow it. + unsafe { + if alloc.borrow_mut().prepare_ffi().is_err() { + // Don't mess up unwinding by maybe leaving the memory partly protected + alloc.borrow_mut().unprep_ffi(); + panic!("Cannot protect memory for FFI call!"); + } + } + + // Send over the info. + // NB: if we do not wait to receive a blank confirmation response, it is + // possible that the supervisor is alerted of the SIGSTOP *before* it has + // actually received the start_info, thus deadlocking! This way, we can + // enforce an ordering for these events. + sv.message_tx.send(TraceRequest::StartFfi(start_info)).unwrap(); + sv.confirm_rx.recv().unwrap(); + *sv_guard = Some(sv); + // We need to be stopped for the supervisor to be able to make certain + // modifications to our memory - simply waiting on the recv() doesn't + // count. + signal::raise(signal::SIGSTOP).unwrap(); + (sv_guard, Some(raw_stack_ptr)) + } + + /// Undoes FFI-related preparations, allowing Miri to continue as normal, then + /// gets the memory accesses and changes performed during the FFI call. Note + /// that this may include some spurious accesses done by `libffi` itself in + /// the process of executing the function call. + /// + /// SAFETY: The `sv_guard` and `raw_stack_ptr` passed must be the same ones + /// received by a prior call to `start_ffi`, and the allocator must be the + /// one passed to it also. + pub unsafe fn end_ffi( + alloc: &Rc<RefCell<IsolatedAlloc>>, + mut sv_guard: std::sync::MutexGuard<'static, Option<Supervisor>>, + raw_stack_ptr: Option<*mut [u8; CALLBACK_STACK_SIZE]>, + ) -> Option<MemEvents> { + // We can't use IPC channels here to signal that FFI mode has ended, + // since they might allocate memory which could get us stuck in a SIGTRAP + // with no easy way out! While this could be worked around, it is much + // simpler and more robust to simply use the signals which are left for + // arbitrary usage. Since this will block until we are continued by the + // supervisor, we can assume past this point that everything is back to + // normal. + signal::raise(signal::SIGUSR1).unwrap(); + + // This is safe! It just sets memory to normal expected permissions. + alloc.borrow_mut().unprep_ffi(); + + // If this is `None`, then `raw_stack_ptr` is None and does not need to + // be deallocated (and there's no need to worry about the guard, since + // it contains nothing). + let sv = sv_guard.take()?; + // SAFETY: Caller upholds that this pointer was allocated as a box with + // this type. + unsafe { + drop(Box::from_raw(raw_stack_ptr.unwrap())); + } + // On the off-chance something really weird happens, don't block forever. + let ret = sv + .event_rx + .try_recv_timeout(std::time::Duration::from_secs(5)) + .map_err(|e| { + match e { + ipc::TryRecvError::IpcError(_) => (), + ipc::TryRecvError::Empty => + eprintln!("Waiting for accesses from supervisor timed out!"), + } + }) + .ok(); + // Do *not* leave the supervisor empty, or else we might get another fork... + *sv_guard = Some(sv); + ret + } +} + +/// Initialises the supervisor process. If this function errors, then the +/// supervisor process could not be created successfully; else, the caller +/// is now the child process and can communicate via `start_ffi`/`end_ffi`, +/// receiving back events through `get_events`. +/// +/// # Safety +/// The invariants for `fork()` must be upheld by the caller. +pub unsafe fn init_sv() -> Result<(), SvInitError> { + // FIXME: Much of this could be reimplemented via the mitosis crate if we upstream the + // relevant missing bits. + + // On Linux, this will check whether ptrace is fully disabled by the Yama module. + // If Yama isn't running or we're not on Linux, we'll still error later, but + // this saves a very expensive fork call. + let ptrace_status = std::fs::read_to_string("/proc/sys/kernel/yama/ptrace_scope"); + if let Ok(stat) = ptrace_status { + if let Some(stat) = stat.chars().next() { + // Fast-error if ptrace is fully disabled on the system. + if stat == '3' { + return Err(SvInitError); + } + } + } + + // Initialise the supervisor if it isn't already, placing it into SUPERVISOR. + let mut lock = SUPERVISOR.lock().unwrap(); + if lock.is_some() { + return Ok(()); + } + + // Prepare the IPC channels we need. + let (message_tx, message_rx) = ipc::channel().unwrap(); + let (confirm_tx, confirm_rx) = ipc::channel().unwrap(); + let (event_tx, event_rx) = ipc::channel().unwrap(); + // SAFETY: Calling sysconf(_SC_PAGESIZE) is always safe and cannot error. + let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) }.try_into().unwrap(); + super::parent::PAGE_SIZE.store(page_size, std::sync::atomic::Ordering::Relaxed); + + unsafe { + // TODO: Maybe use clone3() instead for better signalling of when the child exits? + // SAFETY: Caller upholds that only one thread exists. + match unistd::fork().unwrap() { + unistd::ForkResult::Parent { child } => { + // If somehow another thread does exist, prevent it from accessing the lock + // and thus breaking our safety invariants. + std::mem::forget(lock); + // The child process is free to unwind, so we won't to avoid doubly freeing + // system resources. + let init = std::panic::catch_unwind(|| { + let listener = + ChildListener { message_rx, attached: false, override_retcode: None }; + // Trace as many things as possible, to be able to handle them as needed. + let options = ptrace::Options::PTRACE_O_TRACESYSGOOD + | ptrace::Options::PTRACE_O_TRACECLONE + | ptrace::Options::PTRACE_O_TRACEFORK; + // Attach to the child process without stopping it. + match ptrace::seize(child, options) { + // Ptrace works :D + Ok(_) => { + let code = sv_loop(listener, child, event_tx, confirm_tx).unwrap_err(); + // If a return code of 0 is not explicitly given, assume something went + // wrong and return 1. + std::process::exit(code.0.unwrap_or(1)) + } + // Ptrace does not work and we failed to catch that. + Err(_) => { + // If we can't ptrace, Miri continues being the parent. + signal::kill(child, signal::SIGKILL).unwrap(); + SvInitError + } + } + }); + match init { + // The "Ok" case means that we couldn't ptrace. + Ok(e) => return Err(e), + Err(p) => { + eprintln!("Supervisor process panicked!\n{p:?}"); + std::process::exit(1); + } + } + } + unistd::ForkResult::Child => { + // Make sure we never get orphaned and stuck in SIGSTOP or similar + // SAFETY: prctl PR_SET_PDEATHSIG is always safe to call. + let ret = libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM); + assert_eq!(ret, 0); + // First make sure the parent succeeded with ptracing us! + signal::raise(signal::SIGSTOP).unwrap(); + // If we're the child process, save the supervisor info. + *lock = Some(Supervisor { message_tx, confirm_rx, event_rx }); + } + } + } + Ok(()) +} + +/// Instruct the supervisor process to return a particular code. Useful if for +/// whatever reason this code fails to be intercepted normally. In the case of +/// `abort_if_errors()` used in `bin/miri.rs`, the return code is erroneously +/// given as a 0 if this is not used. +pub fn register_retcode_sv(code: i32) { + let mut sv_guard = SUPERVISOR.lock().unwrap(); + if let Some(sv) = sv_guard.take() { + sv.message_tx.send(TraceRequest::OverrideRetcode(code)).unwrap(); + *sv_guard = Some(sv); + } +} diff --git a/src/tools/miri/src/shims/native_lib/trace/messages.rs b/src/tools/miri/src/shims/native_lib/trace/messages.rs new file mode 100644 index 00000000000..8a83dab5c09 --- /dev/null +++ b/src/tools/miri/src/shims/native_lib/trace/messages.rs @@ -0,0 +1,80 @@ +//! Houses the types that are directly sent across the IPC channels. +//! +//! The overall structure of a traced FFI call, from the child process's POV, is +//! as follows: +//! ``` +//! message_tx.send(TraceRequest::StartFfi); +//! confirm_rx.recv(); +//! raise(SIGSTOP); +//! /* do ffi call */ +//! raise(SIGUSR1); // morally equivalent to some kind of "TraceRequest::EndFfi" +//! let events = event_rx.recv(); +//! ``` +//! `TraceRequest::OverrideRetcode` can be sent at any point in the above, including +//! before or after all of them. +//! +//! NB: sending these events out of order, skipping steps, etc. will result in +//! unspecified behaviour from the supervisor process, so use the abstractions +//! in `super::child` (namely `start_ffi()` and `end_ffi()`) to handle this. It is +//! trivially easy to cause a deadlock or crash by messing this up! + +use std::ops::Range; + +/// An IPC request sent by the child process to the parent. +/// +/// The sender for this channel should live on the child process. +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub enum TraceRequest { + /// Requests that tracing begins. Following this being sent, the child must + /// wait to receive a `Confirmation` on the respective channel and then + /// `raise(SIGSTOP)`. + /// + /// To avoid possible issues while allocating memory for IPC channels, ending + /// the tracing is instead done via `raise(SIGUSR1)`. + StartFfi(StartFfiInfo), + /// Manually overrides the code that the supervisor will return upon exiting. + /// Once set, it is permanent. This can be called again to change the value. + OverrideRetcode(i32), +} + +/// Information needed to begin tracing. +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct StartFfiInfo { + /// A vector of page addresses. These should have been automatically obtained + /// with `IsolatedAlloc::pages` and prepared with `IsolatedAlloc::prepare_ffi`. + pub page_ptrs: Vec<usize>, + /// The address of an allocation that can serve as a temporary stack. + /// This should be a leaked `Box<[u8; CALLBACK_STACK_SIZE]>` cast to an int. + pub stack_ptr: usize, +} + +/// A marker type confirming that the supervisor has received the request to begin +/// tracing and is now waiting for a `SIGSTOP`. +/// +/// The sender for this channel should live on the parent process. +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct Confirmation; + +/// The final results of an FFI trace, containing every relevant event detected +/// by the tracer. Sent by the supervisor after receiving a `SIGUSR1` signal. +/// +/// The sender for this channel should live on the parent process. +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct MemEvents { + /// An ordered list of memory accesses that occurred. These should be assumed + /// to be overcautious; that is, if the size of an access is uncertain it is + /// pessimistically rounded up, and if the type (read/write/both) is uncertain + /// it is reported as whatever would be safest to assume; i.e. a read + maybe-write + /// becomes a read + write, etc. + pub acc_events: Vec<AccessEvent>, +} + +/// A single memory access, conservatively overestimated +/// in case of ambiguity. +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub enum AccessEvent { + /// A read may have occurred on no more than the specified address range. + Read(Range<usize>), + /// A write may have occurred on no more than the specified address range. + Write(Range<usize>), +} diff --git a/src/tools/miri/src/shims/native_lib/trace/mod.rs b/src/tools/miri/src/shims/native_lib/trace/mod.rs new file mode 100644 index 00000000000..174b06b3ac5 --- /dev/null +++ b/src/tools/miri/src/shims/native_lib/trace/mod.rs @@ -0,0 +1,8 @@ +mod child; +pub mod messages; +mod parent; + +pub use self::child::{Supervisor, init_sv, register_retcode_sv}; + +/// The size of the temporary stack we use for callbacks that the server executes in the client. +const CALLBACK_STACK_SIZE: usize = 1024; diff --git a/src/tools/miri/src/shims/native_lib/trace/parent.rs b/src/tools/miri/src/shims/native_lib/trace/parent.rs new file mode 100644 index 00000000000..dfb0b35da69 --- /dev/null +++ b/src/tools/miri/src/shims/native_lib/trace/parent.rs @@ -0,0 +1,660 @@ +use std::sync::atomic::{AtomicPtr, AtomicUsize}; + +use ipc_channel::ipc; +use nix::sys::{ptrace, signal, wait}; +use nix::unistd; + +use super::CALLBACK_STACK_SIZE; +use super::messages::{AccessEvent, Confirmation, MemEvents, StartFfiInfo, TraceRequest}; + +/// The flags to use when calling `waitid()`. +/// Since bitwise or on the nix version of these flags is implemented as a trait, +/// this cannot be const directly so we do it this way. +const WAIT_FLAGS: wait::WaitPidFlag = + wait::WaitPidFlag::from_bits_truncate(libc::WUNTRACED | libc::WEXITED); + +/// Arch-specific maximum size a single access might perform. x86 value is set +/// assuming nothing bigger than AVX-512 is available. +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +const ARCH_MAX_ACCESS_SIZE: usize = 64; +/// The largest arm64 simd instruction operates on 16 bytes. +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +const ARCH_MAX_ACCESS_SIZE: usize = 16; + +/// The default word size on a given platform, in bytes. +#[cfg(any(target_arch = "x86", target_arch = "arm"))] +const ARCH_WORD_SIZE: usize = 4; +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +const ARCH_WORD_SIZE: usize = 8; + +/// The address of the page set to be edited, initialised to a sentinel null +/// pointer. +static PAGE_ADDR: AtomicPtr<u8> = AtomicPtr::new(std::ptr::null_mut()); +/// The host pagesize, initialised to a sentinel zero value. +pub static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); +/// How many consecutive pages to unprotect. 1 by default, unlikely to be set +/// higher than 2. +static PAGE_COUNT: AtomicUsize = AtomicUsize::new(1); + +/// Allows us to get common arguments from the `user_regs_t` across architectures. +/// Normally this would land us ABI hell, but thankfully all of our usecases +/// consist of functions with a small number of register-sized integer arguments. +/// See <https://man7.org/linux/man-pages/man2/syscall.2.html> for sources. +trait ArchIndependentRegs { + /// Gets the address of the instruction pointer. + fn ip(&self) -> usize; + /// Set the instruction pointer; remember to also set the stack pointer, or + /// else the stack might get messed up! + fn set_ip(&mut self, ip: usize); + /// Set the stack pointer, ideally to a zeroed-out area. + fn set_sp(&mut self, sp: usize); +} + +// It's fine / desirable behaviour for values to wrap here, we care about just +// preserving the bit pattern. +#[cfg(target_arch = "x86_64")] +#[expect(clippy::as_conversions)] +#[rustfmt::skip] +impl ArchIndependentRegs for libc::user_regs_struct { + #[inline] + fn ip(&self) -> usize { self.rip as _ } + #[inline] + fn set_ip(&mut self, ip: usize) { self.rip = ip as _ } + #[inline] + fn set_sp(&mut self, sp: usize) { self.rsp = sp as _ } +} + +#[cfg(target_arch = "x86")] +#[expect(clippy::as_conversions)] +#[rustfmt::skip] +impl ArchIndependentRegs for libc::user_regs_struct { + #[inline] + fn ip(&self) -> usize { self.eip as _ } + #[inline] + fn set_ip(&mut self, ip: usize) { self.eip = ip as _ } + #[inline] + fn set_sp(&mut self, sp: usize) { self.esp = sp as _ } +} + +#[cfg(target_arch = "aarch64")] +#[expect(clippy::as_conversions)] +#[rustfmt::skip] +impl ArchIndependentRegs for libc::user_regs_struct { + #[inline] + fn ip(&self) -> usize { self.pc as _ } + #[inline] + fn set_ip(&mut self, ip: usize) { self.pc = ip as _ } + #[inline] + fn set_sp(&mut self, sp: usize) { self.sp = sp as _ } +} + +/// A unified event representing something happening on the child process. Wraps +/// `nix`'s `WaitStatus` and our custom signals so it can all be done with one +/// `match` statement. +pub enum ExecEvent { + /// Child process requests that we begin monitoring it. + Start(StartFfiInfo), + /// Child requests that we stop monitoring and pass over the events we + /// detected. + End, + /// The child process with the specified pid was stopped by the given signal. + Status(unistd::Pid, signal::Signal), + /// The child process with the specified pid entered or existed a syscall. + Syscall(unistd::Pid), + /// A child process exited or was killed; if we have a return code, it is + /// specified. + Died(Option<i32>), +} + +/// A listener for the FFI start info channel along with relevant state. +pub struct ChildListener { + /// The matching channel for the child's `Supervisor` struct. + pub message_rx: ipc::IpcReceiver<TraceRequest>, + /// Whether an FFI call is currently ongoing. + pub attached: bool, + /// If `Some`, overrides the return code with the given value. + pub override_retcode: Option<i32>, +} + +impl Iterator for ChildListener { + type Item = ExecEvent; + + // Allows us to monitor the child process by just iterating over the listener. + // NB: This should never return None! + fn next(&mut self) -> Option<Self::Item> { + // Do not block if the child has nothing to report for `waitid`. + let opts = WAIT_FLAGS | wait::WaitPidFlag::WNOHANG; + loop { + // Listen to any child, not just the main one. Important if we want + // to allow the C code to fork further, along with being a bit of + // defensive programming since Linux sometimes assigns threads of + // the same process different PIDs with unpredictable rules... + match wait::waitid(wait::Id::All, opts) { + Ok(stat) => + match stat { + // Child exited normally with a specific code set. + wait::WaitStatus::Exited(_, code) => { + let code = self.override_retcode.unwrap_or(code); + return Some(ExecEvent::Died(Some(code))); + } + // Child was killed by a signal, without giving a code. + wait::WaitStatus::Signaled(_, _, _) => + return Some(ExecEvent::Died(self.override_retcode)), + // Child entered a syscall. Since we're always technically + // tracing, only pass this along if we're actively + // monitoring the child. + wait::WaitStatus::PtraceSyscall(pid) => + if self.attached { + return Some(ExecEvent::Syscall(pid)); + }, + // Child with the given pid was stopped by the given signal. + // It's somewhat dubious when this is returned instead of + // WaitStatus::Stopped, but for our purposes they are the + // same thing. + wait::WaitStatus::PtraceEvent(pid, signal, _) => + if self.attached { + // This is our end-of-FFI signal! + if signal == signal::SIGUSR1 { + self.attached = false; + return Some(ExecEvent::End); + } else { + return Some(ExecEvent::Status(pid, signal)); + } + } else { + // Just pass along the signal. + ptrace::cont(pid, signal).unwrap(); + }, + // Child was stopped at the given signal. Same logic as for + // WaitStatus::PtraceEvent. + wait::WaitStatus::Stopped(pid, signal) => + if self.attached { + if signal == signal::SIGUSR1 { + self.attached = false; + return Some(ExecEvent::End); + } else { + return Some(ExecEvent::Status(pid, signal)); + } + } else { + ptrace::cont(pid, signal).unwrap(); + }, + _ => (), + }, + // This case should only trigger if all children died and we + // somehow missed that, but it's best we not allow any room + // for deadlocks. + Err(_) => return Some(ExecEvent::Died(None)), + } + + // Similarly, do a non-blocking poll of the IPC channel. + if let Ok(req) = self.message_rx.try_recv() { + match req { + TraceRequest::StartFfi(info) => + // Should never trigger - but better to panic explicitly than deadlock! + if self.attached { + panic!("Attempting to begin FFI multiple times!"); + } else { + self.attached = true; + return Some(ExecEvent::Start(info)); + }, + TraceRequest::OverrideRetcode(code) => self.override_retcode = Some(code), + } + } + + // Not ideal, but doing anything else might sacrifice performance. + std::thread::yield_now(); + } + } +} + +/// An error came up while waiting on the child process to do something. +/// It likely died, with this return code if we have one. +#[derive(Debug)] +pub struct ExecEnd(pub Option<i32>); + +/// This is the main loop of the supervisor process. It runs in a separate +/// process from the rest of Miri (but because we fork, addresses for anything +/// created before the fork - like statics - are the same). +pub fn sv_loop( + listener: ChildListener, + init_pid: unistd::Pid, + event_tx: ipc::IpcSender<MemEvents>, + confirm_tx: ipc::IpcSender<Confirmation>, +) -> Result<!, ExecEnd> { + // Get the pagesize set and make sure it isn't still on the zero sentinel value! + let page_size = PAGE_SIZE.load(std::sync::atomic::Ordering::Relaxed); + assert_ne!(page_size, 0); + + // Things that we return to the child process. + let mut acc_events = Vec::new(); + + // Memory allocated for the MiriMachine. + let mut ch_pages = Vec::new(); + let mut ch_stack = None; + + // An instance of the Capstone disassembler, so we don't spawn one on every access. + let cs = get_disasm(); + + // The pid of the last process we interacted with, used by default if we don't have a + // reason to use a different one. + let mut curr_pid = init_pid; + + // There's an initial sigstop we need to deal with. + wait_for_signal(Some(curr_pid), signal::SIGSTOP, false)?; + ptrace::cont(curr_pid, None).unwrap(); + + for evt in listener { + match evt { + // start_ffi was called by the child, so prep memory. + ExecEvent::Start(ch_info) => { + // All the pages that the child process is "allowed to" access. + ch_pages = ch_info.page_ptrs; + // And the temporary callback stack it allocated for us to use later. + ch_stack = Some(ch_info.stack_ptr); + + // We received the signal and are no longer in the main listener loop, + // so we can let the child move on to the end of start_ffi where it will + // raise a SIGSTOP. We need it to be signal-stopped *and waited for* in + // order to do most ptrace operations! + confirm_tx.send(Confirmation).unwrap(); + // We can't trust simply calling `Pid::this()` in the child process to give the right + // PID for us, so we get it this way. + curr_pid = wait_for_signal(None, signal::SIGSTOP, false).unwrap(); + + ptrace::syscall(curr_pid, None).unwrap(); + } + // end_ffi was called by the child. + ExecEvent::End => { + // Hand over the access info we traced. + event_tx.send(MemEvents { acc_events }).unwrap(); + // And reset our values. + acc_events = Vec::new(); + ch_stack = None; + + // No need to monitor syscalls anymore, they'd just be ignored. + ptrace::cont(curr_pid, None).unwrap(); + } + // Child process was stopped by a signal + ExecEvent::Status(pid, signal) => + match signal { + // If it was a segfault, check if it was an artificial one + // caused by it trying to access the MiriMachine memory. + signal::SIGSEGV => + handle_segfault( + pid, + &ch_pages, + ch_stack.unwrap(), + page_size, + &cs, + &mut acc_events, + )?, + // Something weird happened. + _ => { + eprintln!("Process unexpectedly got {signal}; continuing..."); + // In case we're not tracing + if ptrace::syscall(pid, None).is_err() { + // If *this* fails too, something really weird happened + // and it's probably best to just panic. + signal::kill(pid, signal::SIGCONT).unwrap(); + } + } + }, + // Child entered a syscall; we wait for exits inside of this, so it + // should never trigger on return from a syscall we care about. + ExecEvent::Syscall(pid) => { + ptrace::syscall(pid, None).unwrap(); + } + ExecEvent::Died(code) => { + return Err(ExecEnd(code)); + } + } + } + + unreachable!() +} + +/// Spawns a Capstone disassembler for the host architecture. +#[rustfmt::skip] +fn get_disasm() -> capstone::Capstone { + use capstone::prelude::*; + let cs_pre = Capstone::new(); + { + #[cfg(target_arch = "x86_64")] + {cs_pre.x86().mode(arch::x86::ArchMode::Mode64)} + #[cfg(target_arch = "x86")] + {cs_pre.x86().mode(arch::x86::ArchMode::Mode32)} + #[cfg(target_arch = "aarch64")] + {cs_pre.arm64().mode(arch::arm64::ArchMode::Arm)} + #[cfg(target_arch = "arm")] + {cs_pre.arm().mode(arch::arm::ArchMode::Arm)} + } + .detail(true) + .build() + .unwrap() +} + +/// Waits for `wait_signal`. If `init_cont`, it will first do a `ptrace::cont`. +/// We want to avoid that in some cases, like at the beginning of FFI. +/// +/// If `pid` is `None`, only one wait will be done and `init_cont` should be false. +fn wait_for_signal( + pid: Option<unistd::Pid>, + wait_signal: signal::Signal, + init_cont: bool, +) -> Result<unistd::Pid, ExecEnd> { + if init_cont { + ptrace::cont(pid.unwrap(), None).unwrap(); + } + // Repeatedly call `waitid` until we get the signal we want, or the process dies. + loop { + let wait_id = match pid { + Some(pid) => wait::Id::Pid(pid), + None => wait::Id::All, + }; + let stat = wait::waitid(wait_id, WAIT_FLAGS).map_err(|_| ExecEnd(None))?; + let (signal, pid) = match stat { + // Report the cause of death, if we know it. + wait::WaitStatus::Exited(_, code) => { + return Err(ExecEnd(Some(code))); + } + wait::WaitStatus::Signaled(_, _, _) => return Err(ExecEnd(None)), + wait::WaitStatus::Stopped(pid, signal) => (signal, pid), + wait::WaitStatus::PtraceEvent(pid, signal, _) => (signal, pid), + // This covers PtraceSyscall and variants that are impossible with + // the flags set (e.g. WaitStatus::StillAlive). + _ => { + ptrace::cont(pid.unwrap(), None).unwrap(); + continue; + } + }; + if signal == wait_signal { + return Ok(pid); + } else { + ptrace::cont(pid, signal).map_err(|_| ExecEnd(None))?; + } + } +} + +/// Grabs the access that caused a segfault and logs it down if it's to our memory, +/// or kills the child and returns the appropriate error otherwise. +fn handle_segfault( + pid: unistd::Pid, + ch_pages: &[usize], + ch_stack: usize, + page_size: usize, + cs: &capstone::Capstone, + acc_events: &mut Vec<AccessEvent>, +) -> Result<(), ExecEnd> { + /// This is just here to not pollute the main namespace with `capstone::prelude::*`. + #[inline] + fn capstone_disassemble( + instr: &[u8], + addr: usize, + cs: &capstone::Capstone, + acc_events: &mut Vec<AccessEvent>, + ) -> capstone::CsResult<()> { + use capstone::prelude::*; + + // The arch_detail is what we care about, but it relies on these temporaries + // that we can't drop. 0x1000 is the default base address for Captsone, and + // we're expecting 1 instruction. + let insns = cs.disasm_count(instr, 0x1000, 1)?; + let ins_detail = cs.insn_detail(&insns[0])?; + let arch_detail = ins_detail.arch_detail(); + + for op in arch_detail.operands() { + match op { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + arch::ArchOperand::X86Operand(x86_operand) => { + match x86_operand.op_type { + // We only care about memory accesses + arch::x86::X86OperandType::Mem(_) => { + let push = addr..addr.strict_add(usize::from(x86_operand.size)); + // It's called a "RegAccessType" but it also applies to memory + let acc_ty = x86_operand.access.unwrap(); + if acc_ty.is_readable() { + acc_events.push(AccessEvent::Read(push.clone())); + } + if acc_ty.is_writable() { + acc_events.push(AccessEvent::Write(push)); + } + } + _ => (), + } + } + #[cfg(target_arch = "aarch64")] + arch::ArchOperand::Arm64Operand(arm64_operand) => { + // Annoyingly, we don't always get the size here, so just be pessimistic for now. + match arm64_operand.op_type { + arch::arm64::Arm64OperandType::Mem(_) => { + // B = 1 byte, H = 2 bytes, S = 4 bytes, D = 8 bytes, Q = 16 bytes. + let size = match arm64_operand.vas { + // Not an fp/simd instruction. + arch::arm64::Arm64Vas::ARM64_VAS_INVALID => ARCH_WORD_SIZE, + // 1 byte. + arch::arm64::Arm64Vas::ARM64_VAS_1B => 1, + // 2 bytes. + arch::arm64::Arm64Vas::ARM64_VAS_1H => 2, + // 4 bytes. + arch::arm64::Arm64Vas::ARM64_VAS_4B + | arch::arm64::Arm64Vas::ARM64_VAS_2H + | arch::arm64::Arm64Vas::ARM64_VAS_1S => 4, + // 8 bytes. + arch::arm64::Arm64Vas::ARM64_VAS_8B + | arch::arm64::Arm64Vas::ARM64_VAS_4H + | arch::arm64::Arm64Vas::ARM64_VAS_2S + | arch::arm64::Arm64Vas::ARM64_VAS_1D => 8, + // 16 bytes. + arch::arm64::Arm64Vas::ARM64_VAS_16B + | arch::arm64::Arm64Vas::ARM64_VAS_8H + | arch::arm64::Arm64Vas::ARM64_VAS_4S + | arch::arm64::Arm64Vas::ARM64_VAS_2D + | arch::arm64::Arm64Vas::ARM64_VAS_1Q => 16, + }; + let push = addr..addr.strict_add(size); + // FIXME: This now has access type info in the latest + // git version of capstone because this pissed me off + // and I added it. Change this when it updates. + acc_events.push(AccessEvent::Read(push.clone())); + acc_events.push(AccessEvent::Write(push)); + } + _ => (), + } + } + #[cfg(target_arch = "arm")] + arch::ArchOperand::ArmOperand(arm_operand) => + match arm_operand.op_type { + arch::arm::ArmOperandType::Mem(_) => { + // We don't get info on the size of the access, but + // we're at least told if it's a vector instruction. + let size = if arm_operand.vector_index.is_some() { + ARCH_MAX_ACCESS_SIZE + } else { + ARCH_WORD_SIZE + }; + let push = addr..addr.strict_add(size); + let acc_ty = arm_operand.access.unwrap(); + if acc_ty.is_readable() { + acc_events.push(AccessEvent::Read(push.clone())); + } + if acc_ty.is_writable() { + acc_events.push(AccessEvent::Write(push)); + } + } + _ => (), + }, + _ => unimplemented!(), + } + } + + Ok(()) + } + + // Get information on what caused the segfault. This contains the address + // that triggered it. + let siginfo = ptrace::getsiginfo(pid).unwrap(); + // All x86, ARM, etc. instructions only have at most one memory operand + // (thankfully!) + // SAFETY: si_addr is safe to call. + let addr = unsafe { siginfo.si_addr().addr() }; + let page_addr = addr.strict_sub(addr.strict_rem(page_size)); + + if !ch_pages.iter().any(|pg| (*pg..pg.strict_add(page_size)).contains(&addr)) { + // This was a real segfault (not one of the Miri memory pages), so print some debug info and + // quit. + let regs = ptrace::getregs(pid).unwrap(); + eprintln!("Segfault occurred during FFI at {addr:#018x}"); + eprintln!("Expected access on pages: {ch_pages:#018x?}"); + eprintln!("Register dump: {regs:#x?}"); + ptrace::kill(pid).unwrap(); + return Err(ExecEnd(None)); + } + + // Overall structure: + // - Get the address that caused the segfault + // - Unprotect the memory: we force the child to execute `mempr_off`, passing parameters via + // global atomic variables. This is what we use the temporary callback stack for. + // - Step 1 instruction + // - Parse executed code to estimate size & type of access + // - Reprotect the memory by executing `mempr_on` in the child. + // - Continue + + // Ensure the stack is properly zeroed out! + for a in (ch_stack..ch_stack.strict_add(CALLBACK_STACK_SIZE)).step_by(ARCH_WORD_SIZE) { + ptrace::write(pid, std::ptr::with_exposed_provenance_mut(a), 0).unwrap(); + } + + // Guard against both architectures with upwards and downwards-growing stacks. + let stack_ptr = ch_stack.strict_add(CALLBACK_STACK_SIZE / 2); + let regs_bak = ptrace::getregs(pid).unwrap(); + let mut new_regs = regs_bak; + let ip_prestep = regs_bak.ip(); + + // Move the instr ptr into the deprotection code. + #[expect(clippy::as_conversions)] + new_regs.set_ip(mempr_off as usize); + // Don't mess up the stack by accident! + new_regs.set_sp(stack_ptr); + + // Modify the PAGE_ADDR global on the child process to point to the page + // that we want unprotected. + ptrace::write( + pid, + (&raw const PAGE_ADDR).cast_mut().cast(), + libc::c_long::try_from(page_addr).unwrap(), + ) + .unwrap(); + + // Check if we also own the next page, and if so unprotect it in case + // the access spans the page boundary. + let flag = if ch_pages.contains(&page_addr.strict_add(page_size)) { 2 } else { 1 }; + ptrace::write(pid, (&raw const PAGE_COUNT).cast_mut().cast(), flag).unwrap(); + + ptrace::setregs(pid, new_regs).unwrap(); + + // Our mempr_* functions end with a raise(SIGSTOP). + wait_for_signal(Some(pid), signal::SIGSTOP, true)?; + + // Step 1 instruction. + ptrace::setregs(pid, regs_bak).unwrap(); + ptrace::step(pid, None).unwrap(); + // Don't use wait_for_signal here since 1 instruction doesn't give room + // for any uncertainty + we don't want it `cont()`ing randomly by accident + // Also, don't let it continue with unprotected memory if something errors! + let _ = wait::waitid(wait::Id::Pid(pid), WAIT_FLAGS).map_err(|_| ExecEnd(None))?; + + // Zero out again to be safe + for a in (ch_stack..ch_stack.strict_add(CALLBACK_STACK_SIZE)).step_by(ARCH_WORD_SIZE) { + ptrace::write(pid, std::ptr::with_exposed_provenance_mut(a), 0).unwrap(); + } + + // Save registers and grab the bytes that were executed. This would + // be really nasty if it was a jump or similar but those thankfully + // won't do memory accesses and so can't trigger this! + let regs_bak = ptrace::getregs(pid).unwrap(); + new_regs = regs_bak; + let ip_poststep = regs_bak.ip(); + // We need to do reads/writes in word-sized chunks. + let diff = (ip_poststep.strict_sub(ip_prestep)).div_ceil(ARCH_WORD_SIZE); + let instr = (ip_prestep..ip_prestep.strict_add(diff)).fold(vec![], |mut ret, ip| { + // This only needs to be a valid pointer in the child process, not ours. + ret.append( + &mut ptrace::read(pid, std::ptr::without_provenance_mut(ip)) + .unwrap() + .to_ne_bytes() + .to_vec(), + ); + ret + }); + + // Now figure out the size + type of access and log it down. + // This will mark down e.g. the same area being read multiple times, + // since it's more efficient to compress the accesses at the end. + if capstone_disassemble(&instr, addr, cs, acc_events).is_err() { + // Read goes first because we need to be pessimistic. + acc_events.push(AccessEvent::Read(addr..addr.strict_add(ARCH_MAX_ACCESS_SIZE))); + acc_events.push(AccessEvent::Write(addr..addr.strict_add(ARCH_MAX_ACCESS_SIZE))); + } + + // Reprotect everything and continue. + #[expect(clippy::as_conversions)] + new_regs.set_ip(mempr_on as usize); + new_regs.set_sp(stack_ptr); + ptrace::setregs(pid, new_regs).unwrap(); + wait_for_signal(Some(pid), signal::SIGSTOP, true)?; + + ptrace::setregs(pid, regs_bak).unwrap(); + ptrace::syscall(pid, None).unwrap(); + Ok(()) +} + +// We only get dropped into these functions via offsetting the instr pointer +// manually, so we *must not ever* unwind from them. + +/// Disables protections on the page whose address is currently in `PAGE_ADDR`. +/// +/// SAFETY: `PAGE_ADDR` should be set to a page-aligned pointer to an owned page, +/// `PAGE_SIZE` should be the host pagesize, and the range from `PAGE_ADDR` to +/// `PAGE_SIZE` * `PAGE_COUNT` must be owned and allocated memory. No other threads +/// should be running. +pub unsafe extern "C" fn mempr_off() { + use std::sync::atomic::Ordering; + + // Again, cannot allow unwinds to happen here. + let len = PAGE_SIZE.load(Ordering::Relaxed).saturating_mul(PAGE_COUNT.load(Ordering::Relaxed)); + // SAFETY: Upheld by "caller". + unsafe { + // It's up to the caller to make sure this doesn't actually overflow, but + // we mustn't unwind from here, so... + if libc::mprotect( + PAGE_ADDR.load(Ordering::Relaxed).cast(), + len, + libc::PROT_READ | libc::PROT_WRITE, + ) != 0 + { + // Can't return or unwind, but we can do this. + std::process::exit(-1); + } + } + // If this fails somehow we're doomed. + if signal::raise(signal::SIGSTOP).is_err() { + std::process::exit(-1); + } +} + +/// Reenables protection on the page set by `PAGE_ADDR`. +/// +/// SAFETY: See `mempr_off()`. +pub unsafe extern "C" fn mempr_on() { + use std::sync::atomic::Ordering; + + let len = PAGE_SIZE.load(Ordering::Relaxed).wrapping_mul(PAGE_COUNT.load(Ordering::Relaxed)); + // SAFETY: Upheld by "caller". + unsafe { + if libc::mprotect(PAGE_ADDR.load(Ordering::Relaxed).cast(), len, libc::PROT_NONE) != 0 { + std::process::exit(-1); + } + } + if signal::raise(signal::SIGSTOP).is_err() { + std::process::exit(-1); + } +} diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 14ebb22dc2b..b3c58397a02 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -945,8 +945,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_join" => { let [thread, retval] = this.check_shim(abi, CanonAbi::C, link_name, args)?; - let res = this.pthread_join(thread, retval)?; - this.write_scalar(res, dest)?; + this.pthread_join(thread, retval, dest)?; } "pthread_detach" => { let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 8e5a3021b1c..47732f811f1 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -22,8 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let flags = this.read_scalar(flags)?.to_i32()?; // old_address must be a multiple of the page size - #[expect(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero - if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 { + if !old_address.addr().bytes().is_multiple_of(this.machine.page_size) || new_size == 0 { this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index aefeee6f7a3..1738de4dd4f 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -49,7 +49,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { && matches!(&*this.tcx.sess.target.os, "macos" | "solaris" | "illumos") && (flags & map_fixed) != 0 { - return interp_ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this)); + return interp_ok(Scalar::from_maybe_pointer(Pointer::without_provenance(addr), this)); } let prot_read = this.eval_libc_i32("PROT_READ"); @@ -130,8 +130,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // addr must be a multiple of the page size, but apart from that munmap is just implemented // as a dealloc. - #[expect(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero - if addr.addr().bytes() % this.machine.page_size != 0 { + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { return this.set_last_error_and_return_i32(LibcError("EINVAL")); } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index eee2bbcb903..50eb4d92289 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -17,7 +17,7 @@ fn bytewise_equal_atomic_relaxed<'tcx>( // We do this in chunks of 4, so that we are okay to race with (sufficiently aligned) // 4-byte atomic accesses. - assert!(size.bytes() % 4 == 0); + assert!(size.bytes().is_multiple_of(4)); for i in 0..(size.bytes() / 4) { let offset = Size::from_bytes(i.strict_mul(4)); let load = |place: &MPlaceTy<'tcx>| { diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 4b6615b3ea8..a438e71a41d 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -41,7 +41,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + return_dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); if !this.ptr_is_null(this.read_pointer(retval)?)? { @@ -51,12 +52,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?; let Ok(thread) = this.thread_id_try_from(thread) else { - return interp_ok(this.eval_libc("ESRCH")); + this.write_scalar(this.eval_libc("ESRCH"), return_dest)?; + return interp_ok(()); }; - this.join_thread_exclusive(thread)?; - - interp_ok(Scalar::from_u32(0)) + this.join_thread_exclusive( + thread, + /* success_retval */ Scalar::from_u32(0), + return_dest, + ) } fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 10f6df67ad4..de10357f5fa 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -573,8 +573,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "WaitForSingleObject" => { let [handle, timeout] = this.check_shim(abi, sys_conv, link_name, args)?; - let ret = this.WaitForSingleObject(handle, timeout)?; - this.write_scalar(ret, dest)?; + this.WaitForSingleObject(handle, timeout, dest)?; } "GetCurrentProcess" => { let [] = this.check_shim(abi, sys_conv, link_name, args)?; diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs index 56ae88bb1a1..a155ec65a51 100644 --- a/src/tools/miri/src/shims/windows/thread.rs +++ b/src/tools/miri/src/shims/windows/thread.rs @@ -58,13 +58,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, handle_op: &OpTy<'tcx>, timeout_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + return_dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let handle = this.read_handle(handle_op, "WaitForSingleObject")?; let timeout = this.read_scalar(timeout_op)?.to_u32()?; - let thread = match handle { + let joined_thread_id = match handle { Handle::Thread(thread) => thread, // Unlike on posix, the outcome of joining the current thread is not documented. // On current Windows, it just deadlocks. @@ -76,8 +77,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("`WaitForSingleObject` with non-infinite timeout"); } - this.join_thread(thread)?; + this.join_thread( + joined_thread_id, + /* success_retval */ this.eval_windows("c", "WAIT_OBJECT_0"), + return_dest, + )?; - interp_ok(this.eval_windows("c", "WAIT_OBJECT_0")) + interp_ok(()) } } diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 383579198bb..0fec1fb15eb 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1066,17 +1066,6 @@ pub fn libm() { assert_eq!((-1f32).powf(f32::NEG_INFINITY), 1.0); assert_eq!((-1f64).powf(f64::NEG_INFINITY), 1.0); - // For pow (powf in rust) the C standard says: - // x^0 = 1 for all x even a sNaN - // FIXME(#4286): this does not match the behavior of all implementations. - assert_eq!(SNAN_F32.powf(0.0), 1.0); - assert_eq!(SNAN_F64.powf(0.0), 1.0); - - // For pown (powi in rust) the C standard says: - // x^0 = 1 for all x even a sNaN - // FIXME(#4286): this does not match the behavior of all implementations. - assert_eq!(SNAN_F32.powi(0), 1.0); - assert_eq!(SNAN_F64.powi(0), 1.0); assert_eq!(0f32.powi(10), 0.0); assert_eq!(0f64.powi(100), 0.0); @@ -1358,7 +1347,7 @@ fn test_min_max_nondet() { /// Ensure that if we call the closure often enough, we see both `true` and `false.` #[track_caller] fn ensure_both(f: impl Fn() -> bool) { - let rounds = 16; + let rounds = 32; let first = f(); for _ in 1..rounds { if f() != first { @@ -1500,4 +1489,18 @@ fn test_non_determinism() { test_operations_f32(12., 5.); test_operations_f64(19., 11.); test_operations_f128(25., 18.); + + + // SNaN^0 = (1 | NaN) + ensure_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan()); + ensure_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan()); + + // 1^SNaN = (1 | NaN) + ensure_nondet(|| f32::powf(1.0, SNAN_F32).is_nan()); + ensure_nondet(|| f64::powf(1.0, SNAN_F64).is_nan()); + + // same as powf (keep it consistent): + // x^SNaN = (1 | NaN) + ensure_nondet(|| f32::powi(SNAN_F32, 0).is_nan()); + ensure_nondet(|| f64::powi(SNAN_F64, 0).is_nan()); } diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index cadbbd58af5..3ffdb6868ac 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -260,6 +260,7 @@ fn test_f32() { // Intrinsics let nan = F32::nan(Neg, Quiet, 0).as_f32(); + let snan = F32::nan(Neg, Signaling, 1).as_f32(); check_all_outcomes( HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), || F32::from(f32::min(nan, nan)), @@ -313,6 +314,18 @@ fn test_f32() { HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), || F32::from(nan.ln_gamma().0), ); + check_all_outcomes( + HashSet::from_iter([ + F32::from(1.0), + F32::nan(Pos, Quiet, 0), + F32::nan(Neg, Quiet, 0), + F32::nan(Pos, Quiet, 1), + F32::nan(Neg, Quiet, 1), + F32::nan(Pos, Signaling, 1), + F32::nan(Neg, Signaling, 1), + ]), + || F32::from(snan.powf(0.0)), + ); } fn test_f64() { @@ -376,6 +389,7 @@ fn test_f64() { // Intrinsics let nan = F64::nan(Neg, Quiet, 0).as_f64(); + let snan = F64::nan(Neg, Signaling, 1).as_f64(); check_all_outcomes( HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), || F64::from(f64::min(nan, nan)), @@ -433,6 +447,18 @@ fn test_f64() { HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), || F64::from(nan.ln_gamma().0), ); + check_all_outcomes( + HashSet::from_iter([ + F64::from(1.0), + F64::nan(Pos, Quiet, 0), + F64::nan(Neg, Quiet, 0), + F64::nan(Pos, Quiet, 1), + F64::nan(Neg, Quiet, 1), + F64::nan(Pos, Signaling, 1), + F64::nan(Neg, Signaling, 1), + ]), + || F64::from(snan.powf(0.0)), + ); } fn test_casts() { diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index e14ce51f35a..726d4c01cc3 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -570,6 +570,10 @@ fn simd_round() { f32x4::from_array([1.0, 1.0, 2.0, -5.0]) ); assert_eq!( + unsafe { intrinsics::simd_round_ties_even(f32x4::from_array([0.9, 1.001, 2.0, -4.5])) }, + f32x4::from_array([1.0, 1.0, 2.0, -4.0]) + ); + assert_eq!( f32x4::from_array([0.9, 1.001, 2.0, -4.5]).trunc(), f32x4::from_array([0.0, 1.0, 2.0, -4.0]) ); @@ -587,6 +591,10 @@ fn simd_round() { f64x4::from_array([1.0, 1.0, 2.0, -5.0]) ); assert_eq!( + unsafe { intrinsics::simd_round_ties_even(f64x4::from_array([0.9, 1.001, 2.0, -4.5])) }, + f64x4::from_array([1.0, 1.0, 2.0, -4.0]) + ); + assert_eq!( f64x4::from_array([0.9, 1.001, 2.0, -4.5]).trunc(), f64x4::from_array([0.0, 1.0, 2.0, -4.0]) ); diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index 53ce772fa77..705a1750ae8 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -72,6 +72,8 @@ change-id = 115898 [rust] channel = "{channel}" verbose-tests = true +# rust-lld cannot be combined with an external LLVM +lld = false [build] rustc = "{rustc}" diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 15ed03ad5c2..3226f467ba4 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -4,12 +4,12 @@ version = "0.2.0" edition = "2021" [dependencies] -bstr = "1.6.0" -object = "0.36.2" -similar = "2.5.0" +bstr = "1.12" +object = "0.37" +similar = "2.7" wasmparser = { version = "0.219", default-features = false, features = ["std"] } -regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace -gimli = "0.31.0" +regex = "1.11" +gimli = "0.32" build_helper = { path = "../../build_helper" } serde_json = "1.0" libc = "0.2" diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 79fb7a2d2ea..770652494f4 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -17,6 +17,10 @@ env: RUST_BACKTRACE: short RUSTUP_MAX_RETRIES: 10 +defaults: + run: + shell: bash + jobs: changes: runs-on: ubuntu-latest @@ -80,6 +84,7 @@ jobs: CC: deny_c strategy: + fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] @@ -99,7 +104,7 @@ jobs: rustup toolchain install nightly --profile minimal --component rustfmt # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json - name: Install Rust Problem Matcher - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'macos-latest' run: echo "::add-matcher::.github/rust.json" # - name: Cache Dependencies @@ -116,23 +121,9 @@ jobs: if: matrix.os == 'ubuntu-latest' run: cargo codegen --check - - name: Compile tests - run: cargo test --no-run - - name: Run tests run: cargo nextest run --no-fail-fast --hide-progress-bar --status-level fail - - name: Cancel parallel jobs - if: failure() - run: | - # https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#cancel-a-workflow-run - curl -L \ - -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/cancel - - name: Run Clippy if: matrix.os == 'macos-latest' run: cargo clippy --all-targets -- -D clippy::disallowed_macros -D clippy::dbg_macro -D clippy::todo -D clippy::print_stdout -D clippy::print_stderr @@ -333,3 +324,21 @@ jobs: jq -C <<< '${{ toJson(needs) }}' # Check if all jobs that we depend on (in the needs array) were successful (or have been skipped). jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJson(needs) }}' + + cancel-if-matrix-failed: + needs: rust + if: ${{ always() }} + runs-on: ubuntu-latest + steps: + - name: Cancel parallel jobs + run: | + if jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJson(needs) }}'; then + exit 0 + fi + # https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#cancel-a-workflow-run + curl -L \ + -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/cancel diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index a758ecfd467..5bd90130f4c 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -134,13 +134,13 @@ jobs: - name: Run analysis-stats on rust-analyzer if: matrix.target == 'x86_64-unknown-linux-gnu' - run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats . + run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats . -q - name: Run analysis-stats on rust std library if: matrix.target == 'x86_64-unknown-linux-gnu' env: RUSTC_BOOTSTRAP: 1 - run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std + run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std -q - name: Upload artifacts uses: actions/upload-artifact@v4 diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 2c7b4641641..caa8f28d8e2 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1458,7 +1458,7 @@ dependencies = [ "edition", "expect-test", "ra-ap-rustc_lexer", - "rustc-literal-escaper 0.0.3", + "rustc-literal-escaper 0.0.4", "stdx", "tracing", ] @@ -1927,9 +1927,9 @@ checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04" [[package]] name = "rustc-literal-escaper" -version = "0.0.3" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78744cd17f5d01c75b709e49807d1363e02a940ccee2e9e72435843fdb0d076e" +checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b" [[package]] name = "rustc-stable-hash" @@ -2207,7 +2207,7 @@ dependencies = [ "rayon", "rowan", "rustc-hash 2.1.1", - "rustc-literal-escaper 0.0.3", + "rustc-literal-escaper 0.0.4", "rustc_apfloat", "smol_str", "stdx", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 449c75859cf..0a8e6feb46e 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -143,7 +143,7 @@ serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } serde_json = "1.0.140" rustc-hash = "2.1.1" -rustc-literal-escaper = "0.0.3" +rustc-literal-escaper = "0.0.4" smallvec = { version = "1.15.1", features = [ "const_new", "union", diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index 3b423a86f97..ea06fd9c48f 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] la-arena.workspace = true diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index d7764a16c04..ba349666145 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] rustc-hash.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index c6922eca49f..abb4819a767 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] arrayvec.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 00408e95ae6..c67bb2422ac 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -25,11 +25,10 @@ use crate::{ import_map::ImportMap, item_tree::{ItemTree, file_item_tree_query}, lang_item::{self, LangItem}, - nameres::{assoc::TraitItems, crate_def_map, diagnostics::DefDiagnostics}, + nameres::crate_def_map, signatures::{ ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature, StructSignature, TraitAliasSignature, TraitSignature, TypeAliasSignature, UnionSignature, - VariantFields, }, tt, visibility::{self, Visibility}, @@ -113,24 +112,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { // region:data - #[salsa::invoke(VariantFields::query)] - fn variant_fields_with_source_map( - &self, - id: VariantId, - ) -> (Arc<VariantFields>, Arc<ExpressionStoreSourceMap>); - - #[salsa::transparent] - #[salsa::invoke(TraitItems::trait_items_query)] - fn trait_items(&self, e: TraitId) -> Arc<TraitItems>; - - #[salsa::invoke(TraitItems::trait_items_with_diagnostics_query)] - fn trait_items_with_diagnostics(&self, tr: TraitId) -> (Arc<TraitItems>, DefDiagnostics); - - #[salsa::tracked] - fn variant_fields(&self, id: VariantId) -> Arc<VariantFields> { - self.variant_fields_with_source_map(id).0 - } - #[salsa::tracked] fn trait_signature(&self, trait_: TraitId) -> Arc<TraitSignature> { self.trait_signature_with_source_map(trait_).0 diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index f617c3225ae..85bd193223f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -9,7 +9,10 @@ pub mod scope; #[cfg(test)] mod tests; -use std::ops::{Deref, Index}; +use std::{ + ops::{Deref, Index}, + sync::LazyLock, +}; use cfg::{CfgExpr, CfgOptions}; use either::Either; @@ -19,6 +22,7 @@ use rustc_hash::FxHashMap; use smallvec::SmallVec; use span::{Edition, SyntaxContext}; use syntax::{AstPtr, SyntaxNodePtr, ast}; +use triomphe::Arc; use tt::TextRange; use crate::{ @@ -220,6 +224,12 @@ impl ExpressionStoreBuilder { } impl ExpressionStore { + pub fn empty_singleton() -> Arc<Self> { + static EMPTY: LazyLock<Arc<ExpressionStore>> = + LazyLock::new(|| Arc::new(ExpressionStoreBuilder::default().finish())); + EMPTY.clone() + } + /// Returns an iterator over all block expressions in this store that define inner items. pub fn blocks<'a>( &'a self, @@ -636,6 +646,12 @@ impl Index<PathId> for ExpressionStore { // FIXME: Change `node_` prefix to something more reasonable. // Perhaps `expr_syntax` and `expr_id`? impl ExpressionStoreSourceMap { + pub fn empty_singleton() -> Arc<Self> { + static EMPTY: LazyLock<Arc<ExpressionStoreSourceMap>> = + LazyLock::new(|| Arc::new(ExpressionStoreSourceMap::default())); + EMPTY.clone() + } + pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> { match id { ExprOrPatId::ExprId(id) => self.expr_syntax(id), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index efa1374a446..c0e51b338b4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2250,7 +2250,7 @@ impl ExprCollector<'_> { Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())), Some(ModuleDefId::EnumVariantId(variant)) // FIXME: This can cause a cycle if the user is writing invalid code - if self.db.variant_fields(variant.into()).shape != FieldsShape::Record => + if variant.fields(self.db).shape != FieldsShape::Record => { (None, Pat::Path(name.into())) } @@ -2825,14 +2825,7 @@ impl ExprCollector<'_> { let use_format_args_since_1_89_0 = fmt_args().is_some() && fmt_unsafe_arg().is_none(); let idx = if use_format_args_since_1_89_0 { - self.collect_format_args_impl( - syntax_ptr, - fmt, - hygiene, - argmap, - lit_pieces, - format_options, - ) + self.collect_format_args_impl(syntax_ptr, fmt, argmap, lit_pieces, format_options) } else { self.collect_format_args_before_1_89_0_impl( syntax_ptr, @@ -2962,7 +2955,6 @@ impl ExprCollector<'_> { &mut self, syntax_ptr: AstPtr<ast::Expr>, fmt: FormatArgs, - hygiene: HygieneId, argmap: FxIndexSet<(usize, ArgumentType)>, lit_pieces: ExprId, format_options: ExprId, @@ -2997,8 +2989,11 @@ impl ExprCollector<'_> { let args = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); let args_name = Name::new_symbol_root(sym::args); - let args_binding = - self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene); + let args_binding = self.alloc_binding( + args_name.clone(), + BindingAnnotation::Unannotated, + HygieneId::ROOT, + ); let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); self.add_definition_to_binding(args_binding, args_pat); // TODO: We don't have `super let` yet. @@ -3008,13 +3003,16 @@ impl ExprCollector<'_> { initializer: Some(args), else_branch: None, }; - (vec![let_stmt], self.alloc_expr_desugared(Expr::Path(Path::from(args_name)))) + (vec![let_stmt], self.alloc_expr_desugared(Expr::Path(args_name.into()))) } else { // Generate: // super let args = (&arg0, &arg1, &...); let args_name = Name::new_symbol_root(sym::args); - let args_binding = - self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene); + let args_binding = self.alloc_binding( + args_name.clone(), + BindingAnnotation::Unannotated, + HygieneId::ROOT, + ); let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); self.add_definition_to_binding(args_binding, args_pat); let elements = arguments @@ -3057,8 +3055,11 @@ impl ExprCollector<'_> { .collect(); let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); - let args_binding = - self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene); + let args_binding = self.alloc_binding( + args_name.clone(), + BindingAnnotation::Unannotated, + HygieneId::ROOT, + ); let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); self.add_definition_to_binding(args_binding, args_pat); let let_stmt2 = Statement::Let { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 56c7655f9ea..87bcd33ed7b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -121,7 +121,7 @@ pub fn print_variant_body_hir(db: &dyn DefDatabase, owner: VariantId, edition: E VariantId::UnionId(it) => format!("union {}", item_name(db, it, "<missing>")), }; - let fields = db.variant_fields(owner); + let fields = owner.fields(db); let mut p = Printer { db, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index a9a0e36312c..94e683cb0f8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -331,13 +331,13 @@ impl GenericParams { } #[inline] - pub fn no_predicates(&self) -> bool { + pub fn has_no_predicates(&self) -> bool { self.where_predicates.is_empty() } #[inline] - pub fn where_predicates(&self) -> std::slice::Iter<'_, WherePredicate> { - self.where_predicates.iter() + pub fn where_predicates(&self) -> &[WherePredicate] { + &self.where_predicates } /// Iterator of type_or_consts field diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index a6138fb6821..f31f355cfa5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -16,7 +16,7 @@ use crate::{ AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, - nameres::{DefMap, crate_def_map}, + nameres::{DefMap, assoc::TraitItems, crate_def_map}, visibility::Visibility, }; @@ -221,7 +221,7 @@ impl ImportMap { trait_import_info: &ImportInfo, ) { let _p = tracing::info_span!("collect_trait_assoc_items").entered(); - for &(ref assoc_item_name, item) in &db.trait_items(tr).items { + for &(ref assoc_item_name, item) in &TraitItems::query(db, tr).items { let module_def_id = match item { AssocItemId::FunctionId(f) => ModuleDefId::from(f), AssocItemId::ConstId(c) => ModuleDefId::from(c), @@ -482,7 +482,7 @@ mod tests { use expect_test::{Expect, expect}; use test_fixture::WithFixture; - use crate::{ItemContainerId, Lookup, test_db::TestDB}; + use crate::{ItemContainerId, Lookup, nameres::assoc::TraitItems, test_db::TestDB}; use super::*; @@ -580,7 +580,7 @@ mod tests { let trait_info = dependency_imports.import_info_for(ItemInNs::Types(trait_id.into()))?; - let trait_items = db.trait_items(trait_id); + let trait_items = TraitItems::query(db, trait_id); let (assoc_item_name, _) = trait_items .items .iter() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index faff7d036a2..750308026ee 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -9,8 +9,10 @@ use triomphe::Arc; use crate::{ AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId, - StaticId, StructId, TraitId, TypeAliasId, UnionId, db::DefDatabase, expr_store::path::Path, - nameres::crate_def_map, + StaticId, StructId, TraitId, TypeAliasId, UnionId, + db::DefDatabase, + expr_store::path::Path, + nameres::{assoc::TraitItems, crate_def_map}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -113,14 +115,16 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt match def { ModuleDefId::TraitId(trait_) => { lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait); - db.trait_items(trait_).items.iter().for_each(|&(_, assoc_id)| match assoc_id { - AssocItemId::FunctionId(f) => { - lang_items.collect_lang_item(db, f, LangItemTarget::Function); + TraitItems::query(db, trait_).items.iter().for_each(|&(_, assoc_id)| { + match assoc_id { + AssocItemId::FunctionId(f) => { + lang_items.collect_lang_item(db, f, LangItemTarget::Function); + } + AssocItemId::TypeAliasId(alias) => { + lang_items.collect_lang_item(db, alias, LangItemTarget::TypeAlias) + } + AssocItemId::ConstId(_) => {} } - AssocItemId::TypeAliasId(alias) => { - lang_items.collect_lang_item(db, alias, LangItemTarget::TypeAlias) - } - AssocItemId::ConstId(_) => {} }); } ModuleDefId::AdtId(AdtId::EnumId(e)) => { @@ -304,6 +308,8 @@ impl LangItem { language_item_table! { // Variant name, Name, Getter method name, Target Generic requirements; Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); + MetaSized, sym::meta_sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); + PointeeSized, sym::pointee_sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index a562f2d0af2..bdf8b453e2d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -87,9 +87,12 @@ use crate::{ attr::Attrs, builtin_type::BuiltinType, db::DefDatabase, + expr_store::ExpressionStoreSourceMap, hir::generics::{LocalLifetimeParamId, LocalTypeOrConstParamId}, nameres::{ - LocalDefMap, assoc::ImplItems, block_def_map, crate_def_map, crate_local_def_map, + LocalDefMap, + assoc::{ImplItems, TraitItems}, + block_def_map, crate_def_map, crate_local_def_map, diagnostics::DefDiagnostics, }, signatures::{EnumVariants, InactiveEnumVariantCode, VariantFields}, @@ -252,9 +255,35 @@ impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function); type StructLoc = ItemLoc<ast::Struct>; impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct); +impl StructId { + pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { + VariantFields::firewall(db, self.into()) + } + + pub fn fields_with_source_map( + self, + db: &dyn DefDatabase, + ) -> (Arc<VariantFields>, Arc<ExpressionStoreSourceMap>) { + VariantFields::query(db, self.into()) + } +} + pub type UnionLoc = ItemLoc<ast::Union>; impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union); +impl UnionId { + pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { + VariantFields::firewall(db, self.into()) + } + + pub fn fields_with_source_map( + self, + db: &dyn DefDatabase, + ) -> (Arc<VariantFields>, Arc<ExpressionStoreSourceMap>) { + VariantFields::query(db, self.into()) + } +} + pub type EnumLoc = ItemLoc<ast::Enum>; impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum); @@ -282,6 +311,13 @@ impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static); pub type TraitLoc = ItemLoc<ast::Trait>; impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait); +impl TraitId { + #[inline] + pub fn trait_items(self, db: &dyn DefDatabase) -> &TraitItems { + TraitItems::query(db, self) + } +} + pub type TraitAliasLoc = ItemLoc<ast::TraitAlias>; impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias); @@ -328,6 +364,20 @@ pub struct EnumVariantLoc { } impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant); impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId); + +impl EnumVariantId { + pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { + VariantFields::firewall(db, self.into()) + } + + pub fn fields_with_source_map( + self, + db: &dyn DefDatabase, + ) -> (Arc<VariantFields>, Arc<ExpressionStoreSourceMap>) { + VariantFields::query(db, self.into()) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Macro2Loc { pub container: ModuleId, @@ -1015,8 +1065,15 @@ pub enum VariantId { impl_from!(EnumVariantId, StructId, UnionId for VariantId); impl VariantId { - pub fn variant_data(self, db: &dyn DefDatabase) -> Arc<VariantFields> { - db.variant_fields(self) + pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { + VariantFields::firewall(db, self) + } + + pub fn fields_with_source_map( + self, + db: &dyn DefDatabase, + ) -> (Arc<VariantFields>, Arc<ExpressionStoreSourceMap>) { + VariantFields::query(db, self) } pub fn file_id(self, db: &dyn DefDatabase) -> HirFileId { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index 7aaa918d1c9..07210df8873 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -38,16 +38,18 @@ pub struct TraitItems { pub macro_calls: ThinVec<(AstId<ast::Item>, MacroCallId)>, } +#[salsa::tracked] impl TraitItems { #[inline] - pub(crate) fn trait_items_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitItems> { - db.trait_items_with_diagnostics(tr).0 + pub(crate) fn query(db: &dyn DefDatabase, tr: TraitId) -> &TraitItems { + &Self::query_with_diagnostics(db, tr).0 } - pub(crate) fn trait_items_with_diagnostics_query( + #[salsa::tracked(returns(ref))] + pub fn query_with_diagnostics( db: &dyn DefDatabase, tr: TraitId, - ) -> (Arc<TraitItems>, DefDiagnostics) { + ) -> (TraitItems, DefDiagnostics) { let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db); let collector = @@ -55,7 +57,7 @@ impl TraitItems { let source = ast_id.with_value(collector.ast_id_map.get(ast_id.value)).to_node(db); let (items, macro_calls, diagnostics) = collector.collect(source.assoc_item_list()); - (Arc::new(TraitItems { macro_calls, items }), DefDiagnostics::new(diagnostics)) + (TraitItems { macro_calls, items }, DefDiagnostics::new(diagnostics)) } pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 78fdc275606..0c3274d849a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -41,6 +41,7 @@ use crate::{ macro_call_as_call_id, nameres::{ BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, ModuleData, ModuleOrigin, ResolveMode, + assoc::TraitItems, attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id}, crate_def_map, diagnostics::DefDiagnostic, @@ -1020,8 +1021,7 @@ impl<'db> DefCollector<'db> { let resolutions = if true { vec![] } else { - self.db - .trait_items(it) + TraitItems::query(self.db, it) .items .iter() .map(|&(ref name, variant)| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index e8235b1c96f..4641b220daa 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -24,8 +24,8 @@ use crate::{ item_scope::{BUILTIN_SCOPE, ImportOrExternCrate}, item_tree::FieldsShape, nameres::{ - BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, crate_def_map, - sub_namespace_match, + BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, assoc::TraitItems, + crate_def_map, sub_namespace_match, }, per_ns::PerNs, visibility::{RawVisibility, Visibility}, @@ -584,8 +584,11 @@ impl DefMap { // now resulting in a cycle. // To properly implement this, trait item collection needs to be done in def map // collection... - let item = - if true { None } else { db.trait_items(t).assoc_item_by_name(segment) }; + let item = if true { + None + } else { + TraitItems::query(db, t).assoc_item_by_name(segment) + }; return match item { Some(item) => ResolvePathResult::new( match item { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 377a545ebf4..1958eb6c6a1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -1,6 +1,6 @@ //! Item signature IR definitions -use std::ops::Not as _; +use std::{cell::LazyCell, ops::Not as _}; use bitflags::bitflags; use cfg::{CfgExpr, CfgOptions}; @@ -731,29 +731,26 @@ pub struct VariantFields { pub store: Arc<ExpressionStore>, pub shape: FieldsShape, } + +#[salsa::tracked] impl VariantFields { - #[inline] + #[salsa::tracked(returns(clone))] pub(crate) fn query( db: &dyn DefDatabase, id: VariantId, ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { - let (shape, (fields, store, source_map)) = match id { + let (shape, result) = match id { VariantId::EnumVariantId(id) => { let loc = id.lookup(db); let parent = loc.parent.lookup(db); let source = loc.source(db); let shape = adt_shape(source.value.kind()); - let span_map = db.span_map(source.file_id); - let override_visibility = visibility_from_ast( - db, - source.value.parent_enum().visibility(), - &mut |range| span_map.span_for_range(range).ctx, - ); + let enum_vis = Some(source.value.parent_enum().visibility()); let fields = lower_field_list( db, parent.container, source.map(|src| src.field_list()), - Some(override_visibility), + enum_vis, ); (shape, fields) } @@ -777,10 +774,29 @@ impl VariantFields { (FieldsShape::Record, fields) } }; + match result { + Some((fields, store, source_map)) => ( + Arc::new(VariantFields { fields, store: Arc::new(store), shape }), + Arc::new(source_map), + ), + None => ( + Arc::new(VariantFields { + fields: Arena::default(), + store: ExpressionStore::empty_singleton(), + shape, + }), + ExpressionStoreSourceMap::empty_singleton(), + ), + } + } - (Arc::new(VariantFields { fields, store: Arc::new(store), shape }), Arc::new(source_map)) + #[salsa::tracked(returns(deref))] + pub(crate) fn firewall(db: &dyn DefDatabase, id: VariantId) -> Arc<Self> { + Self::query(db, id).0 } +} +impl VariantFields { pub fn len(&self) -> usize { self.fields.len() } @@ -798,31 +814,24 @@ fn lower_field_list( db: &dyn DefDatabase, module: ModuleId, fields: InFile<Option<ast::FieldList>>, - override_visibility: Option<RawVisibility>, -) -> (Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap) { + override_visibility: Option<Option<ast::Visibility>>, +) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> { let file_id = fields.file_id; - match fields.value { - Some(ast::FieldList::RecordFieldList(fields)) => lower_fields( + match fields.value? { + ast::FieldList::RecordFieldList(fields) => lower_fields( db, module, InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))), |_, field| as_name_opt(field.name()), override_visibility, ), - Some(ast::FieldList::TupleFieldList(fields)) => lower_fields( + ast::FieldList::TupleFieldList(fields) => lower_fields( db, module, InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))), |idx, _| Name::new_tuple_field(idx), override_visibility, ), - None => lower_fields( - db, - module, - InFile::new(file_id, std::iter::empty::<(Option<ast::Type>, ast::RecordField)>()), - |_, _| Name::missing(), - None, - ), } } @@ -831,22 +840,34 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>( module: ModuleId, fields: InFile<impl Iterator<Item = (Option<ast::Type>, Field)>>, mut field_name: impl FnMut(usize, &Field) -> Name, - override_visibility: Option<RawVisibility>, -) -> (Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap) { - let mut arena = Arena::new(); + override_visibility: Option<Option<ast::Visibility>>, +) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> { let cfg_options = module.krate.cfg_options(db); let mut col = ExprCollector::new(db, module, fields.file_id); + let override_visibility = override_visibility.map(|vis| { + LazyCell::new(|| { + let span_map = db.span_map(fields.file_id); + visibility_from_ast(db, vis, &mut |range| span_map.span_for_range(range).ctx) + }) + }); + + let mut arena = Arena::new(); let mut idx = 0; + let mut has_fields = false; for (ty, field) in fields.value { + has_fields = true; match Attrs::is_cfg_enabled_for(db, &field, col.span_map(), cfg_options) { Ok(()) => { let type_ref = col.lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator); - let visibility = override_visibility.clone().unwrap_or_else(|| { - visibility_from_ast(db, field.visibility(), &mut |range| { - col.span_map().span_for_range(range).ctx - }) - }); + let visibility = override_visibility.as_ref().map_or_else( + || { + visibility_from_ast(db, field.visibility(), &mut |range| { + col.span_map().span_for_range(range).ctx + }) + }, + |it| RawVisibility::clone(it), + ); let is_unsafe = field .syntax() .children_with_tokens() @@ -867,9 +888,12 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>( } } } + if !has_fields { + return None; + } let store = col.store.finish(); arena.shrink_to_fit(); - (arena, store, col.source_map) + Some((arena, store, col.source_map)) } #[derive(Debug, PartialEq, Eq)] @@ -948,7 +972,7 @@ impl EnumVariants { self.variants.iter().all(|&(v, _, _)| { // The condition check order is slightly modified from rustc // to improve performance by early returning with relatively fast checks - let variant = &db.variant_fields(v.into()); + let variant = v.fields(db); if !variant.fields().is_empty() { return false; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs index 2514e88864f..b5eb84c25f2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs @@ -273,7 +273,7 @@ pub(crate) fn field_visibilities_query( db: &dyn DefDatabase, variant_id: VariantId, ) -> Arc<ArenaMap<LocalFieldId, Visibility>> { - let variant_fields = db.variant_fields(variant_id); + let variant_fields = variant_id.fields(db); let fields = variant_fields.fields(); if fields.is_empty() { return Arc::default(); diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index ed818c5be3f..80a3c084865 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 94c97713f06..986f8764f5c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -433,20 +433,19 @@ fn unescape(s: &str) -> Option<Cow<'_, str>> { let mut buf = String::new(); let mut prev_end = 0; let mut has_error = false; - unescape::unescape_unicode(s, unescape::Mode::Str, &mut |char_range, unescaped_char| match ( - unescaped_char, - buf.capacity() == 0, - ) { - (Ok(c), false) => buf.push(c), - (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { - prev_end = char_range.end - } - (Ok(c), true) => { - buf.reserve_exact(s.len()); - buf.push_str(&s[..prev_end]); - buf.push(c); + unescape::unescape_str(s, |char_range, unescaped_char| { + match (unescaped_char, buf.capacity() == 0) { + (Ok(c), false) => buf.push(c), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } + (Ok(c), true) => { + buf.reserve_exact(s.len()); + buf.push_str(&s[..prev_end]); + buf.push(c); + } + (Err(_), _) => has_error = true, } - (Err(_), _) => has_error = true, }); match (has_error, buf.capacity() == 0) { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 3180b8dae10..f9abe4f5566 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -12,7 +12,7 @@ use span::{Edition, FileId, Span}; use stdx::format_to; use syntax::{ format_smolstr, - unescape::{Mode, unescape_byte, unescape_char, unescape_unicode}, + unescape::{unescape_byte, unescape_char, unescape_str}, }; use syntax_bridge::syntax_node_to_token_tree; @@ -430,7 +430,7 @@ fn compile_error_expand( kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), suffix: _, })), - ] => ExpandError::other(span, Box::from(unescape_str(text).as_str())), + ] => ExpandError::other(span, Box::from(unescape_symbol(text).as_str())), _ => ExpandError::other(span, "`compile_error!` argument must be a string"), }; @@ -481,7 +481,7 @@ fn concat_expand( format_to!(text, "{}", it.symbol.as_str()) } tt::LitKind::Str => { - text.push_str(unescape_str(&it.symbol).as_str()); + text.push_str(unescape_symbol(&it.symbol).as_str()); record_span(it.span); } tt::LitKind::StrRaw(_) => { @@ -691,7 +691,7 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { span, kind: tt::LitKind::Str, suffix: _, - })) => Ok((unescape_str(text), *span)), + })) => Ok((unescape_symbol(text), *span)), TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, @@ -712,7 +712,7 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { span, kind: tt::LitKind::Str, suffix: _, - })) => Some((unescape_str(text), *span)), + })) => Some((unescape_symbol(text), *span)), TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, @@ -897,11 +897,11 @@ fn quote_expand( ) } -fn unescape_str(s: &Symbol) -> Symbol { +fn unescape_symbol(s: &Symbol) -> Symbol { if s.as_str().contains('\\') { let s = s.as_str(); let mut buf = String::with_capacity(s.len()); - unescape_unicode(s, Mode::Str, &mut |_, c| { + unescape_str(s, |_, c| { if let Ok(c) = c { buf.push(c) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 8b65126e7b7..7cc0a26d37c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 7acc9456ec9..cc8f7bf04a5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -208,7 +208,7 @@ pub(crate) fn deref_by_trait( }; let trait_id = trait_id()?; let target = - db.trait_items(trait_id).associated_type_by_name(&Name::new_symbol_root(sym::Target))?; + trait_id.trait_items(db).associated_type_by_name(&Name::new_symbol_root(sym::Target))?; let projection = { let b = TyBuilder::subst_for_def(db, trait_id, None); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 79454428112..26b635298a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -315,9 +315,8 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> { crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { if let Some((future_trait, future_output)) = LangItem::Future.resolve_trait(self.db, self.krate).and_then(|trait_| { - let alias = self - .db - .trait_items(trait_) + let alias = trait_ + .trait_items(self.db) .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; Some((trait_, alias)) }) @@ -711,7 +710,7 @@ pub(crate) fn trait_datum_query( }; let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); let associated_ty_ids = - db.trait_items(trait_).associated_types().map(to_assoc_type_id).collect(); + trait_.trait_items(db).associated_types().map(to_assoc_type_id).collect(); let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; let well_known = db.lang_attr(trait_.into()).and_then(well_known_trait_from_lang_item); let trait_datum = TraitDatum { @@ -802,7 +801,7 @@ pub(crate) fn adt_datum_query( // this slows down rust-analyzer by quite a bit unfortunately, so enabling this is currently not worth it let _variant_id_to_fields = |id: VariantId| { - let variant_data = &id.variant_data(db); + let variant_data = &id.fields(db); let fields = if variant_data.fields().is_empty() { vec![] } else { @@ -879,7 +878,7 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId) let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; let impl_datum_bound = rust_ir::ImplDatumBound { trait_ref, where_clauses }; - let trait_data = db.trait_items(trait_); + let trait_data = trait_.trait_items(db); let associated_ty_value_ids = impl_id .impl_items(db) .items @@ -931,8 +930,9 @@ fn type_alias_associated_ty_value( .into_value_and_skipped_binders() .0; // we don't return any assoc ty values if the impl'd trait can't be resolved - let assoc_ty = db - .trait_items(trait_ref.hir_trait_id()) + let assoc_ty = trait_ref + .hir_trait_id() + .trait_items(db) .associated_type_by_name(&type_alias_data.name) .expect("assoc ty value should not exist"); // validated when building the impl data as well let (ty, binders) = db.ty(type_alias.into()).into_value_and_skipped_binders(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 1873f12fb7c..9c0f8f40080 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -307,7 +307,7 @@ impl<'a> DeclValidator<'a> { /// Check incorrect names for struct fields. fn validate_struct_fields(&mut self, struct_id: StructId) { - let data = self.db.variant_fields(struct_id.into()); + let data = struct_id.fields(self.db); if data.shape != FieldsShape::Record { return; }; @@ -468,7 +468,7 @@ impl<'a> DeclValidator<'a> { /// Check incorrect names for fields of enum variant. fn validate_enum_variant_fields(&mut self, variant_id: EnumVariantId) { - let variant_data = self.db.variant_fields(variant_id.into()); + let variant_data = variant_id.fields(self.db); if variant_data.shape != FieldsShape::Record { return; }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index df2eb410b99..5d56957be6d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -494,7 +494,7 @@ impl FilterMapNextChecker { Some(next_function_id), match next_function_id.lookup(db).container { ItemContainerId::TraitId(iterator_trait_id) => { - let iterator_trait_items = &db.trait_items(iterator_trait_id).items; + let iterator_trait_items = &iterator_trait_id.trait_items(db).items; iterator_trait_items.iter().find_map(|(name, it)| match it { &AssocItemId::FunctionId(id) if *name == sym::filter_map => Some(id), _ => None, @@ -558,7 +558,7 @@ pub fn record_literal_missing_fields( return None; } - let variant_data = variant_def.variant_data(db); + let variant_data = variant_def.fields(db); let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); let missed_fields: Vec<LocalFieldId> = variant_data @@ -588,7 +588,7 @@ pub fn record_pattern_missing_fields( return None; } - let variant_data = variant_def.variant_data(db); + let variant_data = variant_def.fields(db); let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); let missed_fields: Vec<LocalFieldId> = variant_data diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index 916876d4ac9..0bce32a6778 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -169,13 +169,13 @@ impl<'a> PatCtxt<'a> { } hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => { - let expected_len = variant.unwrap().variant_data(self.db).fields().len(); + let expected_len = variant.unwrap().fields(self.db).fields().len(); let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis); self.lower_variant_or_leaf(pat, ty, subpatterns) } hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => { - let variant_data = variant.unwrap().variant_data(self.db); + let variant_data = variant.unwrap().fields(self.db); let subpatterns = args .iter() .map(|field| { @@ -345,7 +345,7 @@ impl HirDisplay for Pat { )?, }; - let variant_data = variant.variant_data(f.db); + let variant_data = variant.fields(f.db); if variant_data.shape == FieldsShape::Record { write!(f, " {{ ")?; @@ -377,7 +377,7 @@ impl HirDisplay for Pat { } let num_fields = - variant.map_or(subpatterns.len(), |v| v.variant_data(f.db).fields().len()); + variant.map_or(subpatterns.len(), |v| v.fields(f.db).fields().len()); if num_fields != 0 || variant.is_none() { write!(f, "(")?; let subpats = (0..num_fields).map(|i| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 2873a3e09e7..7cf22c64d0f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -6,7 +6,7 @@ use std::fmt; use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; use intern::sym; use rustc_pattern_analysis::{ - Captures, IndexVec, PatCx, PrivateUninhabitedField, + IndexVec, PatCx, PrivateUninhabitedField, constructor::{Constructor, ConstructorSet, VariantVisibility}, usefulness::{PlaceValidity, UsefulnessReport, compute_match_usefulness}, }; @@ -138,15 +138,15 @@ impl<'db> MatchCheckCtx<'db> { } // This lists the fields of a variant along with their types. - fn list_variant_fields<'a>( - &'a self, - ty: &'a Ty, + fn list_variant_fields( + &self, + ty: &Ty, variant: VariantId, - ) -> impl Iterator<Item = (LocalFieldId, Ty)> + Captures<'a> + Captures<'db> { + ) -> impl Iterator<Item = (LocalFieldId, Ty)> { let (_, substs) = ty.as_adt().unwrap(); let field_tys = self.db.field_types(variant); - let fields_len = variant.variant_data(self.db).fields().len() as u32; + let fields_len = variant.fields(self.db).fields().len() as u32; (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).map(move |fid| { let ty = field_tys[fid].clone().substitute(Interner, substs); @@ -229,7 +229,7 @@ impl<'db> MatchCheckCtx<'db> { } }; let variant = Self::variant_id_for_adt(self.db, &ctor, adt).unwrap(); - arity = variant.variant_data(self.db).fields().len(); + arity = variant.fields(self.db).fields().len(); } _ => { never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); @@ -349,7 +349,7 @@ impl PatCx for MatchCheckCtx<'_> { 1 } else { let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); - variant.variant_data(self.db).fields().len() + variant.fields(self.db).fields().len() } } _ => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 1aa7e0fcf88..507bab29208 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -888,7 +888,7 @@ fn render_const_scalar( write!(f, "{}", data.name.display(f.db, f.edition()))?; let field_types = f.db.field_types(s.into()); render_variant_after_name( - &f.db.variant_fields(s.into()), + s.fields(f.db), f, &field_types, f.db.trait_environment(adt.0.into()), @@ -920,7 +920,7 @@ fn render_const_scalar( )?; let field_types = f.db.field_types(var_id.into()); render_variant_after_name( - &f.db.variant_fields(var_id.into()), + var_id.fields(f.db), f, &field_types, f.db.trait_environment(adt.0.into()), @@ -1394,7 +1394,7 @@ impl HirDisplay for Ty { let future_trait = LangItem::Future.resolve_trait(db, body.module(db).krate()); let output = future_trait.and_then(|t| { - db.trait_items(t) + t.trait_items(db) .associated_type_by_name(&Name::new_symbol_root(sym::Output)) }); write!(f, "impl ")?; @@ -2178,6 +2178,7 @@ impl HirDisplayWithExpressionStore for TypeRefId { f.write_joined( generic_params .where_predicates() + .iter() .filter_map(|it| match it { WherePredicate::TypeBound { target, bound } | WherePredicate::ForLifetime { lifetimes: _, target, bound } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 48094945c11..30949c83bfa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -101,7 +101,7 @@ where // rustc checks for non-lifetime binders here, but we don't support HRTB yet - let trait_data = db.trait_items(trait_); + let trait_data = trait_.trait_items(db); for (_, assoc_item) in &trait_data.items { dyn_compatibility_violation_for_assoc_item(db, trait_, *assoc_item, cb)?; } @@ -164,7 +164,7 @@ fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { // Same as the above, `predicates_reference_self` fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { - let trait_data = db.trait_items(trait_); + let trait_data = trait_.trait_items(db); trait_data .items .iter() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index a3ed39934cd..f14872e68c3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -60,7 +60,16 @@ impl Generics { } pub(crate) fn where_predicates(&self) -> impl Iterator<Item = &WherePredicate> { - self.params.where_predicates() + self.params.where_predicates().iter() + } + + pub(crate) fn has_no_predicates(&self) -> bool { + self.params.has_no_predicates() + && self.parent_generics.as_ref().is_none_or(|g| g.params.has_no_predicates()) + } + + pub(crate) fn is_empty(&self) -> bool { + self.params.is_empty() && self.parent_generics.as_ref().is_none_or(|g| g.params.is_empty()) } pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 80478f19371..ce53198e966 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1813,7 +1813,7 @@ impl<'db> InferenceContext<'db> { } fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> { - self.db.trait_items(trait_).associated_type_by_name(&Name::new_symbol_root(sym::Output)) + trait_.trait_items(self.db).associated_type_by_name(&Name::new_symbol_root(sym::Output)) } fn resolve_lang_trait(&self, lang: LangItem) -> Option<TraitId> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 8d345defdc1..4e95eca3f94 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -382,7 +382,7 @@ fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<Pointe return Err(()); }; - let struct_data = table.db.variant_fields(id.into()); + let struct_data = id.fields(table.db); if let Some((last_field, _)) = struct_data.fields().iter().last() { let last_field_ty = table.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index b756bb859d3..65a273cdf8d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -677,7 +677,7 @@ impl CapturedItem { match proj { ProjectionElem::Deref => {} ProjectionElem::Field(Either::Left(f)) => { - let variant_data = f.parent.variant_data(db); + let variant_data = f.parent.fields(db); match variant_data.shape { FieldsShape::Record => { result.push('_'); @@ -720,7 +720,7 @@ impl CapturedItem { // In source code autoderef kicks in. ProjectionElem::Deref => {} ProjectionElem::Field(Either::Left(f)) => { - let variant_data = f.parent.variant_data(db); + let variant_data = f.parent.fields(db); match variant_data.shape { FieldsShape::Record => format_to!( result, @@ -782,7 +782,7 @@ impl CapturedItem { if field_need_paren { result = format!("({result})"); } - let variant_data = f.parent.variant_data(db); + let variant_data = f.parent.fields(db); let field = match variant_data.shape { FieldsShape::Record => { variant_data.fields()[f.local_id].name.as_str().to_owned() @@ -1210,9 +1210,8 @@ impl InferenceContext<'_> { if let Some(deref_trait) = self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) { - if let Some(deref_fn) = self - .db - .trait_items(deref_trait) + if let Some(deref_fn) = deref_trait + .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::deref_mut)) { break 'b deref_fn == f; @@ -1560,7 +1559,7 @@ impl InferenceContext<'_> { self.consume_place(place) } VariantId::StructId(s) => { - let vd = &*self.db.variant_fields(s.into()); + let vd = s.fields(self.db); for field_pat in args.iter() { let arg = field_pat.pat; let Some(local_id) = vd.field(&field_pat.name) else { @@ -1612,7 +1611,7 @@ impl InferenceContext<'_> { self.consume_place(place) } VariantId::StructId(s) => { - let vd = &*self.db.variant_fields(s.into()); + let vd = s.fields(self.db); let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); let fields = vd.fields().iter(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 64031279296..d40d52c134d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -542,7 +542,7 @@ impl InferenceContext<'_> { _ if fields.is_empty() => {} Some(def) => { let field_types = self.db.field_types(def); - let variant_data = def.variant_data(self.db); + let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); for field in fields.iter() { let field_def = { @@ -654,9 +654,8 @@ impl InferenceContext<'_> { match op { UnaryOp::Deref => { if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) { - if let Some(deref_fn) = self - .db - .trait_items(deref_trait) + if let Some(deref_fn) = deref_trait + .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::deref)) { // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that @@ -813,9 +812,8 @@ impl InferenceContext<'_> { self.table.new_lifetime_var(), )); self.write_expr_adj(*base, adj.into_boxed_slice()); - if let Some(func) = self - .db - .trait_items(index_trait) + if let Some(func) = index_trait + .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::index)) { let subst = TyBuilder::subst_for_def(self.db, index_trait, None); @@ -1148,7 +1146,7 @@ impl InferenceContext<'_> { let Some(trait_) = fn_x.get_id(self.db, self.table.trait_env.krate) else { return; }; - let trait_data = self.db.trait_items(trait_); + let trait_data = trait_.trait_items(self.db); if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) { let subst = TyBuilder::subst_for_def(self.db, trait_, None) .push(callee_ty.clone()) @@ -1316,7 +1314,7 @@ impl InferenceContext<'_> { let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| { let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?; - let func = self.db.trait_items(trait_id).method_by_name(&name)?; + let func = trait_id.trait_items(self.db).method_by_name(&name)?; Some((trait_id, func)) }); let (trait_, func) = match trait_func { @@ -1568,12 +1566,12 @@ impl InferenceContext<'_> { }); } &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref parameters) => { - let local_id = self.db.variant_fields(s.into()).field(name)?; + let local_id = s.fields(self.db).field(name)?; let field = FieldId { parent: s.into(), local_id }; (field, parameters.clone()) } &TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), ref parameters) => { - let local_id = self.db.variant_fields(u.into()).field(name)?; + let local_id = u.fields(self.db).field(name)?; let field = FieldId { parent: u.into(), local_id }; (field, parameters.clone()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index ac450c0b559..d2eaf212365 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -129,9 +129,8 @@ impl InferenceContext<'_> { if let Some(index_trait) = LangItem::IndexMut.resolve_trait(self.db, self.table.trait_env.krate) { - if let Some(index_fn) = self - .db - .trait_items(index_trait) + if let Some(index_fn) = index_trait + .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::index_mut)) { *f = index_fn; @@ -194,9 +193,8 @@ impl InferenceContext<'_> { }); if is_mut_ptr { mutability = Mutability::Not; - } else if let Some(deref_fn) = self - .db - .trait_items(deref_trait) + } else if let Some(deref_fn) = deref_trait + .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::deref_mut)) { *f = deref_fn; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 4bc3e167ebf..99d3b5c7a84 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -38,7 +38,7 @@ impl InferenceContext<'_> { decl: Option<DeclContext>, ) -> Ty { let (ty, def) = self.resolve_variant(id.into(), path, true); - let var_data = def.map(|it| it.variant_data(self.db)); + let var_data = def.map(|it| it.fields(self.db)); if let Some(variant) = def { self.write_variant_resolution(id.into(), variant); } @@ -60,7 +60,7 @@ impl InferenceContext<'_> { _ if subs.is_empty() => {} Some(def) => { let field_types = self.db.field_types(def); - let variant_data = def.variant_data(self.db); + let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); let (pre, post) = match ellipsis { @@ -129,7 +129,7 @@ impl InferenceContext<'_> { _ if subs.len() == 0 => {} Some(def) => { let field_types = self.db.field_types(def); - let variant_data = def.variant_data(self.db); + let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); let substs = ty.as_adt().map(TupleExt::tail); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index c327c13b664..bc8648ecdd9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -278,7 +278,7 @@ impl InferenceContext<'_> { ) -> Option<(ValueNs, Substitution)> { let trait_ = trait_ref.hir_trait_id(); let item = - self.db.trait_items(trait_).items.iter().map(|(_name, id)| *id).find_map(|item| { + trait_.trait_items(self.db).items.iter().map(|(_name, id)| *id).find_map(|item| { match item { AssocItemId::FunctionId(func) => { if segment.name == &self.db.function_signature(func).name { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 631b571465f..c07755535f2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -859,7 +859,7 @@ impl<'a> InferenceTable<'a> { ] { let krate = self.trait_env.krate; let fn_trait = fn_trait_name.get_id(self.db, krate)?; - let trait_data = self.db.trait_items(fn_trait); + let trait_data = fn_trait.trait_items(self.db); let output_assoc_type = trait_data.associated_type_by_name(&Name::new_symbol_root(output_assoc_name))?; @@ -1001,7 +1001,7 @@ impl<'a> InferenceTable<'a> { // Must use a loop here and not recursion because otherwise users will conduct completely // artificial examples of structs that have themselves as the tail field and complain r-a crashes. while let Some((AdtId::StructId(id), subst)) = ty.as_adt() { - let struct_data = self.db.variant_fields(id.into()); + let struct_data = id.fields(self.db); if let Some((last_field, _)) = struct_data.fields().iter().next_back() { let last_field_ty = self.db.field_types(id.into())[last_field] .clone() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 79a99321f10..b16b6a11784 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -132,7 +132,7 @@ impl UninhabitedFrom<'_> { variant: VariantId, subst: &Substitution, ) -> ControlFlow<VisiblyUninhabited> { - let variant_data = self.db.variant_fields(variant); + let variant_data = variant.fields(self.db); let fields = variant_data.fields(); if fields.is_empty() { return CONTINUE_OPAQUELY_INHABITED; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index c58bd1b773e..3fa2bfbd1b7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -375,7 +375,7 @@ pub(crate) fn layout_of_ty_cycle_result( fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { match pointee.kind(Interner) { &TyKind::Adt(AdtId(hir_def::AdtId::StructId(i)), ref subst) => { - let data = db.variant_fields(i.into()); + let data = i.fields(db); let mut it = data.fields().iter().rev(); match it.next() { Some((f, _)) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index dff986fec3c..236f316366d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -42,7 +42,7 @@ pub fn layout_of_adt_query( AdtId::StructId(s) => { let sig = db.struct_signature(s); let mut r = SmallVec::<[_; 1]>::new(); - r.push(handle_variant(s.into(), &db.variant_fields(s.into()))?); + r.push(handle_variant(s.into(), s.fields(db))?); ( r, sig.repr.unwrap_or_default(), @@ -52,7 +52,7 @@ pub fn layout_of_adt_query( AdtId::UnionId(id) => { let data = db.union_signature(id); let mut r = SmallVec::new(); - r.push(handle_variant(id.into(), &db.variant_fields(id.into()))?); + r.push(handle_variant(id.into(), id.fields(db))?); (r, data.repr.unwrap_or_default(), false) } AdtId::EnumId(e) => { @@ -60,7 +60,7 @@ pub fn layout_of_adt_query( let r = variants .variants .iter() - .map(|&(v, _, _)| handle_variant(v.into(), &db.variant_fields(v.into()))) + .map(|&(v, _, _)| handle_variant(v.into(), v.fields(db))) .collect::<Result<SmallVec<_>, _>>()?; (r, db.enum_signature(e).repr.unwrap_or_default(), false) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 148f2a41e7d..e787fd9b1e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -891,8 +891,8 @@ pub fn callable_sig_from_fn_trait( ) -> Option<(FnTrait, CallableSig)> { let krate = trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; - let output_assoc_type = db - .trait_items(fn_once_trait) + let output_assoc_type = fn_once_trait + .trait_items(db) .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; let mut table = InferenceTable::new(db, trait_env.clone()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 0a546768dab..f32b6af4d85 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -581,11 +581,28 @@ impl<'a> TyLoweringContext<'a> { match bound { &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => { // FIXME Don't silently drop the hrtb lifetimes here - if let Some((trait_ref, ctx)) = self.lower_trait_ref_from_path(path, self_ty) { - if !ignore_bindings { - assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref.clone()); + if let Some((trait_ref, mut ctx)) = + self.lower_trait_ref_from_path(path, self_ty.clone()) + { + // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented + // sized-hierarchy correctly. + let meta_sized = LangItem::MetaSized + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + let pointee_sized = LangItem::PointeeSized + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + if meta_sized.is_some_and(|it| it == trait_ref.hir_trait_id()) { + // Ignore this bound + } else if pointee_sized.is_some_and(|it| it == trait_ref.hir_trait_id()) { + // Regard this as `?Sized` bound + ctx.ty_ctx().unsized_types.insert(self_ty); + } else { + if !ignore_bindings { + assoc_bounds = + ctx.assoc_type_bindings_from_type_bound(trait_ref.clone()); + } + clause = + Some(crate::wrap_empty_binders(WhereClause::Implemented(trait_ref))); } - clause = Some(crate::wrap_empty_binders(WhereClause::Implemented(trait_ref))); } } &TypeBound::Path(path, TraitBoundModifier::Maybe) => { @@ -711,7 +728,7 @@ impl<'a> TyLoweringContext<'a> { .unwrap_or(it), None => it, }, - None => static_lifetime(), + None => error_lifetime(), }, }) .intern(Interner) @@ -805,7 +822,7 @@ fn named_associated_type_shorthand_candidates<R>( ) -> Option<R> { let mut search = |t| { all_super_trait_refs(db, t, |t| { - let data = db.trait_items(t.hir_trait_id()); + let data = t.hir_trait_id().trait_items(db); for (name, assoc_id) in &data.items { if let AssocItemId::TypeAliasId(alias) = assoc_id { @@ -883,7 +900,12 @@ pub(crate) fn field_types_with_diagnostics_query( db: &dyn HirDatabase, variant_id: VariantId, ) -> (Arc<ArenaMap<LocalFieldId, Binders<Ty>>>, Diagnostics) { - let var_data = db.variant_fields(variant_id); + let var_data = variant_id.fields(db); + let fields = var_data.fields(); + if fields.is_empty() { + return (Arc::new(ArenaMap::default()), None); + } + let (resolver, def): (_, GenericDefId) = match variant_id { VariantId::StructId(it) => (it.resolver(db), it.into()), VariantId::UnionId(it) => (it.resolver(db), it.into()), @@ -899,7 +921,7 @@ pub(crate) fn field_types_with_diagnostics_query( LifetimeElisionKind::AnonymousReportError, ) .with_type_param_mode(ParamLoweringMode::Variable); - for (field_id, field_data) in var_data.fields().iter() { + for (field_id, field_data) in fields.iter() { res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(field_data.type_ref))); } (Arc::new(res), create_diagnostics(ctx.diagnostics)) @@ -920,6 +942,10 @@ pub(crate) fn generic_predicates_for_param_query( assoc_name: Option<Name>, ) -> GenericPredicates { let generics = generics(db, def); + if generics.has_no_predicates() && generics.is_empty() { + return GenericPredicates(None); + } + let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( db, @@ -936,8 +962,32 @@ pub(crate) fn generic_predicates_for_param_query( | WherePredicate::TypeBound { target, bound, .. } => { let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; if invalid_target { - // If this is filtered out without lowering, `?Sized` is not gathered into `ctx.unsized_types` - if let TypeBound::Path(_, TraitBoundModifier::Maybe) = bound { + // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented + // sized-hierarchy correctly. + // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into + // `ctx.unsized_types` + let lower = || -> bool { + match bound { + TypeBound::Path(_, TraitBoundModifier::Maybe) => true, + TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { + let TypeRef::Path(path) = &ctx.store[path.type_ref()] else { + return false; + }; + let Some(pointee_sized) = + LangItem::PointeeSized.resolve_trait(ctx.db, ctx.resolver.krate()) + else { + return false; + }; + // Lower the path directly with `Resolver` instead of PathLoweringContext` + // to prevent diagnostics duplications. + ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and( + |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized), + ) + } + _ => false, + } + }(); + if lower { ctx.lower_where_predicate(pred, true).for_each(drop); } return false; @@ -957,7 +1007,7 @@ pub(crate) fn generic_predicates_for_param_query( }; all_super_traits(db, tr).iter().any(|tr| { - db.trait_items(*tr).items.iter().any(|(name, item)| { + tr.trait_items(db).items.iter().any(|(name, item)| { matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name }) }) @@ -1025,6 +1075,10 @@ pub(crate) fn trait_environment_query( def: GenericDefId, ) -> Arc<TraitEnvironment> { let generics = generics(db, def); + if generics.has_no_predicates() && generics.is_empty() { + return TraitEnvironment::empty(def.krate(db)); + } + let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( db, @@ -1128,6 +1182,10 @@ where F: Fn(&WherePredicate, GenericDefId) -> bool, { let generics = generics(db, def); + if generics.has_no_predicates() && generics.is_empty() { + return (GenericPredicates(None), None); + } + let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( db, @@ -1154,7 +1212,7 @@ where } } - if generics.len() > 0 { + if !generics.is_empty() { let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); let explicitly_unsized_tys = ctx.unsized_types; if let Some(implicitly_sized_predicates) = @@ -1229,7 +1287,7 @@ pub(crate) fn generic_defaults_with_diagnostics_query( def: GenericDefId, ) -> (GenericDefaults, Diagnostics) { let generic_params = generics(db, def); - if generic_params.len() == 0 { + if generic_params.is_empty() { return (GenericDefaults(None), None); } let resolver = def.resolver(db); @@ -1418,7 +1476,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS /// Build the type of a tuple struct constructor. fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Option<Binders<Ty>> { - let struct_data = db.variant_fields(def.into()); + let struct_data = def.fields(db); match struct_data.shape { FieldsShape::Record => None, FieldsShape::Unit => Some(type_for_adt(db, def.into())), @@ -1451,7 +1509,7 @@ fn type_for_enum_variant_constructor( def: EnumVariantId, ) -> Option<Binders<Ty>> { let e = def.lookup(db).parent; - match db.variant_fields(def.into()).shape { + match def.fields(db).shape { FieldsShape::Record => None, FieldsShape::Unit => Some(type_for_adt(db, e.into())), FieldsShape::Tuple => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 726eaf8b0a1..06686b6a164 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -173,7 +173,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { self.skip_resolved_segment(); let segment = self.current_or_prev_segment; let found = - self.ctx.db.trait_items(trait_).associated_type_by_name(segment.name); + trait_.trait_items(self.ctx.db).associated_type_by_name(segment.name); match found { Some(associated_ty) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 25f1782bdd8..a6150a9bc17 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -1302,7 +1302,7 @@ fn iterate_trait_method_candidates( // trait, but if we find out it doesn't, we'll skip the rest of the // iteration let mut known_implemented = false; - for &(_, item) in db.trait_items(t).items.iter() { + for &(_, item) in t.trait_items(db).items.iter() { // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. let visible = @@ -1429,7 +1429,7 @@ fn iterate_inherent_methods( ) -> ControlFlow<()> { let db = table.db; for t in traits { - let data = db.trait_items(t); + let data = t.trait_items(db); for &(_, item) in data.items.iter() { // We don't pass `visible_from_module` as all trait items should be visible. let visible = match is_valid_trait_method_candidate( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index a8156ec375b..1ec55a82092 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -657,12 +657,12 @@ impl Evaluator<'_> { cached_ptr_size, cached_fn_trait_func: LangItem::Fn .resolve_trait(db, crate_id) - .and_then(|x| db.trait_items(x).method_by_name(&Name::new_symbol_root(sym::call))), + .and_then(|x| x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call))), cached_fn_mut_trait_func: LangItem::FnMut.resolve_trait(db, crate_id).and_then(|x| { - db.trait_items(x).method_by_name(&Name::new_symbol_root(sym::call_mut)) + x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_mut)) }), cached_fn_once_trait_func: LangItem::FnOnce.resolve_trait(db, crate_id).and_then(|x| { - db.trait_items(x).method_by_name(&Name::new_symbol_root(sym::call_once)) + x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_once)) }), }) } @@ -1749,8 +1749,7 @@ impl Evaluator<'_> { AdtId::UnionId(_) => not_supported!("unsizing unions"), AdtId::EnumId(_) => not_supported!("unsizing enums"), }; - let Some((last_field, _)) = - self.db.variant_fields(id.into()).fields().iter().next_back() + let Some((last_field, _)) = id.fields(self.db).fields().iter().next_back() else { not_supported!("unsizing struct without field"); }; @@ -2232,7 +2231,7 @@ impl Evaluator<'_> { } chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { AdtId::StructId(s) => { - let data = this.db.variant_fields(s.into()); + let data = s.fields(this.db); let layout = this.layout(ty)?; let field_types = this.db.field_types(s.into()); for (f, _) in data.fields().iter() { @@ -2261,7 +2260,7 @@ impl Evaluator<'_> { bytes, e, ) { - let data = &this.db.variant_fields(v.into()); + let data = v.fields(this.db); let field_types = this.db.field_types(v.into()); for (f, _) in data.fields().iter() { let offset = @@ -2808,7 +2807,7 @@ impl Evaluator<'_> { ) -> Result<()> { let Some(drop_fn) = (|| { let drop_trait = LangItem::Drop.resolve_trait(self.db, self.crate_id)?; - self.db.trait_items(drop_trait).method_by_name(&Name::new_symbol_root(sym::drop)) + drop_trait.trait_items(self.db).method_by_name(&Name::new_symbol_root(sym::drop)) })() else { // in some tests we don't have drop trait in minicore, and // we can ignore drop in them. @@ -2838,7 +2837,7 @@ impl Evaluator<'_> { return Ok(()); } let layout = self.layout_adt(id.0, subst.clone())?; - let variant_fields = self.db.variant_fields(s.into()); + let variant_fields = s.fields(self.db); match variant_fields.shape { FieldsShape::Record | FieldsShape::Tuple => { let field_types = self.db.field_types(s.into()); @@ -2918,7 +2917,7 @@ pub fn render_const_using_debug_impl( not_supported!("core::fmt::Debug not found"); }; let Some(debug_fmt_fn) = - db.trait_items(debug_trait).method_by_name(&Name::new_symbol_root(sym::fmt)) + debug_trait.trait_items(db).method_by_name(&Name::new_symbol_root(sym::fmt)) else { not_supported!("core::fmt::Debug::fmt not found"); }; @@ -3045,7 +3044,10 @@ impl IntValue { (8, true) => Self::I64(i64::from_le_bytes(bytes.try_into().unwrap())), (16, false) => Self::U128(u128::from_le_bytes(bytes.try_into().unwrap())), (16, true) => Self::I128(i128::from_le_bytes(bytes.try_into().unwrap())), - _ => panic!("invalid integer size"), + (len, is_signed) => { + never!("invalid integer size: {len}, signed: {is_signed}"); + Self::I32(0) + } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 6ebde013344..e9665d5ae9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1257,9 +1257,8 @@ impl Evaluator<'_> { args.push(IntervalAndTy::new(addr, field, self, locals)?); } if let Some(target) = LangItem::FnOnce.resolve_trait(self.db, self.crate_id) { - if let Some(def) = self - .db - .trait_items(target) + if let Some(def) = target + .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::call_once)) { self.exec_fn_trait( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs index 984648cfec3..bc331a23d98 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -31,7 +31,7 @@ impl Evaluator<'_> { Some(len) => len, _ => { if let AdtId::StructId(id) = id.0 { - let struct_data = self.db.variant_fields(id.into()); + let struct_data = id.fields(self.db); let fields = struct_data.fields(); let Some((first_field, _)) = fields.iter().next() else { not_supported!("simd type with no field"); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 3abbbe45e6f..c1f86960e15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -984,3 +984,17 @@ fn main<'a, T: Foo + Bar + Baz>( |e| matches!(e, MirEvalError::MirLowerError(_, MirLowerError::GenericArgNotProvided(..))), ); } + +#[test] +fn format_args_pass() { + check_pass( + r#" +//- minicore: fmt +fn main() { + let x1 = format_args!(""); + let x2 = format_args!("{}", x1); + let x3 = format_args!("{} {}", x1, x2); +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 71e038b92f0..845d6b8eae1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -503,7 +503,7 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(Some(current)) } ValueNs::EnumVariantId(variant_id) => { - let variant_fields = &self.db.variant_fields(variant_id.into()); + let variant_fields = variant_id.fields(self.db); if variant_fields.shape == FieldsShape::Unit { let ty = self.infer.type_of_expr[expr_id].clone(); current = self.lower_enum_variant( @@ -856,7 +856,7 @@ impl<'ctx> MirLowerCtx<'ctx> { TyKind::Adt(_, s) => s.clone(), _ => not_supported!("Non ADT record literal"), }; - let variant_fields = self.db.variant_fields(variant_id); + let variant_fields = variant_id.fields(self.db); match variant_id { VariantId::EnumVariantId(_) | VariantId::StructId(_) => { let mut operands = vec![None; variant_fields.fields().len()]; @@ -1176,8 +1176,7 @@ impl<'ctx> MirLowerCtx<'ctx> { place, Rvalue::Aggregate( AggregateKind::Adt(st.into(), subst.clone()), - self.db - .variant_fields(st.into()) + st.fields(self.db) .fields() .iter() .map(|it| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index ad664693e29..e7bffead931 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -193,9 +193,8 @@ impl MirLowerCtx<'_> { if let Some(deref_trait) = self.resolve_lang_item(LangItem::DerefMut)?.as_trait() { - if let Some(deref_fn) = self - .db - .trait_items(deref_trait) + if let Some(deref_fn) = deref_trait + .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::deref_mut)) { break 'b deref_fn == f; @@ -347,9 +346,8 @@ impl MirLowerCtx<'_> { .resolve_lang_item(trait_lang_item)? .as_trait() .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?; - let deref_fn = self - .db - .trait_items(deref_trait) + let deref_fn = deref_trait + .trait_items(self.db) .method_by_name(&trait_method_name) .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?; let deref_fn_op = Operand::const_zst( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index b3c1f6f387f..61c0685c48a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -609,7 +609,7 @@ impl MirLowerCtx<'_> { } self.pattern_matching_variant_fields( shape, - &self.db.variant_fields(v.into()), + v.fields(self.db), variant, current, current_else, @@ -619,7 +619,7 @@ impl MirLowerCtx<'_> { } VariantId::StructId(s) => self.pattern_matching_variant_fields( shape, - &self.db.variant_fields(s.into()), + s.fields(self.db), variant, current, current_else, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 8764e48b538..78a69cf4509 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -326,7 +326,7 @@ impl<'a> MirPrettyCtx<'a> { w!(this, ")"); } ProjectionElem::Field(Either::Left(field)) => { - let variant_fields = this.db.variant_fields(field.parent); + let variant_fields = field.parent.fields(this.db); let name = &variant_fields.fields()[field.local_id].name; match field.parent { hir_def::VariantId::EnumVariantId(e) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 9ca6ee476c6..79754bc8a09 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -486,7 +486,7 @@ pub(crate) fn visit_module( }); } ModuleDefId::TraitId(it) => { - let trait_data = db.trait_items(it); + let trait_data = it.trait_items(db); for &(_, item) in trait_data.items.iter() { match item { AssocItemId::FunctionId(it) => cb(it.into()), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index ddc5b715194..3894b4b6f7b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -561,7 +561,7 @@ trait Foo {} fn test(f: impl Foo, g: &(impl Foo + ?Sized)) { let _: &dyn Foo = &f; let _: &dyn Foo = g; - //^ expected &'? (dyn Foo + 'static), got &'? impl Foo + ?Sized + //^ expected &'? (dyn Foo + '?), got &'? impl Foo + ?Sized } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index a986b54a7b0..6e3faa05a62 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -67,11 +67,11 @@ trait B: A {} fn test<'a>( _: &(dyn A<Assoc = ()> + Send), - //^ &(dyn A<Assoc = ()> + Send + 'static) + //^ &(dyn A<Assoc = ()> + Send) _: &'a (dyn Send + A<Assoc = ()>), - //^ &'a (dyn A<Assoc = ()> + Send + 'static) + //^ &'a (dyn A<Assoc = ()> + Send) _: &dyn B<Assoc = ()>, - //^ &(dyn B<Assoc = ()> + 'static) + //^ &(dyn B<Assoc = ()>) ) {} "#, ); @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &(dyn Foo<'?> + 'static) + // ^^^ &dyn Foo<'?> "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 905fd8a3bca..0377ce95f19 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -567,7 +567,7 @@ fn main() { "ast_id_map_shim", "parse_shim", "real_span_map_shim", - "trait_items_with_diagnostics_shim", + "query_with_diagnostics_", "body_shim", "body_with_source_map_shim", "attrs_shim", @@ -596,8 +596,8 @@ fn main() { "struct_signature_with_source_map_shim", "generic_predicates_shim", "value_ty_shim", - "variant_fields_shim", - "variant_fields_with_source_map_shim", + "firewall_", + "query_", "lang_item", "inherent_impls_in_crate_shim", "impl_signature_shim", @@ -674,7 +674,7 @@ fn main() { "file_item_tree_query", "real_span_map_shim", "crate_local_def_map", - "trait_items_with_diagnostics_shim", + "query_with_diagnostics_", "body_with_source_map_shim", "attrs_shim", "body_shim", @@ -695,11 +695,9 @@ fn main() { "return_type_impl_traits_shim", "infer_shim", "function_signature_with_source_map_shim", - "trait_environment_shim", "expr_scopes_shim", "struct_signature_with_source_map_shim", - "generic_predicates_shim", - "variant_fields_with_source_map_shim", + "query_", "inherent_impls_in_crate_shim", "impl_signature_with_source_map_shim", "impl_signature_shim", @@ -709,7 +707,6 @@ fn main() { "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", "generic_predicates_shim", - "generic_predicates_shim", ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 94826acca30..c58ca6c67a8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1153,9 +1153,9 @@ fn dyn_trait_super_trait_not_in_scope() { 51..55 'self': &'? Self 64..69 '{ 0 }': u32 66..67 '0': u32 - 176..177 'd': &'? (dyn Trait + 'static) + 176..177 'd': &'? (dyn Trait + '?) 191..207 '{ ...o(); }': () - 197..198 'd': &'? (dyn Trait + 'static) + 197..198 'd': &'? (dyn Trait + '?) 197..204 'd.foo()': u32 "#]], ); @@ -2019,10 +2019,10 @@ impl dyn Error + Send { /// Attempts to downcast the box to a concrete type. pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> { let err: Box<dyn Error> = self; - // ^^^^ expected Box<dyn Error + 'static>, got Box<dyn Error + Send + 'static> + // ^^^^ expected Box<dyn Error + '?>, got Box<dyn Error + Send + '?> // FIXME, type mismatch should not occur <dyn Error>::downcast(err).map_err(|_| loop {}) - //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error + 'static>) -> Result<Box<{unknown}>, Box<dyn Error + 'static>> + //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error + '?>) -> Result<Box<{unknown}>, Box<dyn Error + '?>> } } "#, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index ff8adeef1db..238753e12e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -629,7 +629,7 @@ fn issue_4053_diesel_where_clauses() { 488..522 '{ ... }': () 498..502 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> 498..508 'self.order': O - 498..515 'self.o...into()': dyn QueryFragment<DB> + 'static + 498..515 'self.o...into()': dyn QueryFragment<DB> + '? "#]], ); } @@ -773,7 +773,7 @@ fn issue_4800() { "#, expect![[r#" 379..383 'self': &'? mut PeerSet<D> - 401..424 '{ ... }': dyn Future<Output = ()> + 'static + 401..424 '{ ... }': dyn Future<Output = ()> + '? 411..418 'loop {}': ! 416..418 '{}': () 575..579 'self': &'? mut Self diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index cf51671afb2..43e8f3747ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2741,11 +2741,11 @@ impl B for Astruct {} 715..744 '#[rust...1i32])': Box<[i32; 1], Global> 737..743 '[1i32]': [i32; 1] 738..742 '1i32': i32 - 755..756 'v': Vec<Box<dyn B + 'static, Global>, Global> - 776..793 '<[_]> ...to_vec': fn into_vec<Box<dyn B + 'static, Global>, Global>(Box<[Box<dyn B + 'static, Global>], Global>) -> Vec<Box<dyn B + 'static, Global>, Global> - 776..850 '<[_]> ...ct)]))': Vec<Box<dyn B + 'static, Global>, Global> - 794..849 '#[rust...uct)])': Box<[Box<dyn B + 'static, Global>; 1], Global> - 816..848 '[#[rus...ruct)]': [Box<dyn B + 'static, Global>; 1] + 755..756 'v': Vec<Box<dyn B + '?, Global>, Global> + 776..793 '<[_]> ...to_vec': fn into_vec<Box<dyn B + '?, Global>, Global>(Box<[Box<dyn B + '?, Global>], Global>) -> Vec<Box<dyn B + '?, Global>, Global> + 776..850 '<[_]> ...ct)]))': Vec<Box<dyn B + '?, Global>, Global> + 794..849 '#[rust...uct)])': Box<[Box<dyn B + '?, Global>; 1], Global> + 816..848 '[#[rus...ruct)]': [Box<dyn B + '?, Global>; 1] 817..847 '#[rust...truct)': Box<Astruct, Global> 839..846 'Astruct': Astruct "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index e5d1fbe9def..56e31a1af1b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1475,26 +1475,26 @@ fn test(x: Box<dyn Trait<u64>>, y: &dyn Trait<u64>) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 198..200 '{}': Box<dyn Trait<u64> + 'static> - 210..211 'x': Box<dyn Trait<u64> + 'static> - 234..235 'y': &'? (dyn Trait<u64> + 'static) + 198..200 '{}': Box<dyn Trait<u64> + '?> + 210..211 'x': Box<dyn Trait<u64> + '?> + 234..235 'y': &'? (dyn Trait<u64> + '?) 254..371 '{ ...2(); }': () - 260..261 'x': Box<dyn Trait<u64> + 'static> - 267..268 'y': &'? (dyn Trait<u64> + 'static) - 278..279 'z': Box<dyn Trait<u64> + 'static> - 282..285 'bar': fn bar() -> Box<dyn Trait<u64> + 'static> - 282..287 'bar()': Box<dyn Trait<u64> + 'static> - 293..294 'x': Box<dyn Trait<u64> + 'static> + 260..261 'x': Box<dyn Trait<u64> + '?> + 267..268 'y': &'? (dyn Trait<u64> + '?) + 278..279 'z': Box<dyn Trait<u64> + '?> + 282..285 'bar': fn bar() -> Box<dyn Trait<u64> + '?> + 282..287 'bar()': Box<dyn Trait<u64> + '?> + 293..294 'x': Box<dyn Trait<u64> + '?> 293..300 'x.foo()': u64 - 306..307 'y': &'? (dyn Trait<u64> + 'static) + 306..307 'y': &'? (dyn Trait<u64> + '?) 306..313 'y.foo()': u64 - 319..320 'z': Box<dyn Trait<u64> + 'static> + 319..320 'z': Box<dyn Trait<u64> + '?> 319..326 'z.foo()': u64 - 332..333 'x': Box<dyn Trait<u64> + 'static> + 332..333 'x': Box<dyn Trait<u64> + '?> 332..340 'x.foo2()': i64 - 346..347 'y': &'? (dyn Trait<u64> + 'static) + 346..347 'y': &'? (dyn Trait<u64> + '?) 346..354 'y.foo2()': i64 - 360..361 'z': Box<dyn Trait<u64> + 'static> + 360..361 'z': Box<dyn Trait<u64> + '?> 360..368 'z.foo2()': i64 "#]], ); @@ -1523,14 +1523,14 @@ fn test(s: S<u32, i32>) { expect![[r#" 32..36 'self': &'? Self 102..106 'self': &'? S<T, U> - 128..139 '{ loop {} }': &'? (dyn Trait<T, U> + 'static) + 128..139 '{ loop {} }': &'? (dyn Trait<T, U> + '?) 130..137 'loop {}': ! 135..137 '{}': () 175..179 'self': &'? Self 251..252 's': S<u32, i32> 267..289 '{ ...z(); }': () 273..274 's': S<u32, i32> - 273..280 's.bar()': &'? (dyn Trait<u32, i32> + 'static) + 273..280 's.bar()': &'? (dyn Trait<u32, i32> + '?) 273..286 's.bar().baz()': (u32, i32) "#]], ); @@ -1556,20 +1556,20 @@ fn test(x: Trait, y: &Trait) -> u64 { }"#, expect![[r#" 26..30 'self': &'? Self - 60..62 '{}': dyn Trait + 'static - 72..73 'x': dyn Trait + 'static - 82..83 'y': &'? (dyn Trait + 'static) + 60..62 '{}': dyn Trait + '? + 72..73 'x': dyn Trait + '? + 82..83 'y': &'? (dyn Trait + '?) 100..175 '{ ...o(); }': u64 - 106..107 'x': dyn Trait + 'static - 113..114 'y': &'? (dyn Trait + 'static) - 124..125 'z': dyn Trait + 'static - 128..131 'bar': fn bar() -> dyn Trait + 'static - 128..133 'bar()': dyn Trait + 'static - 139..140 'x': dyn Trait + 'static + 106..107 'x': dyn Trait + '? + 113..114 'y': &'? (dyn Trait + '?) + 124..125 'z': dyn Trait + '? + 128..131 'bar': fn bar() -> dyn Trait + '? + 128..133 'bar()': dyn Trait + '? + 139..140 'x': dyn Trait + '? 139..146 'x.foo()': u64 - 152..153 'y': &'? (dyn Trait + 'static) + 152..153 'y': &'? (dyn Trait + '?) 152..159 'y.foo()': u64 - 165..166 'z': dyn Trait + 'static + 165..166 'z': dyn Trait + '? 165..172 'z.foo()': u64 "#]], ); @@ -1589,10 +1589,10 @@ fn main() { expect![[r#" 31..35 'self': &'? S 37..39 '{}': () - 47..48 '_': &'? (dyn Fn(S) + 'static) + 47..48 '_': &'? (dyn Fn(S) + '?) 58..60 '{}': () 71..105 '{ ...()); }': () - 77..78 'f': fn f(&'? (dyn Fn(S) + 'static)) + 77..78 'f': fn f(&'? (dyn Fn(S) + '?)) 77..102 'f(&|nu...foo())': () 79..101 '&|numb....foo()': &'? impl Fn(S) 80..101 '|numbe....foo()': impl Fn(S) @@ -2927,13 +2927,13 @@ fn test(x: &dyn Foo) { foo(x); }"#, expect![[r#" - 21..22 'x': &'? (dyn Foo + 'static) + 21..22 'x': &'? (dyn Foo + '?) 34..36 '{}': () - 46..47 'x': &'? (dyn Foo + 'static) + 46..47 'x': &'? (dyn Foo + '?) 59..74 '{ foo(x); }': () - 65..68 'foo': fn foo(&'? (dyn Foo + 'static)) + 65..68 'foo': fn foo(&'? (dyn Foo + '?)) 65..71 'foo(x)': () - 69..70 'x': &'? (dyn Foo + 'static) + 69..70 'x': &'? (dyn Foo + '?) "#]], ); } @@ -3210,13 +3210,13 @@ fn foo() { 218..324 '{ ...&s); }': () 228..229 's': Option<i32> 232..236 'None': Option<i32> - 246..247 'f': Box<dyn FnOnce(&'? Option<i32>) + 'static> - 281..310 'Box { ... {}) }': Box<dyn FnOnce(&'? Option<i32>) + 'static> + 246..247 'f': Box<dyn FnOnce(&'? Option<i32>) + '?> + 281..310 'Box { ... {}) }': Box<dyn FnOnce(&'? Option<i32>) + '?> 294..308 '&mut (|ps| {})': &'? mut impl FnOnce(&'? Option<i32>) 300..307 '|ps| {}': impl FnOnce(&'? Option<i32>) 301..303 'ps': &'? Option<i32> 305..307 '{}': () - 316..317 'f': Box<dyn FnOnce(&'? Option<i32>) + 'static> + 316..317 'f': Box<dyn FnOnce(&'? Option<i32>) + '?> 316..321 'f(&s)': () 318..320 '&s': &'? Option<i32> 319..320 's': Option<i32> @@ -4252,9 +4252,9 @@ fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) { "#, expect![[r#" 90..94 'self': &'? Self - 127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + 'static) + 127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + '?) 164..195 '{ ...f(); }': () - 170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + 'static) + 170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + '?) 170..184 'v.get::<i32>()': &'? i32 170..192 'v.get:...eref()': &'? i32 "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 4c8e635eff9..d07c1aa33b4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -1,7 +1,7 @@ //! Helper functions for working with def, which don't need to be a separate //! query, but can't be computed directly from `*Data` (ie, which need a `db`). -use std::iter; +use std::{cell::LazyCell, iter}; use base_db::Crate; use chalk_ir::{ @@ -161,11 +161,12 @@ impl Iterator for ClauseElaborator<'_> { } fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) { - let resolver = trait_.resolver(db); + let resolver = LazyCell::new(|| trait_.resolver(db)); let (generic_params, store) = db.generic_params_and_store(trait_.into()); let trait_self = generic_params.trait_self_param(); generic_params .where_predicates() + .iter() .filter_map(|pred| match pred { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { @@ -218,7 +219,7 @@ pub(super) fn associated_type_by_name_including_super_traits( name: &Name, ) -> Option<(TraitRef, TypeAliasId)> { all_super_trait_refs(db, trait_ref, |t| { - let assoc_type = db.trait_items(t.hir_trait_id()).associated_type_by_name(name)?; + let assoc_type = t.hir_trait_id().trait_items(db).associated_type_by_name(name)?; Some((t, assoc_type)) }) } diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index 2af3c2e4c35..c68ff706e48 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] rustc-hash.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 0bce69a179b..c8645b62823 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -207,7 +207,7 @@ fn resolve_assoc_or_field( // Doc paths in this context may only resolve to an item of this trait // (i.e. no items of its supertraits), so we need to handle them here // independently of others. - return db.trait_items(id).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { + return id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { let def = match *assoc_id { AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()), AssocItemId::ConstId(it) => ModuleDef::Const(it.into()), diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 074bde91fb6..aba2e032b32 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -490,7 +490,7 @@ impl<'db> AnyDiagnostic<'db> { ) -> Option<AnyDiagnostic<'db>> { match diagnostic { BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => { - let variant_data = variant.variant_data(db); + let variant_data = variant.fields(db); let missed_fields = missed_fields .into_iter() .map(|idx| variant_data.fields()[idx].name.clone()) diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 112558bdd04..2960ebedf38 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -404,7 +404,7 @@ impl HirDisplay for TupleField { impl HirDisplay for Variant { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write!(f, "{}", self.name(f.db).display(f.db, f.edition()))?; - let data = f.db.variant_fields(self.id.into()); + let data = self.id.fields(f.db); match data.shape { FieldsShape::Unit => {} FieldsShape::Tuple => { @@ -633,7 +633,7 @@ fn has_disaplayable_predicates( params: &GenericParams, store: &ExpressionStore, ) -> bool { - params.where_predicates().any(|pred| { + params.where_predicates().iter().any(|pred| { !matches!( pred, WherePredicate::TypeBound { target, .. } @@ -668,7 +668,7 @@ fn write_where_predicates( _ => false, }; - let mut iter = params.where_predicates().peekable(); + let mut iter = params.where_predicates().iter().peekable(); while let Some(pred) = iter.next() { if matches!(pred, TypeBound { target, .. } if is_unnamed_type_target(*target)) { continue; diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 3b39707cf60..e8a18169712 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -54,7 +54,7 @@ use hir_def::{ }, item_tree::ImportAlias, layout::{self, ReprOptions, TargetDataLayout}, - nameres::{self, diagnostics::DefDiagnostic}, + nameres::{self, assoc::TraitItems, diagnostics::DefDiagnostic}, per_ns::PerNs, resolver::{HasResolver, Resolver}, signatures::{ImplFlags, StaticFlags, TraitFlags, VariantFields}, @@ -649,7 +649,7 @@ impl Module { acc.extend(def.diagnostics(db, style_lints)) } ModuleDef::Trait(t) => { - for diag in db.trait_items_with_diagnostics(t.id).1.iter() { + for diag in TraitItems::query_with_diagnostics(db, t.id).1.iter() { emit_def_diagnostic(db, acc, diag, edition); } @@ -668,25 +668,25 @@ impl Module { Adt::Struct(s) => { let source_map = db.struct_signature_with_source_map(s.id).1; expr_store_diagnostics(db, acc, &source_map); - let source_map = db.variant_fields_with_source_map(s.id.into()).1; - expr_store_diagnostics(db, acc, &source_map); + let source_map = &s.id.fields_with_source_map(db).1; + expr_store_diagnostics(db, acc, source_map); push_ty_diagnostics( db, acc, db.field_types_with_diagnostics(s.id.into()).1, - &source_map, + source_map, ); } Adt::Union(u) => { let source_map = db.union_signature_with_source_map(u.id).1; expr_store_diagnostics(db, acc, &source_map); - let source_map = db.variant_fields_with_source_map(u.id.into()).1; - expr_store_diagnostics(db, acc, &source_map); + let source_map = &u.id.fields_with_source_map(db).1; + expr_store_diagnostics(db, acc, source_map); push_ty_diagnostics( db, acc, db.field_types_with_diagnostics(u.id.into()).1, - &source_map, + source_map, ); } Adt::Enum(e) => { @@ -711,14 +711,14 @@ impl Module { } } for &(v, _, _) in &variants.variants { - let source_map = db.variant_fields_with_source_map(v.into()).1; + let source_map = &v.fields_with_source_map(db).1; push_ty_diagnostics( db, acc, db.field_types_with_diagnostics(v.into()).1, - &source_map, + source_map, ); - expr_store_diagnostics(db, acc, &source_map); + expr_store_diagnostics(db, acc, source_map); } } } @@ -822,7 +822,7 @@ impl Module { // Negative impls can't have items, don't emit missing items diagnostic for them if let (false, Some(trait_)) = (impl_is_negative, trait_) { - let items = &db.trait_items(trait_.into()).items; + let items = &trait_.id.trait_items(db).items; let required_items = items.iter().filter(|&(_, assoc)| match *assoc { AssocItemId::FunctionId(it) => !db.function_signature(it).has_body(), AssocItemId::ConstId(id) => !db.const_signature(id).has_body(), @@ -1311,7 +1311,7 @@ impl AstNode for FieldSource { impl Field { pub fn name(&self, db: &dyn HirDatabase) -> Name { - db.variant_fields(self.parent.into()).fields()[self.id].name.clone() + VariantId::from(self.parent).fields(db).fields()[self.id].name.clone() } pub fn index(&self) -> usize { @@ -1380,7 +1380,7 @@ impl Field { impl HasVisibility for Field { fn visibility(&self, db: &dyn HirDatabase) -> Visibility { - let variant_data = db.variant_fields(self.parent.into()); + let variant_data = VariantId::from(self.parent).fields(db); let visibility = &variant_data.fields()[self.id].visibility; let parent_id: hir_def::VariantId = self.parent.into(); // FIXME: RawVisibility::Public doesn't need to construct a resolver @@ -1403,7 +1403,8 @@ impl Struct { } pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { - db.variant_fields(self.id.into()) + self.id + .fields(db) .fields() .iter() .map(|(id, _)| Field { parent: self.into(), id }) @@ -1434,8 +1435,8 @@ impl Struct { } } - fn variant_fields(self, db: &dyn HirDatabase) -> Arc<VariantFields> { - db.variant_fields(self.id.into()) + fn variant_fields(self, db: &dyn HirDatabase) -> &VariantFields { + self.id.fields(db) } pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { @@ -1478,7 +1479,7 @@ impl Union { } pub fn kind(self, db: &dyn HirDatabase) -> StructKind { - match db.variant_fields(self.id.into()).shape { + match self.id.fields(db).shape { hir_def::item_tree::FieldsShape::Record => StructKind::Record, hir_def::item_tree::FieldsShape::Tuple => StructKind::Tuple, hir_def::item_tree::FieldsShape::Unit => StructKind::Unit, @@ -1486,7 +1487,8 @@ impl Union { } pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { - db.variant_fields(self.id.into()) + self.id + .fields(db) .fields() .iter() .map(|(id, _)| Field { parent: self.into(), id }) @@ -1626,7 +1628,8 @@ impl Variant { } pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { - db.variant_fields(self.id.into()) + self.id + .fields(db) .fields() .iter() .map(|(id, _)| Field { parent: self.into(), id }) @@ -1634,7 +1637,7 @@ impl Variant { } pub fn kind(self, db: &dyn HirDatabase) -> StructKind { - match db.variant_fields(self.id.into()).shape { + match self.id.fields(db).shape { hir_def::item_tree::FieldsShape::Record => StructKind::Record, hir_def::item_tree::FieldsShape::Tuple => StructKind::Tuple, hir_def::item_tree::FieldsShape::Unit => StructKind::Unit, @@ -1727,10 +1730,10 @@ impl Adt { pub fn ty_with_args<'db>( self, db: &'db dyn HirDatabase, - args: impl Iterator<Item = Type<'db>>, + args: impl IntoIterator<Item = Type<'db>>, ) -> Type<'db> { let id = AdtId::from(self); - let mut it = args.map(|t| t.ty); + let mut it = args.into_iter().map(|t| t.ty); let ty = TyBuilder::def_ty(db, id.into(), None) .fill(|x| { let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); @@ -2883,7 +2886,7 @@ impl Trait { } pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq<Name>) -> Option<Function> { - db.trait_items(self.id).items.iter().find(|(n, _)| name == *n).and_then(|&(_, it)| match it + self.id.trait_items(db).items.iter().find(|(n, _)| name == *n).and_then(|&(_, it)| match it { AssocItemId::FunctionId(id) => Some(Function { id }), _ => None, @@ -2891,7 +2894,7 @@ impl Trait { } pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { - db.trait_items(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() + self.id.trait_items(db).items.iter().map(|(_name, it)| (*it).into()).collect() } pub fn items_with_supertraits(self, db: &dyn HirDatabase) -> Vec<AssocItem> { @@ -2939,7 +2942,7 @@ impl Trait { } fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> { - db.trait_items(self.id).macro_calls.to_vec().into_boxed_slice() + self.id.trait_items(db).macro_calls.to_vec().into_boxed_slice() } /// `#[rust_analyzer::completions(...)]` mode. @@ -3043,10 +3046,17 @@ pub struct BuiltinType { } impl BuiltinType { + // Constructors are added on demand, feel free to add more. pub fn str() -> BuiltinType { BuiltinType { inner: hir_def::builtin_type::BuiltinType::Str } } + pub fn i32() -> BuiltinType { + BuiltinType { + inner: hir_def::builtin_type::BuiltinType::Int(hir_ty::primitive::BuiltinInt::I32), + } + } + pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| db.all_crates()[0]); Type::new_for_crate(core, TyBuilder::builtin(self.inner)) @@ -3667,7 +3677,7 @@ impl GenericDef { let generics = db.generic_params(def); - if generics.is_empty() && generics.no_predicates() { + if generics.is_empty() && generics.has_no_predicates() { return; } @@ -5000,7 +5010,7 @@ impl<'db> Type<'db> { } let output_assoc_type = - db.trait_items(trait_).associated_type_by_name(&Name::new_symbol_root(sym::Output))?; + trait_.trait_items(db).associated_type_by_name(&Name::new_symbol_root(sym::Output))?; self.normalize_trait_assoc_type(db, &[], output_assoc_type.into()) } @@ -5013,8 +5023,8 @@ impl<'db> Type<'db> { /// This does **not** resolve `IntoIterator`, only `Iterator`. pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { let iterator_trait = LangItem::Iterator.resolve_trait(db, self.env.krate)?; - let iterator_item = db - .trait_items(iterator_trait) + let iterator_item = iterator_trait + .trait_items(db) .associated_type_by_name(&Name::new_symbol_root(sym::Item))?; self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } @@ -5044,8 +5054,8 @@ impl<'db> Type<'db> { return None; } - let into_iter_assoc_type = db - .trait_items(trait_) + let into_iter_assoc_type = trait_ + .trait_items(db) .associated_type_by_name(&Name::new_symbol_root(sym::IntoIter))?; self.normalize_trait_assoc_type(db, &[], into_iter_assoc_type.into()) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index d96975831e0..247bb693983 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2199,6 +2199,10 @@ pub struct SemanticsScope<'db> { } impl<'db> SemanticsScope<'db> { + pub fn file_id(&self) -> HirFileId { + self.file_id + } + pub fn module(&self) -> Module { Module { id: self.resolver.module() } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index fedd8239d03..e7db93d375d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -35,7 +35,7 @@ pub(crate) trait ChildBySource { impl ChildBySource for TraitId { fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) { - let data = db.trait_items(*self); + let data = self.trait_items(db); data.macro_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each( |(ast_id, call_id)| { @@ -191,7 +191,7 @@ impl ChildBySource for VariantId { Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id), } } - let (_, sm) = db.variant_fields_with_source_map(*self); + let (_, sm) = self.fields_with_source_map(db); sm.expansions().for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id)); } } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 48543ca581f..f18ca7cb201 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -156,14 +156,14 @@ impl<'db> SourceAnalyzer<'db> { InFile { file_id, .. }: InFile<&SyntaxNode>, _offset: Option<TextSize>, ) -> SourceAnalyzer<'db> { - let (fields, source_map) = db.variant_fields_with_source_map(def); + let (fields, source_map) = def.fields_with_source_map(db); let resolver = def.resolver(db); SourceAnalyzer { resolver, body_or_sig: Some(BodyOrSig::VariantFields { def, store: fields.store.clone(), - source_map, + source_map: source_map.clone(), }), file_id, } @@ -713,7 +713,7 @@ impl<'db> SourceAnalyzer<'db> { }; let (adt, subst) = self.infer()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer()?.variant_resolution_for_expr_or_pat(expr_id)?; - let variant_data = variant.variant_data(db); + let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); @@ -734,7 +734,7 @@ impl<'db> SourceAnalyzer<'db> { let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer()?.variant_resolution_for_pat(pat_id.as_pat()?)?; - let variant_data = variant.variant_data(db); + let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; let (adt, subst) = self.infer()?.type_of_pat.get(pat_id.as_pat()?)?.as_adt()?; let field_ty = @@ -803,8 +803,8 @@ impl<'db> SourceAnalyzer<'db> { }; container = Either::Right(db.normalize_projection(projection, trait_env.clone())); } - let handle_variants = |variant, subst: &Substitution, container: &mut _| { - let fields = db.variant_fields(variant); + let handle_variants = |variant: VariantId, subst: &Substitution, container: &mut _| { + let fields = variant.fields(db); let field = fields.field(&field_name.as_name())?; let field_types = db.field_types(variant); *container = Either::Right(field_types[field].clone().substitute(Interner, subst)); @@ -1423,7 +1423,7 @@ impl<'db> SourceAnalyzer<'db> { method_name: &Name, ) -> Option<(TraitId, FunctionId)> { let trait_id = lang_trait.resolve_trait(db, self.resolver.krate())?; - let fn_id = db.trait_items(trait_id).method_by_name(method_name)?; + let fn_id = trait_id.trait_items(db).method_by_name(method_name)?; Some((trait_id, fn_id)) } @@ -1580,7 +1580,7 @@ fn resolve_hir_path_( // within the trait's associated types. if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) { if let Some(type_alias_id) = - db.trait_items(trait_id).associated_type_by_name(unresolved.name) + trait_id.trait_items(db).associated_type_by_name(unresolved.name) { return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); } @@ -1731,7 +1731,7 @@ fn resolve_hir_path_qualifier( // within the trait's associated types. if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) { if let Some(type_alias_id) = - db.trait_items(trait_id).associated_type_by_name(unresolved.name) + trait_id.trait_items(db).associated_type_by_name(unresolved.name) { return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); } diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 64f2a910bd1..756650891d4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -334,7 +334,7 @@ impl<'a> SymbolCollector<'a> { fn collect_from_trait(&mut self, trait_id: TraitId, trait_do_not_complete: Complete) { let trait_data = self.db.trait_signature(trait_id); self.with_container_name(Some(trait_data.name.as_str().into()), |s| { - for &(ref name, assoc_item_id) in &self.db.trait_items(trait_id).items { + for &(ref name, assoc_item_id) in &trait_id.trait_items(self.db).items { s.push_assoc_item(assoc_item_id, name, Some(trait_do_not_complete)); } }); diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index 53af980c194..385b0e1eb7c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs index 3e6d0bec68a..517906b429a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs @@ -1,3 +1,5 @@ +use std::slice; + use ide_db::assists::GroupLabel; use stdx::to_lower_snake_case; use syntax::ast::HasVisibility; @@ -52,7 +54,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext<'_> let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text())); // Return early if we've found an existing new fn - let impl_def = find_struct_impl(ctx, &parent_enum, &[fn_name.clone()])?; + let impl_def = find_struct_impl(ctx, &parent_enum, slice::from_ref(&fn_name))?; let target = variant.syntax().text_range(); acc.add_group( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 3974bcf6187..e4b0f830497 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs @@ -1,3 +1,5 @@ +use std::slice; + use ide_db::assists::GroupLabel; use itertools::Itertools; use stdx::to_lower_snake_case; @@ -148,7 +150,7 @@ fn generate_enum_projection_method( let fn_name = format!("{fn_name_prefix}_{}", &to_lower_snake_case(&variant_name.text())); // Return early if we've found an existing new fn - let impl_def = find_struct_impl(ctx, &parent_enum, &[fn_name.clone()])?; + let impl_def = find_struct_impl(ctx, &parent_enum, slice::from_ref(&fn_name))?; let target = variant.syntax().text_range(); acc.add_group( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs index 019ddaf1441..6527d3706e2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs @@ -100,9 +100,7 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!() }"#, fn test_complete_todo_with_msg() { check_assist( term_search, - // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure. - // Should implement super let and remove `fmt_before_1_89_0` - r#"//- minicore: todo, unimplemented, fmt_before_1_89_0 + r#"//- minicore: todo, unimplemented fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#, r#"fn f() { let a: u128 = 1; let b: u128 = a }"#, ) @@ -112,10 +110,8 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#, fn test_complete_unimplemented_with_msg() { check_assist( term_search, - // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure. - // Should implement super let and remove `fmt_before_1_89_0` - r#"//- minicore: todo, unimplemented, fmt_before_1_89_0 -fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#, + r#"//- minicore: todo, unimplemented +fn f() { let a: u128 = 1; let b: u128 = unimplemented$0!("asd") }"#, r#"fn f() { let a: u128 = 1; let b: u128 = a }"#, ) } @@ -124,10 +120,8 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#, fn test_complete_unimplemented() { check_assist( term_search, - // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure. - // Should implement super let and remove `fmt_before_1_89_0` - r#"//- minicore: todo, unimplemented, fmt_before_1_89_0 -fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#, + r#"//- minicore: todo, unimplemented +fn f() { let a: u128 = 1; let b: u128 = unimplemented$0!() }"#, r#"fn f() { let a: u128 = 1; let b: u128 = a }"#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs index 9ea78719b20..d7189aa5dbb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -56,7 +56,8 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op }; let type_ref = &ret_type.ty()?; - let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); + let ty = ctx.sema.resolve_type(type_ref)?; + let ty_adt = ty.as_adt(); let famous_defs = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()); for kind in WrapperKind::ALL { @@ -64,7 +65,7 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op continue; }; - if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == core_wrapper) { + if matches!(ty_adt, Some(hir::Adt::Enum(ret_type)) if ret_type == core_wrapper) { // The return type is already wrapped cov_mark::hit!(wrap_return_type_simple_return_type_already_wrapped); continue; @@ -78,10 +79,23 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op |builder| { let mut editor = builder.make_editor(&parent); let make = SyntaxFactory::with_mappings(); - let alias = wrapper_alias(ctx, &make, &core_wrapper, type_ref, kind.symbol()); - let new_return_ty = alias.unwrap_or_else(|| match kind { - WrapperKind::Option => make.ty_option(type_ref.clone()), - WrapperKind::Result => make.ty_result(type_ref.clone(), make.ty_infer().into()), + let alias = wrapper_alias(ctx, &make, core_wrapper, type_ref, &ty, kind.symbol()); + let (ast_new_return_ty, semantic_new_return_ty) = alias.unwrap_or_else(|| { + let (ast_ty, ty_constructor) = match kind { + WrapperKind::Option => { + (make.ty_option(type_ref.clone()), famous_defs.core_option_Option()) + } + WrapperKind::Result => ( + make.ty_result(type_ref.clone(), make.ty_infer().into()), + famous_defs.core_result_Result(), + ), + }; + let semantic_ty = ty_constructor + .map(|ty_constructor| { + hir::Adt::from(ty_constructor).ty_with_args(ctx.db(), [ty.clone()]) + }) + .unwrap_or_else(|| ty.clone()); + (ast_ty, semantic_ty) }); let mut exprs_to_wrap = Vec::new(); @@ -96,6 +110,17 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op for_each_tail_expr(&body_expr, tail_cb); for ret_expr_arg in exprs_to_wrap { + if let Some(ty) = ctx.sema.type_of_expr(&ret_expr_arg) { + if ty.adjusted().could_unify_with(ctx.db(), &semantic_new_return_ty) { + // The type is already correct, don't wrap it. + // We deliberately don't use `could_unify_with_deeply()`, because as long as the outer + // enum matches it's okay for us, as we don't trigger the assist if the return type + // is already `Option`/`Result`, so mismatched exact type is more likely a mistake + // than something intended. + continue; + } + } + let happy_wrapped = make.expr_call( make.expr_path(make.ident_path(kind.happy_ident())), make.arg_list(iter::once(ret_expr_arg.clone())), @@ -103,12 +128,12 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op editor.replace(ret_expr_arg.syntax(), happy_wrapped.syntax()); } - editor.replace(type_ref.syntax(), new_return_ty.syntax()); + editor.replace(type_ref.syntax(), ast_new_return_ty.syntax()); if let WrapperKind::Result = kind { // Add a placeholder snippet at the first generic argument that doesn't equal the return type. // This is normally the error type, but that may not be the case when we inserted a type alias. - let args = new_return_ty + let args = ast_new_return_ty .path() .unwrap() .segment() @@ -188,27 +213,28 @@ impl WrapperKind { } // Try to find an wrapper type alias in the current scope (shadowing the default). -fn wrapper_alias( - ctx: &AssistContext<'_>, +fn wrapper_alias<'db>( + ctx: &AssistContext<'db>, make: &SyntaxFactory, - core_wrapper: &hir::Enum, - ret_type: &ast::Type, + core_wrapper: hir::Enum, + ast_ret_type: &ast::Type, + semantic_ret_type: &hir::Type<'db>, wrapper: hir::Symbol, -) -> Option<ast::PathType> { +) -> Option<(ast::PathType, hir::Type<'db>)> { let wrapper_path = hir::ModPath::from_segments( hir::PathKind::Plain, iter::once(hir::Name::new_symbol_root(wrapper)), ); - ctx.sema.resolve_mod_path(ret_type.syntax(), &wrapper_path).and_then(|def| { + ctx.sema.resolve_mod_path(ast_ret_type.syntax(), &wrapper_path).and_then(|def| { def.filter_map(|def| match def.into_module_def() { hir::ModuleDef::TypeAlias(alias) => { let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?; - (&enum_ty == core_wrapper).then_some(alias) + (enum_ty == core_wrapper).then_some((alias, enum_ty)) } _ => None, }) - .find_map(|alias| { + .find_map(|(alias, enum_ty)| { let mut inserted_ret_type = false; let generic_args = alias.source(ctx.db())?.value.generic_param_list()?.generic_params().map(|param| { @@ -216,7 +242,7 @@ fn wrapper_alias( // Replace the very first type parameter with the function's return type. ast::GenericParam::TypeParam(_) if !inserted_ret_type => { inserted_ret_type = true; - make.type_arg(ret_type.clone()).into() + make.type_arg(ast_ret_type.clone()).into() } ast::GenericParam::LifetimeParam(_) => { make.lifetime_arg(make.lifetime("'_")).into() @@ -231,7 +257,10 @@ fn wrapper_alias( make.path_segment_generics(make.name_ref(name.as_str()), generic_arg_list), ); - Some(make.ty_path(path)) + let new_ty = + hir::Adt::from(enum_ty).ty_with_args(ctx.db(), [semantic_ret_type.clone()]); + + Some((make.ty_path(path), new_ty)) }) }) } @@ -605,29 +634,39 @@ fn foo() -> Option<i32> { check_assist_by_label( wrap_return_type, r#" -//- minicore: option +//- minicore: option, future +struct F(i32); +impl core::future::Future for F { + type Output = i32; + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> core::task::Poll<Self::Output> { 0 } +} async fn foo() -> i$032 { if true { if false { - 1.await + F(1).await } else { - 2.await + F(2).await } } else { - 24i32.await + F(24i32).await } } "#, r#" +struct F(i32); +impl core::future::Future for F { + type Output = i32; + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> core::task::Poll<Self::Output> { 0 } +} async fn foo() -> Option<i32> { if true { if false { - Some(1.await) + Some(F(1).await) } else { - Some(2.await) + Some(F(2).await) } } else { - Some(24i32.await) + Some(F(24i32).await) } } "#, @@ -1666,29 +1705,39 @@ fn foo() -> Result<i32, ${0:_}> { check_assist_by_label( wrap_return_type, r#" -//- minicore: result +//- minicore: result, future +struct F(i32); +impl core::future::Future for F { + type Output = i32; + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> core::task::Poll<Self::Output> { 0 } +} async fn foo() -> i$032 { if true { if false { - 1.await + F(1).await } else { - 2.await + F(2).await } } else { - 24i32.await + F(24i32).await } } "#, r#" +struct F(i32); +impl core::future::Future for F { + type Output = i32; + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> core::task::Poll<Self::Output> { 0 } +} async fn foo() -> Result<i32, ${0:_}> { if true { if false { - Ok(1.await) + Ok(F(1).await) } else { - Ok(2.await) + Ok(F(2).await) } } else { - Ok(24i32.await) + Ok(F(24i32).await) } } "#, @@ -2460,4 +2509,54 @@ fn foo() -> Result<i32, ${0:_}> { WrapperKind::Result.label(), ); } + + #[test] + fn already_wrapped() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + if false { + 0 + } else { + Some(1) + } +} + "#, + r#" +fn foo() -> Option<i32> { + if false { + Some(0) + } else { + Some(1) + } +} + "#, + WrapperKind::Option.label(), + ); + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + if false { + 0 + } else { + Ok(1) + } +} + "#, + r#" +fn foo() -> Result<i32, ${0:_}> { + if false { + Ok(0) + } else { + Ok(1) + } +} + "#, + WrapperKind::Result.label(), + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index 94c01e333ed..9bad21fc8e9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index 6d1e973dc4c..809e71cc119 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -195,5 +195,5 @@ fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> String matches!(prev_token_kind, SyntaxKind::COMMA | SyntaxKind::L_PAREN | SyntaxKind::PIPE); let leading = if has_leading_comma { "" } else { ", " }; - Some((move |label: &_| (format!("{leading}{label}{trailing}")), param.text_range())) + Some((move |label: &_| format!("{leading}{label}{trailing}"), param.text_range())) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6e3a76f346a..ea5fb39338b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -4,6 +4,7 @@ use std::iter; use hir::{ExpandResult, InFile, Semantics, Type, TypeInfo, Variant}; use ide_db::{RootDatabase, active_parameter::ActiveParameter}; use itertools::Either; +use stdx::always; use syntax::{ AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, TextRange, TextSize, @@ -869,8 +870,15 @@ fn classify_name_ref<'db>( return None; } + let mut receiver_ty = receiver.as_ref().and_then(|it| sema.type_of_expr(it)); + if receiver_is_ambiguous_float_literal { + // `123.|` is parsed as a float but should actually be an integer. + always!(receiver_ty.as_ref().is_none_or(|receiver_ty| receiver_ty.original.is_float())); + receiver_ty = Some(TypeInfo { original: hir::BuiltinType::i32().ty(sema.db), adjusted: None }); + } + let kind = NameRefKind::DotAccess(DotAccess { - receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), + receiver_ty, kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal }, receiver, ctx: DotAccessExprCtx { in_block_expr: is_in_block(field.syntax()), in_breakable: is_in_breakable(field.syntax()) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index b2d18b796f1..33f729f0166 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2241,3 +2241,37 @@ fn main() { "#, ); } + +#[test] +fn ambiguous_float_literal() { + check( + r#" +#![rustc_coherence_is_core] + +impl i32 { + pub fn int_method(self) {} +} +impl f64 { + pub fn float_method(self) {} +} + +fn foo() { + 1.$0 +} + "#, + expect![[r#" + me int_method() fn(self) + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index fcdf10c8561..179d6693602 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -550,3 +550,30 @@ fn inside_extern_blocks() { "#]], ) } + +#[test] +fn tokens_from_macro() { + check_edit( + "fn as_ref", + r#" +//- proc_macros: identity +//- minicore: as_ref +struct Foo; + +#[proc_macros::identity] +impl<'a> AsRef<&'a i32> for Foo { + $0 +} + "#, + r#" +struct Foo; + +#[proc_macros::identity] +impl<'a> AsRef<&'a i32> for Foo { + fn as_ref(&self) -> &&'a i32 { + $0 +} +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 125e11e9e35..c7e2d058257 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -429,18 +429,18 @@ trait Tr<T> { impl Tr<$0 "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - sp Self dyn Tr<{unknown}> + 'static - st Record Record - st S S - st Tuple Tuple - st Unit Unit + sp Self dyn Tr<{unknown}> + st Record Record + st S S + st Tuple Tuple + st Unit Unit tt Tr tt Trait - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 kw crate:: kw self:: "#]], diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index acde1d665da..e065adb0f0b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 232648af661..0ab880bcfe7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -2,7 +2,10 @@ use crate::helpers::mod_path_to_ast; use either::Either; -use hir::{AsAssocItem, HirDisplay, ImportPathConfig, ModuleDef, SemanticsScope}; +use hir::{ + AsAssocItem, HirDisplay, HirFileId, ImportPathConfig, ModuleDef, SemanticsScope, + prettify_macro_expansion, +}; use itertools::Itertools; use rustc_hash::FxHashMap; use span::Edition; @@ -136,6 +139,25 @@ impl<'a> PathTransform<'a> { } } + fn prettify_target_node(&self, node: SyntaxNode) -> SyntaxNode { + match self.target_scope.file_id() { + HirFileId::FileId(_) => node, + HirFileId::MacroFile(file_id) => { + let db = self.target_scope.db; + prettify_macro_expansion( + db, + node, + &db.expansion_span_map(file_id), + self.target_scope.module().krate().into(), + ) + } + } + } + + fn prettify_target_ast<N: AstNode>(&self, node: N) -> N { + N::cast(self.prettify_target_node(node.syntax().clone())).unwrap() + } + fn build_ctx(&self) -> Ctx<'a> { let db = self.source_scope.db; let target_module = self.target_scope.module(); @@ -163,7 +185,7 @@ impl<'a> PathTransform<'a> { .for_each(|(k, v)| match (k.split(db), v) { (Either::Right(k), Some(TypeOrConst::Either(v))) => { if let Some(ty) = v.ty() { - type_substs.insert(k, ty); + type_substs.insert(k, self.prettify_target_ast(ty)); } } (Either::Right(k), None) => { @@ -178,7 +200,7 @@ impl<'a> PathTransform<'a> { } (Either::Left(k), Some(TypeOrConst::Either(v))) => { if let Some(ty) = v.ty() { - const_substs.insert(k, ty.syntax().clone()); + const_substs.insert(k, self.prettify_target_node(ty.syntax().clone())); } } (Either::Left(k), Some(TypeOrConst::Const(v))) => { @@ -189,7 +211,7 @@ impl<'a> PathTransform<'a> { // and sometimes require slight modifications; see // https://doc.rust-lang.org/reference/statements.html#expression-statements // (default values in curly brackets can cause the same problem) - const_substs.insert(k, expr.syntax().clone()); + const_substs.insert(k, self.prettify_target_node(expr.syntax().clone())); } } (Either::Left(k), None) => { @@ -204,6 +226,7 @@ impl<'a> PathTransform<'a> { } _ => (), // ignore mismatching params }); + // No need to prettify lifetimes, there's nothing to prettify. let lifetime_substs: FxHashMap<_, _> = self .generic_def .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index 96be51e1b26..6f1e66948f4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index e2957fcaefb..ac54ac0950f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1171,7 +1171,7 @@ trait B {} fn test(a: &dyn A) -> &dyn B { a - //^ error: expected &(dyn B + 'static), found &(dyn A + 'static) + //^ error: expected &dyn B, found &dyn A } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml index 1212fa9f9c6..0620bd26fef 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 2f8ed88fbb5..06d2776ebe8 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index 194e8c968f7..9bd8504733a 100755 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -2,7 +2,7 @@ use ide_db::{FxHashSet, syntax_helpers::node_ext::vis_eq}; use syntax::{ Direction, NodeOrToken, SourceFile, SyntaxKind::{self, *}, - TextRange, TextSize, + SyntaxNode, TextRange, TextSize, ast::{self, AstNode, AstToken}, match_ast, }; @@ -16,16 +16,21 @@ const REGION_END: &str = "// endregion"; pub enum FoldKind { Comment, Imports, - Mods, + Region, Block, ArgList, - Region, - Consts, - Statics, Array, WhereClause, ReturnType, MatchArm, + // region: item runs + Modules, + Consts, + Statics, + TypeAliases, + TraitAliases, + ExternCrates, + // endregion: item runs } #[derive(Debug)] @@ -41,10 +46,7 @@ pub struct Fold { pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { let mut res = vec![]; let mut visited_comments = FxHashSet::default(); - let mut visited_imports = FxHashSet::default(); - let mut visited_mods = FxHashSet::default(); - let mut visited_consts = FxHashSet::default(); - let mut visited_statics = FxHashSet::default(); + let mut visited_nodes = FxHashSet::default(); // regions can be nested, here is a LIFO buffer let mut region_starts: Vec<TextSize> = vec![]; @@ -93,30 +95,40 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { if module.item_list().is_none() { if let Some(range) = contiguous_range_for_item_group( module, - &mut visited_mods, + &mut visited_nodes, ) { - res.push(Fold { range, kind: FoldKind::Mods }) + res.push(Fold { range, kind: FoldKind::Modules }) } } }, ast::Use(use_) => { - if let Some(range) = contiguous_range_for_item_group(use_, &mut visited_imports) { + if let Some(range) = contiguous_range_for_item_group(use_, &mut visited_nodes) { res.push(Fold { range, kind: FoldKind::Imports }) } }, ast::Const(konst) => { - if let Some(range) = contiguous_range_for_item_group(konst, &mut visited_consts) { + if let Some(range) = contiguous_range_for_item_group(konst, &mut visited_nodes) { res.push(Fold { range, kind: FoldKind::Consts }) } }, ast::Static(statik) => { - if let Some(range) = contiguous_range_for_item_group(statik, &mut visited_statics) { + if let Some(range) = contiguous_range_for_item_group(statik, &mut visited_nodes) { res.push(Fold { range, kind: FoldKind::Statics }) } }, - ast::WhereClause(where_clause) => { - if let Some(range) = fold_range_for_where_clause(where_clause) { - res.push(Fold { range, kind: FoldKind::WhereClause }) + ast::TypeAlias(alias) => { + if let Some(range) = contiguous_range_for_item_group(alias, &mut visited_nodes) { + res.push(Fold { range, kind: FoldKind::TypeAliases }) + } + }, + ast::TraitAlias(alias) => { + if let Some(range) = contiguous_range_for_item_group(alias, &mut visited_nodes) { + res.push(Fold { range, kind: FoldKind::TraitAliases }) + } + }, + ast::ExternCrate(extern_crate) => { + if let Some(range) = contiguous_range_for_item_group(extern_crate, &mut visited_nodes) { + res.push(Fold { range, kind: FoldKind::ExternCrates }) } }, ast::MatchArm(match_arm) => { @@ -137,9 +149,10 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { match kind { COMMENT => Some(FoldKind::Comment), - ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), + ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList), ARRAY_EXPR => Some(FoldKind::Array), RET_TYPE => Some(FoldKind::ReturnType), + WHERE_CLAUSE => Some(FoldKind::WhereClause), ASSOC_ITEM_LIST | RECORD_FIELD_LIST | RECORD_PAT_FIELD_LIST @@ -155,11 +168,14 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { } } -fn contiguous_range_for_item_group<N>(first: N, visited: &mut FxHashSet<N>) -> Option<TextRange> +fn contiguous_range_for_item_group<N>( + first: N, + visited: &mut FxHashSet<SyntaxNode>, +) -> Option<TextRange> where N: ast::HasVisibility + Clone + Hash + Eq, { - if !visited.insert(first.clone()) { + if !visited.insert(first.syntax().clone()) { return None; } @@ -183,7 +199,7 @@ where if let Some(next) = N::cast(node) { let next_vis = next.visibility(); if eq_visibility(next_vis.clone(), last_vis) { - visited.insert(next.clone()); + visited.insert(next.syntax().clone()); last_vis = next_vis; last = next; continue; @@ -259,18 +275,6 @@ fn contiguous_range_for_comment( } } -fn fold_range_for_where_clause(where_clause: ast::WhereClause) -> Option<TextRange> { - let first_where_pred = where_clause.predicates().next(); - let last_where_pred = where_clause.predicates().last(); - - if first_where_pred != last_where_pred { - let start = where_clause.where_token()?.text_range().end(); - let end = where_clause.syntax().text_range().end(); - return Some(TextRange::new(start, end)); - } - None -} - fn fold_range_for_multiline_match_arm(match_arm: ast::MatchArm) -> Option<TextRange> { if fold_kind(match_arm.expr()?.syntax().kind()).is_some() { None @@ -307,16 +311,19 @@ mod tests { let kind = match fold.kind { FoldKind::Comment => "comment", FoldKind::Imports => "imports", - FoldKind::Mods => "mods", + FoldKind::Modules => "mods", FoldKind::Block => "block", FoldKind::ArgList => "arglist", FoldKind::Region => "region", FoldKind::Consts => "consts", FoldKind::Statics => "statics", + FoldKind::TypeAliases => "typealiases", FoldKind::Array => "array", FoldKind::WhereClause => "whereclause", FoldKind::ReturnType => "returntype", FoldKind::MatchArm => "matcharm", + FoldKind::TraitAliases => "traitaliases", + FoldKind::ExternCrates => "externcrates", }; assert_eq!(kind, &attr.unwrap()); } @@ -594,19 +601,18 @@ static SECOND_STATIC: &str = "second";</fold> #[test] fn fold_where_clause() { - // fold multi-line and don't fold single line. check( r#" fn foo() -where<fold whereclause> +<fold whereclause>where A: Foo, B: Foo, C: Foo, D: Foo,</fold> {} fn bar() -where - A: Bar, {} +<fold whereclause>where + A: Bar,</fold> {} "#, ) } @@ -624,4 +630,16 @@ fn bar() -> (bool, bool) { (true, true) } "#, ) } + + #[test] + fn fold_generics() { + check( + r#" +type Foo<T, U> = foo<fold arglist>< + T, + U, +></fold>; +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 574803fb9e8..fd465f31d43 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -291,13 +291,14 @@ fn handle_control_flow_keywords( token: &SyntaxToken, ) -> Option<Vec<NavigationTarget>> { match token.kind() { - // For `fn` / `loop` / `while` / `for` / `async`, return the keyword it self, + // For `fn` / `loop` / `while` / `for` / `async` / `match`, return the keyword it self, // so that VSCode will find the references when using `ctrl + click` T![fn] | T![async] | T![try] | T![return] => nav_for_exit_points(sema, token), T![loop] | T![while] | T![break] | T![continue] => nav_for_break_points(sema, token), T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => { nav_for_break_points(sema, token) } + T![match] | T![=>] | T![if] => nav_for_branch_exit_points(sema, token), _ => None, } } @@ -407,6 +408,91 @@ fn nav_for_exit_points( Some(navs) } +pub(crate) fn find_branch_root( + sema: &Semantics<'_, RootDatabase>, + token: &SyntaxToken, +) -> Vec<SyntaxNode> { + let find_nodes = |node_filter: fn(SyntaxNode) -> Option<SyntaxNode>| { + sema.descend_into_macros(token.clone()) + .into_iter() + .filter_map(|token| node_filter(token.parent()?)) + .collect_vec() + }; + + match token.kind() { + T![match] => find_nodes(|node| Some(ast::MatchExpr::cast(node)?.syntax().clone())), + T![=>] => find_nodes(|node| Some(ast::MatchArm::cast(node)?.syntax().clone())), + T![if] => find_nodes(|node| { + let if_expr = ast::IfExpr::cast(node)?; + + let root_if = iter::successors(Some(if_expr.clone()), |if_expr| { + let parent_if = if_expr.syntax().parent().and_then(ast::IfExpr::cast)?; + let ast::ElseBranch::IfExpr(else_branch) = parent_if.else_branch()? else { + return None; + }; + + (else_branch.syntax() == if_expr.syntax()).then_some(parent_if) + }) + .last()?; + + Some(root_if.syntax().clone()) + }), + _ => vec![], + } +} + +fn nav_for_branch_exit_points( + sema: &Semantics<'_, RootDatabase>, + token: &SyntaxToken, +) -> Option<Vec<NavigationTarget>> { + let db = sema.db; + + let navs = match token.kind() { + T![match] => find_branch_root(sema, token) + .into_iter() + .filter_map(|node| { + let file_id = sema.hir_file_for(&node); + let match_expr = ast::MatchExpr::cast(node)?; + let focus_range = match_expr.match_token()?.text_range(); + let match_expr_in_file = InFile::new(file_id, match_expr.into()); + Some(expr_to_nav(db, match_expr_in_file, Some(focus_range))) + }) + .flatten() + .collect_vec(), + + T![=>] => find_branch_root(sema, token) + .into_iter() + .filter_map(|node| { + let match_arm = ast::MatchArm::cast(node)?; + let match_expr = sema + .ancestors_with_macros(match_arm.syntax().clone()) + .find_map(ast::MatchExpr::cast)?; + let file_id = sema.hir_file_for(match_expr.syntax()); + let focus_range = match_arm.fat_arrow_token()?.text_range(); + let match_expr_in_file = InFile::new(file_id, match_expr.into()); + Some(expr_to_nav(db, match_expr_in_file, Some(focus_range))) + }) + .flatten() + .collect_vec(), + + T![if] => find_branch_root(sema, token) + .into_iter() + .filter_map(|node| { + let file_id = sema.hir_file_for(&node); + let if_expr = ast::IfExpr::cast(node)?; + let focus_range = if_expr.if_token()?.text_range(); + let if_expr_in_file = InFile::new(file_id, if_expr.into()); + Some(expr_to_nav(db, if_expr_in_file, Some(focus_range))) + }) + .flatten() + .collect_vec(), + + _ => return Some(Vec::new()), + }; + + Some(navs) +} + pub(crate) fn find_loops( sema: &Semantics<'_, RootDatabase>, token: &SyntaxToken, @@ -3614,4 +3700,155 @@ fn foo() { "#, ); } + + #[test] + fn goto_def_for_match_keyword() { + check( + r#" +fn main() { + match$0 0 { + // ^^^^^ + 0 => {}, + _ => {}, + } +} +"#, + ); + } + + #[test] + fn goto_def_for_match_arm_fat_arrow() { + check( + r#" +fn main() { + match 0 { + 0 =>$0 {}, + // ^^ + _ => {}, + } +} +"#, + ); + } + + #[test] + fn goto_def_for_if_keyword() { + check( + r#" +fn main() { + if$0 true { + // ^^ + () + } +} +"#, + ); + } + + #[test] + fn goto_def_for_match_nested_in_if() { + check( + r#" +fn main() { + if true { + match$0 0 { + // ^^^^^ + 0 => {}, + _ => {}, + } + } +} +"#, + ); + } + + #[test] + fn goto_def_for_multiple_match_expressions() { + check( + r#" +fn main() { + match 0 { + 0 => {}, + _ => {}, + }; + + match$0 1 { + // ^^^^^ + 1 => {}, + _ => {}, + } +} +"#, + ); + } + + #[test] + fn goto_def_for_nested_match_expressions() { + check( + r#" +fn main() { + match 0 { + 0 => match$0 1 { + // ^^^^^ + 1 => {}, + _ => {}, + }, + _ => {}, + } +} +"#, + ); + } + + #[test] + fn goto_def_for_if_else_chains() { + check( + r#" +fn main() { + if true { + // ^^ + () + } else if$0 false { + () + } else { + () + } +} +"#, + ); + } + + #[test] + fn goto_def_for_match_with_guards() { + check( + r#" +fn main() { + match 42 { + x if x > 0 =>$0 {}, + // ^^ + _ => {}, + } +} +"#, + ); + } + + #[test] + fn goto_def_for_match_with_macro_arm() { + check( + r#" +macro_rules! arm { + () => { 0 => {} }; +} + +fn main() { + match$0 0 { + // ^^^^^ + arm!(), + _ => {}, + } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index 86d72fefe05..b80e81d39c6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -70,11 +70,10 @@ pub(crate) fn goto_type_definition( } let range = token.text_range(); - sema.descend_into_macros_no_opaque(token,false) + sema.descend_into_macros_no_opaque(token, false) .into_iter() .filter_map(|token| { - sema - .token_ancestors_with_macros(token.value) + sema.token_ancestors_with_macros(token.value) // When `token` is within a macro call, we can't determine its type. Don't continue // this traversal because otherwise we'll end up returning the type of *that* macro // call, which is not what we want in general. @@ -103,7 +102,6 @@ pub(crate) fn goto_type_definition( _ => return None, } }; - Some(ty) }) }) diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index aa947921a9b..356bd69aa44 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -37,8 +37,11 @@ pub struct HighlightRelatedConfig { pub break_points: bool, pub closure_captures: bool, pub yield_points: bool, + pub branch_exit_points: bool, } +type HighlightMap = FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>; + // Feature: Highlight Related // // Highlights constructs related to the thing under the cursor: @@ -64,7 +67,7 @@ pub(crate) fn highlight_related( let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?` - T![->] => 4, + T![->] | T![=>] => 4, kind if kind.is_keyword(file_id.edition(sema.db)) => 3, IDENT | INT_NUMBER => 2, T![|] => 1, @@ -78,6 +81,9 @@ pub(crate) fn highlight_related( T![fn] | T![return] | T![->] if config.exit_points => { highlight_exit_points(sema, token).remove(&file_id) } + T![match] | T![=>] | T![if] if config.branch_exit_points => { + highlight_branch_exit_points(sema, token).remove(&file_id) + } T![await] | T![async] if config.yield_points => { highlight_yield_points(sema, token).remove(&file_id) } @@ -300,11 +306,93 @@ fn highlight_references( if res.is_empty() { None } else { Some(res.into_iter().collect()) } } +pub(crate) fn highlight_branch_exit_points( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> { + let mut highlights: HighlightMap = FxHashMap::default(); + + let push_to_highlights = |file_id, range, highlights: &mut HighlightMap| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().insert(hrange); + } + }; + + let push_tail_expr = |tail: Option<ast::Expr>, highlights: &mut HighlightMap| { + let Some(tail) = tail else { + return; + }; + + for_each_tail_expr(&tail, &mut |tail| { + let file_id = sema.hir_file_for(tail.syntax()); + let range = tail.syntax().text_range(); + push_to_highlights(file_id, Some(range), highlights); + }); + }; + + let nodes = goto_definition::find_branch_root(sema, &token).into_iter(); + match token.kind() { + T![match] => { + for match_expr in nodes.filter_map(ast::MatchExpr::cast) { + let file_id = sema.hir_file_for(match_expr.syntax()); + let range = match_expr.match_token().map(|token| token.text_range()); + push_to_highlights(file_id, range, &mut highlights); + + let Some(arm_list) = match_expr.match_arm_list() else { + continue; + }; + for arm in arm_list.arms() { + push_tail_expr(arm.expr(), &mut highlights); + } + } + } + T![=>] => { + for arm in nodes.filter_map(ast::MatchArm::cast) { + let file_id = sema.hir_file_for(arm.syntax()); + let range = arm.fat_arrow_token().map(|token| token.text_range()); + push_to_highlights(file_id, range, &mut highlights); + + push_tail_expr(arm.expr(), &mut highlights); + } + } + T![if] => { + for mut if_to_process in nodes.map(ast::IfExpr::cast) { + while let Some(cur_if) = if_to_process.take() { + let file_id = sema.hir_file_for(cur_if.syntax()); + + let if_kw_range = cur_if.if_token().map(|token| token.text_range()); + push_to_highlights(file_id, if_kw_range, &mut highlights); + + if let Some(then_block) = cur_if.then_branch() { + push_tail_expr(Some(then_block.into()), &mut highlights); + } + + match cur_if.else_branch() { + Some(ast::ElseBranch::Block(else_block)) => { + push_tail_expr(Some(else_block.into()), &mut highlights); + if_to_process = None; + } + Some(ast::ElseBranch::IfExpr(nested_if)) => if_to_process = Some(nested_if), + None => if_to_process = None, + } + } + } + } + _ => {} + } + + highlights + .into_iter() + .map(|(file_id, ranges)| (file_id, ranges.into_iter().collect())) + .collect() +} + fn hl_exit_points( sema: &Semantics<'_, RootDatabase>, def_token: Option<SyntaxToken>, body: ast::Expr, -) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> { +) -> Option<HighlightMap> { let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default(); let mut push_to_highlights = |file_id, range| { @@ -411,7 +499,7 @@ pub(crate) fn highlight_break_points( loop_token: Option<SyntaxToken>, label: Option<ast::Label>, expr: ast::Expr, - ) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> { + ) -> Option<HighlightMap> { let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default(); let mut push_to_highlights = |file_id, range| { @@ -504,7 +592,7 @@ pub(crate) fn highlight_yield_points( sema: &Semantics<'_, RootDatabase>, async_token: Option<SyntaxToken>, body: Option<ast::Expr>, - ) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> { + ) -> Option<HighlightMap> { let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default(); let mut push_to_highlights = |file_id, range| { @@ -597,10 +685,7 @@ fn original_frange( InFile::new(file_id, text_range?).original_node_file_range_opt(db).map(|(frange, _)| frange) } -fn merge_map( - res: &mut FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>, - new: Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>>, -) { +fn merge_map(res: &mut HighlightMap, new: Option<HighlightMap>) { let Some(new) = new else { return; }; @@ -750,6 +835,7 @@ mod tests { references: true, closure_captures: true, yield_points: true, + branch_exit_points: true, }; #[track_caller] @@ -2135,6 +2221,62 @@ fn main() { } #[test] + fn nested_match() { + check( + r#" +fn main() { + match$0 0 { + // ^^^^^ + 0 => match 1 { + 1 => 2, + // ^ + _ => 3, + // ^ + }, + _ => 4, + // ^ + } +} +"#, + ) + } + + #[test] + fn single_arm_highlight() { + check( + r#" +fn main() { + match 0 { + 0 =>$0 { + // ^^ + let x = 1; + x + // ^ + } + _ => 2, + } +} +"#, + ) + } + + #[test] + fn no_branches_when_disabled() { + let config = HighlightRelatedConfig { branch_exit_points: false, ..ENABLED_CONFIG }; + check_with_config( + r#" +fn main() { + match$0 0 { + 0 => 1, + _ => 2, + } +} +"#, + config, + ); + } + + #[test] fn asm() { check( r#" @@ -2165,6 +2307,200 @@ pub unsafe fn bootstrap() -> ! { } #[test] + fn complex_arms_highlight() { + check( + r#" +fn calculate(n: i32) -> i32 { n * 2 } + +fn main() { + match$0 Some(1) { + // ^^^^^ + Some(x) => match x { + 0 => { let y = x; y }, + // ^ + 1 => calculate(x), + //^^^^^^^^^^^^ + _ => (|| 6)(), + // ^^^^^^^^ + }, + None => loop { + break 5; + // ^^^^^^^ + }, + } +} +"#, + ) + } + + #[test] + fn match_in_macro_highlight() { + check( + r#" +macro_rules! M { + ($e:expr) => { $e }; +} + +fn main() { + M!{ + match$0 Some(1) { + // ^^^^^ + Some(x) => x, + // ^ + None => 0, + // ^ + } + } +} +"#, + ) + } + + #[test] + fn match_in_macro_highlight_2() { + check( + r#" +macro_rules! match_ast { + (match $node:ident { $($tt:tt)* }) => { $crate::match_ast!(match ($node) { $($tt)* }) }; + + (match ($node:expr) { + $( $( $path:ident )::+ ($it:pat) => $res:expr, )* + _ => $catch_all:expr $(,)? + }) => {{ + $( if let Some($it) = $($path::)+cast($node.clone()) { $res } else )* + { $catch_all } + }}; +} + +fn main() { + match_ast! { + match$0 Some(1) { + Some(x) => x, + } + } +} + "#, + ); + } + + #[test] + fn nested_if_else() { + check( + r#" +fn main() { + if$0 true { + // ^^ + if false { + 1 + // ^ + } else { + 2 + // ^ + } + } else { + 3 + // ^ + } +} +"#, + ) + } + + #[test] + fn if_else_if_highlight() { + check( + r#" +fn main() { + if$0 true { + // ^^ + 1 + // ^ + } else if false { + // ^^ + 2 + // ^ + } else { + 3 + // ^ + } +} +"#, + ) + } + + #[test] + fn complex_if_branches() { + check( + r#" +fn calculate(n: i32) -> i32 { n * 2 } + +fn main() { + if$0 true { + // ^^ + let x = 5; + calculate(x) + // ^^^^^^^^^^^^ + } else if false { + // ^^ + (|| 10)() + // ^^^^^^^^^ + } else { + loop { + break 15; + // ^^^^^^^^ + } + } +} +"#, + ) + } + + #[test] + fn if_in_macro_highlight() { + check( + r#" +macro_rules! M { + ($e:expr) => { $e }; +} + +fn main() { + M!{ + if$0 true { + // ^^ + 5 + // ^ + } else { + 10 + // ^^ + } + } +} +"#, + ) + } + + #[test] + fn match_in_macro() { + // We should not highlight the outer `match` expression. + check( + r#" +macro_rules! M { + (match) => { 1 }; +} + +fn main() { + match Some(1) { + Some(x) => x, + None => { + M!(match$0) + } + } +} + "#, + ) + } + + #[test] fn labeled_block_tail_expr() { check( r#" diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 36fdd90e8ae..729349365e6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -380,9 +380,9 @@ fn main() { let foo = foo3(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo4(); - // ^^^ &'static (dyn Fn(f64, f64) -> u32 + 'static) + // ^^^ &'static dyn Fn(f64, f64) -> u32 let foo = foo5(); - // ^^^ &'static (dyn Fn(&(dyn Fn(f64, f64) -> u32 + 'static), f64) -> u32 + 'static) + // ^^^ &'static dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 let foo = foo6(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo7(); @@ -413,7 +413,7 @@ fn main() { let foo = foo3(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo4(); - // ^^^ &'static (dyn Fn(f64, f64) -> u32 + 'static) + // ^^^ &'static dyn Fn(f64, f64) -> u32 let foo = foo5(); let foo = foo6(); let foo = foo7(); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index b9a98f88be7..f0003dae3f3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -143,7 +143,7 @@ fn foo<T>() {} file_id: FileId( 1, ), - range: 135..140, + range: 446..451, }, ), ), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index ca3a982760f..d2216e66ccb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -193,7 +193,7 @@ impl Tr for () { //^ impl Tr for () impl dyn Tr { } -//^ impl dyn Tr + 'static +//^ impl dyn Tr static S0: () = 0; static S1: () = {}; diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index c6a323d4081..fe874bc99b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -21,6 +21,7 @@ use hir::{PathResolution, Semantics}; use ide_db::{ FileId, RootDatabase, defs::{Definition, NameClass, NameRefClass}, + helpers::pick_best_token, search::{ReferenceCategory, SearchScope, UsageSearchResult}, }; use itertools::Itertools; @@ -397,7 +398,11 @@ fn handle_control_flow_keywords( .attach_first_edition(file_id) .map(|it| it.edition(sema.db)) .unwrap_or(Edition::CURRENT); - let token = file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword(edition))?; + let token = pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind { + _ if kind.is_keyword(edition) => 4, + T![=>] => 3, + _ => 1, + })?; let references = match token.kind() { T![fn] | T![return] | T![try] => highlight_related::highlight_exit_points(sema, token), @@ -408,6 +413,7 @@ fn handle_control_flow_keywords( T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => { highlight_related::highlight_break_points(sema, token) } + T![if] | T![=>] | T![match] => highlight_related::highlight_branch_exit_points(sema, token), _ => return None, } .into_iter() @@ -1344,6 +1350,159 @@ impl Foo { ); } + #[test] + fn test_highlight_if_branches() { + check( + r#" +fn main() { + let x = if$0 true { + 1 + } else if false { + 2 + } else { + 3 + }; + + println!("x: {}", x); +} +"#, + expect![[r#" + FileId(0) 24..26 + FileId(0) 42..43 + FileId(0) 55..57 + FileId(0) 74..75 + FileId(0) 97..98 + "#]], + ); + } + + #[test] + fn test_highlight_match_branches() { + check( + r#" +fn main() { + $0match Some(42) { + Some(x) if x > 0 => println!("positive"), + Some(0) => println!("zero"), + Some(_) => println!("negative"), + None => println!("none"), + }; +} +"#, + expect![[r#" + FileId(0) 16..21 + FileId(0) 61..81 + FileId(0) 102..118 + FileId(0) 139..159 + FileId(0) 177..193 + "#]], + ); + } + + #[test] + fn test_highlight_match_arm_arrow() { + check( + r#" +fn main() { + match Some(42) { + Some(x) if x > 0 $0=> println!("positive"), + Some(0) => println!("zero"), + Some(_) => println!("negative"), + None => println!("none"), + } +} +"#, + expect![[r#" + FileId(0) 58..60 + FileId(0) 61..81 + "#]], + ); + } + + #[test] + fn test_highlight_nested_branches() { + check( + r#" +fn main() { + let x = $0if true { + if false { + 1 + } else { + match Some(42) { + Some(_) => 2, + None => 3, + } + } + } else { + 4 + }; + + println!("x: {}", x); +} +"#, + expect![[r#" + FileId(0) 24..26 + FileId(0) 65..66 + FileId(0) 140..141 + FileId(0) 167..168 + FileId(0) 215..216 + "#]], + ); + } + + #[test] + fn test_highlight_match_with_complex_guards() { + check( + r#" +fn main() { + let x = $0match (x, y) { + (a, b) if a > b && a % 2 == 0 => 1, + (a, b) if a < b || b % 2 == 1 => 2, + (a, _) if a > 40 => 3, + _ => 4, + }; + + println!("x: {}", x); +} +"#, + expect![[r#" + FileId(0) 24..29 + FileId(0) 80..81 + FileId(0) 124..125 + FileId(0) 155..156 + FileId(0) 171..172 + "#]], + ); + } + + #[test] + fn test_highlight_mixed_if_match_expressions() { + check( + r#" +fn main() { + let x = $0if let Some(x) = Some(42) { + 1 + } else if let None = None { + 2 + } else { + match 42 { + 0 => 3, + _ => 4, + } + }; +} +"#, + expect![[r#" + FileId(0) 24..26 + FileId(0) 60..61 + FileId(0) 73..75 + FileId(0) 102..103 + FileId(0) 153..154 + FileId(0) 173..174 + "#]], + ); + } + fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { check_with_scope(ra_fixture, None, expect) } @@ -2867,4 +3026,66 @@ const FOO$0: i32 = 0; "#]], ); } + + #[test] + fn test_highlight_if_let_match_combined() { + check( + r#" +enum MyEnum { A(i32), B(String), C } + +fn main() { + let val = MyEnum::A(42); + + let x = $0if let MyEnum::A(x) = val { + 1 + } else if let MyEnum::B(s) = val { + 2 + } else { + match val { + MyEnum::C => 3, + _ => 4, + } + }; +} +"#, + expect![[r#" + FileId(0) 92..94 + FileId(0) 128..129 + FileId(0) 141..143 + FileId(0) 177..178 + FileId(0) 237..238 + FileId(0) 257..258 + "#]], + ); + } + + #[test] + fn test_highlight_nested_match_expressions() { + check( + r#" +enum Outer { A(Inner), B } +enum Inner { X, Y(i32) } + +fn main() { + let val = Outer::A(Inner::Y(42)); + + $0match val { + Outer::A(inner) => match inner { + Inner::X => println!("Inner::X"), + Inner::Y(n) if n > 0 => println!("Inner::Y positive: {}", n), + Inner::Y(_) => println!("Inner::Y non-positive"), + }, + Outer::B => println!("Outer::B"), + } +} +"#, + expect![[r#" + FileId(0) 108..113 + FileId(0) 185..205 + FileId(0) 243..279 + FileId(0) 308..341 + FileId(0) 374..394 + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 9ff656cb744..81b6703deef 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index adc581309d1..1ccd20c25e9 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -438,6 +438,8 @@ define_symbols! { shr, simd, sized, + meta_sized, + pointee_sized, skip, slice_len_fn, Some, diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index f3ab093bae0..eef718b7062 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cov-mark = "2.0.0" diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index c80510eedfb..c7da654de6d 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] drop_bomb = "0.1.5" diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs index ea5a3bc8593..55c5dc400b9 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs @@ -122,7 +122,7 @@ fn lifetime_bounds(p: &mut Parser<'_>) { } // test type_param_bounds -// struct S<T: 'a + ?Sized + (Copy) + ~const Drop>; +// struct S<T: 'a + ?Sized + (Copy) + [const] Drop>; pub(super) fn bounds(p: &mut Parser<'_>) { p.expect(T![:]); bounds_without_colon(p); @@ -187,6 +187,11 @@ fn type_bound(p: &mut Parser<'_>) -> bool { p.bump_any(); p.expect(T![const]); } + T!['['] => { + p.bump_any(); + p.expect(T![const]); + p.expect(T![']']); + } // test const_trait_bound // const fn foo(_: impl const Trait) {} T![const] => { diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 0fa9a264545..e6c92dec681 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -11,7 +11,8 @@ use std::ops; use rustc_literal_escaper::{ - EscapeError, Mode, unescape_byte, unescape_char, unescape_mixed, unescape_unicode, + EscapeError, Mode, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, + unescape_str, }; use crate::{ @@ -151,14 +152,14 @@ impl<'a> Converter<'a> { self.res } - fn push(&mut self, kind: SyntaxKind, len: usize, err: Option<&str>) { + fn push(&mut self, kind: SyntaxKind, len: usize, errors: Vec<String>) { self.res.push(kind, self.offset); self.offset += len; - if let Some(err) = err { - let token = self.res.len() as u32; - let msg = err.to_owned(); - self.res.error.push(LexError { msg, token }); + for msg in errors { + if !msg.is_empty() { + self.res.error.push(LexError { msg, token: self.res.len() as u32 }); + } } } @@ -167,14 +168,16 @@ impl<'a> Converter<'a> { // We drop some useful information here (see patterns with double dots `..`) // Storing that info in `SyntaxKind` is not possible due to its layout requirements of // being `u16` that come from `rowan::SyntaxKind`. - let mut err = ""; + let mut errors: Vec<String> = vec![]; let syntax_kind = { match kind { rustc_lexer::TokenKind::LineComment { doc_style: _ } => COMMENT, rustc_lexer::TokenKind::BlockComment { doc_style: _, terminated } => { if !terminated { - err = "Missing trailing `*/` symbols to terminate the block comment"; + errors.push( + "Missing trailing `*/` symbols to terminate the block comment".into(), + ); } COMMENT } @@ -184,9 +187,9 @@ impl<'a> Converter<'a> { invalid_infostring, } => { if *has_invalid_preceding_whitespace { - err = "invalid preceding whitespace for frontmatter opening" + errors.push("invalid preceding whitespace for frontmatter opening".into()); } else if *invalid_infostring { - err = "invalid infostring for frontmatter" + errors.push("invalid infostring for frontmatter".into()); } FRONTMATTER } @@ -198,7 +201,7 @@ impl<'a> Converter<'a> { SyntaxKind::from_keyword(token_text, self.edition).unwrap_or(IDENT) } rustc_lexer::TokenKind::InvalidIdent => { - err = "Ident contains invalid characters"; + errors.push("Ident contains invalid characters".into()); IDENT } @@ -206,7 +209,7 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::GuardedStrPrefix if self.edition.at_least_2024() => { // FIXME: rustc does something better for recovery. - err = "Invalid string literal (reserved syntax)"; + errors.push("Invalid string literal (reserved syntax)".into()); ERROR } rustc_lexer::TokenKind::GuardedStrPrefix => { @@ -222,12 +225,12 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Lifetime { starts_with_number } => { if *starts_with_number { - err = "Lifetime name cannot start with a number"; + errors.push("Lifetime name cannot start with a number".into()); } LIFETIME_IDENT } rustc_lexer::TokenKind::UnknownPrefixLifetime => { - err = "Unknown lifetime prefix"; + errors.push("Unknown lifetime prefix".into()); LIFETIME_IDENT } rustc_lexer::TokenKind::RawLifetime => LIFETIME_IDENT, @@ -262,119 +265,128 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Unknown => ERROR, rustc_lexer::TokenKind::UnknownPrefix if token_text == "builtin" => IDENT, rustc_lexer::TokenKind::UnknownPrefix => { - err = "unknown literal prefix"; + errors.push("unknown literal prefix".into()); IDENT } rustc_lexer::TokenKind::Eof => EOF, } }; - let err = if err.is_empty() { None } else { Some(err) }; - self.push(syntax_kind, token_text.len(), err); + self.push(syntax_kind, token_text.len(), errors); } fn extend_literal(&mut self, len: usize, kind: &rustc_lexer::LiteralKind) { - let mut err = ""; + let invalid_raw_msg = String::from("Invalid raw string literal"); + + let mut errors = vec![]; + let mut no_end_quote = |c: char, kind: &str| { + errors.push(format!("Missing trailing `{c}` symbol to terminate the {kind} literal")); + }; let syntax_kind = match *kind { rustc_lexer::LiteralKind::Int { empty_int, base: _ } => { if empty_int { - err = "Missing digits after the integer base prefix"; + errors.push("Missing digits after the integer base prefix".into()); } INT_NUMBER } rustc_lexer::LiteralKind::Float { empty_exponent, base: _ } => { if empty_exponent { - err = "Missing digits after the exponent symbol"; + errors.push("Missing digits after the exponent symbol".into()); } FLOAT_NUMBER } rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { - err = "Missing trailing `'` symbol to terminate the character literal"; + no_end_quote('\'', "character"); } else { let text = &self.res.text[self.offset + 1..][..len - 1]; - let i = text.rfind('\'').unwrap(); - let text = &text[..i]; + let text = &text[..text.rfind('\'').unwrap()]; if let Err(e) = unescape_char(text) { - err = error_to_diagnostic_message(e, Mode::Char); + errors.push(err_to_msg(e, Mode::Char)); } } CHAR } rustc_lexer::LiteralKind::Byte { terminated } => { if !terminated { - err = "Missing trailing `'` symbol to terminate the byte literal"; + no_end_quote('\'', "byte"); } else { let text = &self.res.text[self.offset + 2..][..len - 2]; - let i = text.rfind('\'').unwrap(); - let text = &text[..i]; + let text = &text[..text.rfind('\'').unwrap()]; if let Err(e) = unescape_byte(text) { - err = error_to_diagnostic_message(e, Mode::Byte); + errors.push(err_to_msg(e, Mode::Byte)); } } - BYTE } rustc_lexer::LiteralKind::Str { terminated } => { if !terminated { - err = "Missing trailing `\"` symbol to terminate the string literal"; + no_end_quote('"', "string"); } else { let text = &self.res.text[self.offset + 1..][..len - 1]; - let i = text.rfind('"').unwrap(); - let text = &text[..i]; - err = unescape_string_error_message(text, Mode::Str); + let text = &text[..text.rfind('"').unwrap()]; + unescape_str(text, |_, res| { + if let Err(e) = res { + errors.push(err_to_msg(e, Mode::Str)); + } + }); } STRING } rustc_lexer::LiteralKind::ByteStr { terminated } => { if !terminated { - err = "Missing trailing `\"` symbol to terminate the byte string literal"; + no_end_quote('"', "byte string"); } else { let text = &self.res.text[self.offset + 2..][..len - 2]; - let i = text.rfind('"').unwrap(); - let text = &text[..i]; - err = unescape_string_error_message(text, Mode::ByteStr); + let text = &text[..text.rfind('"').unwrap()]; + unescape_byte_str(text, |_, res| { + if let Err(e) = res { + errors.push(err_to_msg(e, Mode::ByteStr)); + } + }); } BYTE_STRING } rustc_lexer::LiteralKind::CStr { terminated } => { if !terminated { - err = "Missing trailing `\"` symbol to terminate the string literal"; + no_end_quote('"', "C string") } else { let text = &self.res.text[self.offset + 2..][..len - 2]; - let i = text.rfind('"').unwrap(); - let text = &text[..i]; - err = unescape_string_error_message(text, Mode::CStr); + let text = &text[..text.rfind('"').unwrap()]; + unescape_c_str(text, |_, res| { + if let Err(e) = res { + errors.push(err_to_msg(e, Mode::CStr)); + } + }); } C_STRING } rustc_lexer::LiteralKind::RawStr { n_hashes } => { if n_hashes.is_none() { - err = "Invalid raw string literal"; + errors.push(invalid_raw_msg); } STRING } rustc_lexer::LiteralKind::RawByteStr { n_hashes } => { if n_hashes.is_none() { - err = "Invalid raw string literal"; + errors.push(invalid_raw_msg); } BYTE_STRING } rustc_lexer::LiteralKind::RawCStr { n_hashes } => { if n_hashes.is_none() { - err = "Invalid raw string literal"; + errors.push(invalid_raw_msg); } C_STRING } }; - let err = if err.is_empty() { None } else { Some(err) }; - self.push(syntax_kind, len, err); + self.push(syntax_kind, len, errors); } } -fn error_to_diagnostic_message(error: EscapeError, mode: Mode) -> &'static str { +fn err_to_msg(error: EscapeError, mode: Mode) -> String { match error { EscapeError::ZeroChars => "empty character literal", EscapeError::MoreThanOneChar => "character literal may only contain one codepoint", @@ -410,28 +422,5 @@ fn error_to_diagnostic_message(error: EscapeError, mode: Mode) -> &'static str { EscapeError::UnskippedWhitespaceWarning => "", EscapeError::MultipleSkippedLinesWarning => "", } -} - -fn unescape_string_error_message(text: &str, mode: Mode) -> &'static str { - let mut error_message = ""; - match mode { - Mode::CStr => { - unescape_mixed(text, mode, &mut |_, res| { - if let Err(e) = res { - error_message = error_to_diagnostic_message(e, mode); - } - }); - } - Mode::ByteStr | Mode::Str => { - unescape_unicode(text, mode, &mut |_, res| { - if let Err(e) = res { - error_message = error_to_diagnostic_message(e, mode); - } - }); - } - _ => { - // Other Modes are not supported yet or do not apply - } - } - error_message + .into() } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rast index dee860c2418..259637c898a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rast @@ -40,8 +40,9 @@ SOURCE_FILE PLUS "+" WHITESPACE " " TYPE_BOUND - TILDE "~" + L_BRACK "[" CONST_KW "const" + R_BRACK "]" WHITESPACE " " PATH_TYPE PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rs index 5da3083b9c5..8f37af78e9f 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rs @@ -1 +1 @@ -struct S<T: 'a + ?Sized + (Copy) + ~const Drop>; +struct S<T: 'a + ?Sized + (Copy) + [const] Drop>; diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index 4cc70726da0..f0dafab70c1 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] camino.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index f5ba40a994b..dac8e094357 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] serde.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 8fd675d0d31..4034f244393 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] object.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml index c416d997a89..bc04482273e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" license = "MIT OR Apache-2.0" [lib] +doctest = false [build-dependencies] cargo_metadata = "0.20.0" diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs index b97569d4dbd..b9e84a474dd 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs @@ -109,13 +109,11 @@ fn main() { let mut artifact_path = None; for message in Message::parse_stream(output.stdout.as_slice()) { - if let Message::CompilerArtifact(artifact) = message.unwrap() { - if artifact.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro) - && (artifact.package_id.repr.starts_with(&repr) - || artifact.package_id.repr == pkgid) - { - artifact_path = Some(PathBuf::from(&artifact.filenames[0])); - } + if let Message::CompilerArtifact(artifact) = message.unwrap() + && artifact.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro) + && (artifact.package_id.repr.starts_with(&repr) || artifact.package_id.repr == pkgid) + { + artifact_path = Some(PathBuf::from(&artifact.filenames[0])); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml index 33b7c2bb0ad..e1678bddff8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" publish = false [lib] +doctest = false proc-macro = true [dependencies] diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index bae891c1984..4828419003a 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] cfg-if = "1.0.1" diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 64ea75922fb..27fe9f79bbc 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] anyhow.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 4435376eab6..bbaa8f4f292 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -20,9 +20,7 @@ use toolchain::Tool; use crate::{ CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot, - TargetKind, - toolchain_info::{QueryConfig, version}, - utf8_stdout, + TargetKind, utf8_stdout, }; /// Output of the build script and proc-macro building steps for a workspace. @@ -64,6 +62,7 @@ impl WorkspaceBuildScripts { workspace: &CargoWorkspace, progress: &dyn Fn(String), sysroot: &Sysroot, + toolchain: Option<&semver::Version>, ) -> io::Result<WorkspaceBuildScripts> { let current_dir = workspace.workspace_root(); @@ -74,6 +73,7 @@ impl WorkspaceBuildScripts { workspace.manifest_path(), current_dir, sysroot, + toolchain, )?; Self::run_per_ws(cmd, workspace, progress) } @@ -95,6 +95,7 @@ impl WorkspaceBuildScripts { &ManifestPath::try_from(working_directory.clone()).unwrap(), working_directory, &Sysroot::empty(), + None, )?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are @@ -356,7 +357,7 @@ impl WorkspaceBuildScripts { }); } Message::CompilerMessage(message) => { - progress(message.target.name); + progress(format!("received compiler message for: {}", message.target.name)); if let Some(diag) = message.message.rendered.as_deref() { push_err(diag); @@ -387,12 +388,13 @@ impl WorkspaceBuildScripts { manifest_path: &ManifestPath, current_dir: &AbsPath, sysroot: &Sysroot, + toolchain: Option<&semver::Version>, ) -> io::Result<Command> { - let mut cmd = match config.run_build_script_command.as_deref() { + match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = toolchain::command(program, current_dir, &config.extra_env); cmd.args(args); - cmd + Ok(cmd) } _ => { let mut cmd = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env); @@ -444,40 +446,35 @@ impl WorkspaceBuildScripts { cmd.arg("--keep-going"); - cmd + // If [`--compile-time-deps` flag](https://github.com/rust-lang/cargo/issues/14434) is + // available in current toolchain's cargo, use it to build compile time deps only. + const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION: semver::Version = semver::Version { + major: 1, + minor: 89, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; + + let cargo_comp_time_deps_available = + toolchain.is_some_and(|v| *v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION); + + if cargo_comp_time_deps_available { + cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); + cmd.arg("-Zunstable-options"); + cmd.arg("--compile-time-deps"); + } else if config.wrap_rustc_in_build_scripts { + // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use + // that to compile only proc macros and build scripts during the initial + // `cargo check`. + // We don't need this if we are using `--compile-time-deps` flag. + let myself = std::env::current_exe()?; + cmd.env("RUSTC_WRAPPER", myself); + cmd.env("RA_RUSTC_WRAPPER", "1"); + } + Ok(cmd) } - }; - - // If [`--compile-time-deps` flag](https://github.com/rust-lang/cargo/issues/14434) is - // available in current toolchain's cargo, use it to build compile time deps only. - const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION: semver::Version = semver::Version { - major: 1, - minor: 90, - patch: 0, - pre: semver::Prerelease::EMPTY, - build: semver::BuildMetadata::EMPTY, - }; - - let query_config = QueryConfig::Cargo(sysroot, manifest_path); - let toolchain = version::get(query_config, &config.extra_env).ok().flatten(); - let cargo_comp_time_deps_available = - toolchain.is_some_and(|v| v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION); - - if cargo_comp_time_deps_available { - cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); - cmd.arg("-Zunstable-options"); - cmd.arg("--compile-time-deps"); - } else if config.wrap_rustc_in_build_scripts { - // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use - // that to compile only proc macros and build scripts during the initial - // `cargo check`. - // We don't need this if we are using `--compile-time-deps` flag. - let myself = std::env::current_exe()?; - cmd.env("RUSTC_WRAPPER", myself); - cmd.env("RA_RUSTC_WRAPPER", "1"); } - - Ok(cmd) } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 58507418e4d..4bacc904174 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -48,6 +48,7 @@ pub struct CargoWorkspace { is_sysroot: bool, /// Environment variables set in the `.cargo/config` file. config_env: Env, + requires_rustc_private: bool, } impl ops::Index<Package> for CargoWorkspace { @@ -513,6 +514,7 @@ impl CargoWorkspace { let workspace_root = AbsPathBuf::assert(meta.workspace_root); let target_directory = AbsPathBuf::assert(meta.target_directory); let mut is_virtual_workspace = true; + let mut requires_rustc_private = false; meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); for meta_pkg in meta.packages { @@ -577,6 +579,7 @@ impl CargoWorkspace { metadata: meta.rust_analyzer.unwrap_or_default(), }); let pkg_data = &mut packages[pkg]; + requires_rustc_private |= pkg_data.metadata.rustc_private; pkg_by_id.insert(id, pkg); for meta_tgt in meta_targets { let cargo_metadata::Target { name, kind, required_features, src_path, .. } = @@ -626,6 +629,7 @@ impl CargoWorkspace { target_directory, manifest_path: ws_manifest_path, is_virtual_workspace, + requires_rustc_private, is_sysroot, config_env: cargo_config_env, } @@ -724,4 +728,8 @@ impl CargoWorkspace { pub fn is_sysroot(&self) -> bool { self.is_sysroot } + + pub fn requires_rustc_private(&self) -> bool { + self.requires_rustc_private + } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index 450def5461d..9e0415c3b39 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -1,6 +1,6 @@ //! Cargo-like environment variables injection. use base_db::Env; -use paths::Utf8Path; +use paths::{Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use toolchain::Tool; @@ -123,6 +123,26 @@ fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env { env } +pub(crate) fn cargo_config_build_target_dir( + manifest: &ManifestPath, + extra_env: &FxHashMap<String, Option<String>>, + sysroot: &Sysroot, +) -> Option<Utf8PathBuf> { + let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env); + cargo_config + .args(["-Z", "unstable-options", "config", "get", "build.target-dir"]) + .env("RUSTC_BOOTSTRAP", "1"); + if manifest.is_rust_manifest() { + cargo_config.arg("-Zscript"); + } + utf8_stdout(&mut cargo_config) + .map(|stdout| { + Utf8Path::new(stdout.trim_start_matches("build.target-dir = ").trim_matches('"')) + .to_owned() + }) + .ok() +} + #[test] fn parse_output_cargo_config_env_works() { let stdout = r#" diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index a6743a32b14..5bc64df535c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -16,6 +16,7 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; use span::{Edition, FileId}; +use toolchain::Tool; use tracing::instrument; use triomphe::Arc; @@ -25,10 +26,14 @@ use crate::{ WorkspaceBuildScripts, build_dependencies::BuildScriptOutput, cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource}, - env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, + env::{ + cargo_config_build_target_dir, cargo_config_env, inject_cargo_env, + inject_cargo_package_env, inject_rustc_tool_env, + }, project_json::{Crate, CrateArrayIdx}, sysroot::RustLibSrcWorkspace, toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version}, + utf8_stdout, }; use tracing::{debug, error, info}; @@ -208,8 +213,7 @@ impl ProjectWorkspace { config: &CargoConfig, progress: &(dyn Fn(String) + Sync), ) -> Result<ProjectWorkspace, anyhow::Error> { - progress("Discovering sysroot".to_owned()); - let workspace_dir = cargo_toml.parent(); + progress("discovering sysroot".to_owned()); let CargoConfig { features, rustc_source, @@ -224,6 +228,7 @@ impl ProjectWorkspace { no_deps, .. } = config; + let workspace_dir = cargo_toml.parent(); let mut sysroot = match (sysroot, sysroot_src) { (Some(RustLibSource::Discover), None) => Sysroot::discover(workspace_dir, extra_env), (Some(RustLibSource::Discover), Some(sysroot_src)) => { @@ -238,8 +243,33 @@ impl ProjectWorkspace { (None, _) => Sysroot::empty(), }; + // Resolve the Cargo.toml to the workspace root as we base the `target` dir off of it. + let mut cmd = sysroot.tool(Tool::Cargo, workspace_dir, extra_env); + cmd.args(["locate-project", "--workspace", "--manifest-path", cargo_toml.as_str()]); + let cargo_toml = &match utf8_stdout(&mut cmd) { + Ok(output) => { + #[derive(serde_derive::Deserialize)] + struct Root { + root: Utf8PathBuf, + } + match serde_json::from_str::<Root>(&output) { + Ok(object) => ManifestPath::try_from(AbsPathBuf::assert(object.root)) + .expect("manifest path should be absolute"), + Err(e) => { + tracing::error!(%e, %cargo_toml, "failed fetching cargo workspace root"); + cargo_toml.clone() + } + } + } + Err(e) => { + tracing::error!(%e, %cargo_toml, "failed fetching cargo workspace root"); + cargo_toml.clone() + } + }; + let workspace_dir = cargo_toml.parent(); + tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot"); - progress("Querying project metadata".to_owned()); + progress("querying project metadata".to_owned()); let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml); let targets = target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default(); @@ -252,8 +282,11 @@ impl ProjectWorkspace { .ok() .flatten(); - let target_dir = - config.target_dir.clone().unwrap_or_else(|| workspace_dir.join("target").into()); + let target_dir = config + .target_dir + .clone() + .or_else(|| cargo_config_build_target_dir(cargo_toml, extra_env, &sysroot)) + .unwrap_or_else(|| workspace_dir.join("target").into()); // We spawn a bunch of processes to query various information about the workspace's // toolchain and sysroot @@ -374,11 +407,17 @@ impl ProjectWorkspace { )) }); - let (rustc_cfg, data_layout, rustc, loaded_sysroot, cargo_metadata, cargo_config_extra_env) = - match join { - Ok(it) => it, - Err(e) => std::panic::resume_unwind(e), - }; + let ( + rustc_cfg, + data_layout, + mut rustc, + loaded_sysroot, + cargo_metadata, + cargo_config_extra_env, + ) = match join { + Ok(it) => it, + Err(e) => std::panic::resume_unwind(e), + }; let (meta, error) = cargo_metadata.with_context(|| { format!( @@ -391,6 +430,14 @@ impl ProjectWorkspace { sysroot.set_workspace(loaded_sysroot); } + if !cargo.requires_rustc_private() { + if let Err(e) = &mut rustc { + // We don't need the rustc sources here, + // so just discard the error. + _ = e.take(); + } + } + Ok(ProjectWorkspace { kind: ProjectWorkspaceKind::Cargo { cargo, @@ -413,17 +460,25 @@ impl ProjectWorkspace { config: &CargoConfig, progress: &(dyn Fn(String) + Sync), ) -> ProjectWorkspace { - progress("Discovering sysroot".to_owned()); + progress("discovering sysroot".to_owned()); let mut sysroot = Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone()); tracing::info!(workspace = %project_json.manifest_or_root(), src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot"); - progress("Querying project metadata".to_owned()); + progress("querying project metadata".to_owned()); let sysroot_project = project_json.sysroot_project.take(); let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref()); let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env) .unwrap_or_default(); let toolchain = version::get(query_config, &config.extra_env).ok().flatten(); + let project_root = project_json.project_root(); + let target_dir = config + .target_dir + .clone() + .or_else(|| { + cargo_config_build_target_dir(project_json.manifest()?, &config.extra_env, &sysroot) + }) + .unwrap_or_else(|| project_root.join("target").into()); // We spawn a bunch of processes to query various information about the workspace's // toolchain and sysroot @@ -441,7 +496,6 @@ impl ProjectWorkspace { ) }); let loaded_sysroot = s.spawn(|| { - let project_root = project_json.project_root(); if let Some(sysroot_project) = sysroot_project { sysroot.load_workspace( &RustSourceWorkspaceConfig::Json(*sysroot_project), @@ -449,10 +503,6 @@ impl ProjectWorkspace { progress, ) } else { - let target_dir = config - .target_dir - .clone() - .unwrap_or_else(|| project_root.join("target").into()); sysroot.load_workspace( &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config( config, @@ -507,7 +557,12 @@ impl ProjectWorkspace { .unwrap_or_default(); let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env); let data_layout = target_data_layout::get(query_config, None, &config.extra_env); - let target_dir = config.target_dir.clone().unwrap_or_else(|| dir.join("target").into()); + let target_dir = config + .target_dir + .clone() + .or_else(|| cargo_config_build_target_dir(detached_file, &config.extra_env, &sysroot)) + .unwrap_or_else(|| dir.join("target").into()); + let loaded_sysroot = sysroot.load_workspace( &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config( config, @@ -581,10 +636,16 @@ impl ProjectWorkspace { match &self.kind { ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, None)), .. } | ProjectWorkspaceKind::Cargo { cargo, error: None, .. } => { - WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, &self.sysroot) - .with_context(|| { - format!("Failed to run build scripts for {}", cargo.workspace_root()) - }) + WorkspaceBuildScripts::run_for_workspace( + config, + cargo, + progress, + &self.sysroot, + self.toolchain.as_ref(), + ) + .with_context(|| { + format!("Failed to run build scripts for {}", cargo.workspace_root()) + }) } _ => Ok(WorkspaceBuildScripts::default()), } @@ -1166,14 +1227,10 @@ fn cargo_to_crate_graph( // Mapping of a package to its library target let mut pkg_to_lib_crate = FxHashMap::default(); let mut pkg_crates = FxHashMap::default(); - // Does any crate signal to rust-analyzer that they need the rustc_private crates? - let mut has_private = false; let workspace_proc_macro_cwd = Arc::new(cargo.workspace_root().to_path_buf()); // Next, create crates for each package, target pair for pkg in cargo.packages() { - has_private |= cargo[pkg].metadata.rustc_private; - let cfg_options = { let mut cfg_options = cfg_options.clone(); @@ -1318,7 +1375,7 @@ fn cargo_to_crate_graph( add_dep(crate_graph, from, name, to); } - if has_private { + if cargo.requires_rustc_private() { // If the user provided a path to rustc sources, we add all the rustc_private crates // and create dependencies on them for the crates which opt-in to that if let Some((rustc_workspace, rustc_build_scripts)) = rustc { @@ -1588,7 +1645,7 @@ fn add_target_crate_root( None => Err("proc-macro crate build data is missing dylib path".to_owned()), } } - None => Err("proc-macro crate is missing its build data".to_owned()), + None => Err("build scripts have not been built".to_owned()), }; proc_macros.insert(crate_id, proc_macro); } diff --git a/src/tools/rust-analyzer/crates/query-group-macro/Cargo.toml b/src/tools/rust-analyzer/crates/query-group-macro/Cargo.toml index 8b03d8f8cc7..5991120a30d 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/Cargo.toml +++ b/src/tools/rust-analyzer/crates/query-group-macro/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false proc-macro = true [dependencies] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 5e63521d747..b301a7189b3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -13,6 +13,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [[bin]] name = "rust-analyzer" diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 05e1b832cd1..e716d140752 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -94,6 +94,8 @@ config_data! { + /// Enables highlighting of related return values while the cursor is on any `match`, `if`, or match arm arrow (`=>`). + highlightRelated_branchExitPoints_enable: bool = true, /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. highlightRelated_breakPoints_enable: bool = true, /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. @@ -1629,6 +1631,7 @@ impl Config { exit_points: self.highlightRelated_exitPoints_enable().to_owned(), yield_points: self.highlightRelated_yieldPoints_enable().to_owned(), closure_captures: self.highlightRelated_closureCaptures_enable().to_owned(), + branch_exit_points: self.highlightRelated_branchExitPoints_enable().to_owned(), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index afd9eff5264..a76a65220d3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -2433,17 +2433,14 @@ fn run_rustfmt( } _ => { // Something else happened - e.g. `rustfmt` is missing or caught a signal - Err(LspError::new( - -32900, - format!( - r#"rustfmt exited with: - Status: {} - stdout: {captured_stdout} - stderr: {captured_stderr}"#, - output.status, - ), - ) - .into()) + tracing::error!( + ?command, + %output.status, + %captured_stdout, + %captured_stderr, + "rustfmt failed" + ); + Ok(None) } }; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 4efe330f16a..8a848fb848c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -900,14 +900,17 @@ pub(crate) fn folding_range( FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region), - FoldKind::Mods + FoldKind::Modules | FoldKind::Block | FoldKind::ArgList | FoldKind::Consts | FoldKind::Statics + | FoldKind::TypeAliases | FoldKind::WhereClause | FoldKind::ReturnType | FoldKind::Array + | FoldKind::TraitAliases + | FoldKind::ExternCrates | FoldKind::MatchArm => None, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 189d95ec7ed..133d5a6cf7a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -428,7 +428,7 @@ impl GlobalState { let expansion_res = match client { Ok(client) => match res { Ok((crate_name, path)) => { - progress(path.to_string()); + progress(format!("loading proc-macros: {path}")); let ignored_proc_macros = ignored_proc_macros .iter() .find_map(|(name, macros)| { diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index a6d57816608..2c19f00f082 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] backtrace = { version = "0.3.75", optional = true } diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml index cccd41d5429..b0fd40ff59f 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] rustc-hash.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 9d3aaa8d4e3..1ee93013e3e 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] either.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index c81da06682e..3f439472337 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -669,7 +669,7 @@ TypeBoundList = TypeBound = Lifetime -| ('~' 'const' | 'const')? 'async'? '?'? Type +| ('~' 'const' | '[' 'const' ']' | 'const')? 'async'? '?'? Type | 'use' UseBoundGenericArgs UseBoundGenericArgs = diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 04c7e8a578c..79a9f4da338 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1766,6 +1766,10 @@ impl TypeBound { support::child(&self.syntax) } #[inline] + pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) } + #[inline] + pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) } + #[inline] pub fn question_mark_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![?]) } #[inline] pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index ced3b713d8d..4afdda78a0e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -1,9 +1,11 @@ //! There are many AstNodes, but only a few tokens, so we hand-write them here. +use std::ops::Range; use std::{borrow::Cow, num::ParseIntError}; use rustc_literal_escaper::{ - EscapeError, MixedUnit, Mode, unescape_byte, unescape_char, unescape_mixed, unescape_unicode, + EscapeError, MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, + unescape_str, }; use stdx::always; @@ -150,7 +152,7 @@ impl QuoteOffsets { pub trait IsString: AstToken { const RAW_PREFIX: &'static str; - const MODE: Mode; + fn unescape(s: &str, callback: impl FnMut(Range<usize>, Result<char, EscapeError>)); fn is_raw(&self) -> bool { self.text().starts_with(Self::RAW_PREFIX) } @@ -185,7 +187,7 @@ pub trait IsString: AstToken { let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; - unescape_unicode(text, Self::MODE, &mut |range, unescaped_char| { + Self::unescape(text, &mut |range: Range<usize>, unescaped_char| { if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) { cb(TextRange::new(s, e) + offset, unescaped_char); } @@ -203,7 +205,9 @@ pub trait IsString: AstToken { impl IsString for ast::String { const RAW_PREFIX: &'static str = "r"; - const MODE: Mode = Mode::Str; + fn unescape(s: &str, cb: impl FnMut(Range<usize>, Result<char, EscapeError>)) { + unescape_str(s, cb) + } } impl ast::String { @@ -218,20 +222,19 @@ impl ast::String { let mut buf = String::new(); let mut prev_end = 0; let mut has_error = None; - unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match ( - unescaped_char, - buf.capacity() == 0, - ) { - (Ok(c), false) => buf.push(c), - (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { - prev_end = char_range.end - } - (Ok(c), true) => { - buf.reserve_exact(text.len()); - buf.push_str(&text[..prev_end]); - buf.push(c); + unescape_str(text, |char_range, unescaped_char| { + match (unescaped_char, buf.capacity() == 0) { + (Ok(c), false) => buf.push(c), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } + (Ok(c), true) => { + buf.reserve_exact(text.len()); + buf.push_str(&text[..prev_end]); + buf.push(c); + } + (Err(e), _) => has_error = Some(e), } - (Err(e), _) => has_error = Some(e), }); match (has_error, buf.capacity() == 0) { @@ -244,7 +247,9 @@ impl ast::String { impl IsString for ast::ByteString { const RAW_PREFIX: &'static str = "br"; - const MODE: Mode = Mode::ByteStr; + fn unescape(s: &str, mut callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) { + unescape_byte_str(s, |range, res| callback(range, res.map(char::from))) + } } impl ast::ByteString { @@ -259,20 +264,19 @@ impl ast::ByteString { let mut buf: Vec<u8> = Vec::new(); let mut prev_end = 0; let mut has_error = None; - unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match ( - unescaped_char, - buf.capacity() == 0, - ) { - (Ok(c), false) => buf.push(c as u8), - (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { - prev_end = char_range.end - } - (Ok(c), true) => { - buf.reserve_exact(text.len()); - buf.extend_from_slice(&text.as_bytes()[..prev_end]); - buf.push(c as u8); + unescape_byte_str(text, |char_range, unescaped_byte| { + match (unescaped_byte, buf.capacity() == 0) { + (Ok(b), false) => buf.push(b), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } + (Ok(b), true) => { + buf.reserve_exact(text.len()); + buf.extend_from_slice(&text.as_bytes()[..prev_end]); + buf.push(b); + } + (Err(e), _) => has_error = Some(e), } - (Err(e), _) => has_error = Some(e), }); match (has_error, buf.capacity() == 0) { @@ -285,25 +289,10 @@ impl ast::ByteString { impl IsString for ast::CString { const RAW_PREFIX: &'static str = "cr"; - const MODE: Mode = Mode::CStr; - - fn escaped_char_ranges(&self, cb: &mut dyn FnMut(TextRange, Result<char, EscapeError>)) { - let text_range_no_quotes = match self.text_range_between_quotes() { - Some(it) => it, - None => return, - }; - - let start = self.syntax().text_range().start(); - let text = &self.text()[text_range_no_quotes - start]; - let offset = text_range_no_quotes.start() - start; - - unescape_mixed(text, Self::MODE, &mut |range, unescaped_char| { - let text_range = - TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()); - // XXX: This method should only be used for highlighting ranges. The unescaped - // char/byte is not used. For simplicity, we return an arbitrary placeholder char. - cb(text_range + offset, unescaped_char.map(|_| ' ')); - }); + // NOTE: This method should only be used for highlighting ranges. The unescaped + // char/byte is not used. For simplicity, we return an arbitrary placeholder char. + fn unescape(s: &str, mut callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) { + unescape_c_str(s, |range, _res| callback(range, Ok('_'))) } } @@ -323,10 +312,7 @@ impl ast::CString { MixedUnit::Char(c) => buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()), MixedUnit::HighByte(b) => buf.push(b), }; - unescape_mixed(text, Self::MODE, &mut |char_range, unescaped| match ( - unescaped, - buf.capacity() == 0, - ) { + unescape_c_str(text, |char_range, unescaped| match (unescaped, buf.capacity() == 0) { (Ok(u), false) => extend_unit(&mut buf, u), (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { prev_end = char_range.end diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs index 5bfeb3bff87..4180f9cd185 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs @@ -6,7 +6,9 @@ mod block; use itertools::Itertools; use rowan::Direction; -use rustc_literal_escaper::{self, EscapeError, Mode, unescape_mixed, unescape_unicode}; +use rustc_literal_escaper::{ + EscapeError, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, +}; use crate::{ AstNode, SyntaxError, @@ -47,7 +49,7 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec<SyntaxError>) { } fn rustc_unescape_error_to_string(err: EscapeError) -> (&'static str, bool) { - use rustc_literal_escaper::EscapeError as EE; + use EscapeError as EE; #[rustfmt::skip] let err_message = match err { @@ -142,7 +144,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { ast::LiteralKind::String(s) => { if !s.is_raw() { if let Some(without_quotes) = unquote(text, 1, '"') { - unescape_unicode(without_quotes, Mode::Str, &mut |range, char| { + unescape_str(without_quotes, |range, char| { if let Err(err) = char { push_err(1, range.start, err); } @@ -153,7 +155,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { ast::LiteralKind::ByteString(s) => { if !s.is_raw() { if let Some(without_quotes) = unquote(text, 2, '"') { - unescape_unicode(without_quotes, Mode::ByteStr, &mut |range, char| { + unescape_byte_str(without_quotes, |range, char| { if let Err(err) = char { push_err(1, range.start, err); } @@ -164,7 +166,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { ast::LiteralKind::CString(s) => { if !s.is_raw() { if let Some(without_quotes) = unquote(text, 2, '"') { - unescape_mixed(without_quotes, Mode::CStr, &mut |range, char| { + unescape_c_str(without_quotes, |range, char| { if let Err(err) = char { push_err(1, range.start, err); } @@ -174,20 +176,16 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { } ast::LiteralKind::Char(_) => { if let Some(without_quotes) = unquote(text, 1, '\'') { - unescape_unicode(without_quotes, Mode::Char, &mut |range, char| { - if let Err(err) = char { - push_err(1, range.start, err); - } - }); + if let Err(err) = unescape_char(without_quotes) { + push_err(1, 0, err); + } } } ast::LiteralKind::Byte(_) => { if let Some(without_quotes) = unquote(text, 2, '\'') { - unescape_unicode(without_quotes, Mode::Byte, &mut |range, char| { - if let Err(err) = char { - push_err(2, range.start, err); - } - }); + if let Err(err) = unescape_byte(without_quotes) { + push_err(2, 0, err); + } } } ast::LiteralKind::IntNumber(_) diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index c27e850ce7f..6d1930aa268 100644 --- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] # Avoid adding deps here, this crate is widely used in tests it should compile fast! diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index d13a81d287f..d48063fb86f 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -26,7 +26,7 @@ //! deref: sized //! derive: //! discriminant: -//! drop: +//! drop: sized //! env: option //! eq: sized //! error: fmt @@ -37,7 +37,7 @@ //! future: pin //! coroutine: pin //! dispatch_from_dyn: unsize, pin -//! hash: +//! hash: sized //! include: //! index: sized //! infallible: @@ -77,33 +77,46 @@ pub mod marker { // region:sized + #[lang = "pointee_sized"] + #[fundamental] + #[rustc_specialization_trait] + #[rustc_coinductive] + pub trait PointeeSized {} + + #[lang = "meta_sized"] + #[fundamental] + #[rustc_specialization_trait] + #[rustc_coinductive] + pub trait MetaSized: PointeeSized {} + #[lang = "sized"] #[fundamental] #[rustc_specialization_trait] - pub trait Sized {} + #[rustc_coinductive] + pub trait Sized: MetaSized {} // endregion:sized // region:send pub unsafe auto trait Send {} - impl<T: ?Sized> !Send for *const T {} - impl<T: ?Sized> !Send for *mut T {} + impl<T: PointeeSized> !Send for *const T {} + impl<T: PointeeSized> !Send for *mut T {} // region:sync - unsafe impl<T: Sync + ?Sized> Send for &T {} - unsafe impl<T: Send + ?Sized> Send for &mut T {} + unsafe impl<T: Sync + PointeeSized> Send for &T {} + unsafe impl<T: Send + PointeeSized> Send for &mut T {} // endregion:sync // endregion:send // region:sync pub unsafe auto trait Sync {} - impl<T: ?Sized> !Sync for *const T {} - impl<T: ?Sized> !Sync for *mut T {} + impl<T: PointeeSized> !Sync for *const T {} + impl<T: PointeeSized> !Sync for *mut T {} // endregion:sync // region:unsize #[lang = "unsize"] - pub trait Unsize<T: ?Sized> {} + pub trait Unsize<T: PointeeSized>: PointeeSized {} // endregion:unsize // region:unpin @@ -120,7 +133,7 @@ pub mod marker { // endregion:derive mod copy_impls { - use super::Copy; + use super::{Copy, PointeeSized}; macro_rules! impl_copy { ($($t:ty)*) => { @@ -137,9 +150,9 @@ pub mod marker { bool char } - impl<T: ?Sized> Copy for *const T {} - impl<T: ?Sized> Copy for *mut T {} - impl<T: ?Sized> Copy for &T {} + impl<T: PointeeSized> Copy for *const T {} + impl<T: PointeeSized> Copy for *mut T {} + impl<T: PointeeSized> Copy for &T {} impl Copy for ! {} } // endregion:copy @@ -151,7 +164,7 @@ pub mod marker { // region:phantom_data #[lang = "phantom_data"] - pub struct PhantomData<T: ?Sized>; + pub struct PhantomData<T: PointeeSized>; // endregion:phantom_data // region:discriminant @@ -206,9 +219,11 @@ pub mod default { // region:hash pub mod hash { + use crate::marker::PointeeSized; + pub trait Hasher {} - pub trait Hash { + pub trait Hash: PointeeSized { fn hash<H: Hasher>(&self, state: &mut H); } @@ -221,10 +236,11 @@ pub mod hash { // region:cell pub mod cell { + use crate::marker::PointeeSized; use crate::mem; #[lang = "unsafe_cell"] - pub struct UnsafeCell<T: ?Sized> { + pub struct UnsafeCell<T: PointeeSized> { value: T, } @@ -238,7 +254,7 @@ pub mod cell { } } - pub struct Cell<T: ?Sized> { + pub struct Cell<T: PointeeSized> { value: UnsafeCell<T>, } @@ -357,7 +373,7 @@ pub mod convert { // endregion:from // region:as_ref - pub trait AsRef<T: ?Sized> { + pub trait AsRef<T: crate::marker::PointeeSized>: crate::marker::PointeeSized { fn as_ref(&self) -> &T; } // endregion:as_ref @@ -368,9 +384,11 @@ pub mod convert { pub mod mem { // region:manually_drop + use crate::marker::PointeeSized; + #[lang = "manually_drop"] #[repr(transparent)] - pub struct ManuallyDrop<T: ?Sized> { + pub struct ManuallyDrop<T: PointeeSized> { value: T, } @@ -381,7 +399,7 @@ pub mod mem { } // region:deref - impl<T: ?Sized> crate::ops::Deref for ManuallyDrop<T> { + impl<T: PointeeSized> crate::ops::Deref for ManuallyDrop<T> { type Target = T; fn deref(&self) -> &T { &self.value @@ -428,7 +446,7 @@ pub mod mem { pub mod ptr { // region:drop #[lang = "drop_in_place"] - pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { + pub unsafe fn drop_in_place<T: crate::marker::PointeeSized>(to_drop: *mut T) { unsafe { drop_in_place(to_drop) } } pub const unsafe fn read<T>(src: *const T) -> T { @@ -444,7 +462,7 @@ pub mod ptr { // region:pointee #[lang = "pointee_trait"] #[rustc_deny_explicit_impl(implement_via_object = false)] - pub trait Pointee { + pub trait Pointee: crate::marker::PointeeSized { #[lang = "metadata_type"] type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } @@ -452,12 +470,14 @@ pub mod ptr { // region:non_null #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] - pub struct NonNull<T: ?Sized> { + pub struct NonNull<T: crate::marker::PointeeSized> { pointer: *const T, } // region:coerce_unsized - impl<T: ?Sized, U: ?Sized> crate::ops::CoerceUnsized<NonNull<U>> for NonNull<T> where - T: crate::marker::Unsize<U> + impl<T: crate::marker::PointeeSized, U: crate::marker::PointeeSized> + crate::ops::CoerceUnsized<NonNull<U>> for NonNull<T> + where + T: crate::marker::Unsize<U>, { } // endregion:coerce_unsized @@ -478,42 +498,44 @@ pub mod ptr { pub mod ops { // region:coerce_unsized mod unsize { - use crate::marker::Unsize; + use crate::marker::{PointeeSized, Unsize}; #[lang = "coerce_unsized"] - pub trait CoerceUnsized<T: ?Sized> {} + pub trait CoerceUnsized<T> {} - impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} - impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {} - impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {} - impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {} + impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a mut U> for &'a mut T {} + impl<'a, 'b: 'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a U> for &'b mut T {} + impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*mut U> for &'a mut T {} + impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for &'a mut T {} - impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} - impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {} + impl<'a, 'b: 'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a U> for &'b T {} + impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for &'a T {} - impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} - impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {} - impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {} + impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*mut U> for *mut T {} + impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for *mut T {} + impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for *const T {} } pub use self::unsize::CoerceUnsized; // endregion:coerce_unsized // region:deref mod deref { + use crate::marker::PointeeSized; + #[lang = "deref"] - pub trait Deref { + pub trait Deref: PointeeSized { #[lang = "deref_target"] type Target: ?Sized; fn deref(&self) -> &Self::Target; } - impl<T: ?Sized> Deref for &T { + impl<T: PointeeSized> Deref for &T { type Target = T; fn deref(&self) -> &T { loop {} } } - impl<T: ?Sized> Deref for &mut T { + impl<T: PointeeSized> Deref for &mut T { type Target = T; fn deref(&self) -> &T { loop {} @@ -521,19 +543,19 @@ pub mod ops { } // region:deref_mut #[lang = "deref_mut"] - pub trait DerefMut: Deref { + pub trait DerefMut: Deref + PointeeSized { fn deref_mut(&mut self) -> &mut Self::Target; } // endregion:deref_mut // region:receiver #[lang = "receiver"] - pub trait Receiver { + pub trait Receiver: PointeeSized { #[lang = "receiver_target"] type Target: ?Sized; } - impl<P: ?Sized, T: ?Sized> Receiver for P + impl<P: PointeeSized, T: PointeeSized> Receiver for P where P: Deref<Target = T>, { @@ -686,7 +708,7 @@ pub mod ops { #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl<A: Tuple, F: ?Sized> const Fn<A> for &F where - F: ~const Fn<A>, + F: [const] Fn<A>, { extern "rust-call" fn call(&self, args: A) -> F::Output { (**self).call(args) @@ -697,7 +719,7 @@ pub mod ops { #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl<A: Tuple, F: ?Sized> const FnMut<A> for &F where - F: ~const Fn<A>, + F: [const] Fn<A>, { extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output { (**self).call(args) @@ -708,7 +730,7 @@ pub mod ops { #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl<A: Tuple, F: ?Sized> const FnOnce<A> for &F where - F: ~const Fn<A>, + F: [const] Fn<A>, { type Output = F::Output; @@ -721,7 +743,7 @@ pub mod ops { #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl<A: Tuple, F: ?Sized> const FnMut<A> for &mut F where - F: ~const FnMut<A>, + F: [const] FnMut<A>, { extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output { (*self).call_mut(args) @@ -732,7 +754,7 @@ pub mod ops { #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl<A: Tuple, F: ?Sized> const FnOnce<A> for &mut F where - F: ~const FnMut<A>, + F: [const] FnMut<A>, { type Output = F::Output; extern "rust-call" fn call_once(self, args: A) -> F::Output { @@ -1006,18 +1028,18 @@ pub mod ops { // region:dispatch_from_dyn mod dispatch_from_dyn { - use crate::marker::Unsize; + use crate::marker::{PointeeSized, Unsize}; #[lang = "dispatch_from_dyn"] pub trait DispatchFromDyn<T> {} - impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} + impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a U> for &'a T {} - impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} + impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a mut U> for &'a mut T {} - impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {} + impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*const U> for *const T {} - impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} + impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*mut U> for *mut T {} } pub use self::dispatch_from_dyn::DispatchFromDyn; // endregion:dispatch_from_dyn @@ -1025,15 +1047,17 @@ pub mod ops { // region:eq pub mod cmp { + use crate::marker::PointeeSized; + #[lang = "eq"] - pub trait PartialEq<Rhs: ?Sized = Self> { + pub trait PartialEq<Rhs: PointeeSized = Self>: PointeeSized { fn eq(&self, other: &Rhs) -> bool; fn ne(&self, other: &Rhs) -> bool { !self.eq(other) } } - pub trait Eq: PartialEq<Self> {} + pub trait Eq: PartialEq<Self> + PointeeSized {} // region:derive #[rustc_builtin_macro] @@ -1044,11 +1068,11 @@ pub mod cmp { // region:ord #[lang = "partial_ord"] - pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> { + pub trait PartialOrd<Rhs: PointeeSized = Self>: PartialEq<Rhs> + PointeeSized { fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>; } - pub trait Ord: Eq + PartialOrd<Self> { + pub trait Ord: Eq + PartialOrd<Self> + PointeeSized { fn cmp(&self, other: &Self) -> Ordering; } @@ -1071,6 +1095,8 @@ pub mod cmp { // region:fmt pub mod fmt { + use crate::marker::PointeeSized; + pub struct Error; pub type Result = crate::result::Result<(), Error>; pub struct Formatter<'a>; @@ -1106,10 +1132,10 @@ pub mod fmt { } } - pub trait Debug { + pub trait Debug: PointeeSized { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } - pub trait Display { + pub trait Display: PointeeSized { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } @@ -1268,7 +1294,7 @@ pub mod fmt { } } - impl<T: Debug + ?Sized> Debug for &T { + impl<T: Debug + PointeeSized> Debug for &T { fn fmt(&self, f: &mut Formatter<'_>) -> Result { (&**self).fmt(f) } @@ -1512,6 +1538,8 @@ pub mod iter { mod traits { mod iterator { + use crate::marker::PointeeSized; + #[doc(notable_trait)] #[lang = "iterator"] pub trait Iterator { @@ -1543,7 +1571,7 @@ pub mod iter { } // endregion:iterators } - impl<I: Iterator + ?Sized> Iterator for &mut I { + impl<I: Iterator + PointeeSized> Iterator for &mut I { type Item = I::Item; fn next(&mut self) -> Option<I::Item> { (**self).next() diff --git a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml index 315a3a2890f..f561c1c0e2b 100644 --- a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml +++ b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] home = "0.5.11" diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 529fad3244a..82e7c24668f 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] arrayvec.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index 9b32ee17abc..bd6c8331e66 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] tracing.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml index 546195481c6..e8a6195036e 100644 --- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true rust-version.workspace = true [lib] +doctest = false [dependencies] rustc-hash.workspace = true diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md index 1f211a97d78..dffdae94a6e 100644 --- a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md +++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md @@ -6,6 +6,7 @@ - [rust-analyzer Binary](rust_analyzer_binary.md) - [Other Editors](other_editors.md) - [Troubleshooting](troubleshooting.md) + - [FAQ](faq.md) - [Configuration](configuration.md) - [Non-Cargo Based Projects](non_cargo_based_projects.md) - [Security](security.md) diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 9404b1454a0..ebac26e1d60 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -612,6 +612,13 @@ Default: `"client"` Controls file watching implementation. +## rust-analyzer.highlightRelated.branchExitPoints.enable {#highlightRelated.branchExitPoints.enable} + +Default: `true` + +Enables highlighting of related return values while the cursor is on any `match`, `if`, or match arm arrow (`=>`). + + ## rust-analyzer.highlightRelated.breakPoints.enable {#highlightRelated.breakPoints.enable} Default: `true` diff --git a/src/tools/rust-analyzer/docs/book/src/faq.md b/src/tools/rust-analyzer/docs/book/src/faq.md new file mode 100644 index 00000000000..c8720330901 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/faq.md @@ -0,0 +1,7 @@ +# Troubleshooting FAQ + +### I see a warning "Variable `None` should have snake_case name, e.g. `none`" + +rust-analyzer fails to resolve `None`, and thinks you are binding to a variable +named `None`. That's usually a sign of a corrupted sysroot. Try removing and re-installing +it: `rustup component remove rust-src` then `rustup component install rust-src`. diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md index bbdb48bbbc9..befb631ec03 100644 --- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -40,6 +40,9 @@ interface ProjectJson { /// several different "sysroots" in one graph of /// crates. sysroot_src?: string; + /// A ProjectJson describing the crates of the sysroot. + sysroot_project?: ProjectJson; + /// List of groups of common cfg values, to allow /// sharing them between crates. /// diff --git a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md index 1b2841421a4..a357cbef415 100644 --- a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md +++ b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md @@ -1,5 +1,8 @@ # Troubleshooting +First, search the [troubleshooting FAQ](faq.html). If your problem appears +there (and the proposed solution works for you), great! Otherwise, read on. + Start with looking at the rust-analyzer version. Try **rust-analyzer: Show RA Version** in VS Code (using **Command Palette** feature typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 26a21c1468d..3cb4c21ee1f 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1532,6 +1532,16 @@ { "title": "highlightRelated", "properties": { + "rust-analyzer.highlightRelated.branchExitPoints.enable": { + "markdownDescription": "Enables highlighting of related return values while the cursor is on any `match`, `if`, or match arm arrow (`=>`).", + "default": true, + "type": "boolean" + } + } + }, + { + "title": "highlightRelated", + "properties": { "rust-analyzer.highlightRelated.breakPoints.enable": { "markdownDescription": "Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.", "default": true, diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts index cdeea7333a6..073ff2f4703 100644 --- a/src/tools/rust-analyzer/editors/code/src/client.ts +++ b/src/tools/rust-analyzer/editors/code/src/client.ts @@ -3,7 +3,7 @@ import * as lc from "vscode-languageclient/node"; import * as vscode from "vscode"; import * as ra from "../src/lsp_ext"; import * as Is from "vscode-languageclient/lib/common/utils/is"; -import { assert, unwrapUndefinable } from "./util"; +import { assert } from "./util"; import * as diagnostics from "./diagnostics"; import { WorkspaceEdit } from "vscode"; import { type Config, prepareVSCodeConfig } from "./config"; @@ -188,11 +188,17 @@ export async function createClient( context: await client.code2ProtocolConverter.asCodeActionContext(context, token), }; const callback = async ( - values: (lc.Command | lc.CodeAction)[] | null, + values: (lc.Command | lc.CodeAction | object)[] | null, ): Promise<(vscode.Command | vscode.CodeAction)[] | undefined> => { if (values === null) return undefined; const result: (vscode.CodeAction | vscode.Command)[] = []; - const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>(); + const groups = new Map< + string, + { + primary: vscode.CodeAction; + items: { label: string; arguments: lc.CodeAction }[]; + } + >(); for (const item of values) { // In our case we expect to get code edits only from diagnostics if (lc.CodeAction.is(item)) { @@ -204,62 +210,55 @@ export async function createClient( result.push(action); continue; } - assert( - isCodeActionWithoutEditsAndCommands(item), - "We don't expect edits or commands here", - ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind); - const action = new vscode.CodeAction(item.title, kind); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const group = (item as any).group; - action.command = { - command: "rust-analyzer.resolveCodeAction", - title: item.title, - arguments: [item], - }; + assertIsCodeActionWithoutEditsAndCommands(item); + const kind = client.protocol2CodeConverter.asCodeActionKind(item.kind); + const group = item.group; - // Set a dummy edit, so that VS Code doesn't try to resolve this. - action.edit = new WorkspaceEdit(); + const mkAction = () => { + const action = new vscode.CodeAction(item.title, kind); + action.command = { + command: "rust-analyzer.resolveCodeAction", + title: item.title, + arguments: [item], + }; + // Set a dummy edit, so that VS Code doesn't try to resolve this. + action.edit = new WorkspaceEdit(); + return action; + }; if (group) { let entry = groups.get(group); if (!entry) { - entry = { index: result.length, items: [] }; + entry = { primary: mkAction(), items: [] }; groups.set(group, entry); - result.push(action); + } else { + entry.items.push({ + label: item.title, + arguments: item, + }); } - entry.items.push(action); } else { - result.push(action); + result.push(mkAction()); } } - for (const [group, { index, items }] of groups) { - if (items.length === 1) { - const item = unwrapUndefinable(items[0]); - result[index] = item; - } else { - const action = new vscode.CodeAction(group); - const item = unwrapUndefinable(items[0]); - action.kind = item.kind; - action.command = { + for (const [group, { items, primary }] of groups) { + // This group contains more than one item, so rewrite it to be a group action + if (items.length !== 0) { + const args = [ + { + label: primary.title, + arguments: primary.command!.arguments![0], + }, + ...items, + ]; + primary.title = group; + primary.command = { command: "rust-analyzer.applyActionGroup", title: "", - arguments: [ - items.map((item) => { - return { - label: item.title, - arguments: item.command!.arguments![0], - }; - }), - ], + arguments: [args], }; - - // Set a dummy edit, so that VS Code doesn't try to resolve this. - action.edit = new WorkspaceEdit(); - - result[index] = action; } + result.push(primary); } return result; }; @@ -363,17 +362,22 @@ class OverrideFeatures implements lc.StaticFeature { clear(): void {} } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function isCodeActionWithoutEditsAndCommands(value: any): boolean { - const candidate: lc.CodeAction = value; - return ( +function assertIsCodeActionWithoutEditsAndCommands( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + candidate: any, +): asserts candidate is lc.CodeAction & { + group?: string; +} { + assert( candidate && - Is.string(candidate.title) && - (candidate.diagnostics === void 0 || - Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) && - (candidate.kind === void 0 || Is.string(candidate.kind)) && - candidate.edit === void 0 && - candidate.command === void 0 + Is.string(candidate.title) && + (candidate.diagnostics === undefined || + Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) && + (candidate.group === undefined || Is.string(candidate.group)) && + (candidate.kind === undefined || Is.string(candidate.kind)) && + candidate.edit === undefined && + candidate.command === undefined, + `Expected a CodeAction without edits or commands, got: ${JSON.stringify(candidate)}`, ); } diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index 3ac1a933d9e..25b30013fa1 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -1114,11 +1114,11 @@ export function applySnippetWorkspaceEditCommand(_ctx: CtxInit): Cmd { }; } -export function run(ctx: CtxInit): Cmd { +export function run(ctx: CtxInit, mode?: "cursor"): Cmd { let prevRunnable: RunnableQuickPick | undefined; return async () => { - const item = await selectRunnable(ctx, prevRunnable); + const item = await selectRunnable(ctx, prevRunnable, false, true, mode); if (!item) return; item.detail = "rerun"; diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 5e500730693..996298524f1 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -167,7 +167,7 @@ function createCommands(): Record<string, CommandFactory> { viewCrateGraph: { enabled: commands.viewCrateGraph }, viewFullCrateGraph: { enabled: commands.viewFullCrateGraph }, expandMacro: { enabled: commands.expandMacro }, - run: { enabled: commands.run }, + run: { enabled: (ctx) => (mode?: "cursor") => commands.run(ctx, mode)() }, copyRunCommandLine: { enabled: commands.copyRunCommandLine }, debug: { enabled: commands.debug }, newDebugConfig: { enabled: commands.newDebugConfig }, diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index 40027cc7c85..95166c427b2 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -18,10 +18,15 @@ export async function selectRunnable( prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true, + mode?: "cursor", ): Promise<RunnableQuickPick | undefined> { const editor = ctx.activeRustEditor ?? ctx.activeCargoTomlEditor; if (!editor) return; + if (mode === "cursor") { + return selectRunnableAtCursor(ctx, editor, prevRunnable); + } + // show a placeholder while we get the runnables from the server const quickPick = vscode.window.createQuickPick(); quickPick.title = "Select Runnable"; @@ -54,6 +59,58 @@ export async function selectRunnable( ); } +async function selectRunnableAtCursor( + ctx: CtxInit, + editor: RustEditor, + prevRunnable?: RunnableQuickPick, +): Promise<RunnableQuickPick | undefined> { + const runnableQuickPicks = await getRunnables(ctx.client, editor, prevRunnable, false); + let runnableQuickPickAtCursor = null; + const cursorPosition = ctx.client.code2ProtocolConverter.asPosition(editor.selection.active); + for (const runnableQuickPick of runnableQuickPicks) { + if (!runnableQuickPick.runnable.location?.targetRange) { + continue; + } + const runnableQuickPickRange = runnableQuickPick.runnable.location.targetRange; + if ( + runnableQuickPickAtCursor?.runnable?.location?.targetRange != null && + rangeContainsOtherRange( + runnableQuickPickRange, + runnableQuickPickAtCursor.runnable.location.targetRange, + ) + ) { + continue; + } + if (rangeContainsPosition(runnableQuickPickRange, cursorPosition)) { + runnableQuickPickAtCursor = runnableQuickPick; + } + } + if (runnableQuickPickAtCursor == null) { + return; + } + return Promise.resolve(runnableQuickPickAtCursor); +} + +function rangeContainsPosition(range: lc.Range, position: lc.Position): boolean { + return ( + (position.line > range.start.line || + (position.line === range.start.line && position.character >= range.start.character)) && + (position.line < range.end.line || + (position.line === range.end.line && position.character <= range.end.character)) + ); +} + +function rangeContainsOtherRange(range: lc.Range, otherRange: lc.Range) { + return ( + (range.start.line < otherRange.start.line || + (range.start.line === otherRange.start.line && + range.start.character <= otherRange.start.character)) && + (range.end.line > otherRange.end.line || + (range.end.line === otherRange.end.line && + range.end.character >= otherRange.end.character)) + ); +} + export class RunnableQuickPick implements vscode.QuickPickItem { public label: string; public description?: string | undefined; diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index a454087b0cd..902793225ea 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -27733d46d79f4eb92e240fbba502c43022665735 +ad3b7257615c28aaf8212a189ec032b8af75de51 diff --git a/src/tools/rust-installer/README.md b/src/tools/rust-installer/README.md index 99d8e5ca4cf..505ffe4093f 100644 --- a/src/tools/rust-installer/README.md +++ b/src/tools/rust-installer/README.md @@ -51,7 +51,7 @@ To combine installers. * Make install.sh not have to be customized, pull it's data from a config file. -* Be more resiliant to installation failures, particularly if the disk +* Be more resilient to installation failures, particularly if the disk is full. * Pre-install and post-uninstall scripts. * Allow components to depend on or contradict other components. diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 7e27e0258c2..18da874dbef 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1368,7 +1368,6 @@ ui/infinite/issue-41731-infinite-macro-println.rs ui/intrinsics/issue-28575.rs ui/intrinsics/issue-84297-reifying-copy.rs ui/invalid/issue-114435-layout-type-err.rs -ui/issue-11881.rs ui/issue-15924.rs ui/issue-16822.rs ui/issues-71798.rs @@ -2847,7 +2846,6 @@ ui/macros/issue-98466.rs ui/macros/issue-99261.rs ui/macros/issue-99265.rs ui/macros/issue-99907.rs -ui/macros/rfc-3086-metavar-expr/issue-111904.rs ui/malformed/issue-107423-unused-delim-only-one-no-pair.rs ui/malformed/issue-69341-malformed-derive-inert.rs ui/marker_trait_attr/issue-61651-type-mismatch.rs diff --git a/tests/codegen/align-fn.rs b/tests/codegen/align-fn.rs index 267da060240..90073ff3081 100644 --- a/tests/codegen/align-fn.rs +++ b/tests/codegen/align-fn.rs @@ -1,10 +1,10 @@ -//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 +//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 -Clink-dead-code #![crate_type = "lib"] #![feature(fn_align)] // CHECK: align 16 -#[no_mangle] +#[unsafe(no_mangle)] #[align(16)] pub fn fn_align() {} @@ -12,12 +12,12 @@ pub struct A; impl A { // CHECK: align 16 - #[no_mangle] + #[unsafe(no_mangle)] #[align(16)] pub fn method_align(self) {} // CHECK: align 16 - #[no_mangle] + #[unsafe(no_mangle)] #[align(16)] pub fn associated_fn() {} } @@ -25,46 +25,94 @@ impl A { trait T: Sized { fn trait_fn() {} - // CHECK: align 32 - #[align(32)] fn trait_method(self) {} + + #[align(8)] + fn trait_method_inherit_low(self); + + #[align(32)] + fn trait_method_inherit_high(self); + + #[align(32)] + fn trait_method_inherit_default(self) {} + + #[align(4)] + #[align(128)] + #[align(8)] + fn inherit_highest(self) {} } impl T for A { - // CHECK: align 16 - #[no_mangle] + // CHECK-LABEL: trait_fn + // CHECK-SAME: align 16 + #[unsafe(no_mangle)] #[align(16)] fn trait_fn() {} - // CHECK: align 16 - #[no_mangle] + // CHECK-LABEL: trait_method + // CHECK-SAME: align 16 + #[unsafe(no_mangle)] #[align(16)] fn trait_method(self) {} -} -impl T for () {} + // The prototype's align is ignored because the align here is higher. + // CHECK-LABEL: trait_method_inherit_low + // CHECK-SAME: align 16 + #[unsafe(no_mangle)] + #[align(16)] + fn trait_method_inherit_low(self) {} + + // The prototype's align is used because it is higher. + // CHECK-LABEL: trait_method_inherit_high + // CHECK-SAME: align 32 + #[unsafe(no_mangle)] + #[align(16)] + fn trait_method_inherit_high(self) {} + + // The prototype's align inherited. + // CHECK-LABEL: trait_method_inherit_default + // CHECK-SAME: align 32 + #[unsafe(no_mangle)] + fn trait_method_inherit_default(self) {} + + // The prototype's highest align inherited. + // CHECK-LABEL: inherit_highest + // CHECK-SAME: align 128 + #[unsafe(no_mangle)] + #[align(32)] + #[align(64)] + fn inherit_highest(self) {} +} -pub fn foo() { - ().trait_method(); +trait HasDefaultImpl: Sized { + // CHECK-LABEL: inherit_from_default_method + // CHECK-LABEL: inherit_from_default_method + // CHECK-SAME: align 32 + #[align(32)] + fn inherit_from_default_method(self) {} } +pub struct InstantiateDefaultMethods; + +impl HasDefaultImpl for InstantiateDefaultMethods {} + // CHECK-LABEL: align_specified_twice_1 // CHECK-SAME: align 64 -#[no_mangle] +#[unsafe(no_mangle)] #[align(32)] #[align(64)] pub fn align_specified_twice_1() {} // CHECK-LABEL: align_specified_twice_2 // CHECK-SAME: align 128 -#[no_mangle] +#[unsafe(no_mangle)] #[align(128)] #[align(32)] pub fn align_specified_twice_2() {} // CHECK-LABEL: align_specified_twice_3 // CHECK-SAME: align 256 -#[no_mangle] +#[unsafe(no_mangle)] #[align(32)] #[align(256)] pub fn align_specified_twice_3() {} diff --git a/tests/crashes/132882.rs b/tests/crashes/132882.rs deleted file mode 100644 index 6b5e4dba803..00000000000 --- a/tests/crashes/132882.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #132882 - -use std::ops::Add; - -pub trait Numoid -where - for<N: Numoid> &'a Self: Add<Self>, -{ -} - -pub fn compute<N: Numoid>(a: N) -> N { - &a + a -} diff --git a/tests/run-make/short-ice/rmake.rs b/tests/run-make/short-ice/rmake.rs index 483def62fc7..dbe0f692aef 100644 --- a/tests/run-make/short-ice/rmake.rs +++ b/tests/run-make/short-ice/rmake.rs @@ -5,9 +5,12 @@ // See https://github.com/rust-lang/rust/issues/107910 //@ needs-target-std -//@ ignore-i686-pc-windows-msvc -// Reason: the assert_eq! on line 37 fails, almost seems like it missing debug info? -// Haven't been able to reproduce locally, but it happens on CI. +//@ ignore-windows-msvc +// +// - FIXME(#143198): On `i686-pc-windows-msvc`: the assert_eq! on line 37 fails, almost seems like +// it missing debug info? Haven't been able to reproduce locally, but it happens on CI. +// - FIXME(#143198): On `x86_64-pc-windows-msvc`: full backtrace sometimes do not contain matching +// count of short backtrace markers (e.g. 5x end marker, but 3x start marker). use run_make_support::rustc; diff --git a/tests/rustdoc-json/attrs/link_section_2021.rs b/tests/rustdoc-json/attrs/link_section_2021.rs new file mode 100644 index 00000000000..a1312f4210b --- /dev/null +++ b/tests/rustdoc-json/attrs/link_section_2021.rs @@ -0,0 +1,6 @@ +//@ edition: 2021 +#![no_std] + +//@ is "$.index[?(@.name=='example')].attrs" '["#[link_section = \".text\"]"]' +#[link_section = ".text"] +pub extern "C" fn example() {} diff --git a/tests/rustdoc-json/attrs/link_section_2024.rs b/tests/rustdoc-json/attrs/link_section_2024.rs new file mode 100644 index 00000000000..edb028451a8 --- /dev/null +++ b/tests/rustdoc-json/attrs/link_section_2024.rs @@ -0,0 +1,9 @@ +//@ edition: 2024 +#![no_std] + +// Since the 2024 edition the link_section attribute must use the unsafe qualification. +// However, the unsafe qualification is not shown by rustdoc. + +//@ is "$.index[?(@.name=='example')].attrs" '["#[link_section = \".text\"]"]' +#[unsafe(link_section = ".text")] +pub extern "C" fn example() {} diff --git a/tests/rustdoc-ui/invalid_infered_static_and_const.stderr b/tests/rustdoc-ui/invalid_infered_static_and_const.stderr index 401020224d6..3e116826c49 100644 --- a/tests/rustdoc-ui/invalid_infered_static_and_const.stderr +++ b/tests/rustdoc-ui/invalid_infered_static_and_const.stderr @@ -1,10 +1,10 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for constant items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/invalid_infered_static_and_const.rs:1:24 | LL | const FOO: dyn Fn() -> _ = ""; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for static items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for statics --> $DIR/invalid_infered_static_and_const.rs:2:25 | LL | static BOO: dyn Fn() -> _ = ""; diff --git a/tests/ui-fulldeps/stable-mir/check_allocation.rs b/tests/ui-fulldeps/stable-mir/check_allocation.rs index 692c24f0544..64194e72888 100644 --- a/tests/ui-fulldeps/stable-mir/check_allocation.rs +++ b/tests/ui-fulldeps/stable-mir/check_allocation.rs @@ -19,12 +19,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use stable_mir::crate_def::CrateDef; -use stable_mir::mir::alloc::GlobalAlloc; -use stable_mir::mir::mono::{Instance, InstanceKind, StaticDef}; -use stable_mir::mir::{Body, TerminatorKind}; -use stable_mir::ty::{Allocation, ConstantKind, RigidTy, TyKind}; -use stable_mir::{CrateItem, CrateItems, ItemKind}; use std::ascii::Char; use std::assert_matches::assert_matches; use std::cmp::{max, min}; @@ -33,6 +27,13 @@ use std::ffi::CStr; use std::io::Write; use std::ops::ControlFlow; +use stable_mir::crate_def::CrateDef; +use stable_mir::mir::Body; +use stable_mir::mir::alloc::GlobalAlloc; +use stable_mir::mir::mono::{Instance, StaticDef}; +use stable_mir::ty::{Allocation, ConstantKind}; +use stable_mir::{CrateItem, CrateItems, ItemKind}; + const CRATE_NAME: &str = "input"; /// This function uses the Stable MIR APIs to get information about the test crate. @@ -44,7 +45,6 @@ fn test_stable_mir() -> ControlFlow<()> { check_len(*get_item(&items, (ItemKind::Static, "LEN")).unwrap()); check_cstr(*get_item(&items, (ItemKind::Static, "C_STR")).unwrap()); check_other_consts(*get_item(&items, (ItemKind::Fn, "other_consts")).unwrap()); - check_type_id(*get_item(&items, (ItemKind::Fn, "check_type_id")).unwrap()); ControlFlow::Continue(()) } @@ -107,7 +107,9 @@ fn check_other_consts(item: CrateItem) { // Instance body will force constant evaluation. let body = Instance::try_from(item).unwrap().body().unwrap(); let assigns = collect_consts(&body); - assert_eq!(assigns.len(), 8); + assert_eq!(assigns.len(), 10); + let mut char_id = None; + let mut bool_id = None; for (name, alloc) in assigns { match name.as_str() { "_max_u128" => { @@ -149,35 +151,21 @@ fn check_other_consts(item: CrateItem) { assert_eq!(max(first, second) as u32, u32::MAX); assert_eq!(min(first, second), 10); } + "_bool_id" => { + bool_id = Some(alloc); + } + "_char_id" => { + char_id = Some(alloc); + } _ => { unreachable!("{name} -- {alloc:?}") } } } -} - -/// Check that we can retrieve the type id of char and bool, and that they have different values. -fn check_type_id(item: CrateItem) { - let body = Instance::try_from(item).unwrap().body().unwrap(); - let mut ids: Vec<u128> = vec![]; - for term in body.blocks.iter().map(|bb| &bb.terminator) { - match &term.kind { - TerminatorKind::Call { func, destination, .. } => { - let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { - unreachable!() - }; - let RigidTy::FnDef(def, args) = ty else { unreachable!() }; - let instance = Instance::resolve(def, &args).unwrap(); - assert_eq!(instance.kind, InstanceKind::Intrinsic); - let dest_ty = destination.ty(body.locals()).unwrap(); - let alloc = instance.try_const_eval(dest_ty).unwrap(); - ids.push(alloc.read_uint().unwrap()); - } - _ => { /* Do nothing */ } - } - } - assert_eq!(ids.len(), 2); - assert_ne!(ids[0], ids[1]); + let bool_id = bool_id.unwrap(); + let char_id = char_id.unwrap(); + // FIXME(stable_mir): add `read_ptr` to `Allocation` + assert_ne!(bool_id, char_id); } /// Collects all the constant assignments. @@ -235,6 +223,7 @@ fn generate_input(path: &str) -> std::io::Result<()> { file, r#" #![feature(core_intrinsics)] + #![expect(internal_features)] use std::intrinsics::type_id; static LEN: usize = 2; @@ -254,11 +243,8 @@ fn generate_input(path: &str) -> std::io::Result<()> { let _ptr = &BAR; let _null_ptr: *const u8 = NULL; let _tuple = TUPLE; - }} - - fn check_type_id() {{ - let _char_id = type_id::<char>(); - let _bool_id = type_id::<bool>(); + let _char_id = const {{ type_id::<char>() }}; + let _bool_id = const {{ type_id::<bool>() }}; }} pub fn main() {{ diff --git a/tests/ui/empty-allocation-non-null.rs b/tests/ui/allocator/empty-alloc-nonnull-guarantee.rs index 45035a42a5f..f4081306c77 100644 --- a/tests/ui/empty-allocation-non-null.rs +++ b/tests/ui/allocator/empty-alloc-nonnull-guarantee.rs @@ -1,3 +1,7 @@ +//! Check that the default global Rust allocator produces non-null Box allocations for ZSTs. +//! +//! See https://github.com/rust-lang/rust/issues/11998 + //@ run-pass pub fn main() { diff --git a/tests/ui/associated-types/unconstrained-lifetime-assoc-type.rs b/tests/ui/associated-types/unconstrained-lifetime-assoc-type.rs new file mode 100644 index 00000000000..2c4af7da921 --- /dev/null +++ b/tests/ui/associated-types/unconstrained-lifetime-assoc-type.rs @@ -0,0 +1,21 @@ +//! Regression test for issue #22077 +//! lifetime parameters must be constrained in associated type definitions + +trait Fun { + type Output; + fn call<'x>(&'x self) -> Self::Output; +} + +struct Holder { + x: String, +} + +impl<'a> Fun for Holder { + //~^ ERROR E0207 + type Output = &'a str; + fn call<'b>(&'b self) -> &'b str { + &self.x[..] + } +} + +fn main() {} diff --git a/tests/ui/impl-unused-rps-in-assoc-type.stderr b/tests/ui/associated-types/unconstrained-lifetime-assoc-type.stderr index ef61fa4be48..15d0820c895 100644 --- a/tests/ui/impl-unused-rps-in-assoc-type.stderr +++ b/tests/ui/associated-types/unconstrained-lifetime-assoc-type.stderr @@ -1,5 +1,5 @@ error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-rps-in-assoc-type.rs:11:6 + --> $DIR/unconstrained-lifetime-assoc-type.rs:13:6 | LL | impl<'a> Fun for Holder { | ^^ unconstrained lifetime parameter diff --git a/tests/ui/inline-disallow-on-variant.rs b/tests/ui/attributes/inline-attribute-enum-variant-error.rs index d92a4e8cc8d..305b285d2a4 100644 --- a/tests/ui/inline-disallow-on-variant.rs +++ b/tests/ui/attributes/inline-attribute-enum-variant-error.rs @@ -1,3 +1,5 @@ +//! Test that #[inline] attribute cannot be applied to enum variants + enum Foo { #[inline] //~^ ERROR attribute should be applied diff --git a/tests/ui/inline-disallow-on-variant.stderr b/tests/ui/attributes/inline-attribute-enum-variant-error.stderr index 255f6bc6a19..a4564d8f722 100644 --- a/tests/ui/inline-disallow-on-variant.stderr +++ b/tests/ui/attributes/inline-attribute-enum-variant-error.stderr @@ -1,5 +1,5 @@ error[E0518]: attribute should be applied to function or closure - --> $DIR/inline-disallow-on-variant.rs:2:5 + --> $DIR/inline-attribute-enum-variant-error.rs:4:5 | LL | #[inline] | ^^^^^^^^^ diff --git a/tests/ui/attributes/inline-main.rs b/tests/ui/attributes/inline-main.rs new file mode 100644 index 00000000000..7181ee19b67 --- /dev/null +++ b/tests/ui/attributes/inline-main.rs @@ -0,0 +1,6 @@ +//! Test that #[inline(always)] can be applied to main function + +//@ run-pass + +#[inline(always)] +fn main() {} diff --git a/tests/ui/attributes/inner-attrs-impl-cfg.rs b/tests/ui/attributes/inner-attrs-impl-cfg.rs new file mode 100644 index 00000000000..e7a5cfa9e2f --- /dev/null +++ b/tests/ui/attributes/inner-attrs-impl-cfg.rs @@ -0,0 +1,36 @@ +//! Test inner attributes (#![...]) behavior in impl blocks with cfg conditions. +//! +//! This test verifies that: +//! - Inner attributes can conditionally exclude entire impl blocks +//! - Regular attributes within impl blocks work independently +//! - Attribute parsing doesn't consume too eagerly + +//@ run-pass + +struct Foo; + +impl Foo { + #![cfg(false)] + + fn method(&self) -> bool { + false + } +} + +impl Foo { + #![cfg(not(FALSE))] + + // Check that we don't eat attributes too eagerly. + #[cfg(false)] + fn method(&self) -> bool { + false + } + + fn method(&self) -> bool { + true + } +} + +pub fn main() { + assert!(Foo.method()); +} diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs new file mode 100644 index 00000000000..64c0d223f83 --- /dev/null +++ b/tests/ui/attributes/malformed-attrs.rs @@ -0,0 +1,221 @@ +// This file contains a bunch of malformed attributes. +// We enable a bunch of features to not get feature-gate errs in this test. +#![feature(rustc_attrs)] +#![feature(rustc_allow_const_fn_unstable)] +#![feature(allow_internal_unstable)] +#![feature(fn_align)] +#![feature(optimize_attribute)] +#![feature(dropck_eyepatch)] +#![feature(export_stable)] +#![allow(incomplete_features)] +#![feature(min_generic_const_args)] +#![feature(ffi_const, ffi_pure)] +#![feature(coverage_attribute)] +#![feature(no_sanitize)] +#![feature(marker_trait_attr)] +#![feature(thread_local)] +#![feature(must_not_suspend)] +#![feature(coroutines)] +#![feature(linkage)] +#![feature(cfi_encoding, extern_types)] +#![feature(patchable_function_entry)] +#![feature(omit_gdb_pretty_printer_section)] +#![feature(fundamental)] + + +#![omit_gdb_pretty_printer_section = 1] +//~^ ERROR malformed `omit_gdb_pretty_printer_section` attribute input + +#![windows_subsystem] +//~^ ERROR malformed + +#[unsafe(export_name)] +//~^ ERROR malformed +#[rustc_allow_const_fn_unstable] +//~^ ERROR `rustc_allow_const_fn_unstable` expects a list of feature names +#[allow_internal_unstable] +//~^ ERROR `allow_internal_unstable` expects a list of feature names +#[rustc_confusables] +//~^ ERROR malformed +#[deprecated = 5] +//~^ ERROR malformed +#[doc] +//~^ ERROR valid forms for the attribute are +//~| WARN this was previously accepted by the compiler +#[rustc_macro_transparency] +//~^ ERROR malformed +#[repr] +//~^ ERROR malformed +#[rustc_as_ptr = 5] +//~^ ERROR malformed +#[inline = 5] +//~^ ERROR valid forms for the attribute are +//~| WARN this was previously accepted by the compiler +#[align] +//~^ ERROR malformed +#[optimize] +//~^ ERROR malformed +#[cold = 1] +//~^ ERROR malformed +#[must_use()] +//~^ ERROR valid forms for the attribute are +#[no_mangle = 1] +//~^ ERROR malformed +#[unsafe(naked())] +//~^ ERROR malformed +#[track_caller()] +//~^ ERROR malformed +#[export_name()] +//~^ ERROR malformed +#[used()] +//~^ ERROR malformed +#[crate_name] +//~^ ERROR malformed +#[doc] +//~^ ERROR valid forms for the attribute are +//~| WARN this was previously accepted by the compiler +#[target_feature] +//~^ ERROR malformed +#[export_stable = 1] +//~^ ERROR malformed +#[link] +//~^ ERROR attribute must be of the form +//~| WARN this was previously accepted by the compiler +#[link_name] +//~^ ERROR malformed +#[link_section] +//~^ ERROR malformed +#[coverage] +//~^ ERROR malformed `coverage` attribute input +#[no_sanitize] +//~^ ERROR malformed +#[ignore()] +//~^ ERROR valid forms for the attribute are +//~| WARN this was previously accepted by the compiler +#[no_implicit_prelude = 23] +//~^ ERROR malformed +#[proc_macro = 18] +//~^ ERROR malformed +//~| ERROR the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type +#[cfg] +//~^ ERROR is not followed by parentheses +#[cfg_attr] +//~^ ERROR malformed +#[instruction_set] +//~^ ERROR malformed +#[patchable_function_entry] +//~^ ERROR malformed +fn test() { + #[coroutine = 63] || {} + //~^ ERROR malformed `coroutine` attribute input + //~| ERROR mismatched types [E0308] +} + +#[proc_macro_attribute = 19] +//~^ ERROR malformed +//~| ERROR the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type +#[must_use = 1] +//~^ ERROR malformed +fn test2() { } + +#[proc_macro_derive] +//~^ ERROR malformed `proc_macro_derive` attribute +//~| ERROR the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type +pub fn test3() {} + +#[rustc_layout_scalar_valid_range_start] +//~^ ERROR malformed +#[rustc_layout_scalar_valid_range_end] +//~^ ERROR malformed +#[must_not_suspend()] +//~^ ERROR malformed +#[cfi_encoding] +//~^ ERROR malformed +struct Test; + +#[diagnostic::on_unimplemented] +//~^ WARN missing options for `on_unimplemented` attribute +#[diagnostic::on_unimplemented = 1] +//~^ WARN malformed +trait Hey { + #[type_const = 1] + //~^ ERROR malformed + const HEY: usize = 5; +} + +struct Empty; +#[diagnostic::do_not_recommend()] +//~^ WARN does not expect any arguments +impl Hey for Empty { + +} + +#[marker = 3] +//~^ ERROR malformed +#[fundamental()] +//~^ ERROR malformed +trait EmptyTrait { + +} + + +extern "C" { + #[unsafe(ffi_pure = 1)] + //~^ ERROR malformed + #[link_ordinal] + //~^ ERROR malformed + pub fn baz(); + + #[unsafe(ffi_const = 1)] + //~^ ERROR malformed + #[linkage] + //~^ ERROR malformed + pub fn bar(); +} + +#[allow] +//~^ ERROR malformed +#[expect] +//~^ ERROR malformed +#[warn] +//~^ ERROR malformed +#[deny] +//~^ ERROR malformed +#[forbid] +//~^ ERROR malformed +#[debugger_visualizer] +//~^ ERROR invalid argument +//~| ERROR malformed `debugger_visualizer` attribute input +#[automatically_derived = 18] +//~^ ERROR malformed +mod yooo { + +} + +#[non_exhaustive = 1] +//~^ ERROR malformed +enum Slenum { + +} + +#[thread_local()] +//~^ ERROR malformed +static mut TLS: u8 = 42; + +#[no_link()] +//~^ ERROR malformed +#[macro_use = 1] +//~^ ERROR malformed +extern crate wloop; +//~^ ERROR can't find crate for `wloop` [E0463] + +#[macro_export = 18] +//~^ ERROR malformed `macro_export` attribute input +#[allow_internal_unsafe = 1] +//~^ ERROR malformed +//~| ERROR allow_internal_unsafe side-steps the unsafe_code lint +macro_rules! slump { + () => {} +} + +fn main() {} diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr new file mode 100644 index 00000000000..bf063e8f6e5 --- /dev/null +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -0,0 +1,596 @@ +error: `cfg` is not followed by parentheses + --> $DIR/malformed-attrs.rs:100:1 + | +LL | #[cfg] + | ^^^^^^ help: expected syntax is: `cfg(/* predicate */)` + +error: malformed `cfg_attr` attribute input + --> $DIR/malformed-attrs.rs:102:1 + | +LL | #[cfg_attr] + | ^^^^^^^^^^^ + | + = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute> +help: missing condition and attribute + | +LL | #[cfg_attr(condition, attribute, other_attribute, ...)] + | ++++++++++++++++++++++++++++++++++++++++++++ + +error[E0463]: can't find crate for `wloop` + --> $DIR/malformed-attrs.rs:209:1 + | +LL | extern crate wloop; + | ^^^^^^^^^^^^^^^^^^^ can't find crate + +error: malformed `omit_gdb_pretty_printer_section` attribute input + --> $DIR/malformed-attrs.rs:26:1 + | +LL | #![omit_gdb_pretty_printer_section = 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![omit_gdb_pretty_printer_section]` + +error: malformed `windows_subsystem` attribute input + --> $DIR/malformed-attrs.rs:29:1 + | +LL | #![windows_subsystem] + | ^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![windows_subsystem = "windows|console"]` + +error: malformed `crate_name` attribute input + --> $DIR/malformed-attrs.rs:72:1 + | +LL | #[crate_name] + | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` + +error: malformed `target_feature` attribute input + --> $DIR/malformed-attrs.rs:77:1 + | +LL | #[target_feature] + | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[target_feature(enable = "name")]` + +error: malformed `export_stable` attribute input + --> $DIR/malformed-attrs.rs:79:1 + | +LL | #[export_stable = 1] + | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_stable]` + +error: malformed `coverage` attribute input + --> $DIR/malformed-attrs.rs:88:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(off)] + | +++++ +LL | #[coverage(on)] + | ++++ + +error: malformed `no_sanitize` attribute input + --> $DIR/malformed-attrs.rs:90:1 + | +LL | #[no_sanitize] + | ^^^^^^^^^^^^^^ help: must be of the form: `#[no_sanitize(address, kcfi, memory, thread)]` + +error: malformed `no_implicit_prelude` attribute input + --> $DIR/malformed-attrs.rs:95:1 + | +LL | #[no_implicit_prelude = 23] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[no_implicit_prelude]` + +error: malformed `proc_macro` attribute input + --> $DIR/malformed-attrs.rs:97:1 + | +LL | #[proc_macro = 18] + | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro]` + +error: malformed `instruction_set` attribute input + --> $DIR/malformed-attrs.rs:104:1 + | +LL | #[instruction_set] + | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[instruction_set(set)]` + +error: malformed `patchable_function_entry` attribute input + --> $DIR/malformed-attrs.rs:106:1 + | +LL | #[patchable_function_entry] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` + +error: malformed `coroutine` attribute input + --> $DIR/malformed-attrs.rs:109:5 + | +LL | #[coroutine = 63] || {} + | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[coroutine]` + +error: malformed `proc_macro_attribute` attribute input + --> $DIR/malformed-attrs.rs:114:1 + | +LL | #[proc_macro_attribute = 19] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_attribute]` + +error: malformed `proc_macro_derive` attribute input + --> $DIR/malformed-attrs.rs:121:1 + | +LL | #[proc_macro_derive] + | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` + +error: malformed `must_not_suspend` attribute input + --> $DIR/malformed-attrs.rs:130:1 + | +LL | #[must_not_suspend()] + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[must_not_suspend()] +LL + #[must_not_suspend = "reason"] + | +LL - #[must_not_suspend()] +LL + #[must_not_suspend] + | + +error: malformed `cfi_encoding` attribute input + --> $DIR/malformed-attrs.rs:132:1 + | +LL | #[cfi_encoding] + | ^^^^^^^^^^^^^^^ help: must be of the form: `#[cfi_encoding = "encoding"]` + +error: malformed `type_const` attribute input + --> $DIR/malformed-attrs.rs:141:5 + | +LL | #[type_const = 1] + | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[type_const]` + +error: malformed `marker` attribute input + --> $DIR/malformed-attrs.rs:153:1 + | +LL | #[marker = 3] + | ^^^^^^^^^^^^^ help: must be of the form: `#[marker]` + +error: malformed `fundamental` attribute input + --> $DIR/malformed-attrs.rs:155:1 + | +LL | #[fundamental()] + | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[fundamental]` + +error: malformed `ffi_pure` attribute input + --> $DIR/malformed-attrs.rs:163:5 + | +LL | #[unsafe(ffi_pure = 1)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[ffi_pure]` + +error: malformed `link_ordinal` attribute input + --> $DIR/malformed-attrs.rs:165:5 + | +LL | #[link_ordinal] + | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_ordinal(ordinal)]` + +error: malformed `ffi_const` attribute input + --> $DIR/malformed-attrs.rs:169:5 + | +LL | #[unsafe(ffi_const = 1)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[ffi_const]` + +error: malformed `linkage` attribute input + --> $DIR/malformed-attrs.rs:171:5 + | +LL | #[linkage] + | ^^^^^^^^^^ help: must be of the form: `#[linkage = "external|internal|..."]` + +error: malformed `allow` attribute input + --> $DIR/malformed-attrs.rs:176:1 + | +LL | #[allow] + | ^^^^^^^^ help: must be of the form: `#[allow(lint1, lint2, ..., /*opt*/ reason = "...")]` + +error: malformed `expect` attribute input + --> $DIR/malformed-attrs.rs:178:1 + | +LL | #[expect] + | ^^^^^^^^^ help: must be of the form: `#[expect(lint1, lint2, ..., /*opt*/ reason = "...")]` + +error: malformed `warn` attribute input + --> $DIR/malformed-attrs.rs:180:1 + | +LL | #[warn] + | ^^^^^^^ help: must be of the form: `#[warn(lint1, lint2, ..., /*opt*/ reason = "...")]` + +error: malformed `deny` attribute input + --> $DIR/malformed-attrs.rs:182:1 + | +LL | #[deny] + | ^^^^^^^ help: must be of the form: `#[deny(lint1, lint2, ..., /*opt*/ reason = "...")]` + +error: malformed `forbid` attribute input + --> $DIR/malformed-attrs.rs:184:1 + | +LL | #[forbid] + | ^^^^^^^^^ help: must be of the form: `#[forbid(lint1, lint2, ..., /*opt*/ reason = "...")]` + +error: malformed `debugger_visualizer` attribute input + --> $DIR/malformed-attrs.rs:186:1 + | +LL | #[debugger_visualizer] + | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]` + +error: malformed `automatically_derived` attribute input + --> $DIR/malformed-attrs.rs:189:1 + | +LL | #[automatically_derived = 18] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[automatically_derived]` + +error: malformed `non_exhaustive` attribute input + --> $DIR/malformed-attrs.rs:195:1 + | +LL | #[non_exhaustive = 1] + | ^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[non_exhaustive]` + +error: malformed `thread_local` attribute input + --> $DIR/malformed-attrs.rs:201:1 + | +LL | #[thread_local()] + | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[thread_local]` + +error: malformed `no_link` attribute input + --> $DIR/malformed-attrs.rs:205:1 + | +LL | #[no_link()] + | ^^^^^^^^^^^^ help: must be of the form: `#[no_link]` + +error: malformed `macro_use` attribute input + --> $DIR/malformed-attrs.rs:207:1 + | +LL | #[macro_use = 1] + | ^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[macro_use = 1] +LL + #[macro_use(name1, name2, ...)] + | +LL - #[macro_use = 1] +LL + #[macro_use] + | + +error: malformed `macro_export` attribute input + --> $DIR/malformed-attrs.rs:212:1 + | +LL | #[macro_export = 18] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[macro_export = 18] +LL + #[macro_export(local_inner_macros)] + | +LL - #[macro_export = 18] +LL + #[macro_export] + | + +error: malformed `allow_internal_unsafe` attribute input + --> $DIR/malformed-attrs.rs:214:1 + | +LL | #[allow_internal_unsafe = 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[allow_internal_unsafe]` + +error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type + --> $DIR/malformed-attrs.rs:97:1 + | +LL | #[proc_macro = 18] + | ^^^^^^^^^^^^^^^^^^ + +error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type + --> $DIR/malformed-attrs.rs:114:1 + | +LL | #[proc_macro_attribute = 19] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type + --> $DIR/malformed-attrs.rs:121:1 + | +LL | #[proc_macro_derive] + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0658]: allow_internal_unsafe side-steps the unsafe_code lint + --> $DIR/malformed-attrs.rs:214:1 + | +LL | #[allow_internal_unsafe = 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(allow_internal_unsafe)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]` + --> $DIR/malformed-attrs.rs:42:1 + | +LL | #[doc] + | ^^^^^^ + | + = 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 #57571 <https://github.com/rust-lang/rust/issues/57571> + = note: `#[deny(ill_formed_attribute_input)]` on by default + +error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]` + --> $DIR/malformed-attrs.rs:74:1 + | +LL | #[doc] + | ^^^^^^ + | + = 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 #57571 <https://github.com/rust-lang/rust/issues/57571> + +error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]` + --> $DIR/malformed-attrs.rs:81:1 + | +LL | #[link] + | ^^^^^^^ + | + = 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 #57571 <https://github.com/rust-lang/rust/issues/57571> + +error: valid forms for the attribute are `#[ignore]` and `#[ignore = "reason"]` + --> $DIR/malformed-attrs.rs:92:1 + | +LL | #[ignore()] + | ^^^^^^^^^^^ + | + = 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 #57571 <https://github.com/rust-lang/rust/issues/57571> + +error: invalid argument + --> $DIR/malformed-attrs.rs:186:1 + | +LL | #[debugger_visualizer] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected: `natvis_file = "..."` + = note: OR + = note: expected: `gdb_script_file = "..."` + +error[E0539]: malformed `export_name` attribute input + --> $DIR/malformed-attrs.rs:32:1 + | +LL | #[unsafe(export_name)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_name = "name"]` + +error: `rustc_allow_const_fn_unstable` expects a list of feature names + --> $DIR/malformed-attrs.rs:34:1 + | +LL | #[rustc_allow_const_fn_unstable] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `allow_internal_unstable` expects a list of feature names + --> $DIR/malformed-attrs.rs:36:1 + | +LL | #[allow_internal_unstable] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0539]: malformed `rustc_confusables` attribute input + --> $DIR/malformed-attrs.rs:38:1 + | +LL | #[rustc_confusables] + | ^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` + +error[E0539]: malformed `deprecated` attribute input + --> $DIR/malformed-attrs.rs:40:1 + | +LL | #[deprecated = 5] + | ^^^^^^^^^^^^^^^-^ + | | + | expected a string literal here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[deprecated = 5] +LL + #[deprecated = "reason"] + | +LL - #[deprecated = 5] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated = 5] +LL + #[deprecated] + | + +error[E0539]: malformed `rustc_macro_transparency` attribute input + --> $DIR/malformed-attrs.rs:45:1 + | +LL | #[rustc_macro_transparency] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_macro_transparency = "transparent|semitransparent|opaque"]` + +error[E0539]: malformed `repr` attribute input + --> $DIR/malformed-attrs.rs:47:1 + | +LL | #[repr] + | ^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]` + +error[E0565]: malformed `rustc_as_ptr` attribute input + --> $DIR/malformed-attrs.rs:49:1 + | +LL | #[rustc_as_ptr = 5] + | ^^^^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[rustc_as_ptr]` + +error[E0539]: malformed `align` attribute input + --> $DIR/malformed-attrs.rs:54:1 + | +LL | #[align] + | ^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[align(<alignment in bytes>)]` + +error[E0539]: malformed `optimize` attribute input + --> $DIR/malformed-attrs.rs:56:1 + | +LL | #[optimize] + | ^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[optimize(size|speed|none)]` + +error[E0565]: malformed `cold` attribute input + --> $DIR/malformed-attrs.rs:58:1 + | +LL | #[cold = 1] + | ^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[cold]` + +error: valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` + --> $DIR/malformed-attrs.rs:60:1 + | +LL | #[must_use()] + | ^^^^^^^^^^^^^ + +error[E0565]: malformed `no_mangle` attribute input + --> $DIR/malformed-attrs.rs:62:1 + | +LL | #[no_mangle = 1] + | ^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[no_mangle]` + +error[E0565]: malformed `naked` attribute input + --> $DIR/malformed-attrs.rs:64:1 + | +LL | #[unsafe(naked())] + | ^^^^^^^^^^^^^^--^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[naked]` + +error[E0565]: malformed `track_caller` attribute input + --> $DIR/malformed-attrs.rs:66:1 + | +LL | #[track_caller()] + | ^^^^^^^^^^^^^^--^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[track_caller]` + +error[E0539]: malformed `export_name` attribute input + --> $DIR/malformed-attrs.rs:68:1 + | +LL | #[export_name()] + | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_name = "name"]` + +error[E0805]: malformed `used` attribute input + --> $DIR/malformed-attrs.rs:70:1 + | +LL | #[used()] + | ^^^^^^--^ + | | + | expected a single argument here + | +help: try changing it to one of the following valid forms of the attribute + | +LL | #[used(compiler|linker)] + | +++++++++++++++ +LL - #[used()] +LL + #[used] + | + +error[E0539]: malformed `link_name` attribute input + --> $DIR/malformed-attrs.rs:84:1 + | +LL | #[link_name] + | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]` + +error[E0539]: malformed `link_section` attribute input + --> $DIR/malformed-attrs.rs:86:1 + | +LL | #[link_section] + | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]` + +error[E0539]: malformed `must_use` attribute input + --> $DIR/malformed-attrs.rs:117:1 + | +LL | #[must_use = 1] + | ^^^^^^^^^^^^^-^ + | | + | expected a string literal here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[must_use = 1] +LL + #[must_use = "reason"] + | +LL - #[must_use = 1] +LL + #[must_use] + | + +error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input + --> $DIR/malformed-attrs.rs:126:1 + | +LL | #[rustc_layout_scalar_valid_range_start] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]` + +error[E0539]: malformed `rustc_layout_scalar_valid_range_end` attribute input + --> $DIR/malformed-attrs.rs:128:1 + | +LL | #[rustc_layout_scalar_valid_range_end] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` + +warning: `#[diagnostic::do_not_recommend]` does not expect any arguments + --> $DIR/malformed-attrs.rs:147:1 + | +LL | #[diagnostic::do_not_recommend()] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default + +warning: missing options for `on_unimplemented` attribute + --> $DIR/malformed-attrs.rs:136:1 + | +LL | #[diagnostic::on_unimplemented] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + +warning: malformed `on_unimplemented` attribute + --> $DIR/malformed-attrs.rs:138:1 + | +LL | #[diagnostic::on_unimplemented = 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]` + --> $DIR/malformed-attrs.rs:51:1 + | +LL | #[inline = 5] + | ^^^^^^^^^^^^^ + | + = 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 #57571 <https://github.com/rust-lang/rust/issues/57571> + +error[E0308]: mismatched types + --> $DIR/malformed-attrs.rs:109:23 + | +LL | fn test() { + | - help: a return type might be missing here: `-> _` +LL | #[coroutine = 63] || {} + | ^^^^^ expected `()`, found coroutine + | + = note: expected unit type `()` + found coroutine `{coroutine@$DIR/malformed-attrs.rs:109:23: 109:25}` + +error: aborting due to 72 previous errors; 3 warnings emitted + +Some errors have detailed explanations: E0308, E0463, E0539, E0565, E0658, E0805. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/closures/missing-body.rs b/tests/ui/closures/missing-body.rs new file mode 100644 index 00000000000..461c2be3ccd --- /dev/null +++ b/tests/ui/closures/missing-body.rs @@ -0,0 +1,7 @@ +// Checks that the compiler complains about the missing closure body and does not +// crash. +// This is a regression test for <https://github.com/rust-lang/rust/issues/143128>. + +fn main() { |b: [str; _]| {}; } +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for closures +//~| ERROR the size for values of type `str` cannot be known at compilation time diff --git a/tests/ui/closures/missing-body.stderr b/tests/ui/closures/missing-body.stderr new file mode 100644 index 00000000000..33580fc2fbd --- /dev/null +++ b/tests/ui/closures/missing-body.stderr @@ -0,0 +1,19 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for closures + --> $DIR/missing-body.rs:5:23 + | +LL | fn main() { |b: [str; _]| {}; } + | ^ not allowed in type signatures + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/missing-body.rs:5:17 + | +LL | fn main() { |b: [str; _]| {}; } + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: slice and array elements must have `Sized` type + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0121, E0277. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/codegen/maximal-hir-to-mir-coverage-flag.rs b/tests/ui/codegen/maximal-hir-to-mir-coverage-flag.rs new file mode 100644 index 00000000000..64c31beba28 --- /dev/null +++ b/tests/ui/codegen/maximal-hir-to-mir-coverage-flag.rs @@ -0,0 +1,12 @@ +//! Test that -Z maximal-hir-to-mir-coverage flag is accepted. +//! +//! Original PR: https://github.com/rust-lang/rust/pull/105286 + +//@ compile-flags: -Zmaximal-hir-to-mir-coverage +//@ run-pass + +fn main() { + let x = 1; + let y = x + 1; + println!("{y}"); +} diff --git a/tests/ui/codegen/mono-respects-abi-alignment.rs b/tests/ui/codegen/mono-respects-abi-alignment.rs new file mode 100644 index 00000000000..045d82b761f --- /dev/null +++ b/tests/ui/codegen/mono-respects-abi-alignment.rs @@ -0,0 +1,37 @@ +//! Test that monomorphization correctly distinguishes types with different ABI alignment. +//! +//! On x86_64-linux-gnu and similar platforms, structs get 8-byte "preferred" +//! alignment, but their "ABI" alignment (what actually matters for data layout) +//! is the largest alignment of any field. If monomorphization incorrectly uses +//! "preferred" alignment instead of "ABI" alignment, it might unify types `A` +//! and `B` even though `S<A>` and `S<B>` have field `t` at different offsets, +//! leading to incorrect method dispatch for `unwrap()`. + +//@ run-pass + +#[derive(Copy, Clone)] +struct S<T> { + #[allow(dead_code)] + i: u8, + t: T, +} + +impl<T> S<T> { + fn unwrap(self) -> T { + self.t + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +struct A((u32, u32)); // Different ABI alignment than B + +#[derive(Copy, Clone, PartialEq, Debug)] +struct B(u64); // Different ABI alignment than A + +pub fn main() { + static CA: S<A> = S { i: 0, t: A((13, 104)) }; + static CB: S<B> = S { i: 0, t: B(31337) }; + + assert_eq!(CA.unwrap(), A((13, 104))); + assert_eq!(CB.unwrap(), B(31337)); +} diff --git a/tests/ui/codegen/msvc-opt-level-z-no-corruption.rs b/tests/ui/codegen/msvc-opt-level-z-no-corruption.rs new file mode 100644 index 00000000000..ba97acec822 --- /dev/null +++ b/tests/ui/codegen/msvc-opt-level-z-no-corruption.rs @@ -0,0 +1,37 @@ +//! Test that opt-level=z produces correct code on Windows MSVC targets. +//! +//! A previously outdated version of LLVM caused compilation failures and +//! generated invalid code on Windows specifically with optimization level `z`. +//! The bug manifested as corrupted base pointers due to incorrect register +//! usage in the generated assembly (e.g., `popl %esi` corrupting local variables). +//! After updating to a more recent LLVM version, this test ensures that +//! compilation and execution both succeed with opt-level=z. +//! +//! Regression test for <https://github.com/rust-lang/rust/issues/45034>. + +//@ ignore-cross-compile +// Reason: the compiled binary is executed +//@ only-windows +// Reason: the observed bug only occurred on Windows MSVC targets +//@ run-pass +//@ compile-flags: -C opt-level=z + +#![feature(test)] +extern crate test; + +fn foo(x: i32, y: i32) -> i64 { + (x + y) as i64 +} + +#[inline(never)] +fn bar() { + let _f = Box::new(0); + // This call used to trigger an LLVM bug in opt-level=z where the base + // pointer gets corrupted due to incorrect register allocation + let y: fn(i32, i32) -> i64 = test::black_box(foo); + test::black_box(y(1, 2)); +} + +fn main() { + bar(); +} diff --git a/tests/ui/compiletest-self-test/ui-test-missing-annotations-detection.rs b/tests/ui/compiletest-self-test/ui-test-missing-annotations-detection.rs new file mode 100644 index 00000000000..3a110bdad35 --- /dev/null +++ b/tests/ui/compiletest-self-test/ui-test-missing-annotations-detection.rs @@ -0,0 +1,9 @@ +//! Regression test checks UI tests without error annotations are detected as failing. +//! +//! This tests that when we forget to use any `//~ ERROR` comments whatsoever, +//! the test doesn't succeed +//! Originally created in https://github.com/rust-lang/rust/pull/56244 + +//@ should-fail + +fn main() {} diff --git a/tests/ui/consts/const-fn-type-name.rs b/tests/ui/consts/const-fn-type-name.rs index 5403c26b979..733ab79b7cd 100644 --- a/tests/ui/consts/const-fn-type-name.rs +++ b/tests/ui/consts/const-fn-type-name.rs @@ -5,7 +5,7 @@ #![allow(dead_code)] const fn type_name_wrapper<T>(_: &T) -> &'static str { - core::intrinsics::type_name::<T>() + const { core::intrinsics::type_name::<T>() } } struct Struct<TA, TB, TC> { diff --git a/tests/ui/derives/derive-Debug-enum-variants.rs b/tests/ui/derives/derive-Debug-enum-variants.rs new file mode 100644 index 00000000000..26f527f7664 --- /dev/null +++ b/tests/ui/derives/derive-Debug-enum-variants.rs @@ -0,0 +1,30 @@ +//! Test that `#[derive(Debug)]` for enums correctly formats variant names. + +//@ run-pass + +#[derive(Debug)] +enum Foo { + A(usize), + C, +} + +#[derive(Debug)] +enum Bar { + D, +} + +pub fn main() { + // Test variant with data + let foo_a = Foo::A(22); + assert_eq!("A(22)".to_string(), format!("{:?}", foo_a)); + + if let Foo::A(value) = foo_a { + println!("Value: {}", value); // This needs to remove #[allow(dead_code)] + } + + // Test unit variant + assert_eq!("C".to_string(), format!("{:?}", Foo::C)); + + // Test unit variant from different enum + assert_eq!("D".to_string(), format!("{:?}", Bar::D)); +} diff --git a/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr b/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr index fe1ce5ad18b..ed6e5c3e0c0 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr @@ -302,7 +302,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures LL | trait P<F> where F: Fn() -> _ { | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/bad-assoc-ty.rs:88:38 | LL | fn foo<F>(_: F) where F: Fn() -> _ {} diff --git a/tests/ui/did_you_mean/bad-assoc-ty.edition2021.stderr b/tests/ui/did_you_mean/bad-assoc-ty.edition2021.stderr index 2cf7a150aa2..2ee8ab2760a 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.edition2021.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.edition2021.stderr @@ -284,7 +284,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures LL | trait P<F> where F: Fn() -> _ { | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/bad-assoc-ty.rs:88:38 | LL | fn foo<F>(_: F) where F: Fn() -> _ {} diff --git a/tests/ui/did_you_mean/bad-assoc-ty.rs b/tests/ui/did_you_mean/bad-assoc-ty.rs index 9abda4fd962..39f0a84855a 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.rs +++ b/tests/ui/did_you_mean/bad-assoc-ty.rs @@ -86,7 +86,7 @@ trait P<F> where F: Fn() -> _ { trait Q { fn foo<F>(_: F) where F: Fn() -> _ {} - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated functions } fn main() {} diff --git a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed index db18cf2ad96..0096d3eaea4 100644 --- a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed +++ b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.fixed @@ -7,7 +7,7 @@ trait Foo<T>: Sized { impl Foo<usize> for () { fn bar(i: i32, t: usize, s: &()) -> (usize, i32) { - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated functions //~| ERROR type annotations needed (1, 2) } diff --git a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs index 1217a96112d..9ebc565b8fd 100644 --- a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs +++ b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.rs @@ -7,7 +7,7 @@ trait Foo<T>: Sized { impl Foo<usize> for () { fn bar(i: _, t: _, s: _) -> _ { - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated functions //~| ERROR type annotations needed (1, 2) } diff --git a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr index 6c24a5899ea..3c11ad0cf29 100644 --- a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr +++ b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr @@ -1,4 +1,4 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/replace-impl-infer-ty-from-trait.rs:9:15 | LL | fn bar(i: _, t: _, s: _) -> _ { diff --git a/tests/ui/diverging-fn-tail-35849.rs b/tests/ui/diverging-fn-tail-35849.rs deleted file mode 100644 index f21ce2973e9..00000000000 --- a/tests/ui/diverging-fn-tail-35849.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn assert_sizeof() -> ! { - unsafe { - ::std::mem::transmute::<f64, [u8; 8]>(panic!()) - //~^ ERROR mismatched types - } -} - -fn main() { } diff --git a/tests/ui/early-ret-binop-add.rs b/tests/ui/early-ret-binop-add.rs deleted file mode 100644 index 3fec66f35fb..00000000000 --- a/tests/ui/early-ret-binop-add.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass - -#![allow(dead_code)] -#![allow(unreachable_code)] - -use std::ops::Add; - -fn wsucc<T:Add<Output=T> + Copy>(n: T) -> T { n + { return n } } - -pub fn main() { } diff --git a/tests/ui/elide-errors-on-mismatched-tuple.rs b/tests/ui/elide-errors-on-mismatched-tuple.rs deleted file mode 100644 index 7d87b0a7756..00000000000 --- a/tests/ui/elide-errors-on-mismatched-tuple.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Hide irrelevant E0277 errors (#50333) - -trait T {} - -struct A; -impl T for A {} -impl A { - fn new() -> Self { - Self {} - } -} - -fn main() { - let (a, b, c) = (A::new(), A::new()); // This tuple is 2 elements, should be three - //~^ ERROR mismatched types - let ts: Vec<&dyn T> = vec![&a, &b, &c]; - // There is no E0277 error above, as `a`, `b` and `c` are `TyErr` -} diff --git a/tests/ui/elided-test.rs b/tests/ui/elided-test.rs deleted file mode 100644 index 2bedc25e17b..00000000000 --- a/tests/ui/elided-test.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Since we're not compiling a test runner this function should be elided -// and the build will fail because main doesn't exist -#[test] -fn main() { -} //~ ERROR `main` function not found in crate `elided_test` diff --git a/tests/ui/elided-test.stderr b/tests/ui/elided-test.stderr deleted file mode 100644 index 7aebe5d8264..00000000000 --- a/tests/ui/elided-test.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0601]: `main` function not found in crate `elided_test` - --> $DIR/elided-test.rs:5:2 - | -LL | } - | ^ consider adding a `main` function to `$DIR/elided-test.rs` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0601`. diff --git a/tests/ui/else-if.rs b/tests/ui/else-if.rs deleted file mode 100644 index 2161b28c58c..00000000000 --- a/tests/ui/else-if.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ run-pass - -pub fn main() { - if 1 == 2 { - assert!((false)); - } else if 2 == 3 { - assert!((false)); - } else if 3 == 4 { assert!((false)); } else { assert!((true)); } - if 1 == 2 { assert!((false)); } else if 2 == 2 { assert!((true)); } - if 1 == 2 { - assert!((false)); - } else if 2 == 2 { - if 1 == 1 { - assert!((true)); - } else { if 2 == 1 { assert!((false)); } else { assert!((false)); } } - } - if 1 == 2 { - assert!((false)); - } else { if 1 == 2 { assert!((false)); } else { assert!((true)); } } -} diff --git a/tests/ui/expr/early-return-in-binop.rs b/tests/ui/expr/early-return-in-binop.rs new file mode 100644 index 00000000000..389d25210f7 --- /dev/null +++ b/tests/ui/expr/early-return-in-binop.rs @@ -0,0 +1,19 @@ +//! Test early return within binary operation expressions + +//@ run-pass + +#![allow(dead_code)] +#![allow(unreachable_code)] + +use std::ops::Add; + +/// Function that performs addition with an early return in the right operand +fn add_with_early_return<T: Add<Output = T> + Copy>(n: T) -> T { + n + { return n } +} + +pub fn main() { + // Test with different numeric types to ensure generic behavior works + let _result1 = add_with_early_return(42i32); + let _result2 = add_with_early_return(3.14f64); +} diff --git a/tests/ui/extern/issue-47725.rs b/tests/ui/extern/issue-47725.rs index 012673b9159..60d0cd62347 100644 --- a/tests/ui/extern/issue-47725.rs +++ b/tests/ui/extern/issue-47725.rs @@ -17,7 +17,6 @@ extern "C" { #[link_name] //~^ ERROR malformed `link_name` attribute input //~| HELP must be of the form -//~| NOTE expected this to be of the form `link_name = "..." extern "C" { fn bar() -> u32; } diff --git a/tests/ui/extern/issue-47725.stderr b/tests/ui/extern/issue-47725.stderr index 53d6b4927f8..4fd02a1778b 100644 --- a/tests/ui/extern/issue-47725.stderr +++ b/tests/ui/extern/issue-47725.stderr @@ -2,10 +2,7 @@ error[E0539]: malformed `link_name` attribute input --> $DIR/issue-47725.rs:17:1 | LL | #[link_name] - | ^^^^^^^^^^^^ - | | - | expected this to be of the form `link_name = "..."` - | help: must be of the form: `#[link_name = "name"]` + | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]` warning: attribute should be applied to a foreign function or static --> $DIR/issue-47725.rs:3:1 diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 1243ed5d8f4..02b9e2f06ee 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -387,14 +387,6 @@ LL | #![link()] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:69:1 - | -LL | #![link_section = "1800"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ not a function or static - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 | @@ -411,6 +403,14 @@ LL | #![link_name = "1900"] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:69:1 + | +LL | #![link_section = "1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + warning: `#[must_use]` has no effect when applied to a module --> $DIR/issue-43106-gating-of-builtin-attrs.rs:72:1 | diff --git a/tests/ui/logging-only-prints-once.rs b/tests/ui/fmt/debug-single-call.rs index bb8c29694b5..b59a766c71a 100644 --- a/tests/ui/logging-only-prints-once.rs +++ b/tests/ui/fmt/debug-single-call.rs @@ -1,9 +1,12 @@ +//! Test that Debug::fmt is called exactly once during formatting. +//! +//! This is a regression test for PR https://github.com/rust-lang/rust/pull/10715 + //@ run-pass //@ needs-threads use std::cell::Cell; -use std::fmt; -use std::thread; +use std::{fmt, thread}; struct Foo(Cell<isize>); diff --git a/tests/ui/fn/error-recovery-mismatch.stderr b/tests/ui/fn/error-recovery-mismatch.stderr index c046302cb91..10dab3052be 100644 --- a/tests/ui/fn/error-recovery-mismatch.stderr +++ b/tests/ui/fn/error-recovery-mismatch.stderr @@ -29,7 +29,7 @@ LL | fn fold<T>(&self, _: T, &self._) {} = note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686> = note: `#[warn(anonymous_parameters)]` on by default -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/error-recovery-mismatch.rs:11:35 | LL | fn fold<T>(&self, _: T, &self._) {} diff --git a/tests/ui/generics/unconstrained-type-params-inherent-impl.rs b/tests/ui/generics/unconstrained-type-params-inherent-impl.rs new file mode 100644 index 00000000000..c971de0d1f2 --- /dev/null +++ b/tests/ui/generics/unconstrained-type-params-inherent-impl.rs @@ -0,0 +1,32 @@ +//! Test for unconstrained type parameters in inherent implementations + +struct MyType; + +struct MyType1<T>(T); + +trait Bar { + type Out; +} + +impl<T> MyType { + //~^ ERROR the type parameter `T` is not constrained + // T is completely unused - this should fail +} + +impl<T> MyType1<T> { + // OK: T is used in the self type `MyType1<T>` +} + +impl<T, U> MyType1<T> { + //~^ ERROR the type parameter `U` is not constrained + // T is used in self type, but U is unconstrained - this should fail +} + +impl<T, U> MyType1<T> +where + T: Bar<Out = U>, +{ + // OK: T is used in self type, U is constrained through the where clause +} + +fn main() {} diff --git a/tests/ui/impl-unused-tps-inherent.stderr b/tests/ui/generics/unconstrained-type-params-inherent-impl.stderr index 43f63cf968c..19b02ad396c 100644 --- a/tests/ui/impl-unused-tps-inherent.stderr +++ b/tests/ui/generics/unconstrained-type-params-inherent-impl.stderr @@ -1,14 +1,14 @@ error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps-inherent.rs:9:6 + --> $DIR/unconstrained-type-params-inherent-impl.rs:11:6 | LL | impl<T> MyType { | ^ unconstrained type parameter error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps-inherent.rs:17:8 + --> $DIR/unconstrained-type-params-inherent-impl.rs:20:9 | -LL | impl<T,U> MyType1<T> { - | ^ unconstrained type parameter +LL | impl<T, U> MyType1<T> { + | ^ unconstrained type parameter error: aborting due to 2 previous errors diff --git a/tests/ui/impl-unused-rps-in-assoc-type.rs b/tests/ui/impl-unused-rps-in-assoc-type.rs deleted file mode 100644 index ea41997a698..00000000000 --- a/tests/ui/impl-unused-rps-in-assoc-type.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Test that lifetime parameters must be constrained if they appear in -// an associated type def'n. Issue #22077. - -trait Fun { - type Output; - fn call<'x>(&'x self) -> Self::Output; -} - -struct Holder { x: String } - -impl<'a> Fun for Holder { //~ ERROR E0207 - type Output = &'a str; - fn call<'b>(&'b self) -> &'b str { - &self.x[..] - } -} - -fn main() { } diff --git a/tests/ui/impl-unused-tps-inherent.rs b/tests/ui/impl-unused-tps-inherent.rs deleted file mode 100644 index 83a228e551a..00000000000 --- a/tests/ui/impl-unused-tps-inherent.rs +++ /dev/null @@ -1,25 +0,0 @@ -struct MyType; - -struct MyType1<T>(T); - -trait Bar { - type Out; -} - -impl<T> MyType { - //~^ ERROR the type parameter `T` is not constrained -} - -impl<T> MyType1<T> { - // OK, T is used in `Foo<T>`. -} - -impl<T,U> MyType1<T> { - //~^ ERROR the type parameter `U` is not constrained -} - -impl<T,U> MyType1<T> where T: Bar<Out=U> { - // OK, T is used in `Foo<T>`. -} - -fn main() { } diff --git a/tests/ui/implicit-method-bind.rs b/tests/ui/implicit-method-bind.rs deleted file mode 100644 index 5e27516a89a..00000000000 --- a/tests/ui/implicit-method-bind.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - let _f = 10i32.abs; //~ ERROR attempted to take value of method -} diff --git a/tests/ui/implicit-method-bind.stderr b/tests/ui/implicit-method-bind.stderr deleted file mode 100644 index e9357113f36..00000000000 --- a/tests/ui/implicit-method-bind.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0615]: attempted to take value of method `abs` on type `i32` - --> $DIR/implicit-method-bind.rs:2:20 - | -LL | let _f = 10i32.abs; - | ^^^ method, not a field - | -help: use parentheses to call the method - | -LL | let _f = 10i32.abs(); - | ++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0615`. diff --git a/tests/ui/double-type-import.rs b/tests/ui/imports/duplicate-use-bindings.rs index 6b1eb65d5ae..8cec23ea732 100644 --- a/tests/ui/double-type-import.rs +++ b/tests/ui/imports/duplicate-use-bindings.rs @@ -1,3 +1,5 @@ +//! Test that duplicate use bindings in same namespace produce error + mod foo { pub use self::bar::X; use self::bar::X; diff --git a/tests/ui/double-type-import.stderr b/tests/ui/imports/duplicate-use-bindings.stderr index 8a8fe05ec19..1d4f1fc82db 100644 --- a/tests/ui/double-type-import.stderr +++ b/tests/ui/imports/duplicate-use-bindings.stderr @@ -1,5 +1,5 @@ error[E0252]: the name `X` is defined multiple times - --> $DIR/double-type-import.rs:3:9 + --> $DIR/duplicate-use-bindings.rs:5:9 | LL | pub use self::bar::X; | ------------ previous import of the type `X` here diff --git a/tests/ui/integral-indexing.rs b/tests/ui/indexing/indexing-integral-types.rs index e20553af8a2..a91696a6fd5 100644 --- a/tests/ui/integral-indexing.rs +++ b/tests/ui/indexing/indexing-integral-types.rs @@ -1,16 +1,20 @@ +//! Test that only usize can be used for indexing arrays and slices. + pub fn main() { let v: Vec<isize> = vec![0, 1, 2, 3, 4, 5]; let s: String = "abcdef".to_string(); + + // Valid indexing with usize v[3_usize]; v[3]; - v[3u8]; //~ ERROR the type `[isize]` cannot be indexed by `u8` - v[3i8]; //~ ERROR the type `[isize]` cannot be indexed by `i8` + v[3u8]; //~ ERROR the type `[isize]` cannot be indexed by `u8` + v[3i8]; //~ ERROR the type `[isize]` cannot be indexed by `i8` v[3u32]; //~ ERROR the type `[isize]` cannot be indexed by `u32` v[3i32]; //~ ERROR the type `[isize]` cannot be indexed by `i32` s.as_bytes()[3_usize]; s.as_bytes()[3]; - s.as_bytes()[3u8]; //~ ERROR the type `[u8]` cannot be indexed by `u8` - s.as_bytes()[3i8]; //~ ERROR the type `[u8]` cannot be indexed by `i8` + s.as_bytes()[3u8]; //~ ERROR the type `[u8]` cannot be indexed by `u8` + s.as_bytes()[3i8]; //~ ERROR the type `[u8]` cannot be indexed by `i8` s.as_bytes()[3u32]; //~ ERROR the type `[u8]` cannot be indexed by `u32` s.as_bytes()[3i32]; //~ ERROR the type `[u8]` cannot be indexed by `i32` } diff --git a/tests/ui/integral-indexing.stderr b/tests/ui/indexing/indexing-integral-types.stderr index 26253e078cb..b63991ec2c4 100644 --- a/tests/ui/integral-indexing.stderr +++ b/tests/ui/indexing/indexing-integral-types.stderr @@ -1,5 +1,5 @@ error[E0277]: the type `[isize]` cannot be indexed by `u8` - --> $DIR/integral-indexing.rs:6:7 + --> $DIR/indexing-integral-types.rs:10:7 | LL | v[3u8]; | ^^^ slice indices are of type `usize` or ranges of `usize` @@ -11,7 +11,7 @@ LL | v[3u8]; = note: required for `Vec<isize>` to implement `Index<u8>` error[E0277]: the type `[isize]` cannot be indexed by `i8` - --> $DIR/integral-indexing.rs:7:7 + --> $DIR/indexing-integral-types.rs:11:7 | LL | v[3i8]; | ^^^ slice indices are of type `usize` or ranges of `usize` @@ -23,7 +23,7 @@ LL | v[3i8]; = note: required for `Vec<isize>` to implement `Index<i8>` error[E0277]: the type `[isize]` cannot be indexed by `u32` - --> $DIR/integral-indexing.rs:8:7 + --> $DIR/indexing-integral-types.rs:12:7 | LL | v[3u32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` @@ -35,7 +35,7 @@ LL | v[3u32]; = note: required for `Vec<isize>` to implement `Index<u32>` error[E0277]: the type `[isize]` cannot be indexed by `i32` - --> $DIR/integral-indexing.rs:9:7 + --> $DIR/indexing-integral-types.rs:13:7 | LL | v[3i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` @@ -47,7 +47,7 @@ LL | v[3i32]; = note: required for `Vec<isize>` to implement `Index<i32>` error[E0277]: the type `[u8]` cannot be indexed by `u8` - --> $DIR/integral-indexing.rs:12:18 + --> $DIR/indexing-integral-types.rs:16:18 | LL | s.as_bytes()[3u8]; | ^^^ slice indices are of type `usize` or ranges of `usize` @@ -59,7 +59,7 @@ LL | s.as_bytes()[3u8]; = note: required for `[u8]` to implement `Index<u8>` error[E0277]: the type `[u8]` cannot be indexed by `i8` - --> $DIR/integral-indexing.rs:13:18 + --> $DIR/indexing-integral-types.rs:17:18 | LL | s.as_bytes()[3i8]; | ^^^ slice indices are of type `usize` or ranges of `usize` @@ -71,7 +71,7 @@ LL | s.as_bytes()[3i8]; = note: required for `[u8]` to implement `Index<i8>` error[E0277]: the type `[u8]` cannot be indexed by `u32` - --> $DIR/integral-indexing.rs:14:18 + --> $DIR/indexing-integral-types.rs:18:18 | LL | s.as_bytes()[3u32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` @@ -83,7 +83,7 @@ LL | s.as_bytes()[3u32]; = note: required for `[u8]` to implement `Index<u32>` error[E0277]: the type `[u8]` cannot be indexed by `i32` - --> $DIR/integral-indexing.rs:15:18 + --> $DIR/indexing-integral-types.rs:19:18 | LL | s.as_bytes()[3i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` diff --git a/tests/ui/inlined-main.rs b/tests/ui/inlined-main.rs deleted file mode 100644 index 731ac0dddca..00000000000 --- a/tests/ui/inlined-main.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ run-pass - -#[inline(always)] -fn main() {} diff --git a/tests/ui/inner-attrs-on-impl.rs b/tests/ui/inner-attrs-on-impl.rs deleted file mode 100644 index 1dce1cdd261..00000000000 --- a/tests/ui/inner-attrs-on-impl.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ run-pass - -struct Foo; - -impl Foo { - #![cfg(false)] - - fn method(&self) -> bool { false } -} - -impl Foo { - #![cfg(not(FALSE))] - - // check that we don't eat attributes too eagerly. - #[cfg(false)] - fn method(&self) -> bool { false } - - fn method(&self) -> bool { true } -} - - -pub fn main() { - assert!(Foo.method()); -} diff --git a/tests/ui/inner-module.rs b/tests/ui/inner-module.rs deleted file mode 100644 index 111f2cab857..00000000000 --- a/tests/ui/inner-module.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass - -mod inner { - pub mod inner2 { - pub fn hello() { println!("hello, modular world"); } - } - pub fn hello() { inner2::hello(); } -} - -pub fn main() { inner::hello(); inner::inner2::hello(); } diff --git a/tests/ui/inner-static-type-parameter.rs b/tests/ui/inner-static-type-parameter.rs deleted file mode 100644 index a1994e7529c..00000000000 --- a/tests/ui/inner-static-type-parameter.rs +++ /dev/null @@ -1,11 +0,0 @@ -// see #9186 - -enum Bar<T> { What } //~ ERROR parameter `T` is never used - -fn foo<T>() { - static a: Bar<T> = Bar::What; -//~^ ERROR can't use generic parameters from outer item -} - -fn main() { -} diff --git a/tests/ui/invalid/invalid_rustc_layout_scalar_valid_range.stderr b/tests/ui/invalid/invalid_rustc_layout_scalar_valid_range.stderr index 7879e7358c0..8b9ad78db37 100644 --- a/tests/ui/invalid/invalid_rustc_layout_scalar_valid_range.stderr +++ b/tests/ui/invalid/invalid_rustc_layout_scalar_valid_range.stderr @@ -1,20 +1,38 @@ -error: expected exactly one integer literal argument +error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:3:1 | LL | #[rustc_layout_scalar_valid_range_start(u32::MAX)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--------^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]` -error: expected exactly one integer literal argument +error[E0805]: malformed `rustc_layout_scalar_valid_range_end` attribute input --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:6:1 | LL | #[rustc_layout_scalar_valid_range_end(1, 2)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^ + | | | + | | expected a single argument here + | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` -error: expected exactly one integer literal argument +error[E0539]: malformed `rustc_layout_scalar_valid_range_end` attribute input --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:9:1 | LL | #[rustc_layout_scalar_valid_range_end(a = "a")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` + +error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input + --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:18:1 + | +LL | #[rustc_layout_scalar_valid_range_start(rustc_layout_scalar_valid_range_start)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------------------------------------^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]` error: attribute should be applied to a struct --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:12:1 @@ -27,11 +45,7 @@ LL | | Y = 14, LL | | } | |_- not a struct -error: expected exactly one integer literal argument - --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:18:1 - | -LL | #[rustc_layout_scalar_valid_range_start(rustc_layout_scalar_valid_range_start)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: aborting due to 5 previous errors +Some errors have detailed explanations: E0539, E0805. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/auxiliary/msvc-data-only-lib.rs b/tests/ui/linkage-attr/auxiliary/msvc-static-data-import-lib.rs index b8a8f905e8b..b8a8f905e8b 100644 --- a/tests/ui/auxiliary/msvc-data-only-lib.rs +++ b/tests/ui/linkage-attr/auxiliary/msvc-static-data-import-lib.rs diff --git a/tests/ui/link-section.rs b/tests/ui/linkage-attr/link-section-placement.rs index a8de8c2e1e7..6a143bfedb4 100644 --- a/tests/ui/link-section.rs +++ b/tests/ui/linkage-attr/link-section-placement.rs @@ -1,8 +1,9 @@ +//! Test placement of functions and statics in custom link sections + //@ run-pass // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] - #![allow(non_upper_case_globals)] #[cfg(not(target_vendor = "apple"))] #[link_section = ".moretext"] diff --git a/tests/ui/linkage-attr/msvc-static-data-import.rs b/tests/ui/linkage-attr/msvc-static-data-import.rs new file mode 100644 index 00000000000..e53eb404ef6 --- /dev/null +++ b/tests/ui/linkage-attr/msvc-static-data-import.rs @@ -0,0 +1,18 @@ +//! Test that static data from external crates can be imported on MSVC targets. +//! +//! On Windows MSVC targets, static data from external rlibs must be imported +//! through `__imp_<symbol>` stubs to ensure proper linking. Without this, +//! the linker would fail with "unresolved external symbol" errors when trying +//! to reference static data from another crate. +//! +//! Regression test for <https://github.com/rust-lang/rust/issues/26591>. +//! Fixed in <https://github.com/rust-lang/rust/pull/28646>. + +//@ run-pass +//@ aux-build:msvc-static-data-import-lib.rs + +extern crate msvc_static_data_import_lib; + +fn main() { + println!("The answer is {}!", msvc_static_data_import_lib::FOO); +} diff --git a/tests/ui/missing_debug_impls.rs b/tests/ui/lint/missing-debug-implementations-lint.rs index 3abc0706887..8a93f052f9c 100644 --- a/tests/ui/missing_debug_impls.rs +++ b/tests/ui/lint/missing-debug-implementations-lint.rs @@ -1,3 +1,7 @@ +//! Test the `missing_debug_implementations` lint that warns about public types without Debug. +//! +//! See https://github.com/rust-lang/rust/issues/20855 + //@ compile-flags: --crate-type lib #![deny(missing_debug_implementations)] #![allow(unused)] @@ -10,7 +14,6 @@ pub enum A {} //~ ERROR type does not implement `Debug` pub enum B {} pub enum C {} - impl fmt::Debug for C { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { Ok(()) @@ -23,15 +26,14 @@ pub struct Foo; //~ ERROR type does not implement `Debug` pub struct Bar; pub struct Baz; - impl fmt::Debug for Baz { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { Ok(()) } } +// Private types should not trigger the lint struct PrivateStruct; - enum PrivateEnum {} #[derive(Debug)] diff --git a/tests/ui/missing_debug_impls.stderr b/tests/ui/lint/missing-debug-implementations-lint.stderr index 0538f207b44..288ab981034 100644 --- a/tests/ui/missing_debug_impls.stderr +++ b/tests/ui/lint/missing-debug-implementations-lint.stderr @@ -1,17 +1,17 @@ error: type does not implement `Debug`; consider adding `#[derive(Debug)]` or a manual implementation - --> $DIR/missing_debug_impls.rs:7:1 + --> $DIR/missing-debug-implementations-lint.rs:11:1 | LL | pub enum A {} | ^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/missing_debug_impls.rs:2:9 + --> $DIR/missing-debug-implementations-lint.rs:6:9 | LL | #![deny(missing_debug_implementations)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type does not implement `Debug`; consider adding `#[derive(Debug)]` or a manual implementation - --> $DIR/missing_debug_impls.rs:20:1 + --> $DIR/missing-debug-implementations-lint.rs:23:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/unused/must-use-macros.fixed b/tests/ui/lint/unused/must-use-macros.fixed new file mode 100644 index 00000000000..609d0c6392b --- /dev/null +++ b/tests/ui/lint/unused/must-use-macros.fixed @@ -0,0 +1,60 @@ +// Makes sure the suggestions of the `unused_must_use` lint are not inside +// +// See <https://github.com/rust-lang/rust/issues/143025> + +//@ check-pass +//@ run-rustfix + +#![expect(unused_macros)] +#![warn(unused_must_use)] + +fn main() { + { + macro_rules! cmp { + ($a:tt, $b:tt) => { + $a == $b + }; + } + + // FIXME(Urgau): For some unknown reason the spans we get are not + // recorded to be from any expansions, preventing us from either + // suggesting in front of the macro or not at all. + // cmp!(1, 1); + } + + { + macro_rules! cmp { + ($a:ident, $b:ident) => { + $a == $b + }; //~^ WARN unused comparison that must be used + } + + let a = 1; + let b = 1; + let _ = cmp!(a, b); + //~^ SUGGESTION let _ + } + + { + macro_rules! cmp { + ($a:expr, $b:expr) => { + $a == $b + }; //~^ WARN unused comparison that must be used + } + + let _ = cmp!(1, 1); + //~^ SUGGESTION let _ + } + + { + macro_rules! cmp { + ($a:tt, $b:tt) => { + $a.eq(&$b) + }; + } + + let _ = cmp!(1, 1); + //~^ WARN unused return value + //~| SUGGESTION let _ + } +} diff --git a/tests/ui/lint/unused/must-use-macros.rs b/tests/ui/lint/unused/must-use-macros.rs new file mode 100644 index 00000000000..63e246ed374 --- /dev/null +++ b/tests/ui/lint/unused/must-use-macros.rs @@ -0,0 +1,60 @@ +// Makes sure the suggestions of the `unused_must_use` lint are not inside +// +// See <https://github.com/rust-lang/rust/issues/143025> + +//@ check-pass +//@ run-rustfix + +#![expect(unused_macros)] +#![warn(unused_must_use)] + +fn main() { + { + macro_rules! cmp { + ($a:tt, $b:tt) => { + $a == $b + }; + } + + // FIXME(Urgau): For some unknown reason the spans we get are not + // recorded to be from any expansions, preventing us from either + // suggesting in front of the macro or not at all. + // cmp!(1, 1); + } + + { + macro_rules! cmp { + ($a:ident, $b:ident) => { + $a == $b + }; //~^ WARN unused comparison that must be used + } + + let a = 1; + let b = 1; + cmp!(a, b); + //~^ SUGGESTION let _ + } + + { + macro_rules! cmp { + ($a:expr, $b:expr) => { + $a == $b + }; //~^ WARN unused comparison that must be used + } + + cmp!(1, 1); + //~^ SUGGESTION let _ + } + + { + macro_rules! cmp { + ($a:tt, $b:tt) => { + $a.eq(&$b) + }; + } + + cmp!(1, 1); + //~^ WARN unused return value + //~| SUGGESTION let _ + } +} diff --git a/tests/ui/lint/unused/must-use-macros.stderr b/tests/ui/lint/unused/must-use-macros.stderr new file mode 100644 index 00000000000..2ad174e10b5 --- /dev/null +++ b/tests/ui/lint/unused/must-use-macros.stderr @@ -0,0 +1,48 @@ +warning: unused comparison that must be used + --> $DIR/must-use-macros.rs:28:17 + | +LL | $a == $b + | ^^^^^^^^ the comparison produces a value +... +LL | cmp!(a, b); + | ---------- in this macro invocation + | +note: the lint level is defined here + --> $DIR/must-use-macros.rs:9:9 + | +LL | #![warn(unused_must_use)] + | ^^^^^^^^^^^^^^^ + = note: this warning originates in the macro `cmp` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = cmp!(a, b); + | +++++++ + +warning: unused comparison that must be used + --> $DIR/must-use-macros.rs:41:17 + | +LL | $a == $b + | ^^^^^^^^ the comparison produces a value +... +LL | cmp!(1, 1); + | ---------- in this macro invocation + | + = note: this warning originates in the macro `cmp` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = cmp!(1, 1); + | +++++++ + +warning: unused return value of `std::cmp::PartialEq::eq` that must be used + --> $DIR/must-use-macros.rs:56:9 + | +LL | cmp!(1, 1); + | ^^^^^^^^^^ + | +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = cmp!(1, 1); + | +++++++ + +warning: 3 warnings emitted + diff --git a/tests/ui/lint/unused/unused-attr-duplicate.rs b/tests/ui/lint/unused/unused-attr-duplicate.rs index 407af40654e..bf94a42f6e0 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.rs +++ b/tests/ui/lint/unused/unused-attr-duplicate.rs @@ -102,4 +102,10 @@ pub fn no_mangle_test() {} #[used] //~ ERROR unused attribute static FOO: u32 = 0; +#[link_section = ".text"] +//~^ ERROR unused attribute +//~| WARN this was previously accepted +#[link_section = ".bss"] +pub extern "C" fn example() {} + fn main() {} diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index e8452465efc..feae8528cf7 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -289,5 +289,18 @@ note: attribute also specified here LL | #[used] | ^^^^^^^ -error: aborting due to 23 previous errors +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:105:1 + | +LL | #[link_section = ".text"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:108:1 + | +LL | #[link_section = ".bss"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 24 previous errors diff --git a/tests/ui/log-err-phi.rs b/tests/ui/log-err-phi.rs deleted file mode 100644 index 1bb97758782..00000000000 --- a/tests/ui/log-err-phi.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ run-pass - -pub fn main() { - if false { - println!("{}", "foobar"); - } -} diff --git a/tests/ui/log-knows-the-names-of-variants.rs b/tests/ui/log-knows-the-names-of-variants.rs deleted file mode 100644 index cb82cb4878a..00000000000 --- a/tests/ui/log-knows-the-names-of-variants.rs +++ /dev/null @@ -1,21 +0,0 @@ -//@ run-pass - -#![allow(non_camel_case_types)] -#![allow(dead_code)] -#[derive(Debug)] -enum foo { - a(usize), - b(String), - c, -} - -#[derive(Debug)] -enum bar { - d, e, f -} - -pub fn main() { - assert_eq!("a(22)".to_string(), format!("{:?}", foo::a(22))); - assert_eq!("c".to_string(), format!("{:?}", foo::c)); - assert_eq!("d".to_string(), format!("{:?}", bar::d)); -} diff --git a/tests/ui/loud_ui.rs b/tests/ui/loud_ui.rs deleted file mode 100644 index 2a73e49e172..00000000000 --- a/tests/ui/loud_ui.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ should-fail - -// this test ensures that when we forget to use -// any `//~ ERROR` comments whatsoever, that the test doesn't succeed - -fn main() {} diff --git a/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs b/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs index 695a752fe17..695a752fe17 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs +++ b/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs diff --git a/tests/ui/macros/macro-metavar-expr-concat/hygiene.rs b/tests/ui/macros/metavar-expressions/concat-hygiene.rs index 24b0e36498a..24b0e36498a 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/hygiene.rs +++ b/tests/ui/macros/metavar-expressions/concat-hygiene.rs diff --git a/tests/ui/macros/macro-metavar-expr-concat/hygiene.stderr b/tests/ui/macros/metavar-expressions/concat-hygiene.stderr index ef2326dce85..f3150d385ee 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/hygiene.stderr +++ b/tests/ui/macros/metavar-expressions/concat-hygiene.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `abcdef` in this scope - --> $DIR/hygiene.rs:5:10 + --> $DIR/concat-hygiene.rs:5:10 | LL | ${concat($lhs, $rhs)} | ^^^^^^^^^^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs b/tests/ui/macros/metavar-expressions/concat-raw-identifiers.rs index b1cb2141cc4..b1cb2141cc4 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs +++ b/tests/ui/macros/metavar-expressions/concat-raw-identifiers.rs diff --git a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr b/tests/ui/macros/metavar-expressions/concat-raw-identifiers.stderr index 4e11e20acc5..7abab6a5103 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr +++ b/tests/ui/macros/metavar-expressions/concat-raw-identifiers.stderr @@ -1,47 +1,47 @@ error: expected identifier or string literal - --> $DIR/raw-identifiers.rs:28:22 + --> $DIR/concat-raw-identifiers.rs:28:22 | LL | let ${concat(r#abc, abc)}: () = (); | ^^^^^ error: expected identifier or string literal - --> $DIR/raw-identifiers.rs:32:27 + --> $DIR/concat-raw-identifiers.rs:32:27 | LL | let ${concat(abc, r#abc)}: () = (); | ^^^^^ error: expected identifier or string literal - --> $DIR/raw-identifiers.rs:35:22 + --> $DIR/concat-raw-identifiers.rs:35:22 | LL | let ${concat(r#abc, r#abc)}: () = (); | ^^^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:5:28 + --> $DIR/concat-raw-identifiers.rs:5:28 | LL | let ${concat(abc, $rhs)}: () = (); | ^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:12:23 + --> $DIR/concat-raw-identifiers.rs:12:23 | LL | let ${concat($lhs, abc)}: () = (); | ^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:19:23 + --> $DIR/concat-raw-identifiers.rs:19:23 | LL | let ${concat($lhs, $rhs)}: () = (); | ^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:19:29 + --> $DIR/concat-raw-identifiers.rs:19:29 | LL | let ${concat($lhs, $rhs)}: () = (); | ^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:19:23 + --> $DIR/concat-raw-identifiers.rs:19:23 | LL | let ${concat($lhs, $rhs)}: () = (); | ^^^ @@ -49,31 +49,31 @@ LL | let ${concat($lhs, $rhs)}: () = (); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:42:28 + --> $DIR/concat-raw-identifiers.rs:42:28 | LL | let ${concat(abc, $rhs)}: () = (); | ^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:49:23 + --> $DIR/concat-raw-identifiers.rs:49:23 | LL | let ${concat($lhs, abc)}: () = (); | ^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:56:23 + --> $DIR/concat-raw-identifiers.rs:56:23 | LL | let ${concat($lhs, $rhs)}: () = (); | ^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:56:29 + --> $DIR/concat-raw-identifiers.rs:56:29 | LL | let ${concat($lhs, $rhs)}: () = (); | ^^^ error: `${concat(..)}` currently does not support raw identifiers - --> $DIR/raw-identifiers.rs:56:23 + --> $DIR/concat-raw-identifiers.rs:56:23 | LL | let ${concat($lhs, $rhs)}: () = (); | ^^^ @@ -81,7 +81,7 @@ LL | let ${concat($lhs, $rhs)}: () = (); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: expected pattern, found `$` - --> $DIR/raw-identifiers.rs:28:13 + --> $DIR/concat-raw-identifiers.rs:28:13 | LL | let ${concat(r#abc, abc)}: () = (); | ^ expected pattern diff --git a/tests/ui/macros/macro-metavar-expr-concat/repetitions.rs b/tests/ui/macros/metavar-expressions/concat-repetitions.rs index 52a7d5cd8a7..52a7d5cd8a7 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/repetitions.rs +++ b/tests/ui/macros/metavar-expressions/concat-repetitions.rs diff --git a/tests/ui/macros/macro-metavar-expr-concat/repetitions.stderr b/tests/ui/macros/metavar-expressions/concat-repetitions.stderr index c3006c4be5d..18b0a90c1c8 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/repetitions.stderr +++ b/tests/ui/macros/metavar-expressions/concat-repetitions.stderr @@ -1,17 +1,17 @@ error: invalid syntax - --> $DIR/repetitions.rs:14:20 + --> $DIR/concat-repetitions.rs:14:20 | LL | const ${concat($a, Z)}: i32 = 3; | ^^^^^^^^^^^^^^^ error: invalid syntax - --> $DIR/repetitions.rs:22:17 + --> $DIR/concat-repetitions.rs:22:17 | LL | read::<${concat($t, $en)}>() | ^^^^^^^^^^^^^^^^^ error: invalid syntax - --> $DIR/repetitions.rs:22:17 + --> $DIR/concat-repetitions.rs:22:17 | LL | read::<${concat($t, $en)}>() | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/macros/metavar-expressions/concat-trace-errors.rs b/tests/ui/macros/metavar-expressions/concat-trace-errors.rs new file mode 100644 index 00000000000..45407f5e86d --- /dev/null +++ b/tests/ui/macros/metavar-expressions/concat-trace-errors.rs @@ -0,0 +1,33 @@ +// Our diagnostics should be able to point to a specific input that caused an invalid +// identifier. + +#![feature(macro_metavar_expr_concat)] + +// See what we can do without expanding anything +macro_rules! pre_expansion { + ($a:ident) => { + ${concat("hi", " bye ")}; + ${concat("hi", "-", "bye")}; + ${concat($a, "-")}; + } +} + +macro_rules! post_expansion { + ($a:literal) => { + const _: () = ${concat("hi", $a, "bye")}; + //~^ ERROR is not generating a valid identifier + } +} + +post_expansion!("!"); + +macro_rules! post_expansion_many { + ($a:ident, $b:ident, $c:ident, $d:literal, $e:ident) => { + const _: () = ${concat($a, $b, $c, $d, $e)}; + //~^ ERROR is not generating a valid identifier + } +} + +post_expansion_many!(a, b, c, ".d", e); + +fn main() {} diff --git a/tests/ui/macros/metavar-expressions/concat-trace-errors.stderr b/tests/ui/macros/metavar-expressions/concat-trace-errors.stderr new file mode 100644 index 00000000000..dac8b58a15c --- /dev/null +++ b/tests/ui/macros/metavar-expressions/concat-trace-errors.stderr @@ -0,0 +1,24 @@ +error: `${concat(..)}` is not generating a valid identifier + --> $DIR/concat-trace-errors.rs:17:24 + | +LL | const _: () = ${concat("hi", $a, "bye")}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | post_expansion!("!"); + | -------------------- in this macro invocation + | + = note: this error originates in the macro `post_expansion` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `${concat(..)}` is not generating a valid identifier + --> $DIR/concat-trace-errors.rs:26:24 + | +LL | const _: () = ${concat($a, $b, $c, $d, $e)}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | post_expansion_many!(a, b, c, ".d", e); + | -------------------------------------- in this macro invocation + | + = note: this error originates in the macro `post_expansion_many` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs b/tests/ui/macros/metavar-expressions/concat-unicode-expansion.rs index 4eeb2384deb..4eeb2384deb 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs +++ b/tests/ui/macros/metavar-expressions/concat-unicode-expansion.rs diff --git a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs b/tests/ui/macros/metavar-expressions/concat-usage-errors.rs index 7673bd3200f..7d8756de9e2 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs +++ b/tests/ui/macros/metavar-expressions/concat-usage-errors.rs @@ -1,6 +1,8 @@ +//@ edition: 2021 + #![feature(macro_metavar_expr_concat)] -macro_rules! wrong_concat_declarations { +macro_rules! syntax_errors { ($ex:expr) => { ${concat()} //~^ ERROR expected identifier @@ -90,11 +92,31 @@ macro_rules! unsupported_literals { //~| ERROR expected pattern let ${concat(_a, 1)}: () = (); //~^ ERROR expected identifier or string literal + let ${concat(_a, 1.5)}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat(_a, c"hi")}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat(_a, b"hi")}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat(_a, b'b')}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat(_a, b'b')}: () = (); + //~^ ERROR expected identifier or string literal let ${concat($ident, 'b')}: () = (); //~^ ERROR expected identifier or string literal let ${concat($ident, 1)}: () = (); //~^ ERROR expected identifier or string literal + let ${concat($ident, 1.5)}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat($ident, c"hi")}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat($ident, b"hi")}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat($ident, b'b')}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat($ident, b'b')}: () = (); + //~^ ERROR expected identifier or string literal }}; } @@ -132,7 +154,7 @@ macro_rules! bad_tt_literal { } fn main() { - wrong_concat_declarations!(1); + syntax_errors!(1); dollar_sign_without_referenced_ident!(VAR); diff --git a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr b/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr index 2de6d2b3ce3..8be3e792ec3 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr +++ b/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr @@ -1,71 +1,131 @@ error: expected identifier or string literal - --> $DIR/syntax-errors.rs:5:10 + --> $DIR/concat-usage-errors.rs:7:10 | LL | ${concat()} | ^^^^^^^^^^ error: `concat` must have at least two elements - --> $DIR/syntax-errors.rs:8:11 + --> $DIR/concat-usage-errors.rs:10:11 | LL | ${concat(aaaa)} | ^^^^^^ error: expected identifier or string literal - --> $DIR/syntax-errors.rs:11:10 + --> $DIR/concat-usage-errors.rs:13:10 | LL | ${concat(aaaa,)} | ^^^^^^^^^^^^^^^ error: expected comma - --> $DIR/syntax-errors.rs:16:10 + --> $DIR/concat-usage-errors.rs:18:10 | LL | ${concat(aaaa aaaa)} | ^^^^^^^^^^^^^^^^^^^ error: `concat` must have at least two elements - --> $DIR/syntax-errors.rs:19:11 + --> $DIR/concat-usage-errors.rs:21:11 | LL | ${concat($ex)} | ^^^^^^ error: expected comma - --> $DIR/syntax-errors.rs:25:10 + --> $DIR/concat-usage-errors.rs:27:10 | LL | ${concat($ex, aaaa 123)} | ^^^^^^^^^^^^^^^^^^^^^^^ error: expected identifier or string literal - --> $DIR/syntax-errors.rs:28:10 + --> $DIR/concat-usage-errors.rs:30:10 | LL | ${concat($ex, aaaa,)} | ^^^^^^^^^^^^^^^^^^^^ error: expected identifier or string literal - --> $DIR/syntax-errors.rs:88:26 + --> $DIR/concat-usage-errors.rs:90:26 | LL | let ${concat(_a, 'b')}: () = (); | ^^^ error: expected identifier or string literal - --> $DIR/syntax-errors.rs:91:26 + --> $DIR/concat-usage-errors.rs:93:26 | LL | let ${concat(_a, 1)}: () = (); | ^ error: expected identifier or string literal - --> $DIR/syntax-errors.rs:94:30 + --> $DIR/concat-usage-errors.rs:95:26 + | +LL | let ${concat(_a, 1.5)}: () = (); + | ^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:97:26 + | +LL | let ${concat(_a, c"hi")}: () = (); + | ^^^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:99:26 + | +LL | let ${concat(_a, b"hi")}: () = (); + | ^^^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:101:26 + | +LL | let ${concat(_a, b'b')}: () = (); + | ^^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:103:26 + | +LL | let ${concat(_a, b'b')}: () = (); + | ^^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:106:30 | LL | let ${concat($ident, 'b')}: () = (); | ^^^ error: expected identifier or string literal - --> $DIR/syntax-errors.rs:96:30 + --> $DIR/concat-usage-errors.rs:108:30 | LL | let ${concat($ident, 1)}: () = (); | ^ +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:110:30 + | +LL | let ${concat($ident, 1.5)}: () = (); + | ^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:112:30 + | +LL | let ${concat($ident, c"hi")}: () = (); + | ^^^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:114:30 + | +LL | let ${concat($ident, b"hi")}: () = (); + | ^^^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:116:30 + | +LL | let ${concat($ident, b'b')}: () = (); + | ^^^^ + +error: expected identifier or string literal + --> $DIR/concat-usage-errors.rs:118:30 + | +LL | let ${concat($ident, b'b')}: () = (); + | ^^^^ + error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:22:19 + --> $DIR/concat-usage-errors.rs:24:19 | LL | ${concat($ex, aaaa)} | ^^ @@ -73,13 +133,13 @@ LL | ${concat($ex, aaaa)} = note: currently only string literals are supported error: variable `foo` is not recognized in meta-variable expression - --> $DIR/syntax-errors.rs:35:30 + --> $DIR/concat-usage-errors.rs:37:30 | LL | const ${concat(FOO, $foo)}: i32 = 2; | ^^^ error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:42:14 + --> $DIR/concat-usage-errors.rs:44:14 | LL | let ${concat("1", $ident)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +150,7 @@ LL | starting_number!(_abc); = note: this error originates in the macro `starting_number` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:55:14 + --> $DIR/concat-usage-errors.rs:57:14 | LL | let ${concat("\u{00BD}", $ident)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -101,7 +161,7 @@ LL | starting_invalid_unicode!(_abc); = note: this error originates in the macro `starting_invalid_unicode` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:74:14 + --> $DIR/concat-usage-errors.rs:76:14 | LL | let ${concat($ident, "\u{00BD}")}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +172,7 @@ LL | ending_invalid_unicode!(_abc); = note: this error originates in the macro `ending_invalid_unicode` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected pattern, found `$` - --> $DIR/syntax-errors.rs:88:13 + --> $DIR/concat-usage-errors.rs:90:13 | LL | let ${concat(_a, 'b')}: () = (); | ^ expected pattern @@ -123,7 +183,7 @@ LL | unsupported_literals!(_abc); = note: this error originates in the macro `unsupported_literals` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:81:14 + --> $DIR/concat-usage-errors.rs:83:14 | LL | let ${concat("", "")}: () = (); | ^^^^^^^^^^^^^^^^ @@ -134,7 +194,7 @@ LL | empty!(); = note: this error originates in the macro `empty` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:103:16 + --> $DIR/concat-usage-errors.rs:125:16 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +205,7 @@ LL | bad_literal_string!("\u{00BD}"); = note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:103:16 + --> $DIR/concat-usage-errors.rs:125:16 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +216,7 @@ LL | bad_literal_string!("\x41"); = note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:103:16 + --> $DIR/concat-usage-errors.rs:125:16 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -167,7 +227,7 @@ LL | bad_literal_string!("🤷"); = note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:103:16 + --> $DIR/concat-usage-errors.rs:125:16 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -178,7 +238,7 @@ LL | bad_literal_string!("d[-_-]b"); = note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:103:16 + --> $DIR/concat-usage-errors.rs:125:16 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +249,7 @@ LL | bad_literal_string!("-1"); = note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:103:16 + --> $DIR/concat-usage-errors.rs:125:16 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -200,7 +260,7 @@ LL | bad_literal_string!("1.0"); = note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info) error: `${concat(..)}` is not generating a valid identifier - --> $DIR/syntax-errors.rs:103:16 + --> $DIR/concat-usage-errors.rs:125:16 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -211,7 +271,7 @@ LL | bad_literal_string!("'1'"); = note: this error originates in the macro `bad_literal_string` (in Nightly builds, run with -Z macro-backtrace for more info) error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:116:31 + --> $DIR/concat-usage-errors.rs:138:31 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ @@ -219,7 +279,7 @@ LL | const ${concat(_foo, $literal)}: () = (); = note: currently only string literals are supported error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:116:31 + --> $DIR/concat-usage-errors.rs:138:31 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ @@ -228,7 +288,7 @@ LL | const ${concat(_foo, $literal)}: () = (); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:116:31 + --> $DIR/concat-usage-errors.rs:138:31 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ @@ -237,7 +297,7 @@ LL | const ${concat(_foo, $literal)}: () = (); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:116:31 + --> $DIR/concat-usage-errors.rs:138:31 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ @@ -246,7 +306,7 @@ LL | const ${concat(_foo, $literal)}: () = (); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:116:31 + --> $DIR/concat-usage-errors.rs:138:31 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ @@ -255,7 +315,7 @@ LL | const ${concat(_foo, $literal)}: () = (); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:127:31 + --> $DIR/concat-usage-errors.rs:149:31 | LL | const ${concat(_foo, $tt)}: () = (); | ^^ @@ -263,7 +323,7 @@ LL | const ${concat(_foo, $tt)}: () = (); = note: currently only string literals are supported error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:127:31 + --> $DIR/concat-usage-errors.rs:149:31 | LL | const ${concat(_foo, $tt)}: () = (); | ^^ @@ -272,7 +332,7 @@ LL | const ${concat(_foo, $tt)}: () = (); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/syntax-errors.rs:127:31 + --> $DIR/concat-usage-errors.rs:149:31 | LL | const ${concat(_foo, $tt)}: () = (); | ^^ @@ -280,5 +340,5 @@ LL | const ${concat(_foo, $tt)}: () = (); = note: currently only string literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 33 previous errors +error: aborting due to 43 previous errors diff --git a/tests/ui/macros/rfc-3086-metavar-expr/count-and-length-are-distinct.rs b/tests/ui/macros/metavar-expressions/count-and-length-are-distinct.rs index 8ca453273cd..8ca453273cd 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/count-and-length-are-distinct.rs +++ b/tests/ui/macros/metavar-expressions/count-and-length-are-distinct.rs diff --git a/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.rs b/tests/ui/macros/metavar-expressions/count-empty-index-arg.rs index 3000bfed6a8..69880ee7fa9 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.rs +++ b/tests/ui/macros/metavar-expressions/count-empty-index-arg.rs @@ -1,3 +1,6 @@ +// Issue: https://github.com/rust-lang/rust/issues/111904 +// Ensure that a trailing `,` is not interpreted as a `0`. + #![feature(macro_metavar_expr)] macro_rules! foo { @@ -10,5 +13,4 @@ fn test() { foo!(a, a; b, b); } -fn main() { -} +fn main() {} diff --git a/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.stderr b/tests/ui/macros/metavar-expressions/count-empty-index-arg.stderr index fd53c1686cf..e1f9d020b7f 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/issue-111904.stderr +++ b/tests/ui/macros/metavar-expressions/count-empty-index-arg.stderr @@ -1,11 +1,11 @@ error: `count` followed by a comma must have an associated index indicating its depth - --> $DIR/issue-111904.rs:4:37 + --> $DIR/count-empty-index-arg.rs:7:37 | LL | ( $( $($t:ident),* );* ) => { ${count($t,)} } | ^^^^^ error: expected expression, found `$` - --> $DIR/issue-111904.rs:4:35 + --> $DIR/count-empty-index-arg.rs:7:35 | LL | ( $( $($t:ident),* );* ) => { ${count($t,)} } | ^ expected expression diff --git a/tests/ui/macros/rfc-3086-metavar-expr/dollar-dollar-has-correct-behavior.rs b/tests/ui/macros/metavar-expressions/dollar-dollar-has-correct-behavior.rs index 9b8e3216a68..9b8e3216a68 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/dollar-dollar-has-correct-behavior.rs +++ b/tests/ui/macros/metavar-expressions/dollar-dollar-has-correct-behavior.rs diff --git a/tests/ui/macros/rfc-3086-metavar-expr/feature-gate-macro_metavar_expr.rs b/tests/ui/macros/metavar-expressions/feature-gate-macro_metavar_expr.rs index 51445221c57..51445221c57 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/feature-gate-macro_metavar_expr.rs +++ b/tests/ui/macros/metavar-expressions/feature-gate-macro_metavar_expr.rs diff --git a/tests/ui/macros/rfc-3086-metavar-expr/macro-expansion.rs b/tests/ui/macros/metavar-expressions/macro-expansion.rs index 1d34275874b..1d34275874b 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/macro-expansion.rs +++ b/tests/ui/macros/metavar-expressions/macro-expansion.rs diff --git a/tests/ui/macros/rfc-3086-metavar-expr/out-of-bounds-arguments.rs b/tests/ui/macros/metavar-expressions/out-of-bounds-arguments.rs index 0caa3ea89e4..0caa3ea89e4 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/out-of-bounds-arguments.rs +++ b/tests/ui/macros/metavar-expressions/out-of-bounds-arguments.rs diff --git a/tests/ui/macros/rfc-3086-metavar-expr/out-of-bounds-arguments.stderr b/tests/ui/macros/metavar-expressions/out-of-bounds-arguments.stderr index 0b441cad083..0b441cad083 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/out-of-bounds-arguments.stderr +++ b/tests/ui/macros/metavar-expressions/out-of-bounds-arguments.stderr diff --git a/tests/ui/macros/rfc-3086-metavar-expr/required-feature.rs b/tests/ui/macros/metavar-expressions/required-feature.rs index 77c165e3855..77c165e3855 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/required-feature.rs +++ b/tests/ui/macros/metavar-expressions/required-feature.rs diff --git a/tests/ui/macros/rfc-3086-metavar-expr/required-feature.stderr b/tests/ui/macros/metavar-expressions/required-feature.stderr index f28f822a058..f28f822a058 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/required-feature.stderr +++ b/tests/ui/macros/metavar-expressions/required-feature.stderr diff --git a/tests/ui/macros/metavar-expressions/syntax-errors.rs b/tests/ui/macros/metavar-expressions/syntax-errors.rs new file mode 100644 index 00000000000..8fc76a74baa --- /dev/null +++ b/tests/ui/macros/metavar-expressions/syntax-errors.rs @@ -0,0 +1,117 @@ +// General syntax errors that apply to all matavariable expressions +// +// We don't invoke the macros here to ensure code gets rejected at the definition rather than +// only when expanded. + +#![feature(macro_metavar_expr)] + +macro_rules! dollar_dollar_in_the_lhs { + ( $$ $a:ident ) => { + //~^ ERROR unexpected token: $ + }; +} + +macro_rules! metavar_in_the_lhs { + ( ${ len() } ) => { + //~^ ERROR unexpected token: { + //~| ERROR expected one of: `*`, `+`, or `?` + }; +} + +macro_rules! metavar_token_without_ident { + ( $( $i:ident ),* ) => { ${ ignore() } }; + //~^ ERROR meta-variable expressions must be referenced using a dollar sign +} + +macro_rules! metavar_with_literal_suffix { + ( $( $i:ident ),* ) => { ${ index(1u32) } }; + //~^ ERROR only unsuffixes integer literals are supported in meta-variable expressions +} + +macro_rules! mve_without_parens { + ( $( $i:ident ),* ) => { ${ count } }; + //~^ ERROR meta-variable expression parameter must be wrapped in parentheses +} + +#[rustfmt::skip] +macro_rules! empty_expression { + () => { ${} }; + //~^ ERROR expected identifier or string literal +} + +#[rustfmt::skip] +macro_rules! open_brackets_with_lit { + () => { ${ "hi" } }; + //~^ ERROR expected identifier + } + +macro_rules! mve_wrong_delim { + ( $( $i:ident ),* ) => { ${ count{i} } }; + //~^ ERROR meta-variable expression parameter must be wrapped in parentheses +} + +macro_rules! invalid_metavar { + () => { ${ignore($123)} } + //~^ ERROR expected identifier, found `123` +} + +#[rustfmt::skip] +macro_rules! open_brackets_with_group { + ( $( $i:ident ),* ) => { ${ {} } }; + //~^ ERROR expected identifier +} + +macro_rules! extra_garbage_after_metavar { + ( $( $i:ident ),* ) => { + ${count() a b c} + //~^ ERROR unexpected token: a + ${count($i a b c)} + //~^ ERROR unexpected token: a + ${count($i, 1 a b c)} + //~^ ERROR unexpected token: a + ${count($i) a b c} + //~^ ERROR unexpected token: a + + ${ignore($i) a b c} + //~^ ERROR unexpected token: a + ${ignore($i a b c)} + //~^ ERROR unexpected token: a + + ${index() a b c} + //~^ ERROR unexpected token: a + ${index(1 a b c)} + //~^ ERROR unexpected token: a + + ${index() a b c} + //~^ ERROR unexpected token: a + ${index(1 a b c)} + //~^ ERROR unexpected token: a + }; +} + +const IDX: usize = 1; +macro_rules! metavar_depth_is_not_literal { + ( $( $i:ident ),* ) => { ${ index(IDX) } }; + //~^ ERROR meta-variable expression depth must be a literal +} + +macro_rules! unknown_count_ident { + ( $( $i:ident )* ) => { + ${count(foo)} + //~^ ERROR meta-variable expressions must be referenced using a dollar sign + }; +} + +macro_rules! unknown_ignore_ident { + ( $( $i:ident )* ) => { + ${ignore(bar)} + //~^ ERROR meta-variable expressions must be referenced using a dollar sign + }; +} + +macro_rules! unknown_metavar { + ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; + //~^ ERROR unrecognized meta-variable expression +} + +fn main() {} diff --git a/tests/ui/macros/metavar-expressions/syntax-errors.stderr b/tests/ui/macros/metavar-expressions/syntax-errors.stderr new file mode 100644 index 00000000000..20d2358facc --- /dev/null +++ b/tests/ui/macros/metavar-expressions/syntax-errors.stderr @@ -0,0 +1,224 @@ +error: unexpected token: $ + --> $DIR/syntax-errors.rs:9:8 + | +LL | ( $$ $a:ident ) => { + | ^ + +note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions + --> $DIR/syntax-errors.rs:9:8 + | +LL | ( $$ $a:ident ) => { + | ^ + +error: unexpected token: { + --> $DIR/syntax-errors.rs:15:8 + | +LL | ( ${ len() } ) => { + | ^^^^^^^^^ + +note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions + --> $DIR/syntax-errors.rs:15:8 + | +LL | ( ${ len() } ) => { + | ^^^^^^^^^ + +error: expected one of: `*`, `+`, or `?` + --> $DIR/syntax-errors.rs:15:8 + | +LL | ( ${ len() } ) => { + | ^^^^^^^^^ + +error: meta-variables within meta-variable expressions must be referenced using a dollar sign + --> $DIR/syntax-errors.rs:22:33 + | +LL | ( $( $i:ident ),* ) => { ${ ignore() } }; + | ^^^^^^ + +error: only unsuffixes integer literals are supported in meta-variable expressions + --> $DIR/syntax-errors.rs:27:33 + | +LL | ( $( $i:ident ),* ) => { ${ index(1u32) } }; + | ^^^^^ + +error: meta-variable expression parameter must be wrapped in parentheses + --> $DIR/syntax-errors.rs:32:33 + | +LL | ( $( $i:ident ),* ) => { ${ count } }; + | ^^^^^ + +error: meta-variable expression parameter must be wrapped in parentheses + --> $DIR/syntax-errors.rs:49:33 + | +LL | ( $( $i:ident ),* ) => { ${ count{i} } }; + | ^^^^^ + +error: expected identifier, found `123` + --> $DIR/syntax-errors.rs:54:23 + | +LL | () => { ${ignore($123)} } + | ^^^ help: try removing `123` + +error: unexpected token: a + --> $DIR/syntax-errors.rs:66:19 + | +LL | ${count() a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:66:19 + | +LL | ${count() a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:68:20 + | +LL | ${count($i a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:68:20 + | +LL | ${count($i a b c)} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:70:23 + | +LL | ${count($i, 1 a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:70:23 + | +LL | ${count($i, 1 a b c)} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:72:21 + | +LL | ${count($i) a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:72:21 + | +LL | ${count($i) a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:75:22 + | +LL | ${ignore($i) a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:75:22 + | +LL | ${ignore($i) a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:77:21 + | +LL | ${ignore($i a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:77:21 + | +LL | ${ignore($i a b c)} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:80:19 + | +LL | ${index() a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:80:19 + | +LL | ${index() a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:82:19 + | +LL | ${index(1 a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:82:19 + | +LL | ${index(1 a b c)} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:85:19 + | +LL | ${index() a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:85:19 + | +LL | ${index() a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:87:19 + | +LL | ${index(1 a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:87:19 + | +LL | ${index(1 a b c)} + | ^ + +error: meta-variable expression depth must be a literal + --> $DIR/syntax-errors.rs:94:33 + | +LL | ( $( $i:ident ),* ) => { ${ index(IDX) } }; + | ^^^^^ + +error: meta-variables within meta-variable expressions must be referenced using a dollar sign + --> $DIR/syntax-errors.rs:100:11 + | +LL | ${count(foo)} + | ^^^^^ + +error: meta-variables within meta-variable expressions must be referenced using a dollar sign + --> $DIR/syntax-errors.rs:107:11 + | +LL | ${ignore(bar)} + | ^^^^^^ + +error: unrecognized meta-variable expression + --> $DIR/syntax-errors.rs:113:33 + | +LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; + | ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and len + +error: expected identifier or string literal + --> $DIR/syntax-errors.rs:38:14 + | +LL | () => { ${} }; + | ^^ + +error: expected identifier, found `"hi"` + --> $DIR/syntax-errors.rs:44:17 + | +LL | () => { ${ "hi" } }; + | ^^^^ help: try removing `"hi"` + +error: expected identifier or string literal + --> $DIR/syntax-errors.rs:60:33 + | +LL | ( $( $i:ident ),* ) => { ${ {} } }; + | ^^ + +error: aborting due to 25 previous errors + diff --git a/tests/ui/macros/metavar-expressions/usage-errors.rs b/tests/ui/macros/metavar-expressions/usage-errors.rs new file mode 100644 index 00000000000..feff02e2ce4 --- /dev/null +++ b/tests/ui/macros/metavar-expressions/usage-errors.rs @@ -0,0 +1,55 @@ +// Errors for the `count` and `length` metavariable expressions + +#![feature(macro_metavar_expr)] + +// `curly` = Right hand side curly brackets +// `no_rhs_dollar` = No dollar sign at the right hand side meta variable "function" +// `round` = Left hand side round brackets + +macro_rules! curly__no_rhs_dollar__round { + ( $( $i:ident ),* ) => { ${ count($i) } }; +} +const _: u32 = curly__no_rhs_dollar__round!(a, b, c); + +macro_rules! curly__no_rhs_dollar__no_round { + ( $i:ident ) => { ${ count($i) } }; + //~^ ERROR `count` can not be placed inside the innermost repetition +} +curly__no_rhs_dollar__no_round!(a); + +macro_rules! curly__rhs_dollar__no_round { + ( $i:ident ) => { ${ count($i) } }; + //~^ ERROR `count` can not be placed inside the innermost repetition +} +curly__rhs_dollar__no_round !(a); + +#[rustfmt::skip] // autoformatters can break a few of the error traces +macro_rules! no_curly__no_rhs_dollar__round { + ( $( $i:ident ),* ) => { count(i) }; + //~^ ERROR missing `fn` or `struct` for function or struct definition +} +no_curly__no_rhs_dollar__round !(a, b, c); + +#[rustfmt::skip] // autoformatters can break a few of the error traces +macro_rules! no_curly__no_rhs_dollar__no_round { + ( $i:ident ) => { count(i) }; + //~^ ERROR missing `fn` or `struct` for function or struct definition +} +no_curly__no_rhs_dollar__no_round !(a); + +#[rustfmt::skip] // autoformatters can break a few of the error traces +macro_rules! no_curly__rhs_dollar__round { + ( $( $i:ident ),* ) => { count($i) }; + //~^ ERROR variable `i` is still repeating at this depth +} +no_curly__rhs_dollar__round! (a); + +#[rustfmt::skip] // autoformatters can break a few of the error traces +macro_rules! no_curly__rhs_dollar__no_round { + ( $i:ident ) => { count($i) }; + //~^ ERROR cannot find function `count` in this scope +} +const _: u32 = no_curly__rhs_dollar__no_round! (a); +//~^ ERROR cannot find value `a` in this scope + +fn main() {} diff --git a/tests/ui/macros/metavar-expressions/usage-errors.stderr b/tests/ui/macros/metavar-expressions/usage-errors.stderr new file mode 100644 index 00000000000..f66f522e23b --- /dev/null +++ b/tests/ui/macros/metavar-expressions/usage-errors.stderr @@ -0,0 +1,71 @@ +error: `count` can not be placed inside the innermost repetition + --> $DIR/usage-errors.rs:15:24 + | +LL | ( $i:ident ) => { ${ count($i) } }; + | ^^^^^^^^^^^^^ + +error: `count` can not be placed inside the innermost repetition + --> $DIR/usage-errors.rs:21:24 + | +LL | ( $i:ident ) => { ${ count($i) } }; + | ^^^^^^^^^^^^^ + +error: missing `fn` or `struct` for function or struct definition + --> $DIR/usage-errors.rs:28:30 + | +LL | ( $( $i:ident ),* ) => { count(i) }; + | ^^^^^ +... +LL | no_curly__no_rhs_dollar__round !(a, b, c); + | ----------------------------------------- in this macro invocation + | + = note: this error originates in the macro `no_curly__no_rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info) +help: if you meant to call a macro, try + | +LL | ( $( $i:ident ),* ) => { count!(i) }; + | + + +error: missing `fn` or `struct` for function or struct definition + --> $DIR/usage-errors.rs:35:23 + | +LL | ( $i:ident ) => { count(i) }; + | ^^^^^ +... +LL | no_curly__no_rhs_dollar__no_round !(a); + | -------------------------------------- in this macro invocation + | + = note: this error originates in the macro `no_curly__no_rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) +help: if you meant to call a macro, try + | +LL | ( $i:ident ) => { count!(i) }; + | + + +error: variable `i` is still repeating at this depth + --> $DIR/usage-errors.rs:42:36 + | +LL | ( $( $i:ident ),* ) => { count($i) }; + | ^^ + +error[E0425]: cannot find value `a` in this scope + --> $DIR/usage-errors.rs:52:49 + | +LL | ( $i:ident ) => { count($i) }; + | -- due to this macro variable +... +LL | const _: u32 = no_curly__rhs_dollar__no_round! (a); + | ^ not found in this scope + +error[E0425]: cannot find function `count` in this scope + --> $DIR/usage-errors.rs:49:23 + | +LL | ( $i:ident ) => { count($i) }; + | ^^^^^ not found in this scope +... +LL | const _: u32 = no_curly__rhs_dollar__no_round! (a); + | ----------------------------------- in this macro invocation + | + = note: this error originates in the macro `no_curly__rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/macros/must-use-in-macro-55516.stderr b/tests/ui/macros/must-use-in-macro-55516.stderr index 7bf4aaab51c..b93d40d7e5a 100644 --- a/tests/ui/macros/must-use-in-macro-55516.stderr +++ b/tests/ui/macros/must-use-in-macro-55516.stderr @@ -7,7 +7,10 @@ LL | write!(&mut example, "{}", 42); = note: this `Result` may be an `Err` variant, which should be handled = note: `-W unused-must-use` implied by `-W unused` = help: to override `-W unused` add `#[allow(unused_must_use)]` - = note: this warning originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = write!(&mut example, "{}", 42); + | +++++++ warning: 1 warning emitted diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs deleted file mode 100644 index 78cede92526..00000000000 --- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs +++ /dev/null @@ -1,164 +0,0 @@ -#![feature(macro_metavar_expr)] - -// `curly` = Right hand side curly brackets -// `no_rhs_dollar` = No dollar sign at the right hand side meta variable "function" -// `round` = Left hand side round brackets - -macro_rules! curly__no_rhs_dollar__round { - ( $( $i:ident ),* ) => { ${ count($i) } }; -} - -macro_rules! curly__no_rhs_dollar__no_round { - ( $i:ident ) => { ${ count($i) } }; - //~^ ERROR `count` can not be placed inside the innermost repetition -} - -macro_rules! curly__rhs_dollar__no_round { - ( $i:ident ) => { ${ count($i) } }; - //~^ ERROR `count` can not be placed inside the innermost repetition -} - -#[rustfmt::skip] // autoformatters can break a few of the error traces -macro_rules! no_curly__no_rhs_dollar__round { - ( $( $i:ident ),* ) => { count(i) }; - //~^ ERROR cannot find function `count` in this scope - //~| ERROR cannot find value `i` in this scope -} - -#[rustfmt::skip] // autoformatters can break a few of the error traces -macro_rules! no_curly__no_rhs_dollar__no_round { - ( $i:ident ) => { count(i) }; - //~^ ERROR cannot find function `count` in this scope - //~| ERROR cannot find value `i` in this scope -} - -#[rustfmt::skip] // autoformatters can break a few of the error traces -macro_rules! no_curly__rhs_dollar__round { - ( $( $i:ident ),* ) => { count($i) }; - //~^ ERROR variable `i` is still repeating at this depth -} - -#[rustfmt::skip] // autoformatters can break a few of the error traces -macro_rules! no_curly__rhs_dollar__no_round { - ( $i:ident ) => { count($i) }; - //~^ ERROR cannot find function `count` in this scope -} - -// Other scenarios - -macro_rules! dollar_dollar_in_the_lhs { - ( $$ $a:ident ) => { - //~^ ERROR unexpected token: $ - }; -} - -macro_rules! extra_garbage_after_metavar { - ( $( $i:ident ),* ) => { - ${count() a b c} - //~^ ERROR unexpected token: a - //~| ERROR expected expression, found `$` - ${count($i a b c)} - //~^ ERROR unexpected token: a - ${count($i, 1 a b c)} - //~^ ERROR unexpected token: a - ${count($i) a b c} - //~^ ERROR unexpected token: a - - ${ignore($i) a b c} - //~^ ERROR unexpected token: a - ${ignore($i a b c)} - //~^ ERROR unexpected token: a - - ${index() a b c} - //~^ ERROR unexpected token: a - ${index(1 a b c)} - //~^ ERROR unexpected token: a - - ${index() a b c} - //~^ ERROR unexpected token: a - ${index(1 a b c)} - //~^ ERROR unexpected token: a - }; -} - -const IDX: usize = 1; -macro_rules! metavar_depth_is_not_literal { - ( $( $i:ident ),* ) => { ${ index(IDX) } }; - //~^ ERROR meta-variable expression depth must be a literal - //~| ERROR expected expression, found `$` -} - -macro_rules! metavar_in_the_lhs { - ( ${ len() } ) => { - //~^ ERROR unexpected token: { - //~| ERROR expected one of: `*`, `+`, or `?` - }; -} - -macro_rules! metavar_token_without_ident { - ( $( $i:ident ),* ) => { ${ ignore() } }; - //~^ ERROR meta-variable expressions must be referenced using a dollar sign - //~| ERROR expected expression -} - -macro_rules! metavar_with_literal_suffix { - ( $( $i:ident ),* ) => { ${ index(1u32) } }; - //~^ ERROR only unsuffixes integer literals are supported in meta-variable expressions - //~| ERROR expected expression, found `$` -} - -macro_rules! metavar_without_parens { - ( $( $i:ident ),* ) => { ${ count{i} } }; - //~^ ERROR meta-variable expression parameter must be wrapped in parentheses - //~| ERROR expected expression, found `$` -} - -#[rustfmt::skip] -macro_rules! open_brackets_without_tokens { - ( $( $i:ident ),* ) => { ${ {} } }; - //~^ ERROR expected expression, found `$` - //~| ERROR expected identifier -} - -macro_rules! unknown_count_ident { - ( $( $i:ident )* ) => { - ${count(foo)} - //~^ ERROR meta-variable expressions must be referenced using a dollar sign - //~| ERROR expected expression - }; -} - -macro_rules! unknown_ignore_ident { - ( $( $i:ident )* ) => { - ${ignore(bar)} - //~^ ERROR meta-variable expressions must be referenced using a dollar sign - //~| ERROR expected expression - }; -} - -macro_rules! unknown_metavar { - ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; - //~^ ERROR unrecognized meta-variable expression - //~| ERROR expected expression -} - -fn main() { - curly__no_rhs_dollar__round!(a, b, c); - curly__no_rhs_dollar__no_round!(a); - curly__rhs_dollar__no_round!(a); - no_curly__no_rhs_dollar__round!(a, b, c); - no_curly__no_rhs_dollar__no_round!(a); - no_curly__rhs_dollar__round!(a, b, c); - no_curly__rhs_dollar__no_round!(a); - //~^ ERROR cannot find value `a` in this scope - - extra_garbage_after_metavar!(a); - metavar_depth_is_not_literal!(a); - metavar_token_without_ident!(a); - metavar_with_literal_suffix!(a); - metavar_without_parens!(a); - open_brackets_without_tokens!(a); - unknown_count_ident!(a); - unknown_ignore_ident!(a); - unknown_metavar!(a); -} diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr deleted file mode 100644 index d9646760cea..00000000000 --- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr +++ /dev/null @@ -1,382 +0,0 @@ -error: unexpected token: $ - --> $DIR/syntax-errors.rs:50:8 - | -LL | ( $$ $a:ident ) => { - | ^ - -note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions - --> $DIR/syntax-errors.rs:50:8 - | -LL | ( $$ $a:ident ) => { - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:57:19 - | -LL | ${count() a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:57:19 - | -LL | ${count() a b c} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:60:20 - | -LL | ${count($i a b c)} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:60:20 - | -LL | ${count($i a b c)} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:62:23 - | -LL | ${count($i, 1 a b c)} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:62:23 - | -LL | ${count($i, 1 a b c)} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:64:21 - | -LL | ${count($i) a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:64:21 - | -LL | ${count($i) a b c} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:67:22 - | -LL | ${ignore($i) a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:67:22 - | -LL | ${ignore($i) a b c} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:69:21 - | -LL | ${ignore($i a b c)} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:69:21 - | -LL | ${ignore($i a b c)} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:72:19 - | -LL | ${index() a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:72:19 - | -LL | ${index() a b c} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:74:19 - | -LL | ${index(1 a b c)} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:74:19 - | -LL | ${index(1 a b c)} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:77:19 - | -LL | ${index() a b c} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:77:19 - | -LL | ${index() a b c} - | ^ - -error: unexpected token: a - --> $DIR/syntax-errors.rs:79:19 - | -LL | ${index(1 a b c)} - | ^ - | -note: meta-variable expression must not have trailing tokens - --> $DIR/syntax-errors.rs:79:19 - | -LL | ${index(1 a b c)} - | ^ - -error: meta-variable expression depth must be a literal - --> $DIR/syntax-errors.rs:86:33 - | -LL | ( $( $i:ident ),* ) => { ${ index(IDX) } }; - | ^^^^^ - -error: unexpected token: { - --> $DIR/syntax-errors.rs:92:8 - | -LL | ( ${ len() } ) => { - | ^^^^^^^^^ - -note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions - --> $DIR/syntax-errors.rs:92:8 - | -LL | ( ${ len() } ) => { - | ^^^^^^^^^ - -error: expected one of: `*`, `+`, or `?` - --> $DIR/syntax-errors.rs:92:8 - | -LL | ( ${ len() } ) => { - | ^^^^^^^^^ - -error: meta-variables within meta-variable expressions must be referenced using a dollar sign - --> $DIR/syntax-errors.rs:99:33 - | -LL | ( $( $i:ident ),* ) => { ${ ignore() } }; - | ^^^^^^ - -error: only unsuffixes integer literals are supported in meta-variable expressions - --> $DIR/syntax-errors.rs:105:33 - | -LL | ( $( $i:ident ),* ) => { ${ index(1u32) } }; - | ^^^^^ - -error: meta-variable expression parameter must be wrapped in parentheses - --> $DIR/syntax-errors.rs:111:33 - | -LL | ( $( $i:ident ),* ) => { ${ count{i} } }; - | ^^^^^ - -error: meta-variables within meta-variable expressions must be referenced using a dollar sign - --> $DIR/syntax-errors.rs:125:11 - | -LL | ${count(foo)} - | ^^^^^ - -error: meta-variables within meta-variable expressions must be referenced using a dollar sign - --> $DIR/syntax-errors.rs:133:11 - | -LL | ${ignore(bar)} - | ^^^^^^ - -error: unrecognized meta-variable expression - --> $DIR/syntax-errors.rs:140:33 - | -LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; - | ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and len - -error: expected identifier or string literal - --> $DIR/syntax-errors.rs:118:33 - | -LL | ( $( $i:ident ),* ) => { ${ {} } }; - | ^^ - -error: `count` can not be placed inside the innermost repetition - --> $DIR/syntax-errors.rs:12:24 - | -LL | ( $i:ident ) => { ${ count($i) } }; - | ^^^^^^^^^^^^^ - -error: `count` can not be placed inside the innermost repetition - --> $DIR/syntax-errors.rs:17:24 - | -LL | ( $i:ident ) => { ${ count($i) } }; - | ^^^^^^^^^^^^^ - -error: variable `i` is still repeating at this depth - --> $DIR/syntax-errors.rs:37:36 - | -LL | ( $( $i:ident ),* ) => { count($i) }; - | ^^ - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:57:9 - | -LL | ${count() a b c} - | ^ expected expression -... -LL | extra_garbage_after_metavar!(a); - | ------------------------------- in this macro invocation - | - = note: this error originates in the macro `extra_garbage_after_metavar` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:86:30 - | -LL | ( $( $i:ident ),* ) => { ${ index(IDX) } }; - | ^ expected expression -... -LL | metavar_depth_is_not_literal!(a); - | -------------------------------- in this macro invocation - | - = note: this error originates in the macro `metavar_depth_is_not_literal` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:99:30 - | -LL | ( $( $i:ident ),* ) => { ${ ignore() } }; - | ^ expected expression -... -LL | metavar_token_without_ident!(a); - | ------------------------------- in this macro invocation - | - = note: this error originates in the macro `metavar_token_without_ident` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:105:30 - | -LL | ( $( $i:ident ),* ) => { ${ index(1u32) } }; - | ^ expected expression -... -LL | metavar_with_literal_suffix!(a); - | ------------------------------- in this macro invocation - | - = note: this error originates in the macro `metavar_with_literal_suffix` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:111:30 - | -LL | ( $( $i:ident ),* ) => { ${ count{i} } }; - | ^ expected expression -... -LL | metavar_without_parens!(a); - | -------------------------- in this macro invocation - | - = note: this error originates in the macro `metavar_without_parens` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:118:30 - | -LL | ( $( $i:ident ),* ) => { ${ {} } }; - | ^ expected expression -... -LL | open_brackets_without_tokens!(a); - | -------------------------------- in this macro invocation - | - = note: this error originates in the macro `open_brackets_without_tokens` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:125:9 - | -LL | ${count(foo)} - | ^ expected expression -... -LL | unknown_count_ident!(a); - | ----------------------- in this macro invocation - | - = note: this error originates in the macro `unknown_count_ident` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:133:9 - | -LL | ${ignore(bar)} - | ^ expected expression -... -LL | unknown_ignore_ident!(a); - | ------------------------ in this macro invocation - | - = note: this error originates in the macro `unknown_ignore_ident` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected expression, found `$` - --> $DIR/syntax-errors.rs:140:30 - | -LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; - | ^ expected expression -... -LL | unknown_metavar!(a); - | ------------------- in this macro invocation - | - = note: this error originates in the macro `unknown_metavar` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0425]: cannot find value `i` in this scope - --> $DIR/syntax-errors.rs:23:36 - | -LL | ( $( $i:ident ),* ) => { count(i) }; - | ^ not found in this scope -... -LL | no_curly__no_rhs_dollar__round!(a, b, c); - | ---------------------------------------- in this macro invocation - | - = note: this error originates in the macro `no_curly__no_rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0425]: cannot find value `i` in this scope - --> $DIR/syntax-errors.rs:30:29 - | -LL | ( $i:ident ) => { count(i) }; - | ^ not found in this scope -... -LL | no_curly__no_rhs_dollar__no_round!(a); - | ------------------------------------- in this macro invocation - | - = note: this error originates in the macro `no_curly__no_rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0425]: cannot find value `a` in this scope - --> $DIR/syntax-errors.rs:152:37 - | -LL | ( $i:ident ) => { count($i) }; - | -- due to this macro variable -... -LL | no_curly__rhs_dollar__no_round!(a); - | ^ not found in this scope - -error[E0425]: cannot find function `count` in this scope - --> $DIR/syntax-errors.rs:23:30 - | -LL | ( $( $i:ident ),* ) => { count(i) }; - | ^^^^^ not found in this scope -... -LL | no_curly__no_rhs_dollar__round!(a, b, c); - | ---------------------------------------- in this macro invocation - | - = note: this error originates in the macro `no_curly__no_rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0425]: cannot find function `count` in this scope - --> $DIR/syntax-errors.rs:30:23 - | -LL | ( $i:ident ) => { count(i) }; - | ^^^^^ not found in this scope -... -LL | no_curly__no_rhs_dollar__no_round!(a); - | ------------------------------------- in this macro invocation - | - = note: this error originates in the macro `no_curly__no_rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0425]: cannot find function `count` in this scope - --> $DIR/syntax-errors.rs:43:23 - | -LL | ( $i:ident ) => { count($i) }; - | ^^^^^ not found in this scope -... -LL | no_curly__rhs_dollar__no_round!(a); - | ---------------------------------- in this macro invocation - | - = note: this error originates in the macro `no_curly__rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 39 previous errors - -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/maximal_mir_to_hir_coverage.rs b/tests/ui/maximal_mir_to_hir_coverage.rs deleted file mode 100644 index e57c83d007e..00000000000 --- a/tests/ui/maximal_mir_to_hir_coverage.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ compile-flags: -Zmaximal-hir-to-mir-coverage -//@ run-pass - -// Just making sure this flag is accepted and doesn't crash the compiler - -fn main() { - let x = 1; - let y = x + 1; - println!("{y}"); -} diff --git a/tests/ui/maybe-bounds.rs b/tests/ui/maybe-bounds.rs deleted file mode 100644 index 02ed45c656f..00000000000 --- a/tests/ui/maybe-bounds.rs +++ /dev/null @@ -1,9 +0,0 @@ -trait Tr: ?Sized {} -//~^ ERROR `?Trait` is not permitted in supertraits - -type A1 = dyn Tr + (?Sized); -//~^ ERROR `?Trait` is not permitted in trait object types -type A2 = dyn for<'a> Tr + (?Sized); -//~^ ERROR `?Trait` is not permitted in trait object types - -fn main() {} diff --git a/tests/ui/method-output-diff-issue-127263.rs b/tests/ui/method-output-diff-issue-127263.rs deleted file mode 100644 index 85a903e2453..00000000000 --- a/tests/ui/method-output-diff-issue-127263.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn bar() {} -fn foo(x: i32) -> u32 { - 0 -} -fn main() { - let b: fn() -> u32 = bar; //~ ERROR mismatched types [E0308] - let f: fn(i32) = foo; //~ ERROR mismatched types [E0308] -} diff --git a/tests/ui/methods/method-missing-call.rs b/tests/ui/methods/method-missing-call.rs deleted file mode 100644 index 7ce1e9a4f1b..00000000000 --- a/tests/ui/methods/method-missing-call.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Tests to make sure that parens are needed for method calls without arguments. -// outputs text to make sure either an anonymous function is provided or -// open-close '()' parens are given - - -struct Point { - x: isize, - y: isize -} -impl Point { - fn new() -> Point { - Point{x:0, y:0} - } - fn get_x(&self) -> isize { - self.x - } -} - -fn main() { - let point: Point = Point::new(); - let px: isize = point - .get_x;//~ ERROR attempted to take value of method `get_x` on type `Point` - - // Ensure the span is useful - let ys = &[1,2,3,4,5,6,7]; - let a = ys.iter() - .map(|x| x) - .filter(|&&x| x == 1) - .filter_map; //~ ERROR attempted to take value of method `filter_map` on type -} diff --git a/tests/ui/methods/method-missing-call.stderr b/tests/ui/methods/method-missing-call.stderr deleted file mode 100644 index bc508461b69..00000000000 --- a/tests/ui/methods/method-missing-call.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0615]: attempted to take value of method `get_x` on type `Point` - --> $DIR/method-missing-call.rs:22:26 - | -LL | .get_x; - | ^^^^^ method, not a field - | -help: use parentheses to call the method - | -LL | .get_x(); - | ++ - -error[E0615]: attempted to take value of method `filter_map` on type `Filter<Map<std::slice::Iter<'_, {integer}>, {closure@$DIR/method-missing-call.rs:27:20: 27:23}>, {closure@$DIR/method-missing-call.rs:28:23: 28:28}>` - --> $DIR/method-missing-call.rs:29:16 - | -LL | .filter_map; - | ^^^^^^^^^^ method, not a field - | -help: use parentheses to call the method - | -LL | .filter_map(_); - | +++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0615`. diff --git a/tests/ui/methods/method-value-without-call.rs b/tests/ui/methods/method-value-without-call.rs new file mode 100644 index 00000000000..43bee4864b4 --- /dev/null +++ b/tests/ui/methods/method-value-without-call.rs @@ -0,0 +1,33 @@ +//! Test taking a method value without parentheses + +struct Point { + x: isize, + y: isize, +} + +impl Point { + fn new() -> Point { + Point { x: 0, y: 0 } + } + + fn get_x(&self) -> isize { + self.x + } +} + +fn main() { + // Test with primitive type method + let _f = 10i32.abs; //~ ERROR attempted to take value of method + + // Test with custom type method + let point: Point = Point::new(); + let px: isize = point.get_x; //~ ERROR attempted to take value of method `get_x` on type `Point` + + // Test with method chains - ensure the span is useful + let ys = &[1, 2, 3, 4, 5, 6, 7]; + let a = ys + .iter() + .map(|x| x) + .filter(|&&x| x == 1) + .filter_map; //~ ERROR attempted to take value of method `filter_map` on type +} diff --git a/tests/ui/methods/method-value-without-call.stderr b/tests/ui/methods/method-value-without-call.stderr new file mode 100644 index 00000000000..0c3870e2868 --- /dev/null +++ b/tests/ui/methods/method-value-without-call.stderr @@ -0,0 +1,36 @@ +error[E0615]: attempted to take value of method `abs` on type `i32` + --> $DIR/method-value-without-call.rs:20:20 + | +LL | let _f = 10i32.abs; + | ^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let _f = 10i32.abs(); + | ++ + +error[E0615]: attempted to take value of method `get_x` on type `Point` + --> $DIR/method-value-without-call.rs:24:27 + | +LL | let px: isize = point.get_x; + | ^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let px: isize = point.get_x(); + | ++ + +error[E0615]: attempted to take value of method `filter_map` on type `Filter<Map<std::slice::Iter<'_, {integer}>, {closure@$DIR/method-value-without-call.rs:30:14: 30:17}>, {closure@$DIR/method-value-without-call.rs:31:17: 31:22}>` + --> $DIR/method-value-without-call.rs:32:10 + | +LL | .filter_map; + | ^^^^^^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | .filter_map(_); + | +++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0615`. diff --git a/tests/ui/mismatched_types/elide-on-tuple-mismatch.rs b/tests/ui/mismatched_types/elide-on-tuple-mismatch.rs new file mode 100644 index 00000000000..c36d041d296 --- /dev/null +++ b/tests/ui/mismatched_types/elide-on-tuple-mismatch.rs @@ -0,0 +1,25 @@ +//! Regression test for issue #50333: elide irrelevant E0277 errors on tuple mismatch + +// Hide irrelevant E0277 errors (#50333) + +trait T {} + +struct A; + +impl T for A {} + +impl A { + fn new() -> Self { + Self {} + } +} + +fn main() { + // This creates a tuple type mismatch: 2-element tuple destructured into 3 variables + let (a, b, c) = (A::new(), A::new()); + //~^ ERROR mismatched types + + // This line should NOT produce an E0277 error about `Sized` trait bounds, + // because `a`, `b`, and `c` are `TyErr` due to the mismatch above + let _ts: Vec<&dyn T> = vec![&a, &b, &c]; +} diff --git a/tests/ui/elide-errors-on-mismatched-tuple.stderr b/tests/ui/mismatched_types/elide-on-tuple-mismatch.stderr index f852a223b42..7de45eb40ca 100644 --- a/tests/ui/elide-errors-on-mismatched-tuple.stderr +++ b/tests/ui/mismatched_types/elide-on-tuple-mismatch.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types - --> $DIR/elide-errors-on-mismatched-tuple.rs:14:9 + --> $DIR/elide-on-tuple-mismatch.rs:19:9 | -LL | let (a, b, c) = (A::new(), A::new()); // This tuple is 2 elements, should be three +LL | let (a, b, c) = (A::new(), A::new()); | ^^^^^^^^^ -------------------- this expression has type `(A, A)` | | | expected a tuple with 2 elements, found one with 3 elements diff --git a/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.rs b/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.rs new file mode 100644 index 00000000000..e28ca3e55b5 --- /dev/null +++ b/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.rs @@ -0,0 +1,16 @@ +//! This test checks that when there's a type mismatch between a function item and +//! a function pointer, the error message focuses on the actual type difference +//! (return types, argument types) rather than the confusing "pointer vs item" distinction. +//! +//! See https://github.com/rust-lang/rust/issues/127263 + +fn bar() {} + +fn foo(x: i32) -> u32 { + 0 +} + +fn main() { + let b: fn() -> u32 = bar; //~ ERROR mismatched types [E0308] + let f: fn(i32) = foo; //~ ERROR mismatched types [E0308] +} diff --git a/tests/ui/method-output-diff-issue-127263.stderr b/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.stderr index 35b86114f16..8d63f2ea2d3 100644 --- a/tests/ui/method-output-diff-issue-127263.stderr +++ b/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/method-output-diff-issue-127263.rs:6:26 + --> $DIR/fn-pointer-mismatch-diagnostics.rs:14:26 | LL | let b: fn() -> u32 = bar; | ----------- ^^^ expected fn pointer, found fn item @@ -10,7 +10,7 @@ LL | let b: fn() -> u32 = bar; found fn item `fn() -> () {bar}` error[E0308]: mismatched types - --> $DIR/method-output-diff-issue-127263.rs:7:22 + --> $DIR/fn-pointer-mismatch-diagnostics.rs:15:22 | LL | let f: fn(i32) = foo; | ------- ^^^ expected fn pointer, found fn item diff --git a/tests/ui/integral-variable-unification-error.rs b/tests/ui/mismatched_types/int-float-type-mismatch.rs index 8d1621321e8..b45d02730d9 100644 --- a/tests/ui/integral-variable-unification-error.rs +++ b/tests/ui/mismatched_types/int-float-type-mismatch.rs @@ -1,3 +1,6 @@ +//! Check that a type mismatch error is reported when trying +//! to unify a {float} value assignment to an {integer} variable. + fn main() { let mut x //~ NOTE expected due to the type of this binding = diff --git a/tests/ui/integral-variable-unification-error.stderr b/tests/ui/mismatched_types/int-float-type-mismatch.stderr index 1caa6042fd2..43b8609a49d 100644 --- a/tests/ui/integral-variable-unification-error.stderr +++ b/tests/ui/mismatched_types/int-float-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/integral-variable-unification-error.rs:5:9 + --> $DIR/int-float-type-mismatch.rs:8:9 | LL | let mut x | ----- expected due to the type of this binding diff --git a/tests/ui/mod-subitem-as-enum-variant.rs b/tests/ui/mod-subitem-as-enum-variant.rs deleted file mode 100644 index 959024c46f4..00000000000 --- a/tests/ui/mod-subitem-as-enum-variant.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod Mod { - pub struct FakeVariant<T>(pub T); -} - -fn main() { - Mod::FakeVariant::<i32>(0); - Mod::<i32>::FakeVariant(0); - //~^ ERROR type arguments are not allowed on module `Mod` [E0109] -} diff --git a/tests/ui/modules/nested-modules-basic.rs b/tests/ui/modules/nested-modules-basic.rs new file mode 100644 index 00000000000..12eccec2808 --- /dev/null +++ b/tests/ui/modules/nested-modules-basic.rs @@ -0,0 +1,19 @@ +//! Basic test for nested module functionality and path resolution + +//@ run-pass + +mod inner { + pub mod inner2 { + pub fn hello() { + println!("hello, modular world"); + } + } + pub fn hello() { + inner2::hello(); + } +} + +pub fn main() { + inner::hello(); + inner::inner2::hello(); +} diff --git a/tests/ui/monomorphize-abi-alignment.rs b/tests/ui/monomorphize-abi-alignment.rs deleted file mode 100644 index 62df1aca357..00000000000 --- a/tests/ui/monomorphize-abi-alignment.rs +++ /dev/null @@ -1,35 +0,0 @@ -//@ run-pass - -#![allow(non_upper_case_globals)] -#![allow(dead_code)] -/*! - * On x86_64-linux-gnu and possibly other platforms, structs get 8-byte "preferred" alignment, - * but their "ABI" alignment (i.e., what actually matters for data layout) is the largest alignment - * of any field. (Also, `u64` has 8-byte ABI alignment; this is not always true). - * - * On such platforms, if monomorphize uses the "preferred" alignment, then it will unify - * `A` and `B`, even though `S<A>` and `S<B>` have the field `t` at different offsets, - * and apply the wrong instance of the method `unwrap`. - */ - -#[derive(Copy, Clone)] -struct S<T> { i:u8, t:T } - -impl<T> S<T> { - fn unwrap(self) -> T { - self.t - } -} - -#[derive(Copy, Clone, PartialEq, Debug)] -struct A((u32, u32)); - -#[derive(Copy, Clone, PartialEq, Debug)] -struct B(u64); - -pub fn main() { - static Ca: S<A> = S { i: 0, t: A((13, 104)) }; - static Cb: S<B> = S { i: 0, t: B(31337) }; - assert_eq!(Ca.unwrap(), A((13, 104))); - assert_eq!(Cb.unwrap(), B(31337)); -} diff --git a/tests/ui/msvc-data-only.rs b/tests/ui/msvc-data-only.rs deleted file mode 100644 index 15d799085fe..00000000000 --- a/tests/ui/msvc-data-only.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ run-pass -//@ aux-build:msvc-data-only-lib.rs - -extern crate msvc_data_only_lib; - -fn main() { - println!("The answer is {} !", msvc_data_only_lib::FOO); -} diff --git a/tests/ui/msvc-opt-minsize.rs b/tests/ui/msvc-opt-minsize.rs deleted file mode 100644 index c1be168a05d..00000000000 --- a/tests/ui/msvc-opt-minsize.rs +++ /dev/null @@ -1,31 +0,0 @@ -// A previously outdated version of LLVM caused compilation failures on Windows -// specifically with optimization level `z`. After the update to a more recent LLVM -// version, this test checks that compilation and execution both succeed. -// See https://github.com/rust-lang/rust/issues/45034 - -//@ ignore-cross-compile -// Reason: the compiled binary is executed -//@ only-windows -// Reason: the observed bug only occurs on Windows -//@ run-pass -//@ compile-flags: -C opt-level=z - -#![feature(test)] -extern crate test; - -fn foo(x: i32, y: i32) -> i64 { - (x + y) as i64 -} - -#[inline(never)] -fn bar() { - let _f = Box::new(0); - // This call used to trigger an LLVM bug in opt-level z where the base - // pointer gets corrupted, see issue #45034 - let y: fn(i32, i32) -> i64 = test::black_box(foo); - test::black_box(y(1, 2)); -} - -fn main() { - bar(); -} diff --git a/tests/ui/multibyte.rs b/tests/ui/multibyte.rs deleted file mode 100644 index d585a791fb9..00000000000 --- a/tests/ui/multibyte.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ run-pass -// - -// Test that multibyte characters don't crash the compiler -pub fn main() { - println!("마이너스 사인이 없으면"); -} diff --git a/tests/ui/multiline-comment.rs b/tests/ui/multiline-comment.rs deleted file mode 100644 index 98174882032..00000000000 --- a/tests/ui/multiline-comment.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ run-pass - -/* - * This is a multi-line oldcomment. - */ -pub fn main() { } diff --git a/tests/ui/parser/bad-lit-suffixes.rs b/tests/ui/parser/bad-lit-suffixes.rs index 4e8edf4d46e..0a01bb84f01 100644 --- a/tests/ui/parser/bad-lit-suffixes.rs +++ b/tests/ui/parser/bad-lit-suffixes.rs @@ -33,6 +33,7 @@ fn f() {} #[must_use = "string"suffix] //~^ ERROR suffixes on string literals are invalid +//~| ERROR malformed `must_use` attribute input fn g() {} #[link(name = "string"suffix)] @@ -41,4 +42,5 @@ extern "C" {} #[rustc_layout_scalar_valid_range_start(0suffix)] //~^ ERROR invalid suffix `suffix` for number literal +//~| ERROR malformed `rustc_layout_scalar_valid_range_start` attribute input struct S; diff --git a/tests/ui/parser/bad-lit-suffixes.stderr b/tests/ui/parser/bad-lit-suffixes.stderr index 416143e496a..7876d75c5a4 100644 --- a/tests/ui/parser/bad-lit-suffixes.stderr +++ b/tests/ui/parser/bad-lit-suffixes.stderr @@ -23,13 +23,13 @@ LL | #[must_use = "string"suffix] | ^^^^^^^^^^^^^^ invalid suffix `suffix` error: suffixes on string literals are invalid - --> $DIR/bad-lit-suffixes.rs:38:15 + --> $DIR/bad-lit-suffixes.rs:39:15 | LL | #[link(name = "string"suffix)] | ^^^^^^^^^^^^^^ invalid suffix `suffix` error: invalid suffix `suffix` for number literal - --> $DIR/bad-lit-suffixes.rs:42:41 + --> $DIR/bad-lit-suffixes.rs:43:41 | LL | #[rustc_layout_scalar_valid_range_start(0suffix)] | ^^^^^^^ invalid suffix `suffix` @@ -150,5 +150,33 @@ LL | 1.0e10suffix; | = help: valid suffixes are `f32` and `f64` -error: aborting due to 20 previous errors; 2 warnings emitted +error[E0539]: malformed `must_use` attribute input + --> $DIR/bad-lit-suffixes.rs:34:1 + | +LL | #[must_use = "string"suffix] + | ^^^^^^^^^^^^^--------------^ + | | + | expected a string literal here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[must_use = "string"suffix] +LL + #[must_use = "reason"] + | +LL - #[must_use = "string"suffix] +LL + #[must_use] + | + +error[E0805]: malformed `rustc_layout_scalar_valid_range_start` attribute input + --> $DIR/bad-lit-suffixes.rs:43:1 + | +LL | #[rustc_layout_scalar_valid_range_start(0suffix)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------^ + | | | + | | expected a single argument here + | help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]` + +error: aborting due to 22 previous errors; 2 warnings emitted +Some errors have detailed explanations: E0539, E0805. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/parser/multiline-comments-basic.rs b/tests/ui/parser/multiline-comments-basic.rs new file mode 100644 index 00000000000..1aa2a531f5c --- /dev/null +++ b/tests/ui/parser/multiline-comments-basic.rs @@ -0,0 +1,10 @@ +//! Test that basic multiline comments are parsed correctly. +//! +//! Feature implementation test for <https://github.com/rust-lang/rust/issues/66>. + +//@ run-pass + +/* + * This is a multi-line comment. + */ +pub fn main() {} diff --git a/tests/ui/double-ref.rs b/tests/ui/parser/reference-whitespace-parsing.rs index eecf68ff209..7109c5911ae 100644 --- a/tests/ui/double-ref.rs +++ b/tests/ui/parser/reference-whitespace-parsing.rs @@ -1,3 +1,5 @@ +//! Test parsing of multiple references with various whitespace arrangements + //@ run-pass #![allow(dead_code)] diff --git a/tests/ui/parser/unicode-multibyte-chars-no-ice.rs b/tests/ui/parser/unicode-multibyte-chars-no-ice.rs new file mode 100644 index 00000000000..b1bb0c66ae2 --- /dev/null +++ b/tests/ui/parser/unicode-multibyte-chars-no-ice.rs @@ -0,0 +1,9 @@ +//! Test that multibyte Unicode characters don't crash the compiler. +//! +//! Regression test for <https://github.com/rust-lang/rust/issues/4780>. + +//@ run-pass + +pub fn main() { + println!("마이너스 사인이 없으면"); +} diff --git a/tests/ui/resolve/resolve-conflict-extern-crate-vs-extern-crate.stderr b/tests/ui/resolve/resolve-conflict-extern-crate-vs-extern-crate.stderr index a9b45a18af3..f53e9e3b478 100644 --- a/tests/ui/resolve/resolve-conflict-extern-crate-vs-extern-crate.stderr +++ b/tests/ui/resolve/resolve-conflict-extern-crate-vs-extern-crate.stderr @@ -2,6 +2,7 @@ error[E0259]: the name `std` is defined multiple times | = note: `std` 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 + --> $DIR/resolve-conflict-extern-crate-vs-extern-crate.rs:1:17 | LL | extern crate std as other_std; | ++++++++++++ diff --git a/tests/ui/max-min-classes.rs b/tests/ui/resolve/struct-function-same-name.rs index 338a3156a9a..bb2837d7ca6 100644 --- a/tests/ui/max-min-classes.rs +++ b/tests/ui/resolve/struct-function-same-name.rs @@ -1,3 +1,5 @@ +//! Test that a struct and function can have the same name +//! //@ run-pass #![allow(non_snake_case)] @@ -23,7 +25,7 @@ impl Product for Foo { } fn Foo(x: isize, y: isize) -> Foo { - Foo { x: x, y: y } + Foo { x, y } } pub fn main() { diff --git a/tests/ui/lexical-scoping.rs b/tests/ui/resolve/type-param-local-var-shadowing.rs index f858369f7ce..e08379e2acf 100644 --- a/tests/ui/lexical-scoping.rs +++ b/tests/ui/resolve/type-param-local-var-shadowing.rs @@ -1,8 +1,13 @@ +//! Test that items in subscopes correctly shadow type parameters and local variables +//! +//! Regression test for https://github.com/rust-lang/rust/issues/23880 + //@ run-pass -// Tests that items in subscopes can shadow type parameters and local variables (see issue #23880). #![allow(unused)] -struct Foo<X> { x: Box<X> } +struct Foo<X> { + x: Box<X>, +} impl<Bar> Foo<Bar> { fn foo(&self) { type Bar = i32; diff --git a/tests/ui/self/self-infer.rs b/tests/ui/self/self-infer.rs index 9839b8880e9..d6f6d8bfa06 100644 --- a/tests/ui/self/self-infer.rs +++ b/tests/ui/self/self-infer.rs @@ -1,8 +1,8 @@ struct S; impl S { - fn f(self: _) {} //~ERROR the placeholder `_` is not allowed within types on item signatures for functions - fn g(self: &_) {} //~ERROR the placeholder `_` is not allowed within types on item signatures for functions + fn f(self: _) {} //~ERROR the placeholder `_` is not allowed within types on item signatures for methods + fn g(self: &_) {} //~ERROR the placeholder `_` is not allowed within types on item signatures for methods } fn main() {} diff --git a/tests/ui/self/self-infer.stderr b/tests/ui/self/self-infer.stderr index f9db559390f..13d803d9559 100644 --- a/tests/ui/self/self-infer.stderr +++ b/tests/ui/self/self-infer.stderr @@ -1,10 +1,10 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/self-infer.rs:4:16 | LL | fn f(self: _) {} | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/self-infer.rs:5:17 | LL | fn g(self: &_) {} diff --git a/tests/ui/simd/intrinsic/float-math-pass.rs b/tests/ui/simd/intrinsic/float-math-pass.rs index 4c28568a739..01fed8537d0 100644 --- a/tests/ui/simd/intrinsic/float-math-pass.rs +++ b/tests/ui/simd/intrinsic/float-math-pass.rs @@ -85,6 +85,9 @@ fn main() { let r = simd_round(h); assert_eq!(x, r); + let r = simd_round_ties_even(h); + assert_eq!(z, r); + let r = simd_trunc(h); assert_eq!(z, r); } diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs index fdf06b7882e..caec607d6fe 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs @@ -43,6 +43,10 @@ fn main() { simd_shl(y, y); simd_shr(x, x); simd_shr(y, y); + simd_funnel_shl(x, x, x); + simd_funnel_shl(y, y, y); + simd_funnel_shr(x, x, x); + simd_funnel_shr(y, y, y); simd_and(x, x); simd_and(y, y); simd_or(x, x); @@ -73,6 +77,10 @@ fn main() { //~^ ERROR expected SIMD input type, found non-SIMD `i32` simd_shr(0, 0); //~^ ERROR expected SIMD input type, found non-SIMD `i32` + simd_funnel_shl(0, 0, 0); + //~^ ERROR expected SIMD input type, found non-SIMD `i32` + simd_funnel_shr(0, 0, 0); + //~^ ERROR expected SIMD input type, found non-SIMD `i32` simd_and(0, 0); //~^ ERROR expected SIMD input type, found non-SIMD `i32` simd_or(0, 0); @@ -95,6 +103,10 @@ fn main() { //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_shr(z, z); //~^ ERROR unsupported operation on `f32x4` with element `f32` + simd_funnel_shl(z, z, z); + //~^ ERROR unsupported operation on `f32x4` with element `f32` + simd_funnel_shr(z, z, z); + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_and(z, z); //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_or(z, z); diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr b/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr index 76db6d5328f..a27a8d721fb 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr @@ -1,147 +1,171 @@ error[E0511]: invalid monomorphization of `simd_add` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:64:9 + --> $DIR/generic-arithmetic-2.rs:68:9 | LL | simd_add(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_sub` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:66:9 + --> $DIR/generic-arithmetic-2.rs:70:9 | LL | simd_sub(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_mul` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:68:9 + --> $DIR/generic-arithmetic-2.rs:72:9 | LL | simd_mul(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_div` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:70:9 + --> $DIR/generic-arithmetic-2.rs:74:9 | LL | simd_div(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shl` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:72:9 + --> $DIR/generic-arithmetic-2.rs:76:9 | LL | simd_shl(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:74:9 + --> $DIR/generic-arithmetic-2.rs:78:9 | LL | simd_shr(0, 0); | ^^^^^^^^^^^^^^ +error[E0511]: invalid monomorphization of `simd_funnel_shl` intrinsic: expected SIMD input type, found non-SIMD `i32` + --> $DIR/generic-arithmetic-2.rs:80:9 + | +LL | simd_funnel_shl(0, 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0511]: invalid monomorphization of `simd_funnel_shr` intrinsic: expected SIMD input type, found non-SIMD `i32` + --> $DIR/generic-arithmetic-2.rs:82:9 + | +LL | simd_funnel_shr(0, 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0511]: invalid monomorphization of `simd_and` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:76:9 + --> $DIR/generic-arithmetic-2.rs:84:9 | LL | simd_and(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:78:9 + --> $DIR/generic-arithmetic-2.rs:86:9 | LL | simd_or(0, 0); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:80:9 + --> $DIR/generic-arithmetic-2.rs:88:9 | LL | simd_xor(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_neg` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:83:9 + --> $DIR/generic-arithmetic-2.rs:91:9 | LL | simd_neg(0); | ^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bswap` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:85:9 + --> $DIR/generic-arithmetic-2.rs:93:9 | LL | simd_bswap(0); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitreverse` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:87:9 + --> $DIR/generic-arithmetic-2.rs:95:9 | LL | simd_bitreverse(0); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctlz` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:89:9 + --> $DIR/generic-arithmetic-2.rs:97:9 | LL | simd_ctlz(0); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cttz` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:91:9 + --> $DIR/generic-arithmetic-2.rs:99:9 | LL | simd_cttz(0); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shl` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:94:9 + --> $DIR/generic-arithmetic-2.rs:102:9 | LL | simd_shl(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:96:9 + --> $DIR/generic-arithmetic-2.rs:104:9 | LL | simd_shr(z, z); | ^^^^^^^^^^^^^^ +error[E0511]: invalid monomorphization of `simd_funnel_shl` intrinsic: unsupported operation on `f32x4` with element `f32` + --> $DIR/generic-arithmetic-2.rs:106:9 + | +LL | simd_funnel_shl(z, z, z); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0511]: invalid monomorphization of `simd_funnel_shr` intrinsic: unsupported operation on `f32x4` with element `f32` + --> $DIR/generic-arithmetic-2.rs:108:9 + | +LL | simd_funnel_shr(z, z, z); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0511]: invalid monomorphization of `simd_and` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:98:9 + --> $DIR/generic-arithmetic-2.rs:110:9 | LL | simd_and(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:100:9 + --> $DIR/generic-arithmetic-2.rs:112:9 | LL | simd_or(z, z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:102:9 + --> $DIR/generic-arithmetic-2.rs:114:9 | LL | simd_xor(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bswap` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:104:9 + --> $DIR/generic-arithmetic-2.rs:116:9 | LL | simd_bswap(z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitreverse` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:106:9 + --> $DIR/generic-arithmetic-2.rs:118:9 | LL | simd_bitreverse(z); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctlz` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:108:9 + --> $DIR/generic-arithmetic-2.rs:120:9 | LL | simd_ctlz(z); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctpop` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:110:9 + --> $DIR/generic-arithmetic-2.rs:122:9 | LL | simd_ctpop(z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cttz` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:112:9 + --> $DIR/generic-arithmetic-2.rs:124:9 | LL | simd_cttz(z); | ^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: aborting due to 28 previous errors For more information about this error, try `rustc --explain E0511`. diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs b/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs index 3f0325d690b..4c97fb2141d 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs @@ -83,6 +83,80 @@ fn main() { all_eq!(simd_shr(simd_shl(y1, y2), y2), y1); all_eq!(simd_shr(simd_shl(y2, y1), y1), y2); + all_eq!( + simd_funnel_shl(x1, x2, x1), + i32x4([ + (1 << 1) | (2 >> 31), + (2 << 2) | (3 >> 30), + (3 << 3) | (4 >> 29), + (4 << 4) | (5 >> 28) + ]) + ); + all_eq!( + simd_funnel_shl(x2, x1, x1), + i32x4([ + (2 << 1) | (1 >> 31), + (3 << 2) | (2 >> 30), + (4 << 3) | (3 >> 29), + (5 << 4) | (4 >> 28) + ]) + ); + all_eq!( + simd_funnel_shl(y1, y2, y1), + U32::<4>([ + (1 << 1) | (2 >> 31), + (2 << 2) | (3 >> 30), + (3 << 3) | (4 >> 29), + (4 << 4) | (5 >> 28) + ]) + ); + all_eq!( + simd_funnel_shl(y2, y1, y1), + U32::<4>([ + (2 << 1) | (1 >> 31), + (3 << 2) | (2 >> 30), + (4 << 3) | (3 >> 29), + (5 << 4) | (4 >> 28) + ]) + ); + + all_eq!( + simd_funnel_shr(x1, x2, x1), + i32x4([ + (1 << 31) | (2 >> 1), + (2 << 30) | (3 >> 2), + (3 << 29) | (4 >> 3), + (4 << 28) | (5 >> 4) + ]) + ); + all_eq!( + simd_funnel_shr(x2, x1, x1), + i32x4([ + (2 << 31) | (1 >> 1), + (3 << 30) | (2 >> 2), + (4 << 29) | (3 >> 3), + (5 << 28) | (4 >> 4) + ]) + ); + all_eq!( + simd_funnel_shr(y1, y2, y1), + U32::<4>([ + (1 << 31) | (2 >> 1), + (2 << 30) | (3 >> 2), + (3 << 29) | (4 >> 3), + (4 << 28) | (5 >> 4) + ]) + ); + all_eq!( + simd_funnel_shr(y2, y1, y1), + U32::<4>([ + (2 << 31) | (1 >> 1), + (3 << 30) | (2 >> 2), + (4 << 29) | (3 >> 3), + (5 << 28) | (4 >> 4) + ]) + ); + // ensure we get logical vs. arithmetic shifts correct let (a, b, c, d) = (-12, -123, -1234, -12345); all_eq!(simd_shr(i32x4([a, b, c, d]), x1), i32x4([a >> 1, b >> 2, c >> 3, d >> 4])); diff --git a/tests/ui/statics/static-generic-param-soundness.rs b/tests/ui/statics/static-generic-param-soundness.rs new file mode 100644 index 00000000000..aabcca514d3 --- /dev/null +++ b/tests/ui/statics/static-generic-param-soundness.rs @@ -0,0 +1,20 @@ +//! Originally, inner statics in generic functions were generated only once, causing the same +//! static to be shared across all generic instantiations. This created a soundness hole where +//! different types could be coerced through thread-local storage in safe code. +//! +//! This test checks that generic parameters from outer scopes cannot be used in inner statics, +//! preventing this soundness issue. +//! +//! See https://github.com/rust-lang/rust/issues/9186 + +enum Bar<T> { + //~^ ERROR parameter `T` is never used + What, +} + +fn foo<T>() { + static a: Bar<T> = Bar::What; + //~^ ERROR can't use generic parameters from outer item +} + +fn main() {} diff --git a/tests/ui/inner-static-type-parameter.stderr b/tests/ui/statics/static-generic-param-soundness.stderr index 88d33b44c59..47554c7fcb0 100644 --- a/tests/ui/inner-static-type-parameter.stderr +++ b/tests/ui/statics/static-generic-param-soundness.stderr @@ -1,5 +1,5 @@ error[E0401]: can't use generic parameters from outer item - --> $DIR/inner-static-type-parameter.rs:6:19 + --> $DIR/static-generic-param-soundness.rs:16:19 | LL | fn foo<T>() { | - type parameter from outer item @@ -9,9 +9,9 @@ LL | static a: Bar<T> = Bar::What; = note: a `static` is a separate item from the item that contains it error[E0392]: type parameter `T` is never used - --> $DIR/inner-static-type-parameter.rs:3:10 + --> $DIR/static-generic-param-soundness.rs:10:10 | -LL | enum Bar<T> { What } +LL | enum Bar<T> { | ^ unused type parameter | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` diff --git a/tests/ui/stats/macro-stats.rs b/tests/ui/stats/macro-stats.rs index ee265d682fd..d986904ddd6 100644 --- a/tests/ui/stats/macro-stats.rs +++ b/tests/ui/stats/macro-stats.rs @@ -49,12 +49,17 @@ fn opt(x: Option<u32>) { } } -macro_rules! this_is_a_really_really_long_macro_name { +macro_rules! long_name_that_fits_on_a_single_line { + () => {} +} +long_name_that_fits_on_a_single_line!(); + +macro_rules! long_name_that_doesnt_fit_on_one_line { ($t:ty) => { fn f(_: $t) {} } } -this_is_a_really_really_long_macro_name!(u32!()); // AstFragmentKind::{Items,Ty} +long_name_that_doesnt_fit_on_one_line!(u32!()); // AstFragmentKind::{Items,Ty} macro_rules! trait_tys { () => { diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index 00c6b55c6a2..8d0fdb8958a 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -15,12 +15,13 @@ macro-stats #[derive(Copy)] 1 2 2.0 macro-stats p! 1 3 3.0 32 32.0 macro-stats trait_impl_tys! 1 2 2.0 28 28.0 macro-stats foreign_item! 1 1 1.0 21 21.0 -macro-stats this_is_a_really_really_long_macro_name! +macro-stats long_name_that_doesnt_fit_on_one_line! macro-stats 1 1 1.0 18 18.0 macro-stats impl_const! 1 1 1.0 17 17.0 macro-stats trait_tys! 1 2 2.0 15 15.0 macro-stats n99! 2 2 1.0 4 2.0 macro-stats none! 1 1 1.0 4 4.0 macro-stats u32! 1 1 1.0 3 3.0 +macro-stats long_name_that_fits_on_a_single_line! 1 1 1.0 0 0.0 macro-stats #[test] 1 1 1.0 0 0.0 macro-stats =================================================================================== diff --git a/tests/ui/suggestions/bad-infer-in-trait-impl.rs b/tests/ui/suggestions/bad-infer-in-trait-impl.rs index f38b168037b..db6fc9319e1 100644 --- a/tests/ui/suggestions/bad-infer-in-trait-impl.rs +++ b/tests/ui/suggestions/bad-infer-in-trait-impl.rs @@ -4,7 +4,7 @@ trait Foo { impl Foo for () { fn bar(s: _) {} - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated functions //~| ERROR has 1 parameter but the declaration in trait `Foo::bar` has 0 } diff --git a/tests/ui/suggestions/bad-infer-in-trait-impl.stderr b/tests/ui/suggestions/bad-infer-in-trait-impl.stderr index 8b7d67ac041..5aa46545943 100644 --- a/tests/ui/suggestions/bad-infer-in-trait-impl.stderr +++ b/tests/ui/suggestions/bad-infer-in-trait-impl.stderr @@ -1,4 +1,4 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/bad-infer-in-trait-impl.rs:6:15 | LL | fn bar(s: _) {} diff --git a/tests/ui/test-attrs/test-function-elided-no-main.rs b/tests/ui/test-attrs/test-function-elided-no-main.rs new file mode 100644 index 00000000000..97654581567 --- /dev/null +++ b/tests/ui/test-attrs/test-function-elided-no-main.rs @@ -0,0 +1,8 @@ +//! Test that #[test] functions are elided when not running tests, causing missing main error + +#[test] +fn main() { + // This function would normally serve as main, but since it's marked with #[test], + // it gets elided when not running tests +} +//~^ ERROR `main` function not found in crate `test_function_elided_no_main` diff --git a/tests/ui/test-attrs/test-function-elided-no-main.stderr b/tests/ui/test-attrs/test-function-elided-no-main.stderr new file mode 100644 index 00000000000..0bae690be2b --- /dev/null +++ b/tests/ui/test-attrs/test-function-elided-no-main.stderr @@ -0,0 +1,9 @@ +error[E0601]: `main` function not found in crate `test_function_elided_no_main` + --> $DIR/test-function-elided-no-main.rs:7:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/test-function-elided-no-main.rs` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0601`. diff --git a/tests/ui/traits/const-traits/ice-119717-constant-lifetime.stderr b/tests/ui/traits/const-traits/ice-119717-constant-lifetime.stderr index c6e0c699520..a165ef12060 100644 --- a/tests/ui/traits/const-traits/ice-119717-constant-lifetime.stderr +++ b/tests/ui/traits/const-traits/ice-119717-constant-lifetime.stderr @@ -16,7 +16,7 @@ LL | impl<T> const FromResidual for T { = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local = note: only traits defined in the current crate can be implemented for a type parameter -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/ice-119717-constant-lifetime.rs:9:31 | LL | fn from_residual(t: T) -> _ { diff --git a/tests/ui/impl-unused-tps.rs b/tests/ui/traits/constrained-type-params-trait-impl.rs index a5836db3c8e..301bbdb2ccb 100644 --- a/tests/ui/impl-unused-tps.rs +++ b/tests/ui/traits/constrained-type-params-trait-impl.rs @@ -1,3 +1,11 @@ +//! Comprehensive test for type parameter constraints in trait implementations +//! +//! This tests various scenarios of type parameter usage in trait implementations: +//! - Properly constrained parameters through trait bounds +//! - Unconstrained parameters that should cause compilation errors +//! - Complex constraint scenarios with `where` clauses and associated types +//! - Conflicting implementations detection + trait Foo<A> { fn get(&self, A: &A) {} } @@ -7,34 +15,34 @@ trait Bar { } impl<T> Foo<T> for [isize; 0] { - // OK, T is used in `Foo<T>`. + // OK: T is used in the trait bound `Foo<T>` } impl<T, U> Foo<T> for [isize; 1] { //~^ ERROR the type parameter `U` is not constrained + // T is constrained by `Foo<T>`, but U is completely unused } impl<T, U> Foo<T> for [isize; 2] where T: Bar<Out = U>, { - // OK, `U` is now constrained by the output type parameter. + // OK: T is constrained by `Foo<T>`, U is constrained by the where clause } impl<T: Bar<Out = U>, U> Foo<T> for [isize; 3] { - // OK, same as above but written differently. + // OK: Same as above but using bound syntax instead of where clause } impl<T, U> Foo<T> for U { //~^ ERROR conflicting implementations of trait `Foo<_>` for type `[isize; 0]` + // This conflicts with the first impl when U = [isize; 0] } impl<T, U> Bar for T { //~^ ERROR the type parameter `U` is not constrained - type Out = U; - - // Using `U` in an associated type within the impl is not good enough! + // Using U only in associated type definition is insufficient for constraint } impl<T, U> Bar for T @@ -43,7 +51,7 @@ where { //~^^^^ ERROR the type parameter `U` is not constrained by the impl trait, self type, or predicates //~| ERROR conflicting implementations of trait `Bar` - // This crafty self-referential attempt is still no good. + // Self-referential constraint doesn't properly constrain U } impl<T, U, V> Foo<T> for T @@ -53,9 +61,7 @@ where //~^^^^ ERROR the type parameter `U` is not constrained //~| ERROR the type parameter `V` is not constrained //~| ERROR conflicting implementations of trait `Foo<[isize; 0]>` for type `[isize; 0]` - - // Here, `V` is bound by an output type parameter, but the inputs - // are not themselves constrained. + // V is bound through output type, but U and V are not properly constrained as inputs } impl<T, U, V> Foo<(T, U)> for T @@ -63,7 +69,7 @@ where (T, U): Bar<Out = V>, { //~^^^^ ERROR conflicting implementations of trait `Foo<([isize; 0], _)>` for type `[isize; 0]` - // As above, but both T and U ARE constrained. + // Both T and U are constrained through `Foo<(T, U)>`, but creates conflicting impl } fn main() {} diff --git a/tests/ui/impl-unused-tps.stderr b/tests/ui/traits/constrained-type-params-trait-impl.stderr index eff5ffff9b6..2175129a8df 100644 --- a/tests/ui/impl-unused-tps.stderr +++ b/tests/ui/traits/constrained-type-params-trait-impl.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Foo<_>` for type `[isize; 0]` - --> $DIR/impl-unused-tps.rs:28:1 + --> $DIR/constrained-type-params-trait-impl.rs:37:1 | LL | impl<T> Foo<T> for [isize; 0] { | ----------------------------- first implementation here @@ -8,7 +8,7 @@ LL | impl<T, U> Foo<T> for U { | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[isize; 0]` error[E0119]: conflicting implementations of trait `Foo<[isize; 0]>` for type `[isize; 0]` - --> $DIR/impl-unused-tps.rs:49:1 + --> $DIR/constrained-type-params-trait-impl.rs:57:1 | LL | impl<T> Foo<T> for [isize; 0] { | ----------------------------- first implementation here @@ -19,7 +19,7 @@ LL | | (T, U): Bar<Out = V>, | |_________________________^ conflicting implementation for `[isize; 0]` error[E0119]: conflicting implementations of trait `Foo<([isize; 0], _)>` for type `[isize; 0]` - --> $DIR/impl-unused-tps.rs:61:1 + --> $DIR/constrained-type-params-trait-impl.rs:67:1 | LL | impl<T> Foo<T> for [isize; 0] { | ----------------------------- first implementation here @@ -30,37 +30,37 @@ LL | | (T, U): Bar<Out = V>, | |_________________________^ conflicting implementation for `[isize; 0]` error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps.rs:13:9 + --> $DIR/constrained-type-params-trait-impl.rs:21:9 | LL | impl<T, U> Foo<T> for [isize; 1] { | ^ unconstrained type parameter error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps.rs:32:9 + --> $DIR/constrained-type-params-trait-impl.rs:42:9 | LL | impl<T, U> Bar for T { | ^ unconstrained type parameter error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps.rs:40:9 + --> $DIR/constrained-type-params-trait-impl.rs:48:9 | LL | impl<T, U> Bar for T | ^ unconstrained type parameter error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps.rs:49:9 + --> $DIR/constrained-type-params-trait-impl.rs:57:9 | LL | impl<T, U, V> Foo<T> for T | ^ unconstrained type parameter error[E0207]: the type parameter `V` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps.rs:49:12 + --> $DIR/constrained-type-params-trait-impl.rs:57:12 | LL | impl<T, U, V> Foo<T> for T | ^ unconstrained type parameter error[E0119]: conflicting implementations of trait `Bar` - --> $DIR/impl-unused-tps.rs:40:1 + --> $DIR/constrained-type-params-trait-impl.rs:48:1 | LL | impl<T, U> Bar for T { | -------------------- first implementation here diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.rs b/tests/ui/traits/dispatch-from-dyn-invalid-impls.rs index b1d4b261bab..f5f66ca69cf 100644 --- a/tests/ui/invalid_dispatch_from_dyn_impls.rs +++ b/tests/ui/traits/dispatch-from-dyn-invalid-impls.rs @@ -1,20 +1,30 @@ +//! Test various invalid implementations of DispatchFromDyn trait. +//! +//! DispatchFromDyn is a special trait used by the compiler for dyn-compatible dynamic dispatch. +//! This checks that the compiler correctly rejects invalid implementations: +//! - Structs with extra non-coercible fields +//! - Structs with multiple pointer fields +//! - Structs with no coercible fields +//! - Structs with repr(C) or other incompatible representations +//! - Structs with over-aligned fields + #![feature(unsize, dispatch_from_dyn)] -use std::{ - ops::DispatchFromDyn, - marker::{Unsize, PhantomData}, -}; +use std::marker::{PhantomData, Unsize}; +use std::ops::DispatchFromDyn; +// Extra field prevents DispatchFromDyn struct WrapperWithExtraField<T>(T, i32); impl<T, U> DispatchFromDyn<WrapperWithExtraField<U>> for WrapperWithExtraField<T> //~^ ERROR [E0378] where - T: DispatchFromDyn<U>, -{} - + T: DispatchFromDyn<U> +{ +} -struct MultiplePointers<T: ?Sized>{ +// Multiple pointer fields create ambiguous coercion +struct MultiplePointers<T: ?Sized> { ptr1: *const T, ptr2: *const T, } @@ -22,10 +32,11 @@ struct MultiplePointers<T: ?Sized>{ impl<T: ?Sized, U: ?Sized> DispatchFromDyn<MultiplePointers<U>> for MultiplePointers<T> //~^ ERROR implementing `DispatchFromDyn` does not allow multiple fields to be coerced where - T: Unsize<U>, -{} - + T: Unsize<U> +{ +} +// No coercible fields (only PhantomData) struct NothingToCoerce<T: ?Sized> { data: PhantomData<T>, } @@ -33,23 +44,28 @@ struct NothingToCoerce<T: ?Sized> { impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NothingToCoerce<T>> for NothingToCoerce<U> {} //~^ ERROR implementing `DispatchFromDyn` requires a field to be coerced +// repr(C) is incompatible with DispatchFromDyn #[repr(C)] struct HasReprC<T: ?Sized>(Box<T>); impl<T: ?Sized, U: ?Sized> DispatchFromDyn<HasReprC<U>> for HasReprC<T> //~^ ERROR [E0378] where - T: Unsize<U>, -{} + T: Unsize<U> +{ +} +// Over-aligned fields are incompatible #[repr(align(64))] struct OverAlignedZst; + struct OverAligned<T: ?Sized>(Box<T>, OverAlignedZst); impl<T: ?Sized, U: ?Sized> DispatchFromDyn<OverAligned<U>> for OverAligned<T> //~^ ERROR [E0378] - where - T: Unsize<U>, -{} +where + T: Unsize<U> +{ +} fn main() {} diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.stderr b/tests/ui/traits/dispatch-from-dyn-invalid-impls.stderr index 93ec6bbe089..676da0ce81f 100644 --- a/tests/ui/invalid_dispatch_from_dyn_impls.stderr +++ b/tests/ui/traits/dispatch-from-dyn-invalid-impls.stderr @@ -1,25 +1,25 @@ error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else - --> $DIR/invalid_dispatch_from_dyn_impls.rs:10:1 + --> $DIR/dispatch-from-dyn-invalid-impls.rs:19:1 | LL | / impl<T, U> DispatchFromDyn<WrapperWithExtraField<U>> for WrapperWithExtraField<T> LL | | LL | | where -LL | | T: DispatchFromDyn<U>, - | |__________________________^ +LL | | T: DispatchFromDyn<U> + | |_________________________^ | = note: extra field `1` of type `i32` is not allowed error[E0375]: implementing `DispatchFromDyn` does not allow multiple fields to be coerced - --> $DIR/invalid_dispatch_from_dyn_impls.rs:22:1 + --> $DIR/dispatch-from-dyn-invalid-impls.rs:32:1 | LL | / impl<T: ?Sized, U: ?Sized> DispatchFromDyn<MultiplePointers<U>> for MultiplePointers<T> LL | | LL | | where -LL | | T: Unsize<U>, - | |_________________^ +LL | | T: Unsize<U> + | |________________^ | note: the trait `DispatchFromDyn` may only be implemented when a single field is being coerced - --> $DIR/invalid_dispatch_from_dyn_impls.rs:18:5 + --> $DIR/dispatch-from-dyn-invalid-impls.rs:28:5 | LL | ptr1: *const T, | ^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | ptr2: *const T, | ^^^^^^^^^^^^^^ error[E0374]: implementing `DispatchFromDyn` requires a field to be coerced - --> $DIR/invalid_dispatch_from_dyn_impls.rs:33:1 + --> $DIR/dispatch-from-dyn-invalid-impls.rs:44:1 | LL | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NothingToCoerce<T>> for NothingToCoerce<U> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,22 +35,22 @@ LL | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NothingToCoerce<T>> for NothingT = note: expected a single field to be coerced, none found error[E0378]: structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]` - --> $DIR/invalid_dispatch_from_dyn_impls.rs:39:1 + --> $DIR/dispatch-from-dyn-invalid-impls.rs:51:1 | LL | / impl<T: ?Sized, U: ?Sized> DispatchFromDyn<HasReprC<U>> for HasReprC<T> LL | | LL | | where -LL | | T: Unsize<U>, - | |_________________^ +LL | | T: Unsize<U> + | |________________^ error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else - --> $DIR/invalid_dispatch_from_dyn_impls.rs:49:1 + --> $DIR/dispatch-from-dyn-invalid-impls.rs:64:1 | LL | / impl<T: ?Sized, U: ?Sized> DispatchFromDyn<OverAligned<U>> for OverAligned<T> LL | | -LL | | where -LL | | T: Unsize<U>, - | |_____________________^ +LL | | where +LL | | T: Unsize<U> + | |________________^ | = note: extra field `1` of type `OverAlignedZst` is not allowed diff --git a/tests/ui/issue-11881.rs b/tests/ui/traits/encoder-trait-bounds-regression.rs index 1abe0797203..292b921cdf7 100644 --- a/tests/ui/issue-11881.rs +++ b/tests/ui/traits/encoder-trait-bounds-regression.rs @@ -1,14 +1,23 @@ +//! Regression test for issue #11881 +//! +//! Originally, the compiler would ICE when trying to parameterize on certain encoder types +//! due to issues with higher-ranked trait bounds and lifetime inference. This test checks +//! that various encoder patterns work correctly: +//! - Generic encoders with associated error types +//! - Higher-ranked trait bounds (for<'r> Encodable<JsonEncoder<'r>>) +//! - Multiple encoder implementations for the same type +//! - Polymorphic encoding functions + //@ run-pass #![allow(unused_must_use)] #![allow(dead_code)] #![allow(unused_imports)] -use std::fmt; -use std::io::prelude::*; use std::io::Cursor; -use std::slice; +use std::io::prelude::*; use std::marker::PhantomData; +use std::{fmt, slice}; trait Encoder { type Error; @@ -45,7 +54,6 @@ impl Encoder for OpaqueEncoder { type Error = (); } - struct Foo { baz: bool, } @@ -69,7 +77,6 @@ impl<S: Encoder> Encodable<S> for Bar { enum WireProtocol { JSON, Opaque, - // ... } fn encode_json<T: for<'a> Encodable<JsonEncoder<'a>>>(val: &T, wr: &mut Cursor<Vec<u8>>) { diff --git a/tests/ui/traits/eval-caching-error-region.rs b/tests/ui/traits/eval-caching-error-region.rs new file mode 100644 index 00000000000..831b5ab80c1 --- /dev/null +++ b/tests/ui/traits/eval-caching-error-region.rs @@ -0,0 +1,23 @@ +// Regression test for #132882. + +use std::ops::Add; + +pub trait Numoid: Sized +where + &'missing Self: Add<Self>, + //~^ ERROR use of undeclared lifetime name `'missing` +{ +} + +// Proving `N: Numoid`'s well-formedness causes us to have to prove `&'missing N: Add<N>`. +// Since `'missing` is a region error, that will lead to us consider the predicate to hold, +// since it references errors. Since the freshener turns error regions into fresh regions, +// this means that subsequent lookups of `&'?0 N: Add<N>` will also hit this cache entry +// even if candidate assembly can't assemble anything for `&'?0 N: Add<?1>` anyways. This +// led to an ICE. +pub fn compute<N: Numoid>(a: N) { + let _ = &a + a; + //~^ ERROR cannot add `N` to `&N` +} + +fn main() {} diff --git a/tests/ui/traits/eval-caching-error-region.stderr b/tests/ui/traits/eval-caching-error-region.stderr new file mode 100644 index 00000000000..6365d242d2e --- /dev/null +++ b/tests/ui/traits/eval-caching-error-region.stderr @@ -0,0 +1,33 @@ +error[E0261]: use of undeclared lifetime name `'missing` + --> $DIR/eval-caching-error-region.rs:7:6 + | +LL | &'missing Self: Add<Self>, + | ^^^^^^^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'missing` lifetime + | +LL | for<'missing> &'missing Self: Add<Self>, + | +++++++++++++ +help: consider introducing lifetime `'missing` here + | +LL | pub trait Numoid<'missing>: Sized + | ++++++++++ + +error[E0369]: cannot add `N` to `&N` + --> $DIR/eval-caching-error-region.rs:19:16 + | +LL | let _ = &a + a; + | -- ^ - N + | | + | &N + | +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | pub fn compute<N: Numoid>(a: N) where &N: Add<N> { + | ++++++++++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0261, E0369. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/traits/maybe-trait-bounds-forbidden-locations.rs b/tests/ui/traits/maybe-trait-bounds-forbidden-locations.rs new file mode 100644 index 00000000000..04963c98765 --- /dev/null +++ b/tests/ui/traits/maybe-trait-bounds-forbidden-locations.rs @@ -0,0 +1,18 @@ +//! Test that ?Trait bounds are forbidden in supertraits and trait object types. +//! +//! While `?Sized` and other maybe bounds are allowed in type parameter bounds and where clauses, +//! they are explicitly forbidden in certain syntactic positions: +//! - As supertraits in trait definitions +//! - In trait object type expressions +//! +//! See https://github.com/rust-lang/rust/issues/20503 + +trait Tr: ?Sized {} +//~^ ERROR `?Trait` is not permitted in supertraits + +type A1 = dyn Tr + (?Sized); +//~^ ERROR `?Trait` is not permitted in trait object types +type A2 = dyn for<'a> Tr + (?Sized); +//~^ ERROR `?Trait` is not permitted in trait object types + +fn main() {} diff --git a/tests/ui/maybe-bounds.stderr b/tests/ui/traits/maybe-trait-bounds-forbidden-locations.stderr index 230d11fd0ae..bd0baa580bd 100644 --- a/tests/ui/maybe-bounds.stderr +++ b/tests/ui/traits/maybe-trait-bounds-forbidden-locations.stderr @@ -1,5 +1,5 @@ error[E0658]: `?Trait` is not permitted in supertraits - --> $DIR/maybe-bounds.rs:1:11 + --> $DIR/maybe-trait-bounds-forbidden-locations.rs:10:11 | LL | trait Tr: ?Sized {} | ^^^^^^ @@ -9,7 +9,7 @@ LL | trait Tr: ?Sized {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `?Trait` is not permitted in trait object types - --> $DIR/maybe-bounds.rs:4:20 + --> $DIR/maybe-trait-bounds-forbidden-locations.rs:13:20 | LL | type A1 = dyn Tr + (?Sized); | ^^^^^^^^ @@ -18,7 +18,7 @@ LL | type A1 = dyn Tr + (?Sized); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `?Trait` is not permitted in trait object types - --> $DIR/maybe-bounds.rs:6:28 + --> $DIR/maybe-trait-bounds-forbidden-locations.rs:15:28 | LL | type A2 = dyn for<'a> Tr + (?Sized); | ^^^^^^^^ diff --git a/tests/ui/traits/method-argument-mismatch-variance-ice-119867.stderr b/tests/ui/traits/method-argument-mismatch-variance-ice-119867.stderr index d535c39639f..6472ac7363b 100644 --- a/tests/ui/traits/method-argument-mismatch-variance-ice-119867.stderr +++ b/tests/ui/traits/method-argument-mismatch-variance-ice-119867.stderr @@ -1,4 +1,4 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/method-argument-mismatch-variance-ice-119867.rs:8:23 | LL | fn deserialize(s: _) {} diff --git a/tests/ui/transmute/diverging-fn-transmute.rs b/tests/ui/transmute/diverging-fn-transmute.rs new file mode 100644 index 00000000000..aca82037a0c --- /dev/null +++ b/tests/ui/transmute/diverging-fn-transmute.rs @@ -0,0 +1,10 @@ +//! Regression test for issue #35849: transmute with panic in diverging function + +fn assert_sizeof() -> ! { + unsafe { + ::std::mem::transmute::<f64, [u8; 8]>(panic!()) + //~^ ERROR mismatched types + } +} + +fn main() {} diff --git a/tests/ui/diverging-fn-tail-35849.stderr b/tests/ui/transmute/diverging-fn-transmute.stderr index 614f9b9cb5d..b9aeae7ed62 100644 --- a/tests/ui/diverging-fn-tail-35849.stderr +++ b/tests/ui/transmute/diverging-fn-transmute.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/diverging-fn-tail-35849.rs:3:9 + --> $DIR/diverging-fn-transmute.rs:5:9 | LL | fn assert_sizeof() -> ! { | - expected `!` because of return type diff --git a/tests/ui/type-alias-enum-variants/module-type-args-error.rs b/tests/ui/type-alias-enum-variants/module-type-args-error.rs new file mode 100644 index 00000000000..9f7dae4f4f5 --- /dev/null +++ b/tests/ui/type-alias-enum-variants/module-type-args-error.rs @@ -0,0 +1,16 @@ +//! Test that type arguments are properly rejected on modules. +//! +//! Related PR: https://github.com/rust-lang/rust/pull/56225 (RFC 2338 implementation) + +mod Mod { + pub struct FakeVariant<T>(pub T); +} + +fn main() { + // This should work fine - normal generic struct constructor + Mod::FakeVariant::<i32>(0); + + // This should error - type arguments not allowed on modules + Mod::<i32>::FakeVariant(0); + //~^ ERROR type arguments are not allowed on module `Mod` [E0109] +} diff --git a/tests/ui/mod-subitem-as-enum-variant.stderr b/tests/ui/type-alias-enum-variants/module-type-args-error.stderr index 92d972eba42..4c59d19eaa7 100644 --- a/tests/ui/mod-subitem-as-enum-variant.stderr +++ b/tests/ui/type-alias-enum-variants/module-type-args-error.stderr @@ -1,5 +1,5 @@ error[E0109]: type arguments are not allowed on module `Mod` - --> $DIR/mod-subitem-as-enum-variant.rs:7:11 + --> $DIR/module-type-args-error.rs:14:11 | LL | Mod::<i32>::FakeVariant(0); | --- ^^^ type argument not allowed diff --git a/tests/ui/typeck/issue-74086.rs b/tests/ui/typeck/issue-74086.rs index 1993cc7db35..c00ba81f551 100644 --- a/tests/ui/typeck/issue-74086.rs +++ b/tests/ui/typeck/issue-74086.rs @@ -1,4 +1,4 @@ fn main() { static BUG: fn(_) -> u8 = |_| 8; - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for static items + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for statics } diff --git a/tests/ui/typeck/issue-74086.stderr b/tests/ui/typeck/issue-74086.stderr index 25f454ac0c3..02c48201918 100644 --- a/tests/ui/typeck/issue-74086.stderr +++ b/tests/ui/typeck/issue-74086.stderr @@ -1,4 +1,4 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for static items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for statics --> $DIR/issue-74086.rs:2:20 | LL | static BUG: fn(_) -> u8 = |_| 8; diff --git a/tests/ui/typeck/issue-75889.stderr b/tests/ui/typeck/issue-75889.stderr index 1438f481ec7..c76f7c60b2e 100644 --- a/tests/ui/typeck/issue-75889.stderr +++ b/tests/ui/typeck/issue-75889.stderr @@ -1,10 +1,10 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for constant items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/issue-75889.rs:3:24 | LL | const FOO: dyn Fn() -> _ = ""; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for static items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for statics --> $DIR/issue-75889.rs:4:25 | LL | static BOO: dyn Fn() -> _ = ""; diff --git a/tests/ui/typeck/issue-81885.rs b/tests/ui/typeck/issue-81885.rs index d73c77b8f3a..d675231d898 100644 --- a/tests/ui/typeck/issue-81885.rs +++ b/tests/ui/typeck/issue-81885.rs @@ -1,7 +1,7 @@ const TEST4: fn() -> _ = 42; -//~^ ERROR the placeholder `_` is not allowed within types on item signatures for constant items +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants fn main() { const TEST5: fn() -> _ = 42; - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constant items + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants } diff --git a/tests/ui/typeck/issue-81885.stderr b/tests/ui/typeck/issue-81885.stderr index 25a6bb632ef..414fe548883 100644 --- a/tests/ui/typeck/issue-81885.stderr +++ b/tests/ui/typeck/issue-81885.stderr @@ -1,10 +1,10 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for constant items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/issue-81885.rs:1:22 | LL | const TEST4: fn() -> _ = 42; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for constant items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/issue-81885.rs:5:26 | LL | const TEST5: fn() -> _ = 42; diff --git a/tests/ui/typeck/issue-83621-placeholder-static-in-extern.stderr b/tests/ui/typeck/issue-83621-placeholder-static-in-extern.stderr index a4cb53025e3..c4a5c0dea6e 100644 --- a/tests/ui/typeck/issue-83621-placeholder-static-in-extern.stderr +++ b/tests/ui/typeck/issue-83621-placeholder-static-in-extern.stderr @@ -1,4 +1,4 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables +error[E0121]: the placeholder `_` is not allowed within types on item signatures for statics --> $DIR/issue-83621-placeholder-static-in-extern.rs:4:15 | LL | static x: _; diff --git a/tests/ui/typeck/issue-88643.rs b/tests/ui/typeck/issue-88643.rs index 4435cba0207..e562f3e55ac 100644 --- a/tests/ui/typeck/issue-88643.rs +++ b/tests/ui/typeck/issue-88643.rs @@ -8,12 +8,12 @@ use std::collections::HashMap; pub trait T {} static CALLBACKS: HashMap<*const dyn T, dyn FnMut(&mut _) + 'static> = HashMap::new(); -//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for static items [E0121] +//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for statics [E0121] static CALLBACKS2: Vec<dyn Fn(& _)> = Vec::new(); -//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for static items [E0121] +//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for statics [E0121] static CALLBACKS3: Option<dyn Fn(& _)> = None; -//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for static items [E0121] +//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for statics [E0121] fn main() {} diff --git a/tests/ui/typeck/issue-88643.stderr b/tests/ui/typeck/issue-88643.stderr index d5d596b6f42..ad11c3ea8e0 100644 --- a/tests/ui/typeck/issue-88643.stderr +++ b/tests/ui/typeck/issue-88643.stderr @@ -1,16 +1,16 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for static items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for statics --> $DIR/issue-88643.rs:10:56 | LL | static CALLBACKS: HashMap<*const dyn T, dyn FnMut(&mut _) + 'static> = HashMap::new(); | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for static items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for statics --> $DIR/issue-88643.rs:13:33 | LL | static CALLBACKS2: Vec<dyn Fn(& _)> = Vec::new(); | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for static items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for statics --> $DIR/issue-88643.rs:16:36 | LL | static CALLBACKS3: Option<dyn Fn(& _)> = None; diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs index dc790361919..48547c019d6 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item.rs @@ -41,7 +41,7 @@ impl Test9 { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types fn test10(&self, _x : _) { } - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods } fn test11(x: &usize) -> &_ { @@ -56,10 +56,10 @@ unsafe fn test12(x: *const usize) -> *const *const _ { impl Clone for Test9 { fn clone(&self) -> _ { Test9 } - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods fn clone_from(&mut self, other: _) { *self = Test9; } - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods } struct Test10 { @@ -108,15 +108,15 @@ pub fn main() { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types fn fn_test10(&self, _x : _) { } - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods } impl Clone for FnTest9 { fn clone(&self) -> _ { FnTest9 } - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods fn clone_from(&mut self, other: _) { *self = FnTest9; } - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods } struct FnTest10 { @@ -140,19 +140,19 @@ pub fn main() { trait T { fn method_test1(&self, x: _); - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods fn method_test2(&self, x: _) -> _; - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions - //~| ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods + //~| ERROR the placeholder `_` is not allowed within types on item signatures for methods fn method_test3(&self) -> _; - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for methods fn assoc_fn_test1(x: _); - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated functions fn assoc_fn_test2(x: _) -> _; - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions - //~| ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated functions + //~| ERROR the placeholder `_` is not allowed within types on item signatures for associated functions fn assoc_fn_test3() -> _; - //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated functions } struct BadStruct<_>(_); diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index 53476f6c807..87750ee6dc1 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -203,7 +203,7 @@ LL | unsafe fn test12(x: *const usize) -> *const *const _ { | | not allowed in type signatures | help: replace with the correct return type: `*const *const usize` -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:58:24 | LL | fn clone(&self) -> _ { Test9 } @@ -215,7 +215,7 @@ LL - fn clone(&self) -> _ { Test9 } LL + fn clone(&self) -> Test9 { Test9 } | -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:61:37 | LL | fn clone_from(&mut self, other: _) { *self = Test9; } @@ -332,7 +332,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures LL | fn fn_test8(_f: fn() -> _) { } | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:115:28 | LL | fn clone(&self) -> _ { FnTest9 } @@ -344,7 +344,7 @@ LL - fn clone(&self) -> _ { FnTest9 } LL + fn clone(&self) -> FnTest9 { FnTest9 } | -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:118:41 | LL | fn clone_from(&mut self, other: _) { *self = FnTest9; } @@ -389,49 +389,49 @@ LL | fn fn_test13(x: _) -> (i32, _) { (x, x) } | | not allowed in type signatures | help: replace with the correct return type: `(i32, i32)` -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:142:31 | LL | fn method_test1(&self, x: _); | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:144:31 | LL | fn method_test2(&self, x: _) -> _; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:144:37 | LL | fn method_test2(&self, x: _) -> _; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:147:31 | LL | fn method_test3(&self) -> _; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/typeck_type_placeholder_item.rs:149:26 | LL | fn assoc_fn_test1(x: _); | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/typeck_type_placeholder_item.rs:151:26 | LL | fn assoc_fn_test2(x: _) -> _; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/typeck_type_placeholder_item.rs:151:32 | LL | fn assoc_fn_test2(x: _) -> _; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated functions --> $DIR/typeck_type_placeholder_item.rs:154:28 | LL | fn assoc_fn_test3() -> _; @@ -575,7 +575,7 @@ LL | fn test9(&self) -> _ { () } | not allowed in type signatures | help: replace with the correct return type: `()` -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:43:27 | LL | fn test10(&self, _x : _) { } @@ -590,7 +590,7 @@ LL | fn fn_test9(&self) -> _ { () } | not allowed in type signatures | help: replace with the correct return type: `()` -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions +error[E0121]: the placeholder `_` is not allowed within types on item signatures for methods --> $DIR/typeck_type_placeholder_item.rs:110:34 | LL | fn fn_test10(&self, _x : _) { } diff --git a/tests/ui/typeck/typeck_type_placeholder_item_help.rs b/tests/ui/typeck/typeck_type_placeholder_item_help.rs index ab433aaaf16..758b94f9854 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item_help.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item_help.rs @@ -11,7 +11,7 @@ const TEST3: _ = Some(42); //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants const TEST4: fn() -> _ = 42; -//~^ ERROR the placeholder `_` is not allowed within types on item signatures for constant items +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants trait Test5 { const TEST5: _ = 42; diff --git a/tests/ui/typeck/typeck_type_placeholder_item_help.stderr b/tests/ui/typeck/typeck_type_placeholder_item_help.stderr index 5066e2eaa52..2fce00e7a8e 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item_help.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item_help.stderr @@ -31,7 +31,7 @@ LL - const TEST3: _ = Some(42); LL + const TEST3: Option<i32> = Some(42); | -error[E0121]: the placeholder `_` is not allowed within types on item signatures for constant items +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/typeck_type_placeholder_item_help.rs:13:22 | LL | const TEST4: fn() -> _ = 42; |
