diff --git a/.mailmap b/.mailmap index d1e5b3f519591..9166494632a00 100644 --- a/.mailmap +++ b/.mailmap @@ -491,7 +491,6 @@ Milan Landaverde mjptree Ms2ger msizanoen1 -mu001999 Mukilan Thiagarajan Nadrieril Feneanar Nadrieril Feneanar @@ -603,6 +602,7 @@ Robert Habermeier Robert Millar Roc Yu Rohit Joshi Rohit Joshi +Roland Xu Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Ross Smyth <18294397+RossSmyth@users.noreply.github.com> diff --git a/Cargo.lock b/Cargo.lock index 2f05b186b3fc9..873182dde9102 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,7 +687,7 @@ dependencies = [ "color-print", "declare_clippy_lint", "filetime", - "itertools", + "itertools 0.12.1", "pulldown-cmark", "regex", "rustc_tools_util 0.4.2", @@ -705,7 +705,7 @@ name = "clippy_config" version = "0.1.98" dependencies = [ "clippy_utils", - "itertools", + "itertools 0.12.1", "serde", "toml 0.7.8", "walkdir", @@ -718,7 +718,7 @@ dependencies = [ "chrono", "clap", "indoc", - "itertools", + "itertools 0.12.1", "opener", "rustc-literal-escaper", "walkdir", @@ -733,7 +733,7 @@ dependencies = [ "clippy_config", "clippy_utils", "declare_clippy_lint", - "itertools", + "itertools 0.12.1", "quine-mc_cluskey", "regex-syntax", "semver", @@ -751,7 +751,7 @@ version = "0.0.1" dependencies = [ "clippy_config", "clippy_utils", - "itertools", + "itertools 0.12.1", "regex", "rustc-semver", ] @@ -761,7 +761,7 @@ name = "clippy_utils" version = "0.1.98" dependencies = [ "arrayvec", - "itertools", + "itertools 0.12.1", "rustc_apfloat", "serde", ] @@ -933,7 +933,7 @@ name = "coverage-dump" version = "0.1.0" dependencies = [ "anyhow", - "itertools", + "itertools 0.15.0", "leb128", "md-5", "miniz_oxide", @@ -1556,16 +1556,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - [[package]] name = "generate-copyright" version = "0.1.0" @@ -1791,14 +1781,12 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.29.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" +checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" dependencies = [ "log", - "mac", "markup5ever", - "match_token", ] [[package]] @@ -2129,6 +2117,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -2415,35 +2412,15 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - [[package]] name = "markup5ever" -version = "0.14.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" +checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" dependencies = [ "log", - "phf 0.11.3", - "phf_codegen", - "string_cache", - "string_cache_codegen", "tendril", -] - -[[package]] -name = "match_token" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "web_atoms", ] [[package]] @@ -2977,56 +2954,57 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" dependencies = [ - "phf_shared 0.11.3", + "phf_shared 0.12.1", ] [[package]] name = "phf" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_shared 0.12.1", + "phf_shared 0.13.1", + "serde", ] [[package]] name = "phf_codegen" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" dependencies = [ "phf_generator", - "phf_shared 0.11.3", + "phf_shared 0.13.1", ] [[package]] name = "phf_generator" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ - "phf_shared 0.11.3", - "rand 0.8.5", + "fastrand", + "phf_shared 0.13.1", ] [[package]] name = "phf_shared" -version = "0.11.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" dependencies = [ "siphasher", ] [[package]] name = "phf_shared" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ "siphasher", ] @@ -3257,24 +3235,13 @@ dependencies = [ "ptr_meta", ] -[[package]] -name = "rand" -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", -] - [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.3", ] @@ -3289,16 +3256,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[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" @@ -3309,15 +3266,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - [[package]] name = "rand_core" version = "0.9.3" @@ -3554,9 +3502,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-literal-escaper" -version = "0.0.7" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be87abb9e40db7466e0681dc8ecd9dcfd40360cb10b4c8fe24a7c4c3669b198" +checksum = "bfe6f213fb658c8fb95baabd5420393438cf5a98d707f5dd701d9197c705f71e" [[package]] name = "rustc-main" @@ -3686,7 +3634,7 @@ dependencies = [ name = "rustc_ast_passes" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_ast_pretty", @@ -3706,7 +3654,7 @@ dependencies = [ name = "rustc_ast_pretty" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_ast", "rustc_lexer", "rustc_span", @@ -3750,7 +3698,7 @@ name = "rustc_borrowck" version = "0.0.0" dependencies = [ "either", - "itertools", + "itertools 0.15.0", "polonius-engine", "rustc_abi", "rustc_data_structures", @@ -3804,7 +3752,7 @@ version = "0.0.0" dependencies = [ "bitflags", "gimli 0.31.1", - "itertools", + "itertools 0.15.0", "libc", "libloading 0.9.0", "measureme", @@ -3840,7 +3788,7 @@ dependencies = [ "bitflags", "bstr", "find-msvc-tools", - "itertools", + "itertools 0.15.0", "libc", "object 0.37.3", "pathdiff", @@ -4110,7 +4058,7 @@ dependencies = [ name = "rustc_hir_analysis" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_arena", "rustc_ast", @@ -4157,7 +4105,7 @@ dependencies = [ name = "rustc_hir_typeck" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_data_structures", @@ -4312,6 +4260,7 @@ dependencies = [ "rustc_parse_format", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "rustc_trait_selection", "smallvec", @@ -4382,7 +4331,6 @@ dependencies = [ "rustc_fs_util", "rustc_hir", "rustc_hir_pretty", - "rustc_incremental", "rustc_index", "rustc_macros", "rustc_middle", @@ -4435,7 +4383,7 @@ dependencies = [ name = "rustc_mir_build" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_apfloat", "rustc_arena", @@ -4479,7 +4427,7 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_arena", "rustc_ast", @@ -4684,7 +4632,7 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "indexmap", - "itertools", + "itertools 0.15.0", "pulldown-cmark", "rustc_arena", "rustc_ast", @@ -4846,7 +4794,7 @@ checksum = "a3b75158011a63889ba12084cf1224baad7bcad50f6ee7c842f772b74aa148ed" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_data_structures", @@ -4880,7 +4828,7 @@ dependencies = [ name = "rustc_transmute" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_data_structures", "rustc_hir", @@ -4894,7 +4842,7 @@ dependencies = [ name = "rustc_ty_utils" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_data_structures", "rustc_errors", @@ -4971,7 +4919,7 @@ dependencies = [ "base64", "expect-test", "indexmap", - "itertools", + "itertools 0.15.0", "minifier", "proc-macro2", "pulldown-cmark-escape", @@ -5059,7 +5007,7 @@ dependencies = [ "dirs", "getopts", "ignore", - "itertools", + "itertools 0.12.1", "regex", "rustfmt-config_proc_macro", "semver", @@ -5416,25 +5364,24 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "string_cache" -version = "0.8.9" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared 0.11.3", + "phf_shared 0.13.1", "precomputed-hash", - "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.4" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" dependencies = [ "phf_generator", - "phf_shared 0.11.3", + "phf_shared 0.13.1", "proc-macro2", "quote", ] @@ -5524,12 +5471,11 @@ dependencies = [ [[package]] name = "tendril" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" dependencies = [ - "futf", - "mac", + "new_debug_unreachable", "utf-8", ] @@ -5574,7 +5520,7 @@ dependencies = [ "indicatif", "num", "rand 0.9.2", - "rand_chacha 0.9.0", + "rand_chacha", "rayon", ] @@ -5948,7 +5894,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand 0.8.5", "static_assertions", ] @@ -6494,6 +6439,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" +dependencies = [ + "phf 0.13.1", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index f30b923eeed17..5a44323a0b755 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -343,10 +343,16 @@ impl ExternAbi { // This ABI does not support calls at all (except via assembly). false } + Self::RustCall => { + // Argument untupling requires additional support for tail calls to be possible, + // see . There is no real uses of + // tail calling `extern "rust-call"` functions + false + } + Self::C { .. } | Self::System { .. } | Self::Rust - | Self::RustCall | Self::RustCold | Self::RustInvalid | Self::Unadjusted diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 7b68fbc9e77b0..003e811dc7855 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -477,12 +477,14 @@ impl LayoutCalculator { Ok(Some((repr, _))) => match repr { // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _) - if repr.scalar_align(dl).unwrap() != align => + if repr.scalar_platform_align(dl).unwrap() != align => { BackendRepr::Memory { sized: true } } // Vectors require at least element alignment, else disable the opt - BackendRepr::SimdVector { element, count: _ } if element.align(dl).abi > align => { + BackendRepr::SimdVector { element, count: _ } + if element.default_align(dl).abi > align => + { BackendRepr::Memory { sized: true } } // the alignment tests passed and we can use this @@ -986,7 +988,8 @@ impl LayoutCalculator { // roundtripping pointers through ptrtoint/inttoptr. (p @ Primitive::Pointer(_), i @ Primitive::Int(..)) | (i @ Primitive::Int(..), p @ Primitive::Pointer(_)) - if p.size(dl) == i.size(dl) && p.align(dl) == i.align(dl) => + if p.size(dl) == i.size(dl) + && p.default_align(dl) == i.default_align(dl) => { p } diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs index b103e1e97bd96..5fd504def384a 100644 --- a/compiler/rustc_abi/src/layout/simple.rs +++ b/compiler/rustc_abi/src/layout/simple.rs @@ -49,7 +49,7 @@ impl LayoutData { pub fn scalar(cx: &C, scalar: Scalar) -> Self { let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); let size = scalar.size(cx); - let align = scalar.align(cx); + let align = scalar.default_align(cx); let range = scalar.valid_range(cx); @@ -90,8 +90,8 @@ impl LayoutData { pub fn scalar_pair(cx: &C, a: Scalar, b: Scalar) -> Self { let dl = cx.data_layout(); - let b_align = b.align(dl).abi; - let align = a.align(dl).abi.max(b_align).max(dl.aggregate_align); + let b_align = b.default_align(dl).abi; + let align = a.default_align(dl).abi.max(b_align).max(dl.aggregate_align); let b_offset = a.size(dl).align_to(b_align); let size = (b_offset + b.size(dl)).align_to(align); diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index b09afc9ec8af6..9ca4aa2254547 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -1,7 +1,8 @@ use std::fmt; -use std::ops::Deref; +use std::ops::{Deref, Range}; use rustc_data_structures::intern::Interned; +use rustc_data_structures::range_set::RangeSet; use rustc_macros::StableHash; use crate::layout::{FieldIdx, VariantIdx}; @@ -282,4 +283,89 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { } found } + + /// The ranges of bytes that are always ignored by the representation relation of this type. + /// + /// In other words, for any sequence of bytes, if we reset the these padding bytes to uninit, + /// then these two sequences of bytes represent the same value (or they are both invalid). + /// This is the "guaranteed" padding. There may be more bytes that are padding for some + /// but not all variants of this type; those are not included. + /// (E.g. `Option` has no guaranteed padding so the empty range set is returned, but its `None` value still has padding). + pub fn padding_ranges(&self, cx: &C) -> Vec> + where + Ty: TyAbiInterface<'a, C> + Copy, + { + let mut data = RangeSet::new(); + self.add_data_ranges(cx, Size::ZERO, &mut data); + + // Find gaps between the data ranges. + let mut uninit_ranges = Vec::new(); + let mut covered_until = Size::ZERO; + for &(offset, size) in data.0.iter() { + if offset > covered_until { + uninit_ranges.push(covered_until..offset); + } + covered_until = Ord::max(covered_until, offset + size); + } + + // Add trailing padding. + if self.size > covered_until { + uninit_ranges.push(covered_until..self.size); + } + + uninit_ranges + } + + /// Extend `out` with all ranges of bytes that *may* carry relevant data for values of this type. + /// For enums and unions there are offsets that are initialized for some + /// variants but not for others; those offset *will* get added to `out`. + fn add_data_ranges(self, cx: &C, base_offset: Size, out: &mut RangeSet) + where + Ty: TyAbiInterface<'a, C> + Copy, + { + if self.is_zst() { + return; + } + + match &self.variants { + Variants::Empty => { /* done */ } + Variants::Single { index: _ } => match &self.fields { + FieldsShape::Primitive => { + out.add_range(base_offset, self.size); + } + &FieldsShape::Union(field_count) => { + for field in 0..field_count.get() { + let field = self.field(cx, field); + field.add_data_ranges(cx, base_offset, out); + } + } + &FieldsShape::Array { stride, count } => { + let elem = self.field(cx, 0); + + // For scalars we know there is no padding between the elements, + // so the entire array is a single big data range. + if elem.backend_repr.is_scalar() { + out.add_range(base_offset, elem.size * count); + } else { + // FIXME: this is really inefficient for large arrays. + for idx in 0..count { + elem.add_data_ranges(cx, base_offset + idx * stride, out); + } + } + } + FieldsShape::Arbitrary { offsets, in_memory_order: _ } => { + for (field, &offset) in offsets.iter_enumerated() { + let field = self.field(cx, field.as_usize()); + field.add_data_ranges(cx, base_offset + offset, out); + } + } + }, + Variants::Multiple { variants, .. } => { + for variant in variants.indices() { + let variant = self.for_variant(cx, variant); + variant.add_data_ranges(cx, base_offset, out); + } + } + } + } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 166c8bea6f354..e86c7f15e9679 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -792,7 +792,7 @@ impl FromStr for Endian { } /// Size of a type in bytes. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))] pub struct Size { raw: u64, @@ -1404,7 +1404,11 @@ impl Primitive { } } - pub fn align(self, cx: &C) -> AbiAlign { + /// The *platform-specific* ABI alignment of this primitive. + /// + /// This is the type alignment for the corresponding built-in. + /// In other contexts it might have different alignment. + pub fn default_align(self, cx: &C) -> AbiAlign { use Primitive::*; let dl = cx.data_layout(); @@ -1579,8 +1583,12 @@ impl Scalar { } } - pub fn align(self, cx: &impl HasDataLayout) -> AbiAlign { - self.primitive().align(cx) + /// The *platform-specific* ABI alignment of this scalar. + /// + /// This is the type alignment for the corresponding built-in. + /// This is *not* necessarily the correct alignment for a type that has this `BackendRepr::Scalar`! + pub fn default_align(self, cx: &impl HasDataLayout) -> AbiAlign { + self.primitive().default_align(cx) } pub fn size(self, cx: &impl HasDataLayout) -> Size { @@ -1792,6 +1800,14 @@ impl IntoDiagArg for NumScalableVectors { #[cfg_attr(feature = "nightly", derive(StableHash))] pub enum BackendRepr { Scalar(Scalar), + /// The data contained in this type can be entirely represented by two scalars. + /// The two scalars are listed in *memory* order, so the first is at offset zero + /// and the second at a non-zero offset. + /// These need not be `FieldIdx(0)` and `FieldIdx(1)`. + /// + /// As of June 2026 the offset to the second scalar is the size of the first + /// scalar rounded up to the platform alignment of the second scalar. + /// That may soon change, however; see MCP#1007. ScalarPair(Scalar, Scalar), SimdScalableVector { element: Scalar, @@ -1857,10 +1873,16 @@ impl BackendRepr { /// The psABI alignment for a `Scalar` or `ScalarPair` /// /// `None` for other variants. - pub fn scalar_align(&self, cx: &C) -> Option { + /// + /// It's unclear whether this is a meaningful operation, and MCP#1007 proposes changes. + /// You should generally be using the alignment of the place or the type, + /// not calculating something from the `Scalar`s. + pub fn scalar_platform_align(&self, cx: &C) -> Option { match *self { - BackendRepr::Scalar(s) => Some(s.align(cx).abi), - BackendRepr::ScalarPair(s1, s2) => Some(s1.align(cx).max(s2.align(cx)).abi), + BackendRepr::Scalar(s) => Some(s.default_align(cx).abi), + BackendRepr::ScalarPair(s1, s2) => { + Some(s1.default_align(cx).max(s2.default_align(cx)).abi) + } // The align of a Vector can vary in surprising ways BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } @@ -1877,9 +1899,9 @@ impl BackendRepr { BackendRepr::Scalar(s) => Some(s.size(cx)), // May have some padding between the pair. BackendRepr::ScalarPair(s1, s2) => { - let field2_offset = s1.size(cx).align_to(s2.align(cx).abi); + let field2_offset = s1.size(cx).align_to(s2.default_align(cx).abi); let size = (field2_offset + s2.size(cx)).align_to( - self.scalar_align(cx) + self.scalar_platform_align(cx) // We absolutely must have an answer here or everything is FUBAR. .unwrap(), ); diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 24ade7300c33b..a4248a82bce73 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -542,11 +542,12 @@ impl DroplessArena { #[inline] pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + assert!(!mem::needs_drop::()); + assert!(size_of::() != 0); + // Warning: this function is reentrant: `iter` could hold a reference to `&self` and // allocate additional elements while we're iterating. let iter = iter.into_iter(); - assert!(size_of::() != 0); - assert!(!mem::needs_drop::()); let size_hint = iter.size_hint(); @@ -577,6 +578,7 @@ impl DroplessArena { ) -> Result<&mut [T], E> { // Despite the similarity with `alloc_from_iter`, we cannot reuse their fast case, as we // cannot know the minimum length of the iterator in this case. + assert!(!mem::needs_drop::()); assert!(size_of::() != 0); // Takes care of reentrancy. diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index a09c36efe01f6..c60f666d3bfad 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start bitflags = "2.4.1" memchr = "2.7.6" -rustc-literal-escaper = "0.0.7" +rustc-literal-escaper = "0.0.8" rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 11a531353eba0..bd5ce5d18b839 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3059,13 +3059,15 @@ impl FnDecl { } /// The marker index for "no splatted arguments". + /// Higher values are also not supported, for performance reasons. + /// /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`. - pub const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX; + pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; /// Returns a splatted argument index, if any are present. - pub fn splatted(&self) -> Option { + pub fn splatted(&self) -> Option { self.inputs.iter().enumerate().find_map(|(index, arg)| { - if index == Self::NO_SPLATTED_ARG_INDEX as usize { + if index >= usize::from(Self::NO_SPLATTED_ARG_INDEX) { // AST validation has already checked the splatted argument index is valid, so just // ignore invalid indexes here. None @@ -3073,7 +3075,7 @@ impl FnDecl { arg.attrs .iter() .any(|attr| attr.has_name(sym::splat)) - .then_some(u16::try_from(index).unwrap()) + .then_some(u8::try_from(index).unwrap()) } }) } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 3ead3b86cb591..a2a7b542258d1 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -53,9 +53,11 @@ use rustc_middle::span_bug; use rustc_middle::ty::{Asyncness, PerOwnerResolverData}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::kw; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym}; -use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; +use crate::delegation::generics::{ + GenericsGenerationResult, GenericsGenerationResults, GenericsPosition, +}; use crate::diagnostics::{ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, @@ -93,7 +95,7 @@ struct ParamInfo { pub c_variadic: bool, /// The index of the splatted parameter, if any. - pub splatted: Option, + pub splatted: Option, } const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; @@ -194,7 +196,7 @@ impl<'hir> LoweringContext<'_, 'hir> { return self.generate_delegation_error(span, delegation); } - let mut generics = self.uplift_delegation_generics(delegation, sig_id, is_method); + let mut generics = self.uplift_delegation_generics(delegation, sig_id); let (body_id, call_expr_id, unused_target_expr) = self.lower_delegation_body( delegation, @@ -364,11 +366,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn param_info(&self, def_id: DefId) -> ParamInfo { let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder(); - // FIXME(splat): use `sig.splatted()` once FnSig has it ParamInfo { param_count: sig.inputs().len() + usize::from(sig.c_variadic()), c_variadic: sig.c_variadic(), - splatted: None, + splatted: sig.splatted(), } } @@ -404,10 +405,10 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::InferDelegationSig::Output(self.arena.alloc(hir::DelegationInfo { call_expr_id, call_path_res: self.get_resolution_id(call_path_node_id), - child_args_segment_id: generics.child.args_segment_id, - parent_args_segment_id: generics.parent.args_segment_id, - self_ty_id: generics.self_ty_id, - propagate_self_ty: generics.propagate_self_ty, + child_seg_id: generics.child.args_segment_id, + child_seg_id_for_sig: generics.child.segment_id_for_sig(), + parent_seg_id_for_sig: generics.parent.segment_id_for_sig(), + self_ty_propagation_kind: generics.self_ty_propagation_kind, group_id: { let id = match source { DelegationSource::Single => None, @@ -506,6 +507,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res: Res::Local(param_id), args: None, infer_args: false, + delegation_child_segment: false, })); let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments }); @@ -625,28 +627,71 @@ impl<'hir> LoweringContext<'_, 'hir> { }), ); - hir::QPath::Resolved(ty, self.arena.alloc(new_path)) - } - hir::QPath::TypeRelative(ty, segment) => { - let segment = self.process_segment(span, segment, &mut generics.child); + // Explicitly create `Self` self-type in case of infers or static + // free-to-trait reuses. + let ty = match generics.self_ty_propagation_kind { + Some(hir::DelegationSelfTyPropagationKind::SelfParam) => { + let self_param = generics.parent.generics.find_self_param(); + let path = self.create_generic_arg_path(self_param); + let kind = hir::TyKind::Path(path); + + let ty = match ty { + Some(ty) => hir::Ty { kind, ..ty.clone() }, + None => hir::Ty { kind, hir_id: self.next_id(), span }, + }; - hir::QPath::TypeRelative(ty, self.arena.alloc(segment)) + Some(&*self.arena.alloc(ty)) + } + _ => ty, + }; + + hir::QPath::Resolved(ty, self.arena.alloc(new_path)) } + hir::QPath::TypeRelative(..) => unreachable!("until inherent methods are supported"), }; - generics.self_ty_id = match new_path { - hir::QPath::Resolved(ty, _) => ty, - hir::QPath::TypeRelative(ty, _) => Some(ty), + if let Some(hir::DelegationSelfTyPropagationKind::SelfTy(id)) = + generics.self_ty_propagation_kind.as_mut() + { + *id = match new_path { + hir::QPath::Resolved(ty, _) => { + ty.expect("must contain self type as `SelfTy` propagation kind is specified") + } + hir::QPath::TypeRelative(ty, _) => ty, + } + .hir_id; } - .map(|ty| ty.hir_id); let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(new_path), span)); let args = self.arena.alloc_from_iter(args); - let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span)); + let call = self.mk_expr(hir::ExprKind::Call(callee_path, args), span); + + let expr = if let Some((parent, of_trait)) = self.should_wrap_return_value(delegation) { + let res = Res::SelfTyAlias { alias_to: parent.to_def_id(), is_trait_impl: of_trait }; + let ident = Ident::new(kw::SelfUpper, span); + let path = self.create_resolved_path(res, ident, span); + + // FIXME(fn_delegation): add default `..` for all other fields. + let initializer = hir::ExprKind::Struct( + self.arena.alloc(path), + self.arena.alloc_slice(&[hir::ExprField { + hir_id: self.next_id(), + is_shorthand: false, + ident: Ident::new(sym::integer(0), span), + expr: self.arena.alloc(call), + span, + }]), + hir::StructTailExpr::None, + ); + + self.arena.alloc(self.mk_expr(initializer, span)) + } else { + self.arena.alloc(call) + }; let block = self.arena.alloc(hir::Block { stmts, - expr: Some(call), + expr: Some(expr), hir_id: self.next_id(), rules: hir::BlockCheckMode::DefaultBlock, span, @@ -656,31 +701,85 @@ impl<'hir> LoweringContext<'_, 'hir> { (self.mk_expr(hir::ExprKind::Block(block, None), span), call.hir_id) } + fn should_wrap_return_value(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> { + // Heuristic: don't do wrapping if there is no target expression. + if delegation.body.is_none() { + return None; + } + + let tcx = self.tcx; + let parent = tcx.local_parent(self.owner.def_id); + let parent_kind = tcx.def_kind(parent); + + // Apply wrapping for delegations inside + // 1) Trait impls, as the return type of both signature function + // and generated delegation has `Self` generic param returned + // (checked below). + // FIXME(fn_delegation): think of enabling wrapping in more scenarios: + // trait-(impl)-to-free + // trait-(impl)-to-inherent + // inherent-to-free + // 2) Inherent methods when delegating to trait, as we change the type of + // `Self` to type of struct or enum we delegate from. + if !matches!(tcx.def_kind(parent), DefKind::Impl { .. }) { + return None; + } + + let is_trait_impl = parent_kind == DefKind::Impl { of_trait: true }; + + // Check that delegation path resolves to a trait AssocFn, not to a free method. + Some((parent, is_trait_impl)).filter(|_| { + self.get_resolution_id(delegation.id).is_some_and(|id| { + tcx.def_kind(id) == DefKind::AssocFn + // Check that the return type of the callee is `Self` param. + // After previous check we are sure that `sig_id` and `delegation.id` + // point to the same function. + && tcx.def_kind(tcx.parent(id)) == DefKind::Trait + && tcx.fn_sig(id).skip_binder().output().skip_binder().is_param(0) + }) + }) + } + fn process_segment( &mut self, span: Span, segment: &hir::PathSegment<'hir>, result: &mut GenericsGenerationResult<'hir>, ) -> hir::PathSegment<'hir> { - let details = result.generics.args_propagation_details(); - - // Always uplift generic params, because if they are not empty then they - // should be generated in delegation. - let generics = result.generics.into_hir_generics(self, span); - let segment = if details.should_propagate { - let args = generics.into_generic_args(self, span); - - // Needed for better error messages (`trait-impl-wrong-args-count.rs` test). - let args = if args.is_empty() { None } else { Some(args) }; + let infer_indices = result.generics.infer_indices(); + result.generics.into_hir_generics(self, span); + + let mut segment = segment.clone(); + let mut args_iter = result.generics.create_args_iterator(); + + let new_args = segment + .args + .filter(|args| !args.is_empty()) + .map(|args| { + self.arena.alloc_from_iter(args.args.iter().enumerate().map(|(idx, arg)| { + if infer_indices.contains(&idx) { + args_iter.next(self, |_| arg.hir_id()).expect("arg must exist for infer") + } else { + *arg + } + })) + }) + .unwrap_or_else(|| self.arena.alloc_from_iter(args_iter.consume_all(self))); + + // Needed for better error messages (`trait-impl-wrong-args-count.rs` test). + segment.args = (!new_args.is_empty()).then(|| { + &*self.arena.alloc(hir::GenericArgs { + args: new_args, + constraints: &[], + parenthesized: hir::GenericArgsParentheses::No, + span_ext: segment.args.map_or(span, |args| args.span_ext), + }) + }); - hir::PathSegment { args, ..segment.clone() } - } else { - segment.clone() - }; + result.args_segment_id = segment.hir_id; + result.use_for_sig_inheritance = !result.generics.is_trait_impl(); - if details.use_args_in_sig_inheritance { - result.args_segment_id = Some(segment.hir_id); - } + segment.delegation_child_segment = result.generics.pos() == GenericsPosition::Child; segment } diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index f8e3528750035..bdbaa9c39e21a 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -1,6 +1,7 @@ use hir::HirId; use hir::def::{DefKind, Res}; use rustc_ast::*; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::GenericParamDefKind; @@ -9,52 +10,38 @@ use rustc_span::symbol::kw; use rustc_span::{Ident, Span, sym}; use crate::LoweringContext; +use crate::diagnostics::DelegationInfersMismatch; -#[derive(Clone, Copy)] -pub(super) enum DelegationGenericsKind { - /// User-specified args are present: `reuse foo::;`. - UserSpecified, - /// The default case when no user-specified args are present: `reuse Trait::foo;`. - Default, - /// In free-to-trait reuse, when user specified args for trait `reuse Trait::::foo;` - /// in this case we need to both generate `Self` and process user args. - SelfAndUserSpecified, - /// In delegations from trait impl to other entities like free functions or trait functions, - /// we want to generate a function whose generics matches generics of signature function - /// in trait. - TraitImpl(bool /* Has user-specified args */), -} - -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub(super) enum GenericsPosition { Parent, Child, } +#[derive(Debug)] +pub(super) enum GenericArgSlot { + UserSpecified, + Generate(T, Option /* Infer arg index from AST */), +} + pub(super) struct DelegationGenerics { - generics: T, - kind: DelegationGenericsKind, + data: T, pos: GenericsPosition, + trait_impl: bool, } -impl<'hir> DelegationGenerics<&'hir [ty::GenericParamDef]> { - fn default(generics: &'hir [ty::GenericParamDef], pos: GenericsPosition) -> Self { - DelegationGenerics { generics, pos, kind: DelegationGenericsKind::Default } - } +type TyGenerics<'hir> = Vec>; - fn user_specified(generics: &'hir [ty::GenericParamDef], pos: GenericsPosition) -> Self { - DelegationGenerics { generics, pos, kind: DelegationGenericsKind::UserSpecified } - } - - fn trait_impl( - generics: &'hir [ty::GenericParamDef], - user_specified: bool, +impl<'hir> DelegationGenerics> { + fn generate_all( + params: &'hir [ty::GenericParamDef], pos: GenericsPosition, + trait_impl: bool, ) -> Self { DelegationGenerics { - generics, + data: params.iter().map(|p| GenericArgSlot::Generate(p, None)).collect(), pos, - kind: DelegationGenericsKind::TraitImpl(user_specified), + trait_impl, } } } @@ -70,103 +57,183 @@ impl<'hir> DelegationGenerics<&'hir [ty::GenericParamDef]> { /// (i.e., method call scenarios), in such a case this approach helps /// a lot as if `into_hir_generics` will not be called then uplifting will not happen. pub(super) enum HirOrTyGenerics<'hir> { - Ty(DelegationGenerics<&'hir [ty::GenericParamDef]>), + Ty(DelegationGenerics>), Hir(DelegationGenerics<&'hir hir::Generics<'hir>>), } pub(super) struct GenericsGenerationResult<'hir> { pub(super) generics: HirOrTyGenerics<'hir>, - pub(super) args_segment_id: Option, + pub(super) args_segment_id: HirId, + pub(super) use_for_sig_inheritance: bool, +} + +impl GenericsGenerationResult<'_> { + pub(super) fn segment_id_for_sig(&self) -> Option { + self.use_for_sig_inheritance.then(|| self.args_segment_id) + } } pub(super) struct GenericsGenerationResults<'hir> { pub(super) parent: GenericsGenerationResult<'hir>, pub(super) child: GenericsGenerationResult<'hir>, - pub(super) self_ty_id: Option, - pub(super) propagate_self_ty: bool, + pub(super) self_ty_propagation_kind: Option, } -pub(super) struct GenericArgsPropagationDetails { - pub(super) should_propagate: bool, - pub(super) use_args_in_sig_inheritance: bool, +pub(super) struct DelegationGenericArgsIterator<'hir> { + index: usize = Default::default(), + params: &'hir [hir::GenericParam<'hir>], } -impl DelegationGenericsKind { - fn args_propagation_details(self) -> GenericArgsPropagationDetails { - match self { - DelegationGenericsKind::UserSpecified - | DelegationGenericsKind::SelfAndUserSpecified => GenericArgsPropagationDetails { - should_propagate: false, - use_args_in_sig_inheritance: true, - }, - DelegationGenericsKind::TraitImpl(user_specified) => GenericArgsPropagationDetails { - should_propagate: !user_specified, - use_args_in_sig_inheritance: false, - }, - DelegationGenericsKind::Default => GenericArgsPropagationDetails { - should_propagate: true, - use_args_in_sig_inheritance: false, - }, +/// During generic args propagation we need to create generic args +/// (and their `HirId`s) on demand, as some of generic args can not be used +/// and in this case an assert of an unseen `HirId` will be triggered. Moreover, +/// when replacing infers with generated generic params we should reuse existing +/// `HirId` of replaced infer, thus this iterator abstracts the way `HirId`s are +/// created for new generic args. +impl<'hir> DelegationGenericArgsIterator<'hir> { + pub(super) fn next( + &mut self, + ctx: &mut LoweringContext<'_, 'hir>, + hir_id_factory: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> HirId, + ) -> Option> { + let p = loop { + if self.index >= self.params.len() { + return None; + } + + let p = self.params[self.index]; + self.index += 1; + + // Skip self generic arg, we do not need to propagate it. + if p.name.ident().name == kw::SelfUpper || p.is_impl_trait() { + continue; + } + + break p; + }; + + let hir_id = hir_id_factory(ctx); + + Some(match p.kind { + hir::GenericParamKind::Lifetime { .. } => { + hir::GenericArg::Lifetime(ctx.arena.alloc(hir::Lifetime { + hir_id, + ident: p.name.ident(), + kind: hir::LifetimeKind::Param(p.def_id), + source: hir::LifetimeSource::Path { angle_brackets: hir::AngleBrackets::Full }, + syntax: hir::LifetimeSyntax::ExplicitBound, + })) + } + hir::GenericParamKind::Type { .. } => hir::GenericArg::Type(ctx.arena.alloc(hir::Ty { + hir_id, + span: p.span, + kind: hir::TyKind::Path(ctx.create_generic_arg_path(&p)), + })), + hir::GenericParamKind::Const { .. } => { + hir::GenericArg::Const(ctx.arena.alloc(hir::ConstArg { + hir_id, + kind: hir::ConstArgKind::Path(ctx.create_generic_arg_path(&p)), + span: p.span, + })) + } + }) + } + + pub(super) fn consume_all( + mut self, + ctx: &mut LoweringContext<'_, 'hir>, + ) -> Vec> { + let mut args = vec![]; + while let Some(arg) = self.next(ctx, |ctx| ctx.next_id()) { + args.push(arg); } + + args } } impl<'hir> HirOrTyGenerics<'hir> { - pub(super) fn into_hir_generics( - &mut self, - ctx: &mut LoweringContext<'_, 'hir>, - span: Span, - ) -> &mut HirOrTyGenerics<'hir> { + pub(super) fn into_hir_generics(&mut self, ctx: &mut LoweringContext<'_, 'hir>, span: Span) { if let HirOrTyGenerics::Ty(ty) = self { - let rename_self = matches!(ty.pos, GenericsPosition::Child); - let params = ctx.uplift_delegation_generic_params(span, ty.generics, rename_self); + let rename_self = ty.pos == GenericsPosition::Child; + let params = ctx.uplift_delegation_generic_params(span, &ty.data, rename_self); *self = HirOrTyGenerics::Hir(DelegationGenerics { - generics: params, - kind: ty.kind, + data: params, pos: ty.pos, + trait_impl: ty.trait_impl, }); } - - self } fn hir_generics_or_empty(&self) -> &'hir hir::Generics<'hir> { match self { HirOrTyGenerics::Ty(_) => hir::Generics::empty(), - HirOrTyGenerics::Hir(hir) => hir.generics, + HirOrTyGenerics::Hir(hir) => hir.data, } } - pub(super) fn into_generic_args( - &self, - ctx: &mut LoweringContext<'_, 'hir>, - span: Span, - ) -> &'hir hir::GenericArgs<'hir> { + pub(super) fn create_args_iterator(&self) -> DelegationGenericArgsIterator<'hir> { match self { HirOrTyGenerics::Ty(_) => { - bug!("Attempting to get generic args before uplifting to HIR") + bug!("attempting to get generic args before uplifting to HIR") } HirOrTyGenerics::Hir(hir) => { - let add_lifetimes = matches!(hir.pos, GenericsPosition::Parent); - ctx.create_generics_args_from_params(hir.generics.params, add_lifetimes, span) + DelegationGenericArgsIterator { params: hir.data.params, .. } } } } - pub(super) fn args_propagation_details(&self) -> GenericArgsPropagationDetails { + pub(super) fn infer_indices(&self) -> FxHashSet { match self { - HirOrTyGenerics::Ty(ty) => ty.kind.args_propagation_details(), - HirOrTyGenerics::Hir(hir) => hir.kind.args_propagation_details(), + HirOrTyGenerics::Ty(ty) => ty + .data + .iter() + .flat_map(|slot| match slot { + GenericArgSlot::Generate(_, Some(idx)) => Some(*idx), + _ => None, + }) + .collect(), + HirOrTyGenerics::Hir(_) => bug!("accessed infer indices on uplifted generics"), + } + } + + pub(super) fn is_trait_impl(&self) -> bool { + match self { + HirOrTyGenerics::Ty(ty) => ty.trait_impl, + HirOrTyGenerics::Hir(hir) => hir.trait_impl, + } + } + + pub(super) fn find_self_param(&self) -> &'hir hir::GenericParam<'hir> { + match self { + HirOrTyGenerics::Ty(_) => { + bug!("accessed ty-level generics while searching for uplifted `Self` param") + } + HirOrTyGenerics::Hir(hir) => hir + .data + .params + .iter() + .find(|p| p.name.ident().name == kw::SelfUpper) + .expect("`Self` generic param is not found while expected"), + } + } + + pub(crate) fn pos(&self) -> GenericsPosition { + match self { + HirOrTyGenerics::Ty(ty) => ty.pos, + HirOrTyGenerics::Hir(hir) => hir.pos, } } } impl<'hir> GenericsGenerationResult<'hir> { - fn new( - generics: DelegationGenerics<&'hir [ty::GenericParamDef]>, - ) -> GenericsGenerationResult<'hir> { - GenericsGenerationResult { generics: HirOrTyGenerics::Ty(generics), args_segment_id: None } + fn new(generics: DelegationGenerics>) -> GenericsGenerationResult<'hir> { + GenericsGenerationResult { + generics: HirOrTyGenerics::Ty(generics), + args_segment_id: HirId::INVALID, + use_for_sig_inheritance: false, + } } } @@ -208,13 +275,30 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, delegation: &Delegation, sig_id: DefId, - is_method: bool, ) -> GenericsGenerationResults<'hir> { let delegation_parent_kind = self.tcx.def_kind(self.tcx.local_parent(self.owner.def_id)); let segments = &delegation.path.segments; let len = segments.len(); - let child_user_specified = segments[len - 1].args.is_some(); + + let get_user_args = |idx: usize| -> Option<&AngleBracketedArgs> { + let segment = &segments[idx]; + + let Some(args) = segment.args.as_ref() else { return None }; + let GenericArgs::AngleBracketed(args) = args else { + self.tcx.dcx().span_delayed_bug( + segment.span(), + "expected angle-bracketed generic args in delegation segment", + ); + + return None; + }; + + // Treat empty args `reuse foo::<> as bar` as `reuse foo as bar`, + // the same logic applied when we call function `fn f(t: T)` + // like that `f::<>(())`, in HIR no `<>` will be generated. + (!args.args.is_empty()).then(|| args) + }; let sig_params = &self.tcx.generics_of(sig_id).own_params[..]; @@ -223,23 +307,15 @@ impl<'hir> LoweringContext<'_, 'hir> { if matches!(delegation_parent_kind, DefKind::Impl { of_trait: true }) { // Considering parent generics, during signature inheritance // we will take those args that are in trait impl header trait ref. - let parent = DelegationGenerics::trait_impl(&[], true, GenericsPosition::Parent); - let parent = GenericsGenerationResult::new(parent); + let parent = + DelegationGenerics { data: vec![], pos: GenericsPosition::Child, trait_impl: true }; - let child = DelegationGenerics::trait_impl( - sig_params, - child_user_specified, - GenericsPosition::Child, - ); + let parent = GenericsGenerationResult::new(parent); + let child = DelegationGenerics::generate_all(sig_params, GenericsPosition::Child, true); let child = GenericsGenerationResult::new(child); - return GenericsGenerationResults { - parent, - child, - self_ty_id: None, - propagate_self_ty: false, - }; + return GenericsGenerationResults { parent, child, self_ty_propagation_kind: None }; } let delegation_in_free_ctx = @@ -248,7 +324,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let sig_parent = self.tcx.parent(sig_id); let sig_in_trait = matches!(self.tcx.def_kind(sig_parent), DefKind::Trait); let free_to_trait_delegation = delegation_in_free_ctx && sig_in_trait; - let generate_self = free_to_trait_delegation && is_method && delegation.qself.is_none(); + + let qself_is_infer = + delegation.qself.as_ref().is_some_and(|qself| qself.ty.is_maybe_parenthesised_infer()); + + let qself_is_none = delegation.qself.is_none(); + + let generate_self = free_to_trait_delegation && (qself_is_none || qself_is_infer); let can_add_generics_to_parent = len >= 2 && self.get_resolution_id(segments[len - 2].id).is_some_and(|def_id| { @@ -256,57 +338,137 @@ impl<'hir> LoweringContext<'_, 'hir> { }); let parent_generics = if can_add_generics_to_parent { - let sig_parent_params = &self.tcx.generics_of(sig_parent).own_params[..]; - - if segments[len - 2].args.is_some() { - if generate_self { - // Take only first Self parameter, it is trait so Self must be present. - DelegationGenerics { - kind: DelegationGenericsKind::SelfAndUserSpecified, - generics: &sig_parent_params[..1], - pos: GenericsPosition::Parent, - } - } else { - DelegationGenerics::user_specified(&[], GenericsPosition::Parent) + let sig_parent_params = &self.tcx.generics_of(sig_parent).own_params; + + if let Some(args) = get_user_args(len - 2) { + DelegationGenerics { + data: self.create_slots_from_args( + args, + &sig_parent_params[usize::from(!generate_self)..], + generate_self, + ), + pos: GenericsPosition::Parent, + trait_impl: false, } } else { - let skip_self = usize::from(!generate_self); - DelegationGenerics::default( - &sig_parent_params[skip_self..], + DelegationGenerics::generate_all( + &sig_parent_params[usize::from(!generate_self)..], GenericsPosition::Parent, + false, ) } } else { - DelegationGenerics::default(&[], GenericsPosition::Parent) + DelegationGenerics { data: vec![], pos: GenericsPosition::Parent, trait_impl: false } }; - let child_generics = if child_user_specified { + let child_generics = if let Some(args) = get_user_args(len - 1) { let synth_params_index = sig_params.iter().position(|p| p.kind.is_synthetic()).unwrap_or(sig_params.len()); - DelegationGenerics::user_specified( - &sig_params[synth_params_index..], - GenericsPosition::Child, - ) + let mut slots = + self.create_slots_from_args(args, &sig_params[..synth_params_index], false); + + for synth_param in &sig_params[synth_params_index..] { + slots.push(GenericArgSlot::Generate(synth_param, None)); + } + + DelegationGenerics { data: slots, pos: GenericsPosition::Child, trait_impl: false } } else { - DelegationGenerics::default(sig_params, GenericsPosition::Child) + DelegationGenerics::generate_all(sig_params, GenericsPosition::Child, false) }; GenericsGenerationResults { parent: GenericsGenerationResult::new(parent_generics), child: GenericsGenerationResult::new(child_generics), - self_ty_id: None, - propagate_self_ty: free_to_trait_delegation && !generate_self, + self_ty_propagation_kind: match free_to_trait_delegation { + true => Some(match qself_is_none { + true => hir::DelegationSelfTyPropagationKind::SelfParam, + false => match qself_is_infer { + true => hir::DelegationSelfTyPropagationKind::SelfParam, + // HirId is filled during generic args propagation. + false => hir::DelegationSelfTyPropagationKind::SelfTy(HirId::INVALID), + }, + }), + false => None, + }, } } + /// Generates generic argument slots for user-specified `args` and + /// generic `params` of the signature function. This function checks whether + /// there are infers (`kw::UnderscoreLifetime` or `kw::Underscore`) in + /// user-specified args, and if so we add `Generate` slot meaning we have to + /// generate generic param for delegation and propagate it instead of this infer. + /// We zip over user-specified args and signature generic params, so if there are more + /// infers than generic params then we will not process all infers thus not generating + /// more generic params then needed (anyway it is an error). + fn create_slots_from_args( + &self, + args: &AngleBracketedArgs, + params: &'hir [ty::GenericParamDef], + add_first_self: bool, + ) -> TyGenerics<'hir> { + let mut slots = vec![]; + if add_first_self { + slots.push(GenericArgSlot::Generate(¶ms[0], None)); + } + + let params = ¶ms[usize::from(add_first_self)..]; + for (idx, (arg, param)) in args.args.iter().zip(params).enumerate() { + let AngleBracketedArg::Arg(arg) = arg else { continue }; + + let is_infer = match arg { + GenericArg::Lifetime(lt) => lt.ident.name == kw::UnderscoreLifetime, + GenericArg::Type(ty) => ty.is_maybe_parenthesised_infer(), + GenericArg::Const(_) => false, + }; + + // If `'_` is used instead of `_` (or vice versa) we emit a meaningful + // error instead of processing this infer or leaving it as is for signature + // inheritance. + if is_infer + && matches!( + (arg, ¶m.kind), + ( + GenericArg::Lifetime(_), + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } + ) | ( + GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDefKind::Lifetime { .. } + ) + ) + { + let (actual, expected) = if matches!(arg, GenericArg::Lifetime(..)) { + (kw::UnderscoreLifetime, kw::Underscore) + } else { + (kw::Underscore, kw::UnderscoreLifetime) + }; + + self.tcx.dcx().emit_err(DelegationInfersMismatch { + span: arg.span(), + actual, + expected, + }); + } + + slots.push(match is_infer { + true => GenericArgSlot::Generate(param, Some(idx)), + false => GenericArgSlot::UserSpecified, + }); + } + + slots + } + fn uplift_delegation_generic_params( &mut self, span: Span, - params: &'hir [ty::GenericParamDef], + params: &[GenericArgSlot<&ty::GenericParamDef>], rename_self: bool, ) -> &'hir hir::Generics<'hir> { - let params = self.arena.alloc_from_iter(params.iter().map(|p| { + let params = self.arena.alloc_from_iter(params.iter().flat_map(|p| { + let GenericArgSlot::Generate(p, _) = p else { return None }; + let def_kind = match p.kind { GenericParamDefKind::Lifetime => DefKind::LifetimeParam, GenericParamDefKind::Type { .. } => DefKind::TyParam, @@ -354,7 +516,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // `lower_node_id` routine so param's id is added to `self.children`. let hir_id = self.lower_node_id(node_id); - hir::GenericParam { + Some(hir::GenericParam { hir_id, colon_span: Some(span), def_id, @@ -363,7 +525,7 @@ impl<'hir> LoweringContext<'_, 'hir> { pure_wrt_drop: p.pure_wrt_drop, source: hir::GenericParamSource::Generics, span, - } + }) })); // HACK: for now we generate predicates such that all lifetimes are early bound, @@ -413,77 +575,42 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn create_generics_args_from_params( + pub(super) fn create_generic_arg_path( &mut self, - params: &[hir::GenericParam<'hir>], - add_lifetimes: bool, - span: Span, - ) -> &'hir hir::GenericArgs<'hir> { - self.arena.alloc(hir::GenericArgs { - args: self.arena.alloc_from_iter(params.iter().filter_map(|p| { - // Skip self generic arg, we do not need to propagate it. - if p.name.ident().name == kw::SelfUpper || p.is_impl_trait() { - return None; - } + p: &hir::GenericParam<'hir>, + ) -> hir::QPath<'hir> { + let res = Res::Def( + match p.kind { + hir::GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, + hir::GenericParamKind::Type { .. } => DefKind::TyParam, + hir::GenericParamKind::Const { .. } => DefKind::ConstParam, + }, + p.def_id.to_def_id(), + ); - let create_path = |this: &mut Self| { - let res = Res::Def( - match p.kind { - hir::GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, - hir::GenericParamKind::Type { .. } => DefKind::TyParam, - hir::GenericParamKind::Const { .. } => DefKind::ConstParam, - }, - p.def_id.to_def_id(), - ); - - hir::QPath::Resolved( - None, - self.arena.alloc(hir::Path { - segments: this.arena.alloc_slice(&[hir::PathSegment { - args: None, - hir_id: this.next_id(), - ident: p.name.ident(), - infer_args: false, - res, - }]), - res, - span: p.span, - }), - ) - }; + self.create_resolved_path(res, p.name.ident(), p.span) + } - match p.kind { - hir::GenericParamKind::Lifetime { .. } => match add_lifetimes { - true => Some(hir::GenericArg::Lifetime(self.arena.alloc(hir::Lifetime { - hir_id: self.next_id(), - ident: p.name.ident(), - kind: hir::LifetimeKind::Param(p.def_id), - source: hir::LifetimeSource::Path { - angle_brackets: hir::AngleBrackets::Full, - }, - syntax: hir::LifetimeSyntax::ExplicitBound, - }))), - false => None, - }, - hir::GenericParamKind::Type { .. } => { - Some(hir::GenericArg::Type(self.arena.alloc(hir::Ty { - hir_id: self.next_id(), - span: p.span, - kind: hir::TyKind::Path(create_path(self)), - }))) - } - hir::GenericParamKind::Const { .. } => { - Some(hir::GenericArg::Const(self.arena.alloc(hir::ConstArg { - hir_id: self.next_id(), - kind: hir::ConstArgKind::Path(create_path(self)), - span: p.span, - }))) - } - } - })), - constraints: &[], - parenthesized: hir::GenericArgsParentheses::No, - span_ext: span, - }) + pub(super) fn create_resolved_path( + &mut self, + res: Res, + ident: Ident, + span: Span, + ) -> hir::QPath<'hir> { + hir::QPath::Resolved( + None, + self.arena.alloc(hir::Path { + segments: self.arena.alloc_slice(&[hir::PathSegment { + args: None, + hir_id: self.next_id(), + ident, + infer_args: false, + res, + delegation_child_segment: false, + }]), + res, + span, + }), + ) } } diff --git a/compiler/rustc_ast_lowering/src/diagnostics.rs b/compiler/rustc_ast_lowering/src/diagnostics.rs index 31f094209a946..238a16d9b859d 100644 --- a/compiler/rustc_ast_lowering/src/diagnostics.rs +++ b/compiler/rustc_ast_lowering/src/diagnostics.rs @@ -560,3 +560,12 @@ pub(crate) struct DelegationAttemptedBlockWithDefsDeletion { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag("wrong infer used: expected {$expected}, found: {$actual}")] +pub(crate) struct DelegationInfersMismatch { + #[primary_span] + pub span: Span, + pub expected: Symbol, + pub actual: Symbol, +} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b8d708d42ff10..35e16ce78a8a8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -31,6 +31,9 @@ //! in the HIR, especially for multiple identifiers. // tidy-alphabetical-start +#![feature(const_default)] +#![feature(const_trait_impl)] +#![feature(default_field_values)] #![feature(deref_patterns)] #![recursion_limit = "256"] // tidy-alphabetical-end @@ -1011,6 +1014,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res, args, infer_args: args.is_none(), + delegation_child_segment: false, }]), }) } @@ -2788,7 +2792,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::ConstBlock(anon_const) => { let def_id = self.local_def_id(anon_const.id); - assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id)); + assert_eq!(DefKind::InlineConst, self.tcx.def_kind(def_id)); self.lower_anon_const_to_const_arg(anon_const, span) } _ => overly_complex_const(self), diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 42c4af5add12d..18e037527836a 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -412,6 +412,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { Some(generic_args.into_generic_args(self)) }, + delegation_child_segment: false, } } diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index da9b4633fdae9..4fa0e2b1fc7fa 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 186a43ca1693b..06925994b052c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -412,11 +412,11 @@ impl<'a> AstValidator<'a> { }) .unzip(); - // A splatted argument at the "no splatted" marker index is not supported (this is an - // unlikely edge case). + // A splatted argument greater than or equal to the "no splatted" marker index is not + // supported. if let (Some(&splatted_arg_index), Some(&splatted_span)) = (splatted_arg_indexes.last(), splatted_spans.last()) - && splatted_arg_index == FnDecl::NO_SPLATTED_ARG_INDEX + && splatted_arg_index >= u16::from(FnDecl::NO_SPLATTED_ARG_INDEX) { self.dcx().emit_err(diagnostics::InvalidSplattedArg { splatted_arg_index, diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index 70da6ba0591ce..b33f41eb302c0 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_ast = { path = "../rustc_ast" } rustc_lexer = { path = "../rustc_lexer" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 78aedbd4f7f4b..176e91e544ec5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use ast::{ForLoopKind, MatchKind}; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; @@ -170,8 +170,8 @@ impl<'a> State<'a> { } let cb = self.cbox(0); for (pos, field) in fields.iter().with_position() { - let is_first = matches!(pos, Position::First | Position::Only); - let is_last = matches!(pos, Position::Last | Position::Only); + let is_first = pos.is_first(); + let is_last = pos.is_last(); self.maybe_print_comment(field.span.hi()); self.print_outer_attributes(&field.attrs); if is_first { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 86fb0e62bdaa4..8602a21a43487 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -1,5 +1,5 @@ use ast::StaticItem; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_ast::{self as ast, EiiImpl, ModKind, Safety, TraitAlias}; use rustc_span::Ident; @@ -344,9 +344,9 @@ impl<'a> State<'a> { let ast::TraitImplHeader { defaultness, safety, polarity, ref trait_ref } = *of_trait; self.print_defaultness(defaultness); + self.print_constness(*constness); self.print_safety(safety); impl_generics(self); - self.print_constness(*constness); if let ast::ImplPolarity::Negative(_) = polarity { self.word("!"); } @@ -923,7 +923,7 @@ impl<'a> State<'a> { self.zerobreak(); let ib = self.ibox(0); for (pos, use_tree) in items.iter().with_position() { - let is_last = matches!(pos, Position::Last | Position::Only); + let is_last = pos.is_last(); self.print_use_tree(&use_tree.0); if !is_last { self.word(","); diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 3e10e5e70b2c2..119b67b353cb8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -11,7 +11,7 @@ impl CombineAttributeParser for AllowInternalUnstableParser { type Item = (Symbol, Span); const CONVERT: ConvertFn = |items, span| AttributeKind::AllowInternalUnstable(items, span); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::MacroDef), Allow(Target::Fn), Warn(Target::Field), @@ -36,7 +36,7 @@ impl CombineAttributeParser for UnstableFeatureBoundParser { type Item = (Symbol, Span); const CONVERT: ConvertFn = |items, _| AttributeKind::UnstableFeatureBound(items); const STABILITY: AttributeStability = unstable!(staged_api); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Impl { of_trait: true }), Allow(Target::Trait), @@ -59,7 +59,7 @@ impl CombineAttributeParser for RustcAllowConstFnUnstableParser { type Item = Symbol; const CONVERT: ConvertFn = |items, first_span| AttributeKind::RustcAllowConstFnUnstable(items, first_span); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), diff --git a/compiler/rustc_attr_parsing/src/attributes/autodiff.rs b/compiler/rustc_attr_parsing/src/attributes/autodiff.rs index 890cb33d08720..78ed4ff46d997 100644 --- a/compiler/rustc_attr_parsing/src/attributes/autodiff.rs +++ b/compiler/rustc_attr_parsing/src/attributes/autodiff.rs @@ -19,7 +19,7 @@ pub(crate) struct RustcAutodiffParser; impl SingleAttributeParser for RustcAutodiffParser { const PATH: &[Symbol] = &[sym::rustc_autodiff]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), diff --git a/compiler/rustc_attr_parsing/src/attributes/body.rs b/compiler/rustc_attr_parsing/src/attributes/body.rs index b817d9346dafc..f3a4f04e02ae8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/body.rs +++ b/compiler/rustc_attr_parsing/src/attributes/body.rs @@ -8,7 +8,8 @@ pub(crate) struct CoroutineParser; impl NoArgsAttributeParser for CoroutineParser { const PATH: &[Symbol] = &[sym::coroutine]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Closure)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::Closure)]); const STABILITY: AttributeStability = unstable!(coroutines); const CREATE: fn(rustc_span::Span) -> AttributeKind = |_| AttributeKind::Coroutine; } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs index 2e7644fc70551..8a9102a78e25b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs @@ -4,7 +4,7 @@ use super::prelude::*; pub(crate) struct CfiEncodingParser; impl SingleAttributeParser for CfiEncodingParser { const PATH: &[Symbol] = &[sym::cfi_encoding]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Struct), Allow(Target::ForeignTy), Allow(Target::Enum), diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 155df43c813e8..f48fbe6324834 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -8,9 +8,9 @@ use rustc_span::edition::Edition::Edition2024; use super::prelude::*; use crate::attributes::AttributeSafety; use crate::session_diagnostics::{ - EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, - NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, - SanitizeInvalidStatic, + EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport, + NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral, + ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem, }; use crate::target_checking::Policy::AllowSilent; @@ -18,7 +18,7 @@ pub(crate) struct OptimizeParser; impl SingleAttributeParser for OptimizeParser { const PATH: &[Symbol] = &[sym::optimize]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Closure), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -51,7 +51,7 @@ pub(crate) struct ColdParser; impl NoArgsAttributeParser for ColdParser { const PATH: &[Symbol] = &[sym::cold]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), @@ -67,7 +67,7 @@ pub(crate) struct CoverageParser; impl SingleAttributeParser for CoverageParser { const PATH: &[Symbol] = &[sym::coverage]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Closure), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -114,7 +114,7 @@ impl SingleAttributeParser for ExportNameParser { note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them", unsafe_since: Some(Edition2024), }; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Static), Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), @@ -151,7 +151,7 @@ pub(crate) struct RustcObjcClassParser; impl SingleAttributeParser for RustcObjcClassParser { const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName"); const STABILITY: AttributeStability = unstable!(rustc_attrs); @@ -179,7 +179,7 @@ pub(crate) struct RustcObjcSelectorParser; impl SingleAttributeParser for RustcObjcSelectorParser { const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName"); const STABILITY: AttributeStability = unstable!(rustc_attrs); @@ -226,7 +226,7 @@ impl AttributeParser for NakedParser { note: "the `#[naked]` attribute adds the safety obligation that the function's body must respect the function’s calling convention, uphold its signature, and either return or diverge (i.e., not fall through past the end of the assembly code).", unsafe_since: None, }; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -334,7 +334,7 @@ pub(crate) struct TrackCallerParser; impl NoArgsAttributeParser for TrackCallerParser { const PATH: &[Symbol] = &[sym::track_caller]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -359,7 +359,7 @@ impl NoArgsAttributeParser for NoMangleParser { note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them", unsafe_since: Some(Edition2024), }; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Fn), Allow(Target::Static), Allow(Target::Method(MethodKind::Inherent)), @@ -462,7 +462,7 @@ impl AttributeParser for UsedParser { } }, )]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]); fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { @@ -524,16 +524,7 @@ impl CombineAttributeParser for TargetFeatureParser { was_forced: false, }; const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); - const STABILITY: AttributeStability = AttributeStability::Stable; - - fn extend( - cx: &mut AcceptContext<'_, '_>, - args: &ArgParser, - ) -> impl IntoIterator { - parse_tf_attribute(cx, args) - } - - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -544,6 +535,29 @@ impl CombineAttributeParser for TargetFeatureParser { Warn(Target::MacroDef), Warn(Target::MacroCall), ]); + const STABILITY: AttributeStability = AttributeStability::Stable; + + fn extend( + cx: &mut AcceptContext<'_, '_>, + args: &ArgParser, + ) -> impl IntoIterator { + parse_tf_attribute(cx, args) + } + + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + // `#[target_feature]` is incompatible with lang item functions, + // except on WASM where calling target-feature functions is safe (see #84988). + if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc { + // `#[panic_handler]` is checked first so it takes priority in the diagnostic. + let lang_kind = cx + .all_attrs + .iter() + .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s))); + if let Some(kind) = lang_kind { + cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span }); + } + } + } } pub(crate) struct ForceTargetFeatureParser; @@ -561,7 +575,7 @@ impl CombineAttributeParser for ForceTargetFeatureParser { was_forced: true, }; const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -581,7 +595,7 @@ pub(crate) struct InstrumentFnParser; impl SingleAttributeParser for InstrumentFnParser { const PATH: &[Symbol] = &[sym::instrument_fn]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -619,7 +633,7 @@ pub(crate) struct SanitizeParser; impl SingleAttributeParser for SanitizeParser { const PATH: &[Symbol] = &[sym::sanitize]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Closure), Allow(Target::Method(MethodKind::Inherent)), @@ -751,7 +765,7 @@ pub(crate) struct ThreadLocalParser; impl NoArgsAttributeParser for ThreadLocalParser { const PATH: &[Symbol] = &[sym::thread_local]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]); const STABILITY: AttributeStability = unstable!(thread_local); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal; @@ -761,7 +775,7 @@ pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser; impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser { const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis; } @@ -770,7 +784,7 @@ pub(crate) struct RustcEiiForeignItemParser; impl NoArgsAttributeParser for RustcEiiForeignItemParser { const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]); const STABILITY: AttributeStability = unstable!(eii_internals); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem; @@ -780,8 +794,9 @@ pub(crate) struct PatchableFunctionEntryParser; impl SingleAttributeParser for PatchableFunctionEntryParser { const PATH: &[Symbol] = &[sym::patchable_function_entry]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); - const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const TEMPLATE: AttributeTemplate = + template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]); const STABILITY: AttributeStability = unstable!(patchable_function_entry); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { @@ -789,74 +804,85 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { let mut prefix = None; let mut entry = None; + let mut section = None; if meta_item_list.len() == 0 { cx.adcx().expected_at_least_one_argument(meta_item_list.span); return None; } - let mut errored = false; - for item in meta_item_list.mixed() { let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { - continue; + return None; }; let attrib_to_write = match ident.name { sym::prefix_nops => { // Duplicate prefixes are not allowed if prefix.is_some() { - errored = true; cx.adcx().duplicate_key(ident.span, sym::prefix_nops); - continue; + return None; } &mut prefix } sym::entry_nops => { // Duplicate entries are not allowed if entry.is_some() { - errored = true; cx.adcx().duplicate_key(ident.span, sym::entry_nops); - continue; + return None; } &mut entry } + sym::section => { + // Duplicate entries are not allowed + if section.is_some() { + cx.adcx().duplicate_key(ident.span, sym::section); + return None; + } + // Only a string type value is allowed. + let Some(value_str) = value.value_as_str() else { + cx.adcx().expect_string_literal(value); + return None; + }; + // The section name does not allow null characters. + if value_str.as_str().contains('\0') { + cx.emit_err(NullOnSection { span: value.value_span }); + } + // The section name is not allowed to be empty, LLVM does + // not allow them. + if value_str.is_empty() { + cx.emit_err(EmptySection { span: value.value_span }); + } + section = Some(value_str); + // Integer parsing is not needed, process next item. + continue; + } _ => { - errored = true; cx.adcx().expected_specific_argument( ident.span, &[sym::prefix_nops, sym::entry_nops], ); - continue; + return None; } }; let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else { - errored = true; cx.adcx().expected_integer_literal(value.value_span); - continue; + return None; }; let Ok(val) = val.get().try_into() else { - errored = true; cx.adcx().expected_integer_literal_in_range( value.value_span, u8::MIN as isize, u8::MAX as isize, ); - continue; + return None; }; *attrib_to_write = Some(val); } - if errored { - None - } else { - Some(AttributeKind::PatchableFunctionEntry { - prefix: prefix.unwrap_or(0), - entry: entry.unwrap_or(0), - }) - } + Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section }) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 3b2115eeb01e2..091566012d158 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -32,7 +32,7 @@ impl AttributeParser for ConfusablesParser { this.first_span.get_or_insert(cx.attr_span); }, )]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]); fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index eb78bc0775f6b..03012941cb1c9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -2,7 +2,7 @@ use rustc_feature::AttributeStability; use rustc_hir::attrs::{CrateType, WindowsSubsystemKind}; use rustc_session::lint::builtin::UNKNOWN_CRATE_TYPES; use rustc_span::Symbol; -use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::edit_distance::find_best_match_for_name_with_substrings; use super::prelude::*; use crate::diagnostics::{UnknownCrateTypes, UnknownCrateTypesSuggestion}; @@ -13,7 +13,7 @@ impl SingleAttributeParser for CrateNameParser { const PATH: &[Symbol] = &[sym::crate_name]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = AttributeStability::Stable; @@ -32,7 +32,7 @@ impl CombineAttributeParser for CrateTypeParser { const PATH: &[Symbol] = &[sym::crate_type]; type Item = CrateType; const CONVERT: ConvertFn = |items, _| AttributeKind::CrateType(items); - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "crate type", "https://doc.rust-lang.org/reference/linkage.html"); @@ -49,10 +49,10 @@ impl CombineAttributeParser for CrateTypeParser { let Ok(crate_type) = crate_type.try_into() else { // We don't error on invalid `#![crate_type]` when not applied to a crate if cx.shared.target == Target::Crate { - let candidate = find_best_match_for_name( + let candidate = find_best_match_for_name_with_substrings( &CrateType::all_stable().iter().map(|(name, _)| *name).collect::>(), crate_type, - None, + Some(5), ); let span = n.value_span; cx.emit_lint( @@ -76,7 +76,7 @@ impl SingleAttributeParser for RecursionLimitParser { const PATH: &[Symbol] = &[sym::recursion_limit]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"); - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = AttributeStability::Stable; @@ -92,7 +92,7 @@ pub(crate) struct MoveSizeLimitParser; impl SingleAttributeParser for MoveSizeLimitParser { const PATH: &[Symbol] = &[sym::move_size_limit]; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(large_assignments); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { @@ -108,7 +108,7 @@ impl SingleAttributeParser for TypeLengthLimitParser { const PATH: &[Symbol] = &[sym::type_length_limit]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = AttributeStability::Stable; @@ -124,7 +124,7 @@ pub(crate) struct PatternComplexityLimitParser; impl SingleAttributeParser for PatternComplexityLimitParser { const PATH: &[Symbol] = &[sym::pattern_complexity_limit]; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!( rustc_attrs, "the `#[pattern_complexity_limit]` attribute is used for rustc unit tests" @@ -141,7 +141,7 @@ pub(crate) struct NoCoreParser; impl NoArgsAttributeParser for NoCoreParser { const PATH: &[Symbol] = &[sym::no_core]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(no_core); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoCore; } @@ -151,7 +151,7 @@ pub(crate) struct NoStdParser; impl NoArgsAttributeParser for NoStdParser { const PATH: &[Symbol] = &[sym::no_std]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoStd; @@ -162,7 +162,7 @@ pub(crate) struct NoMainParser; impl NoArgsAttributeParser for NoMainParser { const PATH: &[Symbol] = &[sym::no_main]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoMain; @@ -172,7 +172,7 @@ pub(crate) struct RustcCoherenceIsCoreParser; impl NoArgsAttributeParser for RustcCoherenceIsCoreParser { const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCoherenceIsCore; } @@ -182,7 +182,7 @@ pub(crate) struct WindowsSubsystemParser; impl SingleAttributeParser for WindowsSubsystemParser { const PATH: &[Symbol] = &[sym::windows_subsystem]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute"); const STABILITY: AttributeStability = AttributeStability::Stable; @@ -210,7 +210,7 @@ pub(crate) struct PanicRuntimeParser; impl NoArgsAttributeParser for PanicRuntimeParser { const PATH: &[Symbol] = &[sym::panic_runtime]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(panic_runtime); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PanicRuntime; } @@ -219,7 +219,7 @@ pub(crate) struct NeedsPanicRuntimeParser; impl NoArgsAttributeParser for NeedsPanicRuntimeParser { const PATH: &[Symbol] = &[sym::needs_panic_runtime]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(needs_panic_runtime); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsPanicRuntime; } @@ -228,7 +228,7 @@ pub(crate) struct ProfilerRuntimeParser; impl NoArgsAttributeParser for ProfilerRuntimeParser { const PATH: &[Symbol] = &[sym::profiler_runtime]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(profiler_runtime); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ProfilerRuntime; } @@ -238,7 +238,7 @@ pub(crate) struct NoBuiltinsParser; impl NoArgsAttributeParser for NoBuiltinsParser { const PATH: &[Symbol] = &[sym::no_builtins]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins; @@ -248,7 +248,7 @@ pub(crate) struct RustcPreserveUbChecksParser; impl NoArgsAttributeParser for RustcPreserveUbChecksParser { const PATH: &[Symbol] = &[sym::rustc_preserve_ub_checks]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks; } @@ -257,7 +257,7 @@ pub(crate) struct RustcNoImplicitBoundsParser; impl NoArgsAttributeParser for RustcNoImplicitBoundsParser { const PATH: &[Symbol] = &[sym::rustc_no_implicit_bounds]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds; } @@ -266,7 +266,7 @@ pub(crate) struct DefaultLibAllocatorParser; impl NoArgsAttributeParser for DefaultLibAllocatorParser { const PATH: &[Symbol] = &[sym::default_lib_allocator]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(allocator_internals); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator; } @@ -277,7 +277,7 @@ impl CombineAttributeParser for FeatureParser { const PATH: &[Symbol] = &[sym::feature]; type Item = Ident; const CONVERT: ConvertFn = AttributeKind::Feature; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(List: &["feature1, feature2, ..."]); const STABILITY: AttributeStability = AttributeStability::Stable; @@ -323,7 +323,7 @@ impl CombineAttributeParser for RegisterToolParser { const PATH: &[Symbol] = &[sym::register_tool]; type Item = Ident; const CONVERT: ConvertFn = |tools, _span| AttributeKind::RegisterTool(tools); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(List: &["tool1, tool2, ..."]); const STABILITY: AttributeStability = unstable!(register_tool); diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs index 8d2cff35905ed..b514235a48eca 100644 --- a/compiler/rustc_attr_parsing/src/attributes/debugger.rs +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -7,7 +7,7 @@ pub(crate) struct DebuggerVisualizerParser; impl CombineAttributeParser for DebuggerVisualizerParser { const PATH: &[Symbol] = &[sym::debugger_visualizer]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!( List: &[r#"natvis_file = "...", gdb_script_file = "...""#], diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index ee3734bf19541..d401aafed7bec 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -32,7 +32,7 @@ fn get( pub(crate) struct DeprecatedParser; impl SingleAttributeParser for DeprecatedParser { const PATH: &[Symbol] = &[sym::deprecated]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Fn), Allow(Target::Mod), Allow(Target::Struct), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs index 85708273a409b..250326db42bb7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs @@ -16,7 +16,7 @@ impl SingleAttributeParser for DoNotRecommendParser { const PATH: &[Symbol] = &[sym::diagnostic, sym::do_not_recommend]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; // "Allowed" on any target, noop on all but trait impls - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Impl { of_trait: true })]); const TEMPLATE: AttributeTemplate = template!(Word /*doesn't matter */); const STABILITY: AttributeStability = AttributeStability::Stable; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs index dc8260ae944ef..a7b71b9080dca 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs @@ -37,7 +37,7 @@ impl AttributeParser for OnConstParser { // "Allowed" on all targets; noop on anything but non-const trait impls; // this linted on in parser. - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ // FIXME(mejrs) no constness field on `Target`, // so non-constness is still checked in check_attr.rs Allow(Target::Impl { of_trait: true }), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs index ec77ede4127f5..1c8b3418fa746 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs @@ -43,7 +43,7 @@ impl AttributeParser for OnMoveParser { }, )]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Enum), Allow(Target::Struct), Allow(Target::Union), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs index d5636b23a09b1..89cae4b7c55f1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs @@ -42,7 +42,7 @@ impl AttributeParser for OnTypeErrorParser { }, )]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Enum), Allow(Target::Struct), Allow(Target::Union), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs index 86a96f7093587..057d243c6d123 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs @@ -45,7 +45,7 @@ impl AttributeParser for OnUnimplementedParser { }, ), ]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Trait)]); fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs index fc05ae9150d15..bfa26d993b17e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs @@ -40,7 +40,7 @@ impl AttributeParser for OnUnknownParser { }, )]; // "Allowed" for all targets, but noop for all but use statements. - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Use), Allow(Target::Mod), Allow(Target::Crate), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs index 44975cd49c93f..18f980bfebbe8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs @@ -33,7 +33,7 @@ impl AttributeParser for OnUnmatchedArgsParser { }, )]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::MacroDef)]); fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index e91ab584bcf47..23eef6334ccee 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -1,25 +1,29 @@ use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; use rustc_errors::{Applicability, msg}; use rustc_feature::AttributeStability; use rustc_hir::Target; use rustc_hir::attrs::{ - AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow, + AttributeKind, CfgEntry, CfgHideShow, DocAttribute, DocCfgHideShow, DocCfgHideShowValue, + DocInline, HideOrShow, }; use rustc_session::errors::feature_err; use rustc_span::{Span, Symbol, edition, sym}; -use thin_vec::ThinVec; use super::prelude::{ALL_TARGETS, AllowedTargets}; use super::{AcceptMapping, AttributeParser, template}; use crate::context::{AcceptContext, FinalizeContext}; use crate::diagnostics::{ AttrCrateLevelOnly, DocAliasDuplicated, DocAutoCfgExpectsHideOrShow, - DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowUnexpectedItem, DocAutoCfgWrongLiteral, - DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny, DocUnknownInclude, - DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, - IllFormedAttributeInput, MalformedDoc, + DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues, + DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues, + DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocTestLiteral, DocTestTakesList, + DocTestUnknown, DocUnknownAny, DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins, + DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, IllFormedAttributeInput, MalformedDoc, +}; +use crate::parser::{ + ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser, }; -use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser}; use crate::session_diagnostics::{ DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel, DocAttributeNotAttribute, DocKeywordNotKeyword, UnusedDuplicate, @@ -304,6 +308,81 @@ impl DocParser { } } + // Parses the `doc(auto_cfg(hide/show(..., values())))` attribute. + fn parse_auto_cfg_values( + &self, + cx: &mut AcceptContext<'_, '_>, + list: &MetaItemListParser, + values: &mut Option, + ) { + let mut cfg_values = DocCfgHideShow::new(); + + let mut values_set = FxHashSet::default(); + for item in list.mixed() { + match item { + // If it's a string literal, all good. + MetaItemOrLitParser::Lit(MetaItemLit { + kind: LitKind::Str(symbol, _), + span, + .. + }) => match &mut cfg_values { + DocCfgHideShow::Any(any_span) => { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowValuesMix { value_span: *span }, + *any_span, + ); + } + DocCfgHideShow::List(symbols) => { + if values_set.insert(symbol) { + symbols.push(DocCfgHideShowValue::new(*symbol, *span)); + } + } + }, + // If it's any other kind of literal, then it's wrong and we emit a lint. + MetaItemOrLitParser::Lit(lit) => cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItem { attr_name: lit.symbol }, + lit.span, + ), + // If it's a list, then only `any()` and `none()` are allowed and they must not + // contain any item. + MetaItemOrLitParser::MetaItemParser(sub_item) => { + if let Some(ident) = sub_item.ident() + && [sym::any, sym::none].contains(&ident.name) + && let ArgParser::List(list) = sub_item.args() + && list.mixed().count() == 0 + { + if ident.name == sym::any { + if let DocCfgHideShow::List(values) = &cfg_values + && let Some(value) = values.first() + { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowValuesMix { value_span: value.span }, + sub_item.span(), + ); + } else { + cfg_values.merge_with(&DocCfgHideShow::Any(sub_item.span())); + } + } else { + cfg_values.push_none(sub_item.span()); + } + } else { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItem { + attr_name: sub_item.ident().unwrap().name, + }, + sub_item.span(), + ); + } + } + } + } + *values = Some(cfg_values); + } + fn parse_auto_cfg( &mut self, cx: &mut AcceptContext<'_, '_>, @@ -315,7 +394,7 @@ impl DocParser { self.attribute.auto_cfg_change.push((true, path.span())); } ArgParser::List(list) => { - for meta in list.mixed() { + 'main: for meta in list.mixed() { let MetaItemOrLitParser::MetaItemParser(item) = meta else { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, @@ -324,6 +403,8 @@ impl DocParser { ); continue; }; + // Only `hide` and `show` are allowed in `auto_cfg` if it's a list, and both + // must be a list. let (kind, attr_name) = match item.path().word_sym() { Some(sym::hide) => (HideOrShow::Hide, sym::hide), Some(sym::show) => (HideOrShow::Show, sym::show), @@ -345,8 +426,10 @@ impl DocParser { continue; }; - let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() }; + let mut cfg_hide_show = CfgHideShow { kind, values: FxIndexMap::default() }; + let mut cfg_names = FxHashSet::default(); + let mut values = None; for item in list.mixed() { let MetaItemOrLitParser::MetaItemParser(sub_item) = item else { cx.emit_lint( @@ -354,47 +437,60 @@ impl DocParser { DocAutoCfgHideShowUnexpectedItem { attr_name }, item.span(), ); - continue; + continue 'main; }; match sub_item.args() { - a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { + ArgParser::NoArgs if values.is_none() => { let Some(name) = sub_item.path().word_sym() else { - // FIXME: remove this method once merged and uncomment the line - // below instead. - // cx.expected_identifier(sub_item.path().span()); + cx.adcx().expected_identifier(sub_item.path().span()); + continue 'main; + }; + cfg_names.insert(name); + } + // The only accepted list is `values()`. + ArgParser::List(list) if values.is_none() => { + let Some(sym::values) = sub_item.path().word_sym() else { + cx.adcx().expected_identifier(sub_item.path().span()); + continue 'main; + }; + if cfg_names.is_empty() { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - MalformedDoc, - sub_item.path().span(), - ); - continue; - }; - if let Ok(CfgEntry::NameValue { name, value, .. }) = - super::cfg::parse_name_value( - name, - sub_item.path().span(), - a.as_name_value(), + DocAutoCfgHideShowNoIdentBeforeValues, sub_item.span(), - cx, - ) - { - cfg_hide_show.values.push(CfgInfo { - name, - name_span: sub_item.path().span(), - // If `value` is `Some`, `a.name_value()` will always return - // `Some` as well. - value: value - .map(|v| (v, a.as_name_value().unwrap().value_span)), - }) + ); + continue 'main; } + self.parse_auto_cfg_values(cx, list, &mut values); } - _ => { + // No `name = value` is allowed. + ArgParser::NameValue(_) => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, DocAutoCfgHideShowUnexpectedItem { attr_name }, sub_item.span(), ); - continue; + } + // If `values()` was already used, no item should come after it. + _ => { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItemAfterValues, + sub_item.span(), + ); + } + } + } + + let values = values.unwrap_or(DocCfgHideShow::new_with_only_key(item.span())); + #[allow(rustc::potential_query_instability)] + for cfg_name in &cfg_names { + match cfg_hide_show.values.entry(*cfg_name) { + IndexEntry::Vacant(v) => { + v.insert(values.clone()); + } + IndexEntry::Occupied(mut o) => { + o.get_mut().merge_with(&values); } } } @@ -706,8 +802,8 @@ impl AttributeParser for DocParser { }, )]; // FIXME: Currently emitted from 2 different places, generating duplicated warnings. - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); - // const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(ALL_TARGETS); + // const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ // Allow(Target::ExternCrate), // Allow(Target::Use), // Allow(Target::Static), diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs index d436c7f232ec7..83984d48919b7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/dummy.rs +++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs @@ -12,7 +12,7 @@ pub(crate) struct RustcDummyParser; impl SingleAttributeParser for RustcDummyParser { const PATH: &[Symbol] = &[sym::rustc_dummy]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::ManuallyChecked; + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::ManuallyChecked; const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really const STABILITY: AttributeStability = unstable!(rustc_attrs, "the `#[rustc_dummy]` attribute is used for rustc unit tests"); diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index 84b880202a9e6..80328e1c8ef96 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -13,7 +13,7 @@ pub(crate) struct InlineParser; impl SingleAttributeParser for InlineParser { const PATH: &[Symbol] = &[sym::inline]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -66,7 +66,7 @@ pub(crate) struct RustcForceInlineParser; impl SingleAttributeParser for RustcForceInlineParser { const PATH: &[Symbol] = &[sym::rustc_force_inline]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), ]); diff --git a/compiler/rustc_attr_parsing/src/attributes/instruction_set.rs b/compiler/rustc_attr_parsing/src/attributes/instruction_set.rs index 612d5d33d94e0..3a4bb926f759c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/instruction_set.rs +++ b/compiler/rustc_attr_parsing/src/attributes/instruction_set.rs @@ -8,7 +8,7 @@ pub(crate) struct InstructionSetParser; impl SingleAttributeParser for InstructionSetParser { const PATH: &[Symbol] = &[sym::instruction_set]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Fn), Allow(Target::Closure), Allow(Target::Method(MethodKind::Inherent)), diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index f260863840080..2c640ab5385b9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -14,11 +14,11 @@ use super::util::parse_single_integer; use crate::attributes::AttributeSafety; use crate::attributes::cfg::parse_cfg_entry; use crate::session_diagnostics::{ - AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, - ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, - InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange, - LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, RawDylibOnlyWindows, - WholeArchiveNeedsStatic, + AsNeededCompatibility, BothFfiConstAndPure, BundleNeedsStatic, EmptyLinkName, + ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, + InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, + LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, + RawDylibOnlyWindows, WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -26,7 +26,7 @@ pub(crate) struct LinkNameParser; impl SingleAttributeParser for LinkNameParser { const PATH: &[Symbol] = &[sym::link_name]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::ForeignFn), Allow(Target::ForeignStatic), ]); @@ -70,7 +70,7 @@ impl CombineAttributeParser for LinkParser { r#"name = "...", import_name_type = "decorated|noprefix|undecorated""#, r#"name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated""#, ], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute"); - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::ForeignMod)]); const STABILITY: AttributeStability = AttributeStability::Stable; @@ -494,7 +494,7 @@ impl SingleAttributeParser for LinkSectionParser { unsafe_since: Some(Edition2024), }; const STABILITY: AttributeStability = AttributeStability::Stable; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Static), Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), @@ -535,7 +535,7 @@ impl SingleAttributeParser for LinkSectionParser { pub(crate) struct ExportStableParser; impl NoArgsAttributeParser for ExportStableParser { const PATH: &[Symbol] = &[sym::export_stable]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Struct), @@ -558,7 +558,8 @@ impl NoArgsAttributeParser for FfiConstParser { note: "`#[ffi_const]` functions shall have no effects except for its return value, which can only depend on the values of the function parameters, and is not affected by changes to the observable state of the program.", unsafe_since: None, }; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); const STABILITY: AttributeStability = unstable!(ffi_const); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::FfiConst; } @@ -570,15 +571,23 @@ impl NoArgsAttributeParser for FfiPureParser { note: "`#[ffi_pure]` functions shall have no effects except for its return value, which shall not change across two consecutive function calls with the same parameters.", unsafe_since: None, }; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); const STABILITY: AttributeStability = unstable!(ffi_pure); const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; + + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + // `#[ffi_const]` functions cannot be `#[ffi_pure]`. + if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) { + cx.emit_err(BothFfiConstAndPure { attr_span }); + } + } } pub(crate) struct RustcStdInternalSymbolParser; impl NoArgsAttributeParser for RustcStdInternalSymbolParser { const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::ForeignFn), Allow(Target::Static), @@ -592,7 +601,7 @@ pub(crate) struct LinkOrdinalParser; impl SingleAttributeParser for LinkOrdinalParser { const PATH: &[Symbol] = &[sym::link_ordinal]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::ForeignFn), Allow(Target::ForeignStatic), Warn(Target::MacroCall), @@ -632,7 +641,7 @@ pub(crate) struct LinkageParser; impl SingleAttributeParser for LinkageParser { const PATH: &[Symbol] = &[sym::linkage]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -708,7 +717,7 @@ pub(crate) struct NeedsAllocatorParser; impl NoArgsAttributeParser for NeedsAllocatorParser { const PATH: &[Symbol] = &[sym::needs_allocator]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(allocator_internals); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsAllocator; } @@ -717,7 +726,7 @@ pub(crate) struct CompilerBuiltinsParser; impl NoArgsAttributeParser for CompilerBuiltinsParser { const PATH: &[Symbol] = &[sym::compiler_builtins]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(compiler_builtins); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CompilerBuiltins; } diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 75a07e868129e..38afeea07794a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -5,7 +5,7 @@ use super::prelude::*; pub(crate) struct RustcAsPtrParser; impl NoArgsAttributeParser for RustcAsPtrParser { const PATH: &[Symbol] = &[sym::rustc_as_ptr]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -19,7 +19,7 @@ impl NoArgsAttributeParser for RustcAsPtrParser { pub(crate) struct RustcPubTransparentParser; impl NoArgsAttributeParser for RustcPubTransparentParser { const PATH: &[Symbol] = &[sym::rustc_pub_transparent]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Struct), Allow(Target::Enum), Allow(Target::Union), @@ -31,7 +31,7 @@ impl NoArgsAttributeParser for RustcPubTransparentParser { pub(crate) struct RustcPassByValueParser; impl NoArgsAttributeParser for RustcPassByValueParser { const PATH: &[Symbol] = &[sym::rustc_pass_by_value]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Struct), Allow(Target::Enum), Allow(Target::TyAlias), @@ -43,7 +43,7 @@ impl NoArgsAttributeParser for RustcPassByValueParser { pub(crate) struct RustcShouldNotBeCalledOnConstItemsParser; impl NoArgsAttributeParser for RustcShouldNotBeCalledOnConstItemsParser { const PATH: &[Symbol] = &[sym::rustc_should_not_be_called_on_const_items]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::TraitImpl)), ]); @@ -55,7 +55,7 @@ pub(crate) struct AutomaticallyDerivedParser; impl NoArgsAttributeParser for AutomaticallyDerivedParser { const PATH: &[Symbol] = &[sym::automatically_derived]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Impl { of_trait: true }), Error(Target::Crate), Error(Target::WherePredicate), diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs index 06c3cd286a06e..e46d2edaa49ba 100644 --- a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs +++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs @@ -5,7 +5,7 @@ use super::prelude::*; pub(crate) struct LoopMatchParser; impl NoArgsAttributeParser for LoopMatchParser { const PATH: &[Symbol] = &[sym::loop_match]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Loop)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Loop)]); const STABILITY: AttributeStability = unstable!(loop_match); const CREATE: fn(Span) -> AttributeKind = AttributeKind::LoopMatch; } @@ -13,7 +13,7 @@ impl NoArgsAttributeParser for LoopMatchParser { pub(crate) struct ConstContinueParser; impl NoArgsAttributeParser for ConstContinueParser { const PATH: &[Symbol] = &[sym::const_continue]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Break)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Break)]); const STABILITY: AttributeStability = unstable!(loop_match); const CREATE: fn(Span) -> AttributeKind = AttributeKind::ConstContinue; } diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 8ad202bad3d84..f49a7e674d560 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -8,7 +8,7 @@ pub(crate) struct MacroEscapeParser; impl NoArgsAttributeParser for MacroEscapeParser { const PATH: &[Symbol] = &[sym::macro_escape]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = MACRO_USE_ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = MACRO_USE_ALLOWED_TARGETS; const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::MacroEscape; } @@ -32,7 +32,7 @@ const MACRO_USE_TEMPLATE: AttributeTemplate = template!( Word, List: &["name1, name2, ..."], "https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute" ); -const MACRO_USE_ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ +const MACRO_USE_ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Mod), Allow(Target::ExternCrate), Error(Target::WherePredicate), @@ -106,7 +106,7 @@ impl AttributeParser for MacroUseParser { } }, )]; - const ALLOWED_TARGETS: AllowedTargets = MACRO_USE_ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = MACRO_USE_ALLOWED_TARGETS; fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state }) @@ -118,7 +118,7 @@ pub(crate) struct AllowInternalUnsafeParser; impl NoArgsAttributeParser for AllowInternalUnsafeParser { const PATH: &[Symbol] = &[sym::allow_internal_unsafe]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::MacroDef), Warn(Target::Field), @@ -134,7 +134,7 @@ impl SingleAttributeParser for MacroExportParser { const PATH: &[Symbol] = &[sym::macro_export]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; const TEMPLATE: AttributeTemplate = template!(Word, List: &["local_inner_macros"]); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::MacroDef), Error(Target::WherePredicate), Error(Target::Crate), @@ -174,7 +174,8 @@ impl SingleAttributeParser for CollapseDebugInfoParser { List: &["no", "external", "yes"], "https://doc.rust-lang.org/reference/attributes/debugger.html#the-collapse_debuginfo-attribute" ); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); const STABILITY: AttributeStability = AttributeStability::Stable; fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { @@ -204,7 +205,7 @@ pub(crate) struct RustcProcMacroDeclsParser; impl NoArgsAttributeParser for RustcProcMacroDeclsParser { const PATH: &[Symbol] = &[sym::rustc_proc_macro_decls]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Static)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Static)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcProcMacroDecls; } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 5c64e9f2eaed6..d3ddd79a97619 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -105,7 +105,7 @@ pub(crate) trait AttributeParser: Default + 'static { /// /// If an attribute has this symbol, the `accept` function will be called on it. const ATTRIBUTES: AcceptMapping; - const ALLOWED_TARGETS: AllowedTargets; + const ALLOWED_TARGETS: AllowedTargets<'_>; const SAFETY: AttributeSafety = AttributeSafety::Normal; /// The parser has gotten a chance to accept the attributes on an item, @@ -140,13 +140,21 @@ pub(crate) trait SingleAttributeParser: 'static { const SAFETY: AttributeSafety = AttributeSafety::Normal; const STABILITY: AttributeStability; - const ALLOWED_TARGETS: AllowedTargets; + const ALLOWED_TARGETS: AllowedTargets<'_>; /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Unlike [`convert`](Self::convert), this + /// has access to the sibling attributes via [`FinalizeContext::all_attrs`], so it can + /// reject incompatible combinations. `attr_span` is the span of this attribute. + /// + /// Defaults to a no-op. + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`SingleAttributeParser`]. @@ -174,11 +182,13 @@ impl AttributeParser for Single { } }, )]; - const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS; const SAFETY: AttributeSafety = T::SAFETY; - fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { - Some(self.1?.0) + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { + let (kind, span) = self.1?; + T::finalize_check(cx, span); + Some(kind) } } @@ -252,12 +262,20 @@ pub enum AttributeSafety { pub(crate) trait NoArgsAttributeParser: 'static { const PATH: &[Symbol]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets; + const ALLOWED_TARGETS: AllowedTargets<'_>; const SAFETY: AttributeSafety = AttributeSafety::Normal; const STABILITY: AttributeStability; /// Create the [`AttributeKind`] given attribute's [`Span`]. const CREATE: fn(Span) -> AttributeKind; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Has access to the sibling attributes via + /// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations. + /// `attr_span` is the span of this attribute. + /// + /// Defaults to a no-op. + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } pub(crate) struct WithoutArgs(PhantomData); @@ -273,13 +291,17 @@ impl SingleAttributeParser for WithoutArgs { const ON_DUPLICATE: OnDuplicate = T::ON_DUPLICATE; const SAFETY: AttributeSafety = T::SAFETY; const STABILITY: AttributeStability = T::STABILITY; - const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS; const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { let _ = cx.expect_no_args(args); Some(T::CREATE(cx.attr_span)) } + + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + T::finalize_check(cx, attr_span) + } } type ConvertFn = fn(ThinVec, Span) -> AttributeKind; @@ -303,7 +325,7 @@ pub(crate) trait CombineAttributeParser: 'static { const SAFETY: AttributeSafety = AttributeSafety::Normal; const STABILITY: AttributeStability; - const ALLOWED_TARGETS: AllowedTargets; + const ALLOWED_TARGETS: AllowedTargets<'_>; /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; @@ -313,6 +335,14 @@ pub(crate) trait CombineAttributeParser: 'static { cx: &mut AcceptContext<'_, '_>, args: &ArgParser, ) -> impl IntoIterator; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Has access to the sibling attributes via + /// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations. + /// `attr_span` is the span of the first attribute that was encountered. + /// + /// Defaults to a no-op. + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`CombineAttributeParser`]. @@ -342,11 +372,12 @@ impl AttributeParser for Combine { group.first_span.get_or_insert(cx.attr_span); group.items.extend(T::extend(cx, args)) })]; - const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS; const SAFETY: AttributeSafety = T::SAFETY; - fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { if let Some(first_span) = self.first_span { + T::finalize_check(cx, first_span); Some(T::CONVERT(self.items, first_span)) } else { None diff --git a/compiler/rustc_attr_parsing/src/attributes/must_not_suspend.rs b/compiler/rustc_attr_parsing/src/attributes/must_not_suspend.rs index 25fdd5fb3e086..c7c302dd9ee2d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_not_suspend.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_not_suspend.rs @@ -6,7 +6,7 @@ pub(crate) struct MustNotSuspendParser; impl SingleAttributeParser for MustNotSuspendParser { const PATH: &[rustc_span::Symbol] = &[sym::must_not_suspend]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Struct), Allow(Target::Enum), Allow(Target::Union), diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index ae41efd769ed0..62e75a2e7afd9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -7,7 +7,7 @@ pub(crate) struct MustUseParser; impl SingleAttributeParser for MustUseParser { const PATH: &[Symbol] = &[sym::must_use]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ Allow(Target::Fn), Allow(Target::Enum), Allow(Target::Struct), diff --git a/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs b/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs index ce172bc41beee..34ceca9092036 100644 --- a/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs @@ -7,7 +7,7 @@ pub(crate) struct NoImplicitPreludeParser; impl NoArgsAttributeParser for NoImplicitPreludeParser { const PATH: &[rustc_span::Symbol] = &[sym::no_implicit_prelude]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Mod), Allow(Target::Crate)]); const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoImplicitPrelude; diff --git a/compiler/rustc_attr_parsing/src/attributes/no_link.rs b/compiler/rustc_attr_parsing/src/attributes/no_link.rs index d067db5df81e6..772ebb6b34f69 100644 --- a/compiler/rustc_attr_parsing/src/attributes/no_link.rs +++ b/compiler/rustc_attr_parsing/src/attributes/no_link.rs @@ -6,7 +6,7 @@ pub(crate) struct NoLinkParser; impl NoArgsAttributeParser for NoLinkParser { const PATH: &[Symbol] = &[sym::no_link]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::ExternCrate), Warn(Target::Field), Warn(Target::Arm), diff --git a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs index c4cc47e593a6a..cbbaad445f70f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs +++ b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs @@ -12,7 +12,7 @@ pub(crate) struct NonExhaustiveParser; impl NoArgsAttributeParser for NonExhaustiveParser { const PATH: &[Symbol] = &[sym::non_exhaustive]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Enum), Allow(Target::Struct), Allow(Target::Variant), diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs index 57114d850dba8..ad4641c4be7f2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/path.rs +++ b/compiler/rustc_attr_parsing/src/attributes/path.rs @@ -7,7 +7,7 @@ pub(crate) struct PathParser; impl SingleAttributeParser for PathParser { const PATH: &[Symbol] = &[sym::path]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Mod), Error(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!( NameValueStr: "file", diff --git a/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs b/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs index ba84b6f8e98c4..59c35690d3c93 100644 --- a/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs +++ b/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs @@ -12,7 +12,7 @@ pub(crate) struct PinV2Parser; impl NoArgsAttributeParser for PinV2Parser { const PATH: &[Symbol] = &[sym::pin_v2]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Enum), Allow(Target::Struct), Allow(Target::Union), diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index 887eb668d65ab..8b2720d2a9ca5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -3,13 +3,13 @@ use rustc_session::lint::builtin::AMBIGUOUS_DERIVE_HELPERS; use super::prelude::*; -const PROC_MACRO_ALLOWED_TARGETS: AllowedTargets = +const PROC_MACRO_ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn), Warn(Target::Crate), Warn(Target::MacroCall)]); pub(crate) struct ProcMacroParser; impl NoArgsAttributeParser for ProcMacroParser { const PATH: &[Symbol] = &[sym::proc_macro]; - const ALLOWED_TARGETS: AllowedTargets = PROC_MACRO_ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = PROC_MACRO_ALLOWED_TARGETS; const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ProcMacro; } @@ -17,7 +17,7 @@ impl NoArgsAttributeParser for ProcMacroParser { pub(crate) struct ProcMacroAttributeParser; impl NoArgsAttributeParser for ProcMacroAttributeParser { const PATH: &[Symbol] = &[sym::proc_macro_attribute]; - const ALLOWED_TARGETS: AllowedTargets = PROC_MACRO_ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = PROC_MACRO_ALLOWED_TARGETS; const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ProcMacroAttribute; } @@ -25,7 +25,7 @@ impl NoArgsAttributeParser for ProcMacroAttributeParser { pub(crate) struct ProcMacroDeriveParser; impl SingleAttributeParser for ProcMacroDeriveParser { const PATH: &[Symbol] = &[sym::proc_macro_derive]; - const ALLOWED_TARGETS: AllowedTargets = PROC_MACRO_ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = PROC_MACRO_ALLOWED_TARGETS; const TEMPLATE: AttributeTemplate = template!( List: &["TraitName", "TraitName, attributes(name1, name2, ...)"], "https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros" @@ -44,7 +44,8 @@ impl SingleAttributeParser for ProcMacroDeriveParser { pub(crate) struct RustcBuiltinMacroParser; impl SingleAttributeParser for RustcBuiltinMacroParser { const PATH: &[Symbol] = &[sym::rustc_builtin_macro]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); const TEMPLATE: AttributeTemplate = template!(List: &["TraitName", "TraitName, attributes(name1, name2, ...)"]); const STABILITY: AttributeStability = unstable!(rustc_attrs); diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index ddc40e62a7e8e..a41010395e7c1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -18,7 +18,7 @@ impl SingleAttributeParser for CustomMirParser { const PATH: &[rustc_span::Symbol] = &[sym::custom_mir]; const STABILITY: AttributeStability = unstable!(custom_mir); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]); diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index b6e63de1a2095..bf03d87942651 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -63,7 +63,7 @@ impl CombineAttributeParser for ReprParser { reprs } - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::ManuallyChecked; + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::ManuallyChecked; const STABILITY: AttributeStability = AttributeStability::Stable; } @@ -306,7 +306,7 @@ impl RustcAlignParser { impl AttributeParser for RustcAlignParser { const ATTRIBUTES: AcceptMapping = &[(Self::PATH, Self::TEMPLATE, unstable!(fn_align), Self::parse)]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -336,7 +336,7 @@ impl RustcAlignStaticParser { impl AttributeParser for RustcAlignStaticParser { const ATTRIBUTES: AcceptMapping = &[(Self::PATH, Self::TEMPLATE, unstable!(static_align), Self::parse)]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]); fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs index cacf98bef6e93..467289d78da9b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs @@ -6,7 +6,7 @@ pub(crate) struct RustcAllocatorParser; impl NoArgsAttributeParser for RustcAllocatorParser { const PATH: &[Symbol] = &[sym::rustc_allocator]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcAllocator; @@ -16,7 +16,7 @@ pub(crate) struct RustcAllocatorZeroedParser; impl NoArgsAttributeParser for RustcAllocatorZeroedParser { const PATH: &[Symbol] = &[sym::rustc_allocator_zeroed]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcAllocatorZeroed; @@ -26,7 +26,7 @@ pub(crate) struct RustcAllocatorZeroedVariantParser; impl SingleAttributeParser for RustcAllocatorZeroedVariantParser { const PATH: &[Symbol] = &[sym::rustc_allocator_zeroed_variant]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "function"); const STABILITY: AttributeStability = unstable!(rustc_attrs); @@ -42,7 +42,7 @@ pub(crate) struct RustcDeallocatorParser; impl NoArgsAttributeParser for RustcDeallocatorParser { const PATH: &[Symbol] = &[sym::rustc_deallocator]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDeallocator; @@ -52,7 +52,7 @@ pub(crate) struct RustcReallocatorParser; impl NoArgsAttributeParser for RustcReallocatorParser { const PATH: &[Symbol] = &[sym::rustc_reallocator]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcReallocator; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index acd5b465a80fe..0fd3d5d65e3a5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -10,7 +10,7 @@ pub(crate) struct RustcDumpUserArgsParser; impl NoArgsAttributeParser for RustcDumpUserArgsParser { const PATH: &[Symbol] = &[sym::rustc_dump_user_args]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpUserArgs; } @@ -19,7 +19,7 @@ pub(crate) struct RustcDumpDefParentsParser; impl NoArgsAttributeParser for RustcDumpDefParentsParser { const PATH: &[Symbol] = &[sym::rustc_dump_def_parents]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents; } @@ -28,7 +28,7 @@ pub(crate) struct RustcDumpDefPathParser; impl SingleAttributeParser for RustcDumpDefPathParser { const PATH: &[Symbol] = &[sym::rustc_dump_def_path]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::TraitImpl)), Allow(Target::Method(MethodKind::Inherent)), @@ -49,7 +49,7 @@ pub(crate) struct RustcDumpGenericsParser; impl NoArgsAttributeParser for RustcDumpGenericsParser { const PATH: &[Symbol] = &[sym::rustc_dump_generics]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Struct), Allow(Target::Enum), Allow(Target::Union), @@ -78,7 +78,7 @@ pub(crate) struct RustcDumpHiddenTypeOfOpaquesParser; impl NoArgsAttributeParser for RustcDumpHiddenTypeOfOpaquesParser { const PATH: &[Symbol] = &[sym::rustc_dump_hidden_type_of_opaques]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpHiddenTypeOfOpaques; } @@ -87,7 +87,7 @@ pub(crate) struct RustcDumpInferredOutlivesParser; impl NoArgsAttributeParser for RustcDumpInferredOutlivesParser { const PATH: &[Symbol] = &[sym::rustc_dump_inferred_outlives]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Struct), Allow(Target::Enum), Allow(Target::Union), @@ -101,7 +101,8 @@ pub(crate) struct RustcDumpItemBoundsParser; impl NoArgsAttributeParser for RustcDumpItemBoundsParser { const PATH: &[Symbol] = &[sym::rustc_dump_item_bounds]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocTy)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::AssocTy)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds; } @@ -115,7 +116,7 @@ impl CombineAttributeParser for RustcDumpLayoutParser { const CONVERT: ConvertFn = |items, _| AttributeKind::RustcDumpLayout(items); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Struct), Allow(Target::Enum), Allow(Target::Union), @@ -174,7 +175,7 @@ pub(crate) struct RustcDumpObjectLifetimeDefaultsParser; impl NoArgsAttributeParser for RustcDumpObjectLifetimeDefaultsParser { const PATH: &[Symbol] = &[sym::rustc_dump_object_lifetime_defaults]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::AssocConst), Allow(Target::AssocTy), Allow(Target::Const), @@ -201,7 +202,7 @@ pub(crate) struct RustcDumpPredicatesParser; impl NoArgsAttributeParser for RustcDumpPredicatesParser { const PATH: &[Symbol] = &[sym::rustc_dump_predicates]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::AssocConst), Allow(Target::AssocTy), Allow(Target::Const), @@ -229,7 +230,7 @@ pub(crate) struct RustcDumpSymbolNameParser; impl SingleAttributeParser for RustcDumpSymbolNameParser { const PATH: &[Symbol] = &[sym::rustc_dump_symbol_name]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::TraitImpl)), Allow(Target::Method(MethodKind::Inherent)), @@ -250,7 +251,7 @@ pub(crate) struct RustcDumpVariancesParser; impl NoArgsAttributeParser for RustcDumpVariancesParser { const PATH: &[Symbol] = &[sym::rustc_dump_variances]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Enum), Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), @@ -271,7 +272,7 @@ pub(crate) struct RustcDumpVariancesOfOpaquesParser; impl NoArgsAttributeParser for RustcDumpVariancesOfOpaquesParser { const PATH: &[Symbol] = &[sym::rustc_dump_variances_of_opaques]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpVariancesOfOpaques; } @@ -280,7 +281,7 @@ pub(crate) struct RustcDumpVtableParser; impl NoArgsAttributeParser for RustcDumpVtableParser { const PATH: &[Symbol] = &[sym::rustc_dump_vtable]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Impl { of_trait: true }), Allow(Target::TyAlias), ]); diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 6552c9de7c5a5..24df9b100ce95 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -12,6 +12,7 @@ use rustc_span::Symbol; use super::prelude::*; use super::util::parse_single_integer; use crate::diagnostics; +use crate::diagnostics::UnknownExternLangItem; use crate::session_diagnostics::{ AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange, UnknownLangItem, }; @@ -20,7 +21,7 @@ pub(crate) struct RustcMainParser; impl NoArgsAttributeParser for RustcMainParser { const PATH: &[Symbol] = &[sym::rustc_main]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const STABILITY: AttributeStability = unstable!( rustc_attrs, "the `#[rustc_main]` attribute is used internally to specify test entry point function" @@ -32,7 +33,7 @@ pub(crate) struct RustcMustImplementOneOfParser; impl SingleAttributeParser for RustcMustImplementOneOfParser { const PATH: &[Symbol] = &[sym::rustc_must_implement_one_of]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!( rustc_attrs, "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait. Its syntax and semantics are highly experimental and will be subject to change before stabilization" @@ -78,7 +79,7 @@ pub(crate) struct RustcNeverReturnsNullPtrParser; impl NoArgsAttributeParser for RustcNeverReturnsNullPtrParser { const PATH: &[Symbol] = &[sym::rustc_never_returns_null_ptr]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -93,7 +94,7 @@ pub(crate) struct RustcNoImplicitAutorefsParser; impl NoArgsAttributeParser for RustcNoImplicitAutorefsParser { const PATH: &[Symbol] = &[sym::rustc_no_implicit_autorefs]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -109,7 +110,7 @@ pub(crate) struct RustcLegacyConstGenericsParser; impl SingleAttributeParser for RustcLegacyConstGenericsParser { const PATH: &[Symbol] = &[sym::rustc_legacy_const_generics]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const TEMPLATE: AttributeTemplate = template!(List: &["N"]); const STABILITY: AttributeStability = unstable!(rustc_attrs); @@ -149,7 +150,7 @@ pub(crate) struct RustcInheritOverflowChecksParser; impl NoArgsAttributeParser for RustcInheritOverflowChecksParser { const PATH: &[Symbol] = &[sym::rustc_inherit_overflow_checks]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::TraitImpl)), @@ -163,7 +164,7 @@ pub(crate) struct RustcLintOptDenyFieldAccessParser; impl SingleAttributeParser for RustcLintOptDenyFieldAccessParser { const PATH: &[Symbol] = &[sym::rustc_lint_opt_deny_field_access]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Field)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Field)]); const TEMPLATE: AttributeTemplate = template!(Word); const STABILITY: AttributeStability = unstable!(rustc_attrs); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { @@ -178,7 +179,7 @@ pub(crate) struct RustcLintOptTyParser; impl NoArgsAttributeParser for RustcLintOptTyParser { const PATH: &[Symbol] = &[sym::rustc_lint_opt_ty]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintOptTy; } @@ -305,7 +306,7 @@ impl AttributeParser for RustcCguTestAttributeParser { ), ]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]); fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { @@ -317,7 +318,7 @@ pub(crate) struct RustcDeprecatedSafe2024Parser; impl SingleAttributeParser for RustcDeprecatedSafe2024Parser { const PATH: &[Symbol] = &[sym::rustc_deprecated_safe_2024]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -347,7 +348,7 @@ pub(crate) struct RustcConversionSuggestionParser; impl NoArgsAttributeParser for RustcConversionSuggestionParser { const PATH: &[Symbol] = &[sym::rustc_conversion_suggestion]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -362,7 +363,8 @@ pub(crate) struct RustcCaptureAnalysisParser; impl NoArgsAttributeParser for RustcCaptureAnalysisParser { const PATH: &[Symbol] = &[sym::rustc_capture_analysis]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Closure)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::Closure)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCaptureAnalysis; } @@ -371,7 +373,7 @@ pub(crate) struct RustcNeverTypeOptionsParser; impl SingleAttributeParser for RustcNeverTypeOptionsParser { const PATH: &[Symbol] = &[sym::rustc_never_type_options]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(List: &[ r#"fallback = "unit", "never", "no""#, r#"diverging_block_default = "unit", "never""#, @@ -444,7 +446,7 @@ pub(crate) struct RustcTrivialFieldReadsParser; impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; } @@ -453,7 +455,7 @@ pub(crate) struct RustcNoMirInlineParser; impl NoArgsAttributeParser for RustcNoMirInlineParser { const PATH: &[Symbol] = &[sym::rustc_no_mir_inline]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -469,7 +471,7 @@ pub(crate) struct RustcNoWritableParser; impl NoArgsAttributeParser for RustcNoWritableParser { const PATH: &[Symbol] = &[sym::rustc_no_writable]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Closure), Allow(Target::Method(MethodKind::Inherent)), @@ -484,7 +486,7 @@ pub(crate) struct RustcLintQueryInstabilityParser; impl NoArgsAttributeParser for RustcLintQueryInstabilityParser { const PATH: &[Symbol] = &[sym::rustc_lint_query_instability]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -499,7 +501,7 @@ pub(crate) struct RustcRegionsParser; impl NoArgsAttributeParser for RustcRegionsParser { const PATH: &[Symbol] = &[sym::rustc_regions]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -514,7 +516,7 @@ pub(crate) struct RustcLintUntrackedQueryInformationParser; impl NoArgsAttributeParser for RustcLintUntrackedQueryInformationParser { const PATH: &[Symbol] = &[sym::rustc_lint_untracked_query_information]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), @@ -529,7 +531,7 @@ pub(crate) struct RustcSimdMonomorphizeLaneLimitParser; impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser { const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); const STABILITY: AttributeStability = unstable!(rustc_attrs); @@ -543,7 +545,7 @@ pub(crate) struct RustcScalableVectorParser; impl SingleAttributeParser for RustcScalableVectorParser { const PATH: &[Symbol] = &[sym::rustc_scalable_vector]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const TEMPLATE: AttributeTemplate = template!(Word, List: &["count"]); const STABILITY: AttributeStability = unstable!(rustc_attrs); @@ -565,7 +567,7 @@ pub(crate) struct LangParser; impl SingleAttributeParser for LangParser { const PATH: &[Symbol] = &[sym::lang]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes` + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::ManuallyChecked; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); const STABILITY: AttributeStability = unstable!(lang_items); @@ -576,6 +578,26 @@ impl SingleAttributeParser for LangParser { cx.emit_err(UnknownLangItem { span: cx.attr_span, name }); return None; }; + + // Only weak lang items may be applied to foreign items + if [Target::ForeignFn, Target::ForeignStatic, Target::ForeignTy, Target::ForeignMod] + .contains(&cx.target) + && !(lang_item.is_weak() || lang_item.is_weak_only()) + { + cx.emit_err(UnknownExternLangItem { span: cx.attr_span, lang_item: lang_item.name() }); + return None; + } + + // Check the target + let allowed_targets: &[_] = if lang_item == LangItem::PanicImpl { + &[Allow(Target::Fn), Allow(Target::ForeignFn)] + } else if lang_item.is_weak_only() { + &[Allow(Target::ForeignFn)] + } else { + &[Allow(lang_item.target())] + }; + cx.check_target(&format!(" = \"{name}\""), &AllowedTargets::AllowList(allowed_targets)); + Some(AttributeKind::Lang(lang_item)) } } @@ -584,7 +606,7 @@ pub(crate) struct RustcHasIncoherentInherentImplsParser; impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParser { const PATH: &[Symbol] = &[sym::rustc_has_incoherent_inherent_impls]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Trait), Allow(Target::Struct), Allow(Target::Enum), @@ -599,7 +621,7 @@ pub(crate) struct PanicHandlerParser; impl NoArgsAttributeParser for PanicHandlerParser { const PATH: &[Symbol] = &[sym::panic_handler]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes` + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Lang(LangItem::PanicImpl); } @@ -608,7 +630,7 @@ pub(crate) struct RustcNounwindParser; impl NoArgsAttributeParser for RustcNounwindParser { const PATH: &[Symbol] = &[sym::rustc_nounwind]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::ForeignFn), Allow(Target::Method(MethodKind::Inherent)), @@ -623,7 +645,7 @@ pub(crate) struct RustcOffloadKernelParser; impl NoArgsAttributeParser for RustcOffloadKernelParser { const PATH: &[Symbol] = &[sym::rustc_offload_kernel]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOffloadKernel; } @@ -636,7 +658,7 @@ impl CombineAttributeParser for RustcMirParser { type Item = RustcMirKind; const CONVERT: ConvertFn = |items, _| AttributeKind::RustcMir(items); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::TraitImpl)), @@ -712,7 +734,7 @@ pub(crate) struct RustcNonConstTraitMethodParser; impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { const PATH: &[Symbol] = &[sym::rustc_non_const_trait_method]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::Trait { body: false })), ]); @@ -731,7 +753,7 @@ impl CombineAttributeParser for RustcCleanParser { type Item = RustcCleanAttribute; const CONVERT: ConvertFn = |items, _| AttributeKind::RustcClean(items); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ // tidy-alphabetical-start Allow(Target::AssocConst), Allow(Target::AssocTy), @@ -824,7 +846,7 @@ pub(crate) struct RustcIfThisChangedParser; impl SingleAttributeParser for RustcIfThisChangedParser { const PATH: &[Symbol] = &[sym::rustc_if_this_changed]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ // tidy-alphabetical-start Allow(Target::AssocConst), Allow(Target::AssocTy), @@ -882,7 +904,7 @@ impl CombineAttributeParser for RustcThenThisWouldNeedParser { const CONVERT: ConvertFn = |items, _span| AttributeKind::RustcThenThisWouldNeed(items); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ // tidy-alphabetical-start Allow(Target::AssocConst), Allow(Target::AssocTy), @@ -929,7 +951,7 @@ pub(crate) struct RustcInsignificantDtorParser; impl NoArgsAttributeParser for RustcInsignificantDtorParser { const PATH: &[Symbol] = &[sym::rustc_insignificant_dtor]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Enum), Allow(Target::Struct), Allow(Target::ForeignTy), @@ -942,7 +964,7 @@ pub(crate) struct RustcEffectiveVisibilityParser; impl NoArgsAttributeParser for RustcEffectiveVisibilityParser { const PATH: &[Symbol] = &[sym::rustc_effective_visibility]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Use), Allow(Target::Static), Allow(Target::Const), @@ -981,7 +1003,7 @@ pub(crate) struct RustcDiagnosticItemParser; impl SingleAttributeParser for RustcDiagnosticItemParser { const PATH: &[Symbol] = &[sym::rustc_diagnostic_item]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Trait), Allow(Target::Struct), Allow(Target::Enum), @@ -1016,7 +1038,7 @@ pub(crate) struct RustcDoNotConstCheckParser; impl NoArgsAttributeParser for RustcDoNotConstCheckParser { const PATH: &[Symbol] = &[sym::rustc_do_not_const_check]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::TraitImpl)), @@ -1034,7 +1056,7 @@ pub(crate) struct RustcNonnullOptimizationGuaranteedParser; impl NoArgsAttributeParser for RustcNonnullOptimizationGuaranteedParser { const PATH: &[Symbol] = &[sym::rustc_nonnull_optimization_guaranteed]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const STABILITY: AttributeStability = unstable!( rustc_attrs, "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library", @@ -1047,7 +1069,7 @@ pub(crate) struct RustcStrictCoherenceParser; impl NoArgsAttributeParser for RustcStrictCoherenceParser { const PATH: &[Symbol] = &[sym::rustc_strict_coherence]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Trait), Allow(Target::Struct), Allow(Target::Enum), @@ -1062,7 +1084,7 @@ pub(crate) struct RustcReservationImplParser; impl SingleAttributeParser for RustcReservationImplParser { const PATH: &[Symbol] = &[sym::rustc_reservation_impl]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Impl { of_trait: true })]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "reservation message"); const STABILITY: AttributeStability = unstable!(rustc_attrs); @@ -1079,7 +1101,7 @@ pub(crate) struct PreludeImportParser; impl NoArgsAttributeParser for PreludeImportParser { const PATH: &[Symbol] = &[sym::prelude_import]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Use)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Use)]); const STABILITY: AttributeStability = unstable!(prelude_import); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PreludeImport; } @@ -1088,7 +1110,7 @@ pub(crate) struct RustcDocPrimitiveParser; impl SingleAttributeParser for RustcDocPrimitiveParser { const PATH: &[Symbol] = &[sym::rustc_doc_primitive]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Mod)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Mod)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "primitive name"); const STABILITY: AttributeStability = unstable!( rustc_attrs, @@ -1107,7 +1129,7 @@ pub(crate) struct RustcIntrinsicParser; impl NoArgsAttributeParser for RustcIntrinsicParser { const PATH: &[Symbol] = &[sym::rustc_intrinsic]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const STABILITY: AttributeStability = unstable!(intrinsics); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsic; } @@ -1116,7 +1138,7 @@ pub(crate) struct RustcIntrinsicConstStableIndirectParser; impl NoArgsAttributeParser for RustcIntrinsicConstStableIndirectParser { const PATH: &'static [Symbol] = &[sym::rustc_intrinsic_const_stable_indirect]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect; } @@ -1125,7 +1147,7 @@ pub(crate) struct RustcExhaustiveParser; impl NoArgsAttributeParser for RustcExhaustiveParser { const PATH: &'static [Symbol] = &[sym::rustc_must_match_exhaustively]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Enum)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Enum)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcMustMatchExhaustively; } diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index 07c07eb6681df..cdcb8e95da726 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -1,11 +1,17 @@ use rustc_feature::AttributeStability; +use rustc_hir::target::GenericParamKind; use super::prelude::*; pub(crate) struct MayDangleParser; impl NoArgsAttributeParser for MayDangleParser { const PATH: &[Symbol] = &[sym::may_dangle]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ + Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: false }), + Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: true }), + Allow(Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: false }), + Allow(Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: true }), + ]); const STABILITY: AttributeStability = unstable!(dropck_eyepatch); const CREATE: fn(span: Span) -> AttributeKind = AttributeKind::MayDangle; } @@ -14,7 +20,7 @@ pub(crate) struct ComptimeParser; impl NoArgsAttributeParser for ComptimeParser { const PATH: &[Symbol] = &[sym::rustc_comptime]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Fn), ]); diff --git a/compiler/rustc_attr_parsing/src/attributes/splat.rs b/compiler/rustc_attr_parsing/src/attributes/splat.rs index 65fc9fb8123f4..ab34021ad7cdf 100644 --- a/compiler/rustc_attr_parsing/src/attributes/splat.rs +++ b/compiler/rustc_attr_parsing/src/attributes/splat.rs @@ -9,7 +9,7 @@ pub(crate) struct SplatParser; impl NoArgsAttributeParser for SplatParser { const PATH: &[Symbol] = &[sym::splat]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Param)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Param)]); const STABILITY: AttributeStability = unstable!(splat, "the `#[splat]` attribute is experimental"); const CREATE: fn(Span) -> AttributeKind = AttributeKind::Splat; diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index b38bb6d535770..0cfb68d523f9b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -13,7 +13,7 @@ use super::prelude::*; use super::util::parse_version; use crate::session_diagnostics; -const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ +const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Struct), Allow(Target::Enum), @@ -40,6 +40,7 @@ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Static), Allow(Target::ForeignFn), Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), Allow(Target::ExternCrate), ]); @@ -102,7 +103,7 @@ impl AttributeParser for StabilityParser { }, ), ]; - const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = ALLOWED_TARGETS; fn finalize(mut self, cx: &FinalizeContext<'_, '_>) -> Option { if let Some(atum) = self.allowed_through_unstable_modules { @@ -158,7 +159,7 @@ impl AttributeParser for BodyStabilityParser { } }, )]; - const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets<'_> = ALLOWED_TARGETS; fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { let (stability, span) = self.stability?; @@ -171,7 +172,7 @@ pub(crate) struct RustcConstStableIndirectParser; impl NoArgsAttributeParser for RustcConstStableIndirectParser { const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), ]); @@ -233,7 +234,7 @@ impl AttributeParser for ConstStabilityParser { this.promotable = true; }), ]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::TraitImpl)), @@ -460,7 +461,7 @@ pub(crate) struct UnstableRemovedParser; impl CombineAttributeParser for UnstableRemovedParser { type Item = UnstableRemovedFeature; const PATH: &[Symbol] = &[sym::unstable_removed]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(List: &[r#"feature = "name", reason = "...", link = "...", since = "version""#]); const STABILITY: AttributeStability = unstable!(staged_api); diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 00e113f171715..8905591bdba5a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -9,7 +9,7 @@ pub(crate) struct IgnoreParser; impl SingleAttributeParser for IgnoreParser { const PATH: &[Symbol] = &[sym::ignore]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Fn), Error(Target::WherePredicate)]); const TEMPLATE: AttributeTemplate = template!( Word, NameValueStr: "reason", @@ -51,7 +51,7 @@ pub(crate) struct ShouldPanicParser; impl SingleAttributeParser for ShouldPanicParser { const PATH: &[Symbol] = &[sym::should_panic]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[Allow(Target::Fn), Error(Target::WherePredicate)]); const TEMPLATE: AttributeTemplate = template!( Word, List: &[r#"expected = "reason""#], NameValueStr: "reason", @@ -83,7 +83,7 @@ pub(crate) struct ReexportTestHarnessMainParser; impl SingleAttributeParser for ReexportTestHarnessMainParser { const PATH: &[Symbol] = &[sym::reexport_test_harness_main]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); const STABILITY: AttributeStability = unstable!(custom_test_frameworks); @@ -105,7 +105,7 @@ pub(crate) struct RustcAbiParser; impl SingleAttributeParser for RustcAbiParser { const PATH: &[Symbol] = &[sym::rustc_abi]; const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::debug, sym::assert_eq]); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::TyAlias), Allow(Target::Fn), Allow(Target::ForeignFn), @@ -150,7 +150,7 @@ pub(crate) struct RustcDelayedBugFromInsideQueryParser; impl NoArgsAttributeParser for RustcDelayedBugFromInsideQueryParser { const PATH: &[Symbol] = &[sym::rustc_delayed_bug_from_inside_query]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDelayedBugFromInsideQuery; } @@ -159,7 +159,7 @@ pub(crate) struct RustcEvaluateWhereClausesParser; impl NoArgsAttributeParser for RustcEvaluateWhereClausesParser { const PATH: &[Symbol] = &[sym::rustc_evaluate_where_clauses]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), @@ -174,7 +174,7 @@ pub(crate) struct TestRunnerParser; impl SingleAttributeParser for TestRunnerParser { const PATH: &[Symbol] = &[sym::test_runner]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(List: &["path"]); const STABILITY: AttributeStability = unstable!(custom_test_frameworks); @@ -194,7 +194,7 @@ pub(crate) struct RustcTestMarkerParser; impl SingleAttributeParser for RustcTestMarkerParser { const PATH: &[Symbol] = &[sym::rustc_test_marker]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Const), Allow(Target::Fn), Allow(Target::Static), diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index 505cdea2477bf..a2348eef975cf 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -12,7 +12,7 @@ use crate::target_checking::Policy::{Allow, Warn}; pub(crate) struct RustcSkipDuringMethodDispatchParser; impl SingleAttributeParser for RustcSkipDuringMethodDispatchParser { const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const TEMPLATE: AttributeTemplate = template!(List: &["array, boxed_slice"]); const STABILITY: AttributeStability = unstable!(rustc_attrs); @@ -52,7 +52,7 @@ impl SingleAttributeParser for RustcSkipDuringMethodDispatchParser { pub(crate) struct RustcParenSugarParser; impl NoArgsAttributeParser for RustcParenSugarParser { const PATH: &[Symbol] = &[sym::rustc_paren_sugar]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcParenSugar; } @@ -62,7 +62,7 @@ impl NoArgsAttributeParser for RustcParenSugarParser { pub(crate) struct MarkerParser; impl NoArgsAttributeParser for MarkerParser { const PATH: &[Symbol] = &[sym::marker]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Trait), Warn(Target::Field), Warn(Target::Arm), @@ -75,7 +75,7 @@ impl NoArgsAttributeParser for MarkerParser { pub(crate) struct RustcDenyExplicitImplParser; impl NoArgsAttributeParser for RustcDenyExplicitImplParser { const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDenyExplicitImpl; } @@ -83,7 +83,7 @@ impl NoArgsAttributeParser for RustcDenyExplicitImplParser { pub(crate) struct RustcDynIncompatibleTraitParser; impl NoArgsAttributeParser for RustcDynIncompatibleTraitParser { const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDynIncompatibleTrait; } @@ -93,7 +93,7 @@ impl NoArgsAttributeParser for RustcDynIncompatibleTraitParser { pub(crate) struct RustcSpecializationTraitParser; impl NoArgsAttributeParser for RustcSpecializationTraitParser { const PATH: &[Symbol] = &[sym::rustc_specialization_trait]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcSpecializationTrait; } @@ -101,7 +101,7 @@ impl NoArgsAttributeParser for RustcSpecializationTraitParser { pub(crate) struct RustcUnsafeSpecializationMarkerParser; impl NoArgsAttributeParser for RustcUnsafeSpecializationMarkerParser { const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcUnsafeSpecializationMarker; } @@ -111,7 +111,7 @@ impl NoArgsAttributeParser for RustcUnsafeSpecializationMarkerParser { pub(crate) struct RustcCoinductiveParser; impl NoArgsAttributeParser for RustcCoinductiveParser { const PATH: &[Symbol] = &[sym::rustc_coinductive]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCoinductive; } @@ -119,7 +119,7 @@ impl NoArgsAttributeParser for RustcCoinductiveParser { pub(crate) struct RustcAllowIncoherentImplParser; impl NoArgsAttributeParser for RustcAllowIncoherentImplParser { const PATH: &[Symbol] = &[sym::rustc_allow_incoherent_impl]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAllowIncoherentImpl; @@ -128,7 +128,7 @@ impl NoArgsAttributeParser for RustcAllowIncoherentImplParser { pub(crate) struct FundamentalParser; impl NoArgsAttributeParser for FundamentalParser { const PATH: &[Symbol] = &[sym::fundamental]; - const ALLOWED_TARGETS: AllowedTargets = + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct), Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(fundamental); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Fundamental; diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index ad40f30ef418a..7f5cceb501acd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -11,7 +11,8 @@ impl SingleAttributeParser for RustcMacroTransparencyParser { cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); }); const STABILITY: AttributeStability = unstable!(rustc_attrs); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["transparent", "semiopaque", "opaque"]); diff --git a/compiler/rustc_attr_parsing/src/attributes/unroll.rs b/compiler/rustc_attr_parsing/src/attributes/unroll.rs index b10ddcf9dc4fc..50c73ca041cdd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/unroll.rs +++ b/compiler/rustc_attr_parsing/src/attributes/unroll.rs @@ -7,7 +7,7 @@ use super::prelude::*; pub(crate) struct UnrollParser; impl SingleAttributeParser for UnrollParser { const PATH: &[Symbol] = &[sym::unroll]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Loop), Allow(Target::ForLoop), Allow(Target::While), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 14e443c922711..1595d2f80ddc7 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -85,7 +85,7 @@ pub(super) struct GroupTypeInner { pub(super) struct GroupTypeInnerAccept { pub(super) template: AttributeTemplate, pub(super) accept_fn: AcceptFn, - pub(super) allowed_targets: AllowedTargets, + pub(super) allowed_targets: AllowedTargets<'static>, pub(super) safety: AttributeSafety, pub(super) stability: AttributeStability, pub(super) finalizer: FinalizeFn, diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index b22fe88a4de3d..50667952b814d 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -1,4 +1,4 @@ -use rustc_errors::{Applicability, DiagArgValue, E0232, MultiSpan}; +use rustc_errors::{Applicability, DiagArgValue, E0232, E0264, MultiSpan}; use rustc_hir::AttrPath; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -169,11 +169,22 @@ pub(crate) struct DocAutoCfgExpectsHideOrShow; pub(crate) struct AmbiguousDeriveHelpers; #[derive(Diagnostic)] -#[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items")] +#[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or `values(...)`")] pub(crate) struct DocAutoCfgHideShowUnexpectedItem { pub attr_name: Symbol, } +#[derive(Diagnostic)] +#[diag("`any()` was used when other values were provided")] +pub(crate) struct DocAutoCfgHideShowValuesMix { + #[label("value declared here")] + pub value_span: Span, +} + +#[derive(Diagnostic)] +#[diag("unexpected item after `values()`")] +pub(crate) struct DocAutoCfgHideShowUnexpectedItemAfterValues; + #[derive(Diagnostic)] #[diag("`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items")] pub(crate) struct DocAutoCfgHideShowExpectsList { @@ -239,6 +250,10 @@ pub(crate) struct DocUnknownAny { #[diag("expected boolean for `#[doc(auto_cfg = ...)]`")] pub(crate) struct DocAutoCfgWrongLiteral; +#[derive(Diagnostic)] +#[diag("there must be at least one identifier before `values(...)`")] +pub(crate) struct DocAutoCfgHideShowNoIdentBeforeValues; + #[derive(Diagnostic)] #[diag("`#[doc(test(...)]` takes a list of attributes")] pub(crate) struct DocTestTakesList; @@ -801,3 +816,11 @@ pub(crate) struct UnsafeAttribute { pub attr_path: AttrPath, pub note: &'static str, } + +#[derive(Diagnostic)] +#[diag("unknown external lang item: `{$lang_item}`", code = E0264)] +pub(crate) struct UnknownExternLangItem { + #[primary_span] + pub span: Span, + pub lang_item: Symbol, +} diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 494824a8393b3..9e5d7bb2cf375 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -13,6 +13,13 @@ use rustc_target::spec::TargetTuple; use crate::AttributeTemplate; use crate::context::Suggestion; +#[derive(Diagnostic)] +#[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] +pub(crate) struct BothFfiConstAndPure { + #[primary_span] + pub attr_span: Span, +} + #[derive(Diagnostic)] #[diag("{$attr_str} attribute cannot have empty value")] pub(crate) struct DocAliasEmpty<'a> { @@ -73,6 +80,26 @@ pub(crate) struct DocAttributeNotAttribute { pub attribute: Symbol, } +#[derive(Diagnostic)] +#[diag( + "`#[target_feature]` cannot be applied to a {$kind -> + [panic_handler] `#[panic_handler]` + *[other] lang item + } function" +)] +pub(crate) struct TargetFeatureOnLangItem { + #[primary_span] + pub attr_span: Span, + pub kind: Symbol, + #[label( + "{$kind -> + [panic_handler] `#[panic_handler]` + *[other] lang item + } function is not allowed to have `#[target_feature]`" + )] + pub item_span: Span, +} + #[derive(Diagnostic)] #[diag("missing 'since'", code = E0542)] pub(crate) struct MissingSince { @@ -265,6 +292,13 @@ pub(crate) struct EmptyExportName { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`section` may not be empty")] +pub(crate) struct EmptySection { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`export_name` may not contain null characters", code = E0648)] pub(crate) struct NullOnExport { @@ -300,6 +334,13 @@ pub(crate) struct NullOnObjcSelector { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`section` may not contain null characters", code = E0648)] +pub(crate) struct NullOnSection { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`objc::class!` expected a string literal")] pub(crate) struct ObjcClassExpectedStringLiteral { @@ -337,7 +378,7 @@ pub(crate) struct InvalidTarget { pub target: &'static str, pub applied: DiagArgValue, pub only: &'static str, - pub attribute_args: &'static str, + pub attribute_args: String, #[subdiagnostic] pub help: Option, #[warning( diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 9903c6aa68d01..03100381d23f3 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -16,9 +16,9 @@ use crate::target_checking::Policy::Allow; use crate::{AttributeParser, ShouldEmit}; #[derive(Debug)] -pub(crate) enum AllowedTargets { - AllowList(&'static [Policy]), - AllowListWarnRest(&'static [Policy]), +pub(crate) enum AllowedTargets<'a> { + AllowList(&'a [Policy]), + AllowListWarnRest(&'a [Policy]), /// This is useful for argument-dependent target checking. /// If debug assertions are enabled, /// this emits a delayed bug if the `cx.check_target(...)` method is not called during attribute parsing. @@ -31,7 +31,7 @@ pub(crate) enum AllowedResult { Error, } -impl AllowedTargets { +impl AllowedTargets<'_> { pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult { match self { AllowedTargets::AllowList(list) => { @@ -94,8 +94,8 @@ pub(crate) enum Policy { impl<'sess> AttributeParser<'sess> { pub(crate) fn check_target( - allowed_targets: &AllowedTargets, - attribute_args: &'static str, + allowed_targets: &AllowedTargets<'_>, + attribute_args: &str, cx: &mut AcceptContext<'_, 'sess>, ) { if matches!(cx.should_emit, ShouldEmit::Nothing) { @@ -137,7 +137,7 @@ impl<'sess> AttributeParser<'sess> { target: cx.target.plural_name(), only: if only { "only " } else { "" }, applied: DiagArgValue::StrListSepByAnd(applied.into_iter().map(Cow::Owned).collect()), - attribute_args, + attribute_args: attribute_args.to_string(), help: Self::target_checking_help(attribute_args, cx), previously_accepted: matches!(result, AllowedResult::Warn) && !is_diagnostic_attr, on_macro_call: matches!(cx.target, Target::MacroCall), @@ -173,7 +173,7 @@ impl<'sess> AttributeParser<'sess> { } fn target_checking_help( - attribute_args: &'static str, + attribute_args: &str, cx: &AcceptContext<'_, '_>, ) -> Option { match &*cx.attr_path.segments { @@ -416,6 +416,7 @@ pub(crate) fn allowed_targets_applied( // ensure a consistent order target_strings.sort(); + target_strings.dedup(); // If there is now only 1 target left, show that as the only possible target let only_target = target_strings.len() == 1; @@ -443,8 +444,8 @@ fn filter_targets( impl<'f, 'sess> AcceptContext<'f, 'sess> { pub(crate) fn check_target( &mut self, - attribute_args: &'static str, - allowed_targets: &AllowedTargets, + attribute_args: &str, + allowed_targets: &AllowedTargets<'_>, ) { self.ignore_target_checks(); AttributeParser::check_target(allowed_targets, attribute_args, self); diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 55a09dbf1d724..8c9c8349c3149 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1.5.0" -itertools = "0.12" +itertools = "0.15" polonius-engine = "0.13.0" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index caa566e79e29b..86e5a6700b039 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -713,8 +713,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { )); let can_subst = |ty: Ty<'tcx>| { // Normalize before comparing to see through type aliases and projections. - let old_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, generic_args); - let new_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, new_args); + let old_ty = ty::EarlyBinder::bind(tcx, ty).instantiate(tcx, generic_args); + let new_ty = ty::EarlyBinder::bind(tcx, ty).instantiate(tcx, new_args); if let Ok(old_ty) = tcx.try_normalize_erasing_regions( self.infcx.typing_env(self.infcx.param_env), old_ty, @@ -1461,15 +1461,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let cause = ObligationCause::misc(expr.span, self.mir_def_id()); ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait); let errors = ocx.evaluate_obligations_error_on_ambiguity(); - if errors.iter().all(|error| { - match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { - Some(clause) => match clause.self_ty().skip_binder().kind() { - ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait, - _ => false, - }, - None => false, - } - }) { + if !errors.is_empty() + && errors.iter().all(|error| { + match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { + Some(clause) => match clause.self_ty().skip_binder().kind() { + ty::Adt(def, _) => { + def.did().is_local() && clause.def_id() == clone_trait + } + _ => false, + }, + None => false, + } + }) + { let mut type_spans = vec![]; let mut types = FxIndexSet::default(); for clause in errors diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 5d154cc7b9050..f6abafb874165 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -706,7 +706,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { Some(Cause::LiveVar(..) | Cause::DropVar(..)) | None => { // Here, under NLL: no cause was found. Under polonius: no cause was found, or a // boring local was found, which we ignore like NLLs do to match its diagnostics. - if let Some(region) = self.to_error_region_vid(borrow_region_vid) { + if let Some(region) = self.regioncx.to_error_region_vid(borrow_region_vid) { let (category, from_closure, span, region_name, path) = self.free_region_constraint_info(borrow_region_vid, region); if let Some(region_name) = region_name { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 56e0c74c85e97..a5b7a26a10ab4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -116,11 +116,42 @@ impl<'infcx, 'tcx> BorrowckDiagnosticsBuffer<'infcx, 'tcx> { pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) { self.buffered_diags.push(BufferedDiag::NonError(diag)); } + pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) { + self.buffered_diags.push(BufferedDiag::Error(diag)); + } + + pub(crate) fn emit_errors(&mut self) -> Option { + let mut res = None; + + // Buffer any move errors that we collected and de-duplicated. + for (_, (_, diag)) in std::mem::take(&mut self.buffered_move_errors) { + // We have already set tainted for this error, so just buffer it. + self.buffer_error(diag); + } + for (_, (mut diag, count)) in std::mem::take(&mut self.buffered_mut_errors) { + if count > 10 { + diag.note(format!("...and {} other attempted mutable borrows", count - 10)); + } + self.buffer_error(diag); + } + + if !self.buffered_diags.is_empty() { + self.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span()); + for buffered_diag in self.buffered_diags.drain(..) { + match buffered_diag { + BufferedDiag::Error(diag) => res = Some(diag.emit()), + BufferedDiag::NonError(diag) => diag.emit(), + } + } + } + + res + } } impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) { - self.diags_buffer.buffered_diags.push(BufferedDiag::Error(diag)); + self.diags_buffer.buffer_error(diag); } pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) { @@ -152,34 +183,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.diags_buffer.buffered_mut_errors.insert(span, (diag, count)); } - pub(crate) fn emit_errors(&mut self) -> Option { - let mut res = self.infcx.tainted_by_errors(); - - // Buffer any move errors that we collected and de-duplicated. - for (_, (_, diag)) in std::mem::take(&mut self.diags_buffer.buffered_move_errors) { - // We have already set tainted for this error, so just buffer it. - self.buffer_error(diag); - } - for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) { - if count > 10 { - diag.note(format!("...and {} other attempted mutable borrows", count - 10)); - } - self.buffer_error(diag); - } - - if !self.diags_buffer.buffered_diags.is_empty() { - self.diags_buffer.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span()); - for buffered_diag in self.diags_buffer.buffered_diags.drain(..) { - match buffered_diag { - BufferedDiag::Error(diag) => res = Some(diag.emit()), - BufferedDiag::NonError(diag) => diag.emit(), - } - } - } - - res - } - pub(crate) fn has_buffered_diags(&self) -> bool { self.diags_buffer.buffered_diags.is_empty() } diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs index 590c2c9c3965b..da947376446cb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs @@ -28,11 +28,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let infcx = self.infcx; - let mut guar = None; let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> = None; for error in errors { - guar = Some(match error { + match error { DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx), DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => { infcx.dcx().emit_err(err) @@ -76,16 +75,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { "non-defining use of `{}` in the defining scope", Ty::new_opaque( infcx.tcx, + ty::IsRigid::No, opaque_type_key.def_id.to_def_id(), opaque_type_key.args ) ), ), - }); + }; } - let guar = guar.unwrap(); - self.root_cx.set_tainted_by_errors(guar); - self.infcx.set_tainted_by_errors(guar); } /// Try to note when an opaque is involved in a borrowck error and that @@ -220,7 +217,7 @@ impl<'tcx> TypeVisitor> for FindOpaqueRegion<'_, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { // If we find an opaque in a local ty, then for each of its captured regions, // try to find a path between that captured regions and our borrow region... - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *ty.kind() + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *ty.kind() && let hir::OpaqueTyOrigin::FnReturn { parent, in_trait_or_impl: None } = self.tcx.opaque_ty_origin(def_id) { @@ -277,7 +274,7 @@ impl<'tcx> TypeVisitor> for CheckExplicitRegionMentionAndCollectGen fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { match *ty.kind() { - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { if self.seen_opaques.insert(def_id) { for (bound, _) in self .tcx diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index e436d95cdb087..1166e1265dadb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -28,6 +28,7 @@ use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource}; +use crate::consumers::RegionInferenceContext; use crate::nll::ConstraintDescription; use crate::region_infer::{BlameConstraint, TypeTest}; use crate::session_diagnostics::{ @@ -134,7 +135,7 @@ pub(crate) struct ErrorConstraintInfo<'tcx> { pub(super) span: Span, } -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { +impl<'tcx> RegionInferenceContext<'tcx> { /// Converts a region inference variable into a `ty::Region` that /// we can use for error reporting. If `r` is universally bound, /// then we use the name that we have on record for it. If `r` is @@ -142,20 +143,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// to find a good name from that. Returns `None` if we can't find /// one (e.g., this is just some random part of the CFG). pub(super) fn to_error_region(&self, r: RegionVid) -> Option> { - self.to_error_region_vid(r).and_then(|r| self.regioncx.region_definition(r).external_name) + self.to_error_region_vid(r).and_then(|r| self.region_definition(r).external_name) } /// Returns the `RegionVid` corresponding to the region returned by /// `to_error_region`. pub(super) fn to_error_region_vid(&self, r: RegionVid) -> Option { - if self.regioncx.universal_regions().is_universal_region(r) { + if self.universal_regions().is_universal_region(r) { Some(r) } else { // We just want something nameable, even if it's not // actually an upper bound. - let upper_bound = self.regioncx.approx_universal_upper_bound(r); + let upper_bound = self.approx_universal_upper_bound(r); - if self.regioncx.upper_bound_in_region_scc(r, upper_bound) { + if self.upper_bound_in_region_scc(r, upper_bound) { self.to_error_region_vid(upper_bound) } else { None @@ -179,14 +180,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(r) = self.to_error_region(fr) && let ty::ReLateParam(late_param) = r.kind() && let ty::LateParamRegionKind::ClosureEnv = late_param.kind - && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty + && let DefiningTy::Closure(_, args) = self.universal_regions().defining_ty { return args.as_closure().kind() == ty::ClosureKind::FnMut; } false } +} +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // For generic associated types (GATs) which implied 'static requirement // from higher-ranked trait bounds (HRTB). Try to locate span of the trait // and the span which bounded to the trait for adding 'static lifetime suggestion @@ -309,12 +312,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { RegionErrorKind::TypeTestError { type_test } => { // Try to convert the lower-bound region into something named we can print for // the user. - let lower_bound_region = self.to_error_region(type_test.lower_bound); + let lower_bound_region = self.regioncx.to_error_region(type_test.lower_bound); let type_test_span = type_test.span; if let Some(lower_bound_region) = lower_bound_region { - let generic_ty = self.name_regions( + let generic_ty = self.regioncx.name_regions( self.infcx.tcx, type_test.generic_kind.to_ty(self.infcx.tcx), ); @@ -324,7 +327,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.body.source.def_id().expect_local(), type_test_span, Some(origin), - self.name_regions(self.infcx.tcx, type_test.generic_kind), + self.regioncx.name_regions(self.infcx.tcx, type_test.generic_kind), lower_bound_region, )); } else { @@ -450,7 +453,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info); // Check if we can use one of the "nice region errors". - if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { + if let (Some(f), Some(o)) = + (self.regioncx.to_error_region(fr), self.regioncx.to_error_region(outlived_fr)) + { let infer_err = self.infcx.err_ctxt(); let nice = NiceRegionError::new_from_span(&infer_err, self.mir_def_id(), cause.span, o, f); @@ -481,7 +486,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { d.note("meoow :c"); d } - (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => { + (ConstraintCategory::Return(kind), true, false) + if self.regioncx.is_closure_fn_mut(fr) => + { self.report_fnmut_error(&errci, kind) } (ConstraintCategory::Assignment, true, false) @@ -603,7 +610,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let ErrorConstraintInfo { outlived_fr, span, .. } = errci; let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = *output_ty.kind() { + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = *output_ty.kind() { output_ty = self.infcx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip() }; @@ -736,7 +743,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // Only show an extra note if we can find an 'error region' for both of the region // variables. This avoids showing a noisy note that just mentions 'synthetic' regions // that don't help the user understand the error. - match (self.to_error_region(errci.fr), self.to_error_region(errci.outlived_fr)) { + match ( + self.regioncx.to_error_region(errci.fr), + self.regioncx.to_error_region(errci.outlived_fr), + ) { (Some(f), Some(o)) => { self.maybe_suggest_constrain_dyn_trait_impl(&mut diag, f, o, category); @@ -842,7 +852,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { outlived_fr: RegionVid, ) { if let (Some(f), Some(outlived_f)) = - (self.to_error_region(fr), self.to_error_region(outlived_fr)) + (self.regioncx.to_error_region(fr), self.regioncx.to_error_region(outlived_fr)) { if outlived_f.kind() != ty::ReStatic { return; @@ -1013,7 +1023,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } fn suggest_adding_lifetime_params(&self, diag: &mut Diag<'_>, sub: RegionVid, sup: RegionVid) { - let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else { + let (Some(sub), Some(sup)) = + (self.regioncx.to_error_region(sub), self.regioncx.to_error_region(sup)) + else { return; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index a00d39eea12d3..b8037fc83aad0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -283,7 +283,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// named variants. #[instrument(level = "trace", skip(self))] fn give_name_from_error_region(&self, fr: RegionVid) -> Option { - let error_region = self.to_error_region(fr)?; + let error_region = self.regioncx.to_error_region(fr)?; let tcx = self.infcx.tcx; @@ -1015,7 +1015,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else { + let ty::ReEarlyParam(region) = self.regioncx.to_error_region(fr)?.kind() else { return None; }; if region.is_named() { @@ -1050,7 +1050,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else { + let ty::ReEarlyParam(region) = self.regioncx.to_error_region(fr)?.kind() else { return None; }; if region.is_named() { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 94ad74b67b629..22e7d83e5b509 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -48,6 +48,7 @@ use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_trait_selection::traits::query::type_op::{QueryTypeOp, TypeOp, TypeOpOutput}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -323,7 +324,6 @@ fn borrowck_collect_region_constraints<'tcx>( let input_promoted: &IndexSlice<_, _> = &promoted.borrow(); if let Some(e) = input_body.tainted_by_errors { infcx.set_tainted_by_errors(e); - root_cx.set_tainted_by_errors(e); } // Replace all regions with fresh inference variables. This @@ -571,15 +571,16 @@ fn borrowck_check_region_constraints<'tcx>( debug!("mbcx.used_mut: {:?}", mbcx.used_mut); mbcx.lint_unused_mut(); - if let Some(guar) = mbcx.emit_errors() { - mbcx.root_cx.set_tainted_by_errors(guar); - } let result = PropagatedBorrowCheckResults { closure_requirements: opt_closure_req, used_mut_upvars: mbcx.used_mut_upvars, }; + if let Some(guar) = mbcx.diags_buffer.emit_errors().or(infcx.tainted_by_errors()) { + root_cx.set_tainted_by_errors(guar); + } + if let Some(consumer) = &mut root_cx.consumer { consumer.insert_body( def, @@ -707,6 +708,14 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { next_region } + + fn fully_perform + TypeVisitable>>( + &self, + q: Q, + span: Span, + ) -> Result>, ErrorGuaranteed> { + self.param_env.and(q).fully_perform(&self.infcx, self.root_def_id, span) + } } impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { @@ -718,7 +727,7 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { } pub(crate) struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { - root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, + root_cx: &'a BorrowCheckRootCtxt<'tcx>, infcx: &'infcx BorrowckInferCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, @@ -1928,7 +1937,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Never | ty::Tuple(_) | ty::UnsafeBinder(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Bound(_, _) | ty::Infer(_) @@ -1970,7 +1979,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Bound(_, _) | ty::Infer(_) diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index dd6eb17947577..b11f1868b64c9 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -111,7 +111,7 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( /// /// This may result in errors being reported. pub(crate) fn compute_regions<'tcx>( - root_cx: &mut BorrowCheckRootCtxt<'tcx>, + root_cx: &BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, location_table: &PoloniusLocationTable, diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs index c057d99c6a41b..db38879ea1d9b 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs @@ -176,7 +176,7 @@ impl<'tcx> TypeVisitor> for CollectMemberConstraintsVisitor<'_, '_, | ty::CoroutineClosure(def_id, args) | ty::Coroutine(def_id, args) => self.visit_closure_args(def_id, args), - ty::Alias(ty::AliasTy { kind, args, .. }) + ty::Alias(_, ty::AliasTy { kind, args, .. }) if let Some(variances) = self.cx().opt_alias_variances(kind) => { // Skip lifetime parameters that are not captured, since they do diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index f73719e87c787..446c226eade23 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -367,7 +367,7 @@ fn compute_definition_site_hidden_types_from_defining_uses<'tcx>( // usage of the opaque type and we can ignore it. This check is mirrored in typeck's // writeback. if !rcx.infcx.tcx.use_typing_mode_post_typeck_until_borrowck() { - if let &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = + if let &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = hidden_type.ty.skip_binder().kind() && def_id == opaque_type_key.def_id.to_def_id() && args == opaque_type_key.args @@ -500,7 +500,7 @@ impl<'tcx> FallibleTypeFolder> for ToArgRegionsFolder<'_, 'tcx> { Ty::new_coroutine(tcx, def_id, self.fold_closure_args(def_id, args)?) } - ty::Alias(ty::AliasTy { kind, args, .. }) + ty::Alias(_, ty::AliasTy { kind, args, .. }) if let Some(variances) = tcx.opt_alias_variances(kind) => { let args = tcx.mk_args_from_iter(std::iter::zip(variances, args.iter()).map( @@ -512,7 +512,7 @@ impl<'tcx> FallibleTypeFolder> for ToArgRegionsFolder<'_, 'tcx> { } }, ))?; - ty::AliasTy::new_from_args(tcx, kind, args).to_ty(tcx) + ty::AliasTy::new_from_args(tcx, kind, args).to_ty(tcx, ty::IsRigid::No) } _ => ty.try_super_fold_with(self)?, @@ -541,7 +541,7 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>( for &(key, hidden_type) in opaque_types { let Some(expected) = hidden_types.get(&key.def_id) else { if !tcx.use_typing_mode_post_typeck_until_borrowck() { - if let &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = + if let &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = hidden_type.ty.kind() && def_id == key.def_id.to_def_id() && args == key.args diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index a082aba35b8a7..e406f158db216 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -72,7 +72,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { } pub(super) fn used_mut_upvars( - &mut self, + &self, nested_body_def_id: LocalDefId, ) -> &SmallVec<[FieldIdx; 8]> { &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 4d93fa08fe0bd..82e623bb42535 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{ self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions, }; use rustc_span::Span; -use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::query::type_op::TypeOpOutput; use tracing::{debug, instrument}; use crate::constraints::OutlivesConstraint; @@ -287,11 +287,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { - match self.infcx.param_env.and(DeeplyNormalize { value: ty }).fully_perform( - self.infcx, - self.infcx.root_def_id, - self.span, - ) { + match self.infcx.fully_perform(DeeplyNormalize { value: ty }, self.span) { Ok(TypeOpOutput { output: ty, constraints, .. }) => { // FIXME(higher_ranked_auto): What should we do with the assumptions here? if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints { diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 6c7beb85f1e8c..62becc8e298f4 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; +use rustc_trait_selection::traits::query::type_op; use tracing::{debug, instrument}; use type_op::TypeOpOutput; @@ -242,15 +242,14 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { if let Some(c) = constraints_unnorm { constraints.push(c) } - let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = - param_env - .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span) - .unwrap_or_else(|guar| TypeOpOutput { - output: Ty::new_error(self.infcx.tcx, guar), - constraints: None, - error_info: None, - }); + let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self + .infcx + .fully_perform(DeeplyNormalize { value: ty }, span) + .unwrap_or_else(|guar| TypeOpOutput { + output: Ty::new_error(self.infcx.tcx, guar), + constraints: None, + error_info: None, + }); if let Some(c) = constraints_normalize { constraints.push(c) } @@ -299,9 +298,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - let result: Result<_, ErrorGuaranteed> = param_env - .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span); + let result: Result<_, ErrorGuaranteed> = + self.infcx.fully_perform(DeeplyNormalize { value: ty }, span); let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { continue; }; @@ -354,11 +352,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { output: normalized_outlives, constraints: constraints_normalize, error_info: _, - }) = self.infcx.param_env.and(DeeplyNormalize { value: outlives }).fully_perform( - self.infcx, - self.infcx.root_def_id, - span, - ) + }) = self.infcx.fully_perform(DeeplyNormalize { value: outlives }, span) else { self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}")); return; @@ -381,9 +375,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { let TypeOpOutput { output: bounds, constraints, .. } = self .infcx - .param_env - .and(type_op::ImpliedOutlivesBounds { ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span) + .fully_perform(type_op::ImpliedOutlivesBounds { ty }, span) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; debug!(?bounds, ?constraints); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 840210496eb44..5a06f54cd2e41 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -15,7 +15,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::query::dropck_outlives; -use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; use crate::polonius; @@ -638,9 +638,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { ) -> DropData<'tcx> { debug!("compute_drop_data(dropped_ty={:?})", dropped_ty); - let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty }); + let goal = DropckOutlives { dropped_ty }; - match op.fully_perform(typeck.infcx, typeck.root_cx.root_def_id(), DUMMY_SP) { + match typeck.infcx.fully_perform(goal, DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { DropData { dropck_result: output, region_constraint_data: constraints } } @@ -655,7 +655,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { typeck.infcx.probe(|_| { let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx); let errors = match dropck_outlives::compute_dropck_outlives_with_errors( - &ocx, op, span, + &ocx, + typeck.infcx.param_env.and(goal), + span, ) { Ok(_) => ocx.evaluate_obligations_error_on_ambiguity(), Err(e) => e, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 450ac46aab521..123bc1f568495 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -95,7 +95,7 @@ mod relate_tys; /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis /// - `location_map` -- map between MIR `Location` and `PointIndex` pub(crate) fn type_check<'tcx>( - root_cx: &mut BorrowCheckRootCtxt<'tcx>, + root_cx: &BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, @@ -198,7 +198,6 @@ pub(crate) fn type_check<'tcx>( debug!("encountered an error region; removing constraints!"); constraints.outlives_constraints = Default::default(); constraints.type_tests = Default::default(); - root_cx.set_tainted_by_errors(guar); infcx.set_tainted_by_errors(guar); } @@ -229,7 +228,7 @@ enum FieldAccessError { /// way, it accrues region constraints -- these can later be used by /// NLL region checking. struct TypeChecker<'a, 'tcx> { - root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, + root_cx: &'a BorrowCheckRootCtxt<'tcx>, infcx: &'a BorrowckInferCtxt<'tcx>, last_span: Span, body: &'a Body<'tcx>, @@ -469,7 +468,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Necessary for non-trivial patterns whose user-type annotation is an opaque type, // e.g. `let (_a,): Tait = whatever`, see #105897 if !self.infcx.next_trait_solver() - && let ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) = + && let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) = curr_projected_ty.ty.kind() { // There is nothing that we can compare here if we go through an opaque type. @@ -1760,13 +1759,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let tcx = self.tcx(); let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Unevaluated(uv) => match uv.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { - Some(UnevaluatedConst { def: def_id, args: uv.args, promoted: None }) - } + ty::ConstKind::Alias(_, alias_const) => match alias_const.kind { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => Some(UnevaluatedConst { + def: def_id, + args: alias_const.args, + promoted: None, + }), }, _ => None, }, diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index f18884df0b44d..24383eea3600f 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -150,10 +150,10 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { }; let (a, b) = match (a.kind(), b.kind()) { - (&ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), _) => { + (&ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), _) => { (a, enable_subtyping(b, true)?) } - (_, &ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. })) => { + (_, &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. })) => { (enable_subtyping(a, false)?, b) } _ => unreachable!( @@ -386,8 +386,8 @@ impl<'b, 'tcx> TypeRelation> for NllTypeRelating<'_, 'b, 'tcx> { } ( - &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: a_def_id }, .. }), - &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }), + &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: a_def_id }, .. }), + &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }), ) if a_def_id == b_def_id || infcx.next_trait_solver() => { super_combine_tys(&infcx.infcx, self, a, b).map(|_| ()).or_else(|err| { // This behavior is only there for the old solver, the new solver @@ -401,8 +401,8 @@ impl<'b, 'tcx> TypeRelation> for NllTypeRelating<'_, 'b, 'tcx> { if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } })?; } - (&ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }), _) - | (_, &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. })) + (&ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }), _) + | (_, &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. })) if def_id.is_local() && !self.type_checker.infcx.next_trait_solver() => { self.relate_opaques(a, b)?; diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 804d2757cf998..b0adbcd6db4ef 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -615,7 +615,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { match tcx.def_kind(self.mir_def) { - DefKind::InlineConst => { + DefKind::InlineConst if !tcx.is_type_system_inline_const(self.mir_def) => { // This is required for `AscribeUserType` canonical query, which will call // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes // into borrowck, which is ICE #78174. diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 007251ff3df05..89ea581d34d60 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -607,6 +607,8 @@ fn make_format_args( // If there's a lot of unused arguments, // let's check if this format arguments looks like another syntax (printf / shell). let detect_foreign_fmt = unused.len() > args.explicit_args().len() / 2; + let foreign_fmt_str = + if append_newline { fmt_str.strip_suffix('\n').unwrap_or(fmt_str) } else { fmt_str }; report_missing_placeholders( ecx, unused, @@ -616,7 +618,7 @@ fn make_format_args( &invalid_refs, detect_foreign_fmt, str_style, - fmt_str, + foreign_fmt_str, uncooked_fmt_str.1.as_str(), fmt_span, ); diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 0eb493036ce51..012951098fa15 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -16,9 +16,9 @@ use rustc_abi::{CanonAbi, ExternAbi, X86Call}; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{ShimKind, TypeVisitableExt}; use rustc_session::Session; use rustc_span::Spanned; use rustc_target::callconv::{FnAbi, PassMode}; @@ -271,6 +271,7 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ .map(|local| { let arg_ty = fx.monomorphize(fx.mir.local_decls[local].ty); + // FIXME(splat): un-tuple splatted arguments in codegen, for performance // Adapted from https://github.com/rust-lang/rust/blob/145155dc96757002c7b2e9de8489416e2fdbbd57/src/librustc_codegen_llvm/mir/mod.rs#L442-L482 if Some(local) == fx.mir.spread_arg { // This argument (e.g. the last argument in the "rust-call" ABI) @@ -467,7 +468,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( } // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, // it is `func returning noop future` - InstanceKind::DropGlue(_, None) => { + InstanceKind::Shim(ShimKind::DropGlue(_, None)) => { // empty drop glue - a nop. let dest = target.expect("Non terminating drop_in_place_real???"); let ret_block = fx.get_block(dest); @@ -725,7 +726,7 @@ pub(crate) fn codegen_drop<'tcx>( let ret_block = fx.get_block(target); // AsyncDropGlueCtorShim can't be here - if let ty::InstanceKind::DropGlue(_, None) = drop_instance.def { + if let ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None)) = drop_instance.def { // we don't actually need to drop anything fx.bcx.ins().jump(ret_block, &[]); } else { diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index b22afca847aa9..69e472b33abac 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -349,7 +349,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(value), + ty::EarlyBinder::bind(self.tcx, value), ) } diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index c986666f9c46e..829e7d0dfb59c 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -53,7 +53,7 @@ pub(crate) fn codegen_tls_ref<'tcx>( ) -> CValue<'tcx> { let tls_ptr = if !def_id.is_local() && fx.tcx.needs_thread_local_shim(def_id) { let instance = ty::Instance { - def: ty::InstanceKind::ThreadLocalShim(def_id), + def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }; let func_ref = fx.get_function_ref(instance); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index 2dee9176936fd..9ec84d135dc8f 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -19,7 +19,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( } match intrinsic { - "llvm.prefetch" => { + "llvm.prefetch.p0" => { // Nothing to do. This is merely a perf hint. } diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index a2a2cac3faaa8..995a70f24240b 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -56,7 +56,7 @@ fn codegen_field<'tcx>( } fn scalar_pair_calculate_b_offset(tcx: TyCtxt<'_>, a_scalar: Scalar, b_scalar: Scalar) -> Offset32 { - let b_offset = a_scalar.size(&tcx).align_to(b_scalar.align(&tcx).abi); + let b_offset = a_scalar.size(&tcx).align_to(b_scalar.default_align(&tcx).abi); Offset32::new(b_offset.bytes().try_into().unwrap()) } diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 53074e313f9ce..6fd7188f656c5 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -706,7 +706,9 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str { unreachable!("clobber-only") } InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) + | InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::vreg) + | InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::xreg) => "f", InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r", InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => "a", InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => "d", @@ -815,6 +817,12 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl } InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::vreg) => { + cx.type_vector(cx.type_i32(), 4) + } + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::xreg) => { + cx.type_vector(cx.type_i32(), 8) + } InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), @@ -1013,7 +1021,22 @@ fn modifier_to_gcc( } } InlineAsmRegClass::Hexagon(_) => None, - InlineAsmRegClass::LoongArch(_) => None, + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => None, + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => modifier, + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::vreg) => { + if modifier.is_none() { + Some('w') + } else { + modifier + } + } + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::xreg) => { + if modifier.is_none() { + Some('u') + } else { + modifier + } + } InlineAsmRegClass::Mips(_) => None, InlineAsmRegClass::Nvptx(_) => None, InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vsreg) => { diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 7658b86a3a200..7a25fc46fd3fc 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -7,7 +7,6 @@ use gccjit::{CType, Context, FunctionType, GlobalKind}; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; -use rustc_codegen_ssa::traits::DebugInfoCodegenMethods; use rustc_hir::attrs::{AttributeKind, Linkage}; use rustc_hir::find_attr; use rustc_middle::dep_graph; diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 33f0f6fc2f809..f9358872299b7 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -993,7 +993,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { loaded_value.to_rvalue() } - fn volatile_load(&mut self, ty: Type<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> { + fn volatile_load(&mut self, ty: Type<'gcc>, ptr: RValue<'gcc>, _: Align) -> RValue<'gcc> { + // FIXME(antoyo): set alignment. let ptr = self.context.new_cast(self.location, ptr, ty.make_volatile().make_pointer()); // (FractalFir): We insert a local here, to ensure this volatile load can't move across // blocks. @@ -1064,7 +1065,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { }, ) } else if let abi::BackendRepr::ScalarPair(ref a, ref b) = place.layout.backend_repr { - let b_offset = a.size(self).align_to(b.align(self).abi); + let b_offset = a.size(self).align_to(b.default_align(self).abi); let mut load = |i, scalar: &abi::Scalar, align| { let ptr = if i == 0 { @@ -1459,6 +1460,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { ); } + fn vscale(&mut self, _: Self::Type) -> Self::Value { + unimplemented!("`rustc_codegen_gcc` doesn't support scalable vectors yet") + } + fn select( &mut self, cond: RValue<'gcc>, diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index dd0064d34bc4a..7450f11f3ce80 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -2,7 +2,8 @@ use gccjit::{LValue, RValue, ToRValue, Type}; use rustc_abi::Primitive::Pointer; use rustc_abi::{self as abi, HasDataLayout}; use rustc_codegen_ssa::traits::{ - BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, + BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, PacMetadata, + StaticCodegenMethods, }; use rustc_middle::mir::Mutability; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; @@ -58,13 +59,19 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> // or is it using a more efficient representation? match bytes.len() % 8 { 0 => { + debug_assert_eq!( + bytes.len() % 8, + 0, + "bytes length is not a multiple of 8, so bytes.as_chunks will have a remainder" + ); let context = &cx.context; let byte_type = context.new_type::(); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 8); let elements: Vec<_> = bytes - .chunks_exact(8) - .map(|arr| { - let arr: [u8; 8] = arr.try_into().unwrap(); + .as_chunks::<8>() + .0 + .iter() + .map(|&arr| { context.new_rvalue_from_long( byte_type, // Since we are representing arbitrary byte runs as integers, we need to follow the target @@ -79,13 +86,19 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> context.new_array_constructor(None, typ, &elements) } 4 => { + debug_assert_eq!( + bytes.len() % 4, + 0, + "bytes length is not a multiple of 4, so bytes.as_chunks will have a remainder" + ); let context = &cx.context; let byte_type = context.new_type::(); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 4); let elements: Vec<_> = bytes - .chunks_exact(4) - .map(|arr| { - let arr: [u8; 4] = arr.try_into().unwrap(); + .as_chunks::<4>() + .0 + .iter() + .map(|&arr| { context.new_rvalue_from_int( byte_type, match cx.sess().target.options.endian { @@ -229,7 +242,13 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { None } - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> { + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + ty: Type<'gcc>, + _pac: Option, + ) -> RValue<'gcc> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { Scalar::Int(int) => { @@ -278,7 +297,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { } value } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, None), GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index ea71546ea1c0e..3a6c415b38963 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -5,7 +5,9 @@ use gccjit::{Block, CType, Context, Function, FunctionType, LValue, Location, RV use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::errors as ssa_errors; -use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods}; +use rustc_codegen_ssa::traits::{ + BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods, PacMetadata, +}; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::interpret::Allocation; @@ -398,7 +400,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { get_fn(self, instance) } - fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> { + fn get_fn_addr(&self, instance: Instance<'tcx>, _pac: Option) -> RValue<'gcc> { let func_name = self.tcx.symbol_name(instance).name; let func = if let Some(variable) = self.get_declared_value(func_name) { diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 8907d8a42b38f..5e598e5825d1b 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -16,6 +16,66 @@ pub(super) const UNKNOWN_LINE_NUMBER: u32 = 0; pub(super) const UNKNOWN_COLUMN_NUMBER: u32 = 0; impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { + fn dbg_scope_fn( + &mut self, + _instance: Instance<'tcx>, + _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + _maybe_definition_llfn: Option>, + ) -> Self::DIScope { + // FIXME(antoyo): implement. + } + + fn dbg_create_lexical_block( + &mut self, + _pos: BytePos, + _parent_scope: Self::DIScope, + ) -> Self::DIScope { + } + + fn dbg_location_clone_with_discriminator( + &mut self, + loc: Self::DILocation, + _discriminator: u32, + ) -> Option { + Some(loc) + } + + fn extend_scope_to_file( + &mut self, + _scope_metadata: Self::DIScope, + _file: &SourceFile, + ) -> Self::DIScope { + // FIXME(antoyo): implement. + } + + fn dbg_loc( + &mut self, + _scope: Self::DIScope, + _inlined_at: Option, + span: Span, + ) -> Self::DILocation { + let pos = span.lo(); + let DebugLoc { file, line, col } = self.lookup_debug_loc(pos); + match file.name { + rustc_span::FileName::Real(ref name) => self.context.new_location( + name.path(rustc_span::RemapPathScopeComponents::DEBUGINFO).to_string_lossy(), + line as i32, + col as i32, + ), + _ => Location::null(), + } + } + + fn create_dbg_var( + &mut self, + _variable_name: Symbol, + _variable_type: Ty<'tcx>, + _scope_metadata: Self::DIScope, + _variable_kind: VariableKind, + _span: Span, + ) -> Self::DIVariable { + } + // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). fn dbg_var_addr( @@ -103,6 +163,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { DebugLoc { file, line, col } } } + + pub(crate) fn debuginfo_finalize(&self) { + self.context.set_debug_info(true) + } } impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { @@ -114,68 +178,4 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { ) { // FIXME(antoyo) } - - fn dbg_create_lexical_block( - &self, - _pos: BytePos, - _parent_scope: Self::DIScope, - ) -> Self::DIScope { - } - - fn dbg_location_clone_with_discriminator( - &self, - loc: Self::DILocation, - _discriminator: u32, - ) -> Option { - Some(loc) - } - - fn extend_scope_to_file( - &self, - _scope_metadata: Self::DIScope, - _file: &SourceFile, - ) -> Self::DIScope { - // FIXME(antoyo): implement. - } - - fn debuginfo_finalize(&self) { - self.context.set_debug_info(true) - } - - fn create_dbg_var( - &self, - _variable_name: Symbol, - _variable_type: Ty<'tcx>, - _scope_metadata: Self::DIScope, - _variable_kind: VariableKind, - _span: Span, - ) -> Self::DIVariable { - } - - fn dbg_scope_fn( - &self, - _instance: Instance<'tcx>, - _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - _maybe_definition_llfn: Option>, - ) -> Self::DIScope { - // FIXME(antoyo): implement. - } - - fn dbg_loc( - &self, - _scope: Self::DIScope, - _inlined_at: Option, - span: Span, - ) -> Self::DILocation { - let pos = span.lo(); - let DebugLoc { file, line, col } = self.lookup_debug_loc(pos); - match file.name { - rustc_span::FileName::Real(ref name) => self.context.new_location( - name.path(rustc_span::RemapPathScopeComponents::DEBUGINFO).to_string_lossy(), - line as i32, - col as i32, - ), - _ => Location::null(), - } - } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index d58697f1bf270..41efe3e8209bf 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs @@ -1044,7 +1044,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function #[cfg(feature = "master")] pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function<'gcc> { let gcc_name = match name { - "llvm.prefetch" => { + "llvm.prefetch.p0" => { let gcc_name = "__builtin_prefetch"; let func = cx.context.get_builtin_function(gcc_name); cx.functions.borrow_mut().insert(gcc_name.to_string(), func); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a12116d5b9d39..78a4c7e88c895 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -5,7 +5,7 @@ mod simd; use std::iter; use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; -use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; +use rustc_abi::{Align, BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::errors::InvalidMonomorphization; @@ -368,8 +368,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::volatile_load | sym::unaligned_volatile_load => { let ptr = args[0].immediate(); - let load = self.volatile_load(result.layout.gcc_type(self), ptr); - // FIXME(antoyo): set alignment. + let abi_align = result_layout.align.abi; + let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE }; + let load = self.volatile_load(result.layout.gcc_type(self), ptr, ptr_align); if let BackendRepr::Scalar(scalar) = result.layout.backend_repr { self.to_immediate_scalar(load, scalar) } else { diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 5b198eeaf0182..9807a84c0788d 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -325,7 +325,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { return cx.type_i1(); } - let offset = if index == 0 { Size::ZERO } else { a.size(cx).align_to(b.align(cx).abi) }; + let offset = + if index == 0 { Size::ZERO } else { a.size(cx).align_to(b.default_align(cx).abi) }; self.scalar_gcc_type_at(cx, scalar, offset) } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 0ffff2d331b1d..ec850cf7ab3d2 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -12,7 +12,7 @@ bitflags = "2.4.1" # To avoid duplicate dependencies, this should match the version of gimli used # by `rustc_codegen_ssa` via its `thorin-dwp` dependency. gimli = "0.31" -itertools = "0.12" +itertools = "0.15" libc = "0.2" libloading = { version = "0.9.0" } measureme = "12.0.1" diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 9bace9d2acf61..b20df0a6bad02 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -99,9 +99,9 @@ pub(crate) unsafe fn codegen( ); if tcx.sess.opts.debuginfo != DebugInfo::None { - let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod); + let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod, tcx.sess); debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx); - dbg_cx.finalize(tcx.sess); + dbg_cx.finalize(); } } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 2598c1b38ff88..8f192a5a73b63 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -709,7 +709,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> Hexagon(HexagonInlineAsmRegClass::vreg_pair) => "v", Hexagon(HexagonInlineAsmRegClass::qreg) => unreachable!("clobber-only"), LoongArch(LoongArchInlineAsmRegClass::reg) => "r", - LoongArch(LoongArchInlineAsmRegClass::freg) => "f", + LoongArch(LoongArchInlineAsmRegClass::freg) + | LoongArch(LoongArchInlineAsmRegClass::vreg) + | LoongArch(LoongArchInlineAsmRegClass::xreg) => "f", Mips(MipsInlineAsmRegClass::reg) => "r", Mips(MipsInlineAsmRegClass::freg) => "f", Nvptx(NvptxInlineAsmRegClass::reg16) => "h", @@ -814,7 +816,22 @@ fn modifier_to_llvm( } Amdgpu(_) => None, Hexagon(_) => None, - LoongArch(_) => None, + LoongArch(LoongArchInlineAsmRegClass::reg) => None, + LoongArch(LoongArchInlineAsmRegClass::freg) => modifier, + LoongArch(LoongArchInlineAsmRegClass::vreg) => { + if modifier.is_none() { + Some('w') + } else { + modifier + } + } + LoongArch(LoongArchInlineAsmRegClass::xreg) => { + if modifier.is_none() { + Some('u') + } else { + modifier + } + } Mips(_) => None, Nvptx(_) => None, PowerPC(PowerPCInlineAsmRegClass::vsreg) => { @@ -917,6 +934,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' Hexagon(HexagonInlineAsmRegClass::qreg) => unreachable!("clobber-only"), LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(), LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(), + LoongArch(LoongArchInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i32(), 4), + LoongArch(LoongArchInlineAsmRegClass::xreg) => cx.type_vector(cx.type_i32(), 8), Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 0176f891ddda9..774ffa8df9342 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -7,12 +7,17 @@ use rustc_middle::middle::codegen_fn_attrs::{ TargetFeature, }; use rustc_middle::ty::{self, Instance, TyCtxt}; -use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet}; +use rustc_session::config::{ + BranchProtection, FunctionReturn, InstrumentMcount, OptLevel, PAuthKey, PacRet, +}; use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector}; +use rustc_target::spec::{ + Arch, FramePointer, LlvmAbi, SanitizerSet, StackProbeType, StackProtector, +}; use smallvec::SmallVec; +use crate::common::pauth_fn_attrs; use crate::context::SimpleCx; use crate::errors::{PackedStackBackchainNeedsSoftfloat, SanitizerMemtagRequiresMte}; use crate::llvm::AttributePlace::Function; @@ -84,11 +89,26 @@ fn patchable_function_entry_attrs<'ll>( attr: Option, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); - let patchable_spec = attr.unwrap_or_else(|| { - PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry) - }); - let entry = patchable_spec.entry(); - let prefix = patchable_spec.prefix(); + + let mut entry = sess.opts.unstable_opts.patchable_function_entry.entry(); + let mut prefix = sess.opts.unstable_opts.patchable_function_entry.prefix(); + let mut section = sess.opts.unstable_opts.patchable_function_entry.section(); + let section_sym; + + // Apply attribute specified overrides, if any. + if let Some(patchable_spec) = attr { + if let Some(sym) = patchable_spec.section() { + section_sym = sym; + section = Some(section_sym.as_str()); + } + // Override the nop counts if either is present. If only one is present, the + // other count is implied to be 0. + if patchable_spec.entry().is_some() || patchable_spec.prefix().is_some() { + entry = patchable_spec.entry().unwrap_or(0); + prefix = patchable_spec.prefix().unwrap_or(0); + } + } + if entry > 0 { attrs.push(llvm::CreateAttrStringValue( cx.llcx, @@ -103,6 +123,13 @@ fn patchable_function_entry_attrs<'ll>( &format!("{}", prefix), )); } + if let Some(section) = section { + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "patchable-function-entry-section", + section, + )); + } attrs } @@ -177,7 +204,7 @@ pub(crate) fn frame_pointer(sess: &Session) -> FramePointer { let opts = &sess.opts; // "mcount" function relies on stack pointer. // See . - if opts.unstable_opts.instrument_mcount { + if opts.unstable_opts.instrument_mcount == InstrumentMcount::Mcount { fp.ratchet(FramePointer::Always); } fp.ratchet(opts.cg.force_frame_pointers); @@ -214,7 +241,7 @@ fn instrument_function_attr<'ll>( instrument_fn: InstrumentFnAttr, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - if sess.opts.unstable_opts.instrument_mcount { + if sess.opts.unstable_opts.instrument_mcount != InstrumentMcount::Disabled { // Similar to `clang -pg` behavior. Handled by the // `post-inline-ee-instrument` LLVM pass. @@ -224,18 +251,26 @@ fn instrument_function_attr<'ll>( }; if instrument_entry { - // The function name varies on platforms. - // See test/CodeGen/mcount.c in clang. - let mcount_name = match &sess.target.llvm_mcount_intrinsic { - Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), - None => sess.target.mcount.as_ref(), - }; - - attrs.push(llvm::CreateAttrStringValue( - cx.llcx, - "instrument-function-entry-inlined", - mcount_name, - )); + match sess.opts.unstable_opts.instrument_mcount { + InstrumentMcount::Mcount => { + // The function name varies on platforms. + // See test/CodeGen/mcount.c in clang. + let mcount_name = match &sess.target.llvm_mcount_intrinsic { + Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), + None => sess.target.mcount.as_ref(), + }; + + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "instrument-function-entry-inlined", + mcount_name, + )); + } + InstrumentMcount::Fentry => { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "fentry-call", "true")); + } + InstrumentMcount::Disabled => {} + } } } if let Some(options) = &sess.opts.unstable_opts.instrument_xray { @@ -633,6 +668,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } + if sess.target.llvm_abiname == LlvmAbi::Pauthtest { + for &ptrauth_attr in pauth_fn_attrs() { + to_add.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); + } + } + to_add.extend(target_features_attr(cx, tcx, function_features)); attributes::apply_to_llfn(llfn, Function, &to_add); diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index a7dec13422f46..bb4ae6e64560a 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -25,12 +25,13 @@ use rustc_middle::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, Offload}; use rustc_span::Symbol; -use rustc_target::spec::SanitizerSet; +use rustc_target::spec::{LlvmAbi, SanitizerSet}; use super::ModuleLlvm; use crate::attributes; use crate::builder::Builder; use crate::builder::gpu_offload::OffloadGlobals; +use crate::common::pauth_fn_attrs; use crate::context::CodegenCx; use crate::llvm::{self, Value}; @@ -123,7 +124,18 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) { - let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); + let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); + // When pointer authentication is enabled, ensure that the ptrauth-* attributes are + // also attached to the entry wrapper. + // + // FIXME(jchlanda) If it ever becomes necessary to ensure that all compiler + // generated functions receive the ptrauth-* attributes, `declare_fn` or + // `declare_raw_fn` could be used to provide those. + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { + for &ptrauth_attr in pauth_fn_attrs() { + attrs.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); + } + } attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } @@ -140,6 +152,30 @@ pub(crate) fn compile_codegen_unit( cx.add_objc_module_flags(); } + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { + // FIXME(jchlanda): In LLVM/Clang, there are also `aarch64-elf-pauthabi-platform` + // and `aarch64-elf-pauthabi-version` module flags. These are emitted into the + // PAuth core info section of the resulting ELF, which the linker uses to enforce + // binary compatibility. + // + // We intentionally do not emit these flags now, since only a subset of features + // included in clang's pauthtest is currently supported. By default, the absence of + // this info is treated as compatible with any binary. + // + // Please note, that this would cause compatibility issues, specifically runtime + // crashes due to authentication failures (while compiling and linking + // successfully) when linking against binaries that support larger set of features + // (for example, signing of C++ member function pointers, virtual function + // pointers, virtual table pointers). + // + // Link to PAuth core info documentation: + // + if cx.sess().opts.unstable_opts.ptrauth_elf_got { + cx.add_ptrauth_elf_got_flag(); + } + cx.add_ptrauth_sign_personality_flag(); + } + // Finalize code coverage by injecting the coverage map. Note, the coverage map will // also be added to the `llvm.compiler.used` variable, created next. if cx.sess().instrument_coverage() { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index acfec99438059..804547745c5d5 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -7,7 +7,7 @@ pub(crate) mod autodiff; pub(crate) mod gpu_offload; use libc::{c_char, c_uint}; -use rustc_abi::{self as abi, Align, Size, WrappingRange}; +use rustc_abi::{self as abi, Align, CanonAbi, Size, WrappingRange}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -26,7 +26,7 @@ use rustc_sanitizers::{cfi, kcfi}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::callconv::{FnAbi, PassMode}; -use rustc_target::spec::{Arch, HasTargetSpec, SanitizerSet, Target}; +use rustc_target::spec::{Arch, HasTargetSpec, LlvmAbi, SanitizerSet, Target}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -475,6 +475,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let invoke = unsafe { llvm::LLVMBuildInvokeWithOperandBundles( self.llbuilder, @@ -682,9 +687,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn volatile_load(&mut self, ty: &'ll Type, ptr: &'ll Value) -> &'ll Value { + fn volatile_load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { - let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let load = self.load(ty, ptr, align); llvm::LLVMSetVolatile(load, llvm::TRUE); load } @@ -789,7 +794,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { }); OperandValue::Immediate(llval) } else if let abi::BackendRepr::ScalarPair(a, b) = place.layout.backend_repr { - let b_offset = a.size(self).align_to(b.align(self).abi); + let b_offset = a.size(self).align_to(b.default_align(self).abi); let mut load = |i, scalar: abi::Scalar, layout, align, offset| { let llptr = if i == 0 { @@ -1226,6 +1231,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } + fn vscale(&mut self, ty: &'ll Type) -> &'ll Value { + unsafe { llvm::LLVMRustBuildVScale(self.llbuilder, ty) } + } + fn select( &mut self, cond: &'ll Value, @@ -1468,6 +1477,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let call = unsafe { llvm::LLVMBuildCallWithOperandBundles( self.llbuilder, @@ -1898,6 +1912,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let callbr = unsafe { llvm::LLVMBuildCallBr( self.llbuilder, @@ -2017,6 +2036,40 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { kcfi_bundle } + // Emits pauth operand bundle. + fn ptrauth_operand_bundle( + &mut self, + llfn: &'ll Value, + fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + ) -> Option> { + if self.sess().target.llvm_abiname != LlvmAbi::Pauthtest { + return None; + } + // Pointer authentication support is currently limited to extern "C" calls; filter out other + // ABIs. + if fn_abi?.conv != CanonAbi::C { + return None; + } + // Filter out LLVM intrinsics. + if llvm::get_value_name(llfn).starts_with(b"llvm.") { + return None; + } + + // FIXME(jchlanda) Operand bundles should only be attached to indirect function calls. + // However, function pointer signing is currently performed in `get_fn_addr`, which causes + // the logic to be applied too broadly, including to function values (not just pointers). + // As a result, direct calls using signed function values must also receive operand + // bundles. + // Once this is resolved, we should analyze each call and skip direct calls. See the + // discussion in the rust-lang issue: + let key: u32 = 0; + let discriminator: u64 = 0; + Some(llvm::OperandBundleBox::new( + "ptrauth", + &[self.const_u32(key), self.const_u64(discriminator)], + )) + } + /// Emits a call to `llvm.instrprof.increment`. Used by coverage instrumentation. #[instrument(level = "debug", skip(self))] pub(crate) fn instrprof_increment( diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 1cefdaae5ebde..ee17468ec0c03 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,6 +3,9 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; use rustc_ast::expand::typetree::FncTree; use rustc_codegen_ssa::common::TypeKind; +use rustc_codegen_ssa::mir::IntrinsicResult; +use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; +use rustc_codegen_ssa::mir::place::PlaceValue; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir::attrs::RustcAutodiff; @@ -11,7 +14,7 @@ use rustc_middle::{bug, ty}; use rustc_target::callconv::PassMode; use tracing::debug; -use crate::builder::{Builder, PlaceRef, UNNAMED}; +use crate::builder::{Builder, UNNAMED}; use crate::context::SimpleCx; use crate::declare::declare_simple_fn; use crate::llvm::{self, TRUE, Type, Value}; @@ -296,9 +299,10 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( ret_ty: &'ll Type, fn_args: &[&'ll Value], attrs: &RustcAutodiff, - dest: PlaceRef<'tcx, &'ll Value>, + dest_layout: ty::layout::TyAndLayout<'tcx>, + dest_place: Option>, fnc_tree: FncTree, -) { +) -> IntrinsicResult<'tcx, &'ll Value> { // We have to pick the name depending on whether we want forward or reverse mode autodiff. let mut ad_name: String = match attrs.mode { DiffMode::Forward => "__enzyme_fwddiff", @@ -381,11 +385,18 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); let fn_ret_ty = builder.cx.val_ty(call); - if fn_ret_ty != builder.cx.type_void() && fn_ret_ty != builder.cx.type_struct(&[], false) { + if fn_ret_ty == builder.cx.type_void() || fn_ret_ty == builder.cx.type_struct(&[], false) { // If we return void or an empty struct, then our caller (due to how we generated it) // does not expect a return value. As such, we have no pointer (or place) into which // we could store our value, and would store into an undef, which would cause UB. // As such, we just ignore the return value in those cases. - builder.store_to_place(call, dest.val); + IntrinsicResult::Operand(OperandValue::ZeroSized) + } else if let Some(dest_place) = dest_place { + builder.store_to_place(call, dest_place); + IntrinsicResult::WroteIntoPlace + } else { + IntrinsicResult::Operand( + OperandRef::from_immediate_or_packed_pair(builder, call, dest_layout).val, + ) } } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 5c5e9ed7e082c..1c26738b7cf9c 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -4,23 +4,81 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, HasDataLayout as _}; +use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_hashes::Hash128; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Instance, TyCtxt}; use rustc_session::cstore::DllImport; +use rustc_target::spec::LlvmAbi; use tracing::debug; -use crate::consts::const_alloc_to_llvm; +use crate::consts::{IsInitOrFini, IsStatic, const_alloc_to_llvm}; pub(crate) use crate::context::CodegenCx; use crate::context::{GenericCx, SCx}; -use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, Value}; +use crate::llvm::{ + self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, Value, const_ptr_auth, +}; + +#[inline] +pub(crate) fn pauth_fn_attrs() -> &'static [&'static str] { + // FIXME(jchlanda) This is not an exhaustive list of all `ptrauth`-related attributes, but only + // those currently supported. The list is expected to grow as additional functionality is + // implemented, particularly for C++ interoperability. + &[ + "aarch64-jump-table-hardening", + "ptrauth-indirect-gotos", + "ptrauth-calls", + "ptrauth-returns", + "ptrauth-auth-traps", + ] +} + +pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( + cx: &CodegenCx<'ll, '_>, + instance: Instance<'tcx>, + llfn: &'ll llvm::Value, + pac: PacMetadata, +) -> &'ll llvm::Value { + if cx.sess().target.llvm_abiname != LlvmAbi::Pauthtest { + return llfn; + } + + // Only free functions or methods + let def_id = instance.def_id(); + if !matches!(cx.tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { + return llfn; + } + // Only C ABI + let abi = cx.tcx.fn_sig(def_id).skip_binder().abi(); + if !matches!(abi, ExternAbi::C { .. } | ExternAbi::System { .. }) { + return llfn; + } + // Ignore LLVM intrinsics + if llvm::get_value_name(llfn).starts_with(b"llvm.") { + return llfn; + } + if Some(def_id) == cx.tcx.lang_items().eh_personality() { + return llfn; + } + + let addr_diversity = match pac.addr_diversity { + AddressDiversity::None => None, + AddressDiversity::Real => Some(llfn), + AddressDiversity::Synthetic(val) => { + let llval = cx.const_u64(val); + let llty = cx.val_ty(llfn); + Some(unsafe { llvm::LLVMConstIntToPtr(llval, llty) }) + } + }; + const_ptr_auth(llfn, pac.key, pac.disc, addr_diversity) +} /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". @@ -268,7 +326,13 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }) } - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: &'ll Type) -> &'ll Value { + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + llty: &'ll Type, + pac: Option, + ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { Scalar::Int(int) => { @@ -297,8 +361,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { self.const_bitcast(llval, llty) }; } else { - let init = - const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + let init = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ); let alloc = alloc.inner(); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), @@ -319,7 +387,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { value } } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, pac), GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx @@ -330,7 +398,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }), ))) .unwrap_memory(); - let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + let init = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ); self.static_addr_of_impl(init, alloc.inner().align, None) } GlobalAlloc::Static(def_id) => { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index ab3e4b8d9effa..a4d52c18c890b 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -1,6 +1,6 @@ use std::ops::Range; -use rustc_abi::{Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; +use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; use rustc_hir::LangItem; @@ -17,19 +17,30 @@ use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use rustc_span::Symbol; -use rustc_target::spec::Arch; +use rustc_target::spec::{Arch, LlvmAbi}; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; use crate::errors::SymbolAlreadyDefined; -use crate::llvm::{self, Type, Value}; +use crate::llvm::{self, Type, Value, const_ptr_auth}; use crate::type_of::LayoutLlvmExt; use crate::{base, debuginfo}; +/// Indicates whether a value originates from a `static`. +pub(crate) enum IsStatic { + Yes, + No, +} +/// Indicates whether a symbol is part of `.init_array` or `.fini_array`. +pub(crate) enum IsInitOrFini { + Yes, + No, +} pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, alloc: &Allocation, - is_static: bool, + is_static: IsStatic, + is_init_fini: IsInitOrFini, ) -> &'ll Value { // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be @@ -38,7 +49,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( // // Statics have a guaranteed meaningful address so it's less clear that we want to do // something like this; it's also harder. - if !is_static { + if matches!(is_static, IsStatic::No) { assert!(alloc.len() != 0); } let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1); @@ -109,14 +120,31 @@ pub(crate) fn const_alloc_to_llvm<'ll>( as u64; let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); - - llvals.push(cx.scalar_to_backend( + // Under pointer authentication, function pointers stored in init/fini arrays need special + // handling. + let pac_metadata = Some( + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest + && matches!(is_init_fini, IsInitOrFini::Yes) + { + PacMetadata { + // Must correspond to ptrauth_key_init_fini_pointer from `ptrauth.h`. + key: 0, + // ptrauth_string_discriminator("init_fini") + disc: 0xd9d4, + addr_diversity: AddressDiversity::Synthetic(1), + } + } else { + PacMetadata::default() + }, + ); + llvals.push(cx.scalar_to_backend_with_pac( InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx), Scalar::Initialized { value: Primitive::Pointer(address_space), valid_range: WrappingRange::full(pointer_size), }, cx.type_ptr_ext(address_space), + pac_metadata, )); next_offset = offset + pointer_size_bytes; } @@ -141,7 +169,21 @@ fn codegen_static_initializer<'ll, 'tcx>( def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; - Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc)) + let attrs = cx.tcx.codegen_fn_attrs(def_id); + // FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion + // here: + let is_in_init_fini: IsInitOrFini = attrs + .link_section + .map(|link_section| { + let s = link_section.as_str(); + if s.starts_with(".init_array") || s.starts_with(".fini_array") { + IsInitOrFini::Yes + } else { + IsInitOrFini::No + } + }) + .unwrap_or(IsInitOrFini::No); + Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc)) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { @@ -164,6 +206,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( if let Some(linkage) = attrs.import_linkage { debug!("get_static: sym={} linkage={:?}", sym, linkage); + let mut should_sign = false; // Declare a symbol `foo`. If `foo` is an extern_weak symbol, we declare // an extern_weak function, otherwise a global with the desired linkage. let g1 = if matches!(attrs.import_linkage, Some(Linkage::ExternalWeak)) { @@ -176,8 +219,13 @@ fn check_and_apply_linkage<'ll, 'tcx>( && let ty::FnPtr(sig, header) = args.type_at(0).kind() { let fn_sig = sig.with(*header); - let fn_abi = cx.fn_abi_of_fn_ptr(fn_sig, ty::List::empty()); + // Decide if the initializer needs to be signed + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest + && matches!(fn_sig.abi(), ExternAbi::C { .. } | ExternAbi::System { .. }) + { + should_sign = true; + } cx.declare_fn(sym, &fn_abi, None) } else { cx.declare_global(sym, cx.type_i8()) @@ -207,7 +255,24 @@ fn check_and_apply_linkage<'ll, 'tcx>( }); llvm::set_linkage(g2, llvm::Linkage::InternalLinkage); llvm::set_unnamed_address(g2, llvm::UnnamedAddr::Global); - llvm::set_initializer(g2, g1); + + // Sign the function pointer that is used to initialize the global + let initializer = if should_sign { + let key: u32 = 0; + let discriminator: u64 = 0; + + const_ptr_auth( + cx.const_bitcast(g1, llty), + key, + discriminator, + None, /* address_diversity */ + ) + } else { + g1 + }; + + llvm::set_initializer(g2, initializer); + g2 } else if cx.tcx.sess.target.arch == Arch::X86 && common::is_mingw_gnu_toolchain(&cx.tcx.sess.target) @@ -776,7 +841,7 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value { // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the // same `ConstAllocation`? - let cv = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); let gv = self.static_addr_of_impl(cv, alloc.inner().align, kind); // static_addr_of_impl returns the bare global variable, which might not be in the default diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 64e1ae03bde76..621f2cd3f9fc7 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -14,7 +14,6 @@ use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::PatchableFunctionEntry; use rustc_middle::mono::CodegenUnit; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, @@ -343,14 +342,13 @@ pub(crate) unsafe fn create_module<'ll>( // Add "kcfi-offset" module flag with -Z patchable-function-entry (See // https://reviews.llvm.org/D141172). - let pfe = - PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry); - if pfe.prefix() > 0 { + let patchable_prefix_nops = sess.opts.unstable_opts.patchable_function_entry.prefix(); + if patchable_prefix_nops > 0 { llvm::add_module_flag_u32( llmod, llvm::ModuleFlagMergeBehavior::Override, "kcfi-offset", - pfe.prefix().into(), + patchable_prefix_nops.into(), ); } @@ -647,7 +645,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { tcx.sess.instrument_coverage().then(coverageinfo::CguCoverageContext::new); let dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None { - let dctx = debuginfo::CodegenUnitDebugContext::new(llmod); + let dctx = debuginfo::CodegenUnitDebugContext::new(llmod, tcx.sess); debuginfo::metadata::build_compile_unit_di_node( tcx, codegen_unit.name().as_str(), @@ -727,6 +725,24 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } } + pub(crate) fn add_ptrauth_elf_got_flag(&self) { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "ptrauth-elf-got", + 1, + ); + } + + pub(crate) fn add_ptrauth_sign_personality_flag(&self) { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "ptrauth-sign-personality", + 1, + ); + } + // We do our best here to match what Clang does when compiling Objective-C natively. // See Clang's `CGObjCCommonMac::EmitImageInfo`: // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085 @@ -871,8 +887,24 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { get_fn(self, instance) } - fn get_fn_addr(&self, instance: Instance<'tcx>) -> &'ll Value { - get_fn(self, instance) + fn get_fn_addr(&self, instance: Instance<'tcx>, pac: Option) -> &'ll Value { + // When pointer authentication metadata is provided, `get_fn_addr` will + // attempt to sign the pointer using LLVM's `ConstPtrAuth` constant + // expression. + // + // FIXME(jchlanda) Currently, all function addresses requested from + // within LLVM codegen are signed. This behavior is too broad, resulting + // in the logic being applied to function values, not just pointers + // (addresses). + // + // See the discussion in the rust-lang issue: + // , and comment in + // builder's `ptrauth_operand_bundle`. + let llfn = get_fn(self, instance); + match pac { + Some(pac) => common::maybe_sign_fn_ptr(self, instance, llfn, pac), + None => llfn, + } } fn eh_personality(&self) -> &'ll Value { @@ -914,13 +946,16 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let tcx = self.tcx; let llfn = match tcx.lang_items().eh_personality() { - Some(def_id) if name.is_none() => self.get_fn_addr(ty::Instance::expect_resolve( - tcx, - self.typing_env(), - def_id, - ty::List::empty(), - DUMMY_SP, - )), + Some(def_id) if name.is_none() => self.get_fn_addr( + ty::Instance::expect_resolve( + tcx, + self.typing_env(), + def_id, + ty::List::empty(), + DUMMY_SP, + ), + Some(PacMetadata::default()), + ), _ => { let name = name.unwrap_or("rust_eh_personality"); if let Some(llfn) = self.get_declared_value(name) { @@ -978,10 +1013,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool { - matches!( - name, - sym::autodiff | sym::volatile_load | sym::unaligned_volatile_load | sym::black_box - ) + matches!(name, sym::black_box) } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index f88f0c2fb0994..f5b843b4e3e74 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -1,5 +1,6 @@ // .debug_gdb_scripts binary section. +use rustc_abi::Align; use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; use rustc_hir::attrs::DebuggerVisualizerType; @@ -18,10 +19,7 @@ pub(crate) fn insert_reference_to_gdb_debug_scripts_section_global(bx: &mut Buil let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx); // Load just the first byte as that's all that's necessary to force // LLVM to keep around the reference to the global. - let volatile_load_instruction = bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section); - unsafe { - llvm::LLVMSetAlignment(volatile_load_instruction, 1); - } + bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section, Align::ONE); } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index d8d529fee6f8b..580b7a89484d4 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -51,7 +51,6 @@ mod utils; /// A context object for maintaining all state needed by the debuginfo module. pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { - llmod: &'ll llvm::Module, builder: DIBuilderBox<'ll>, created_files: RefCell, &'ll DIFile>>, @@ -62,23 +61,8 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { } impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { - pub(crate) fn new(llmod: &'ll llvm::Module) -> Self { + pub(crate) fn new(llmod: &'ll llvm::Module, sess: &Session) -> Self { debug!("CodegenUnitDebugContext::new"); - let builder = DIBuilderBox::new(llmod); - // DIBuilder inherits context from the module, so we'd better use the same one - CodegenUnitDebugContext { - llmod, - builder, - created_files: Default::default(), - type_map: Default::default(), - adt_stack: Default::default(), - namespace_map: RefCell::new(Default::default()), - recursion_marker_type: OnceCell::new(), - } - } - - pub(crate) fn finalize(&self, sess: &Session) { - unsafe { llvm::LLVMDIBuilderFinalize(self.builder.as_ref()) }; match sess.target.debuginfo_kind { DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => { @@ -89,7 +73,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { // This can be overridden using --llvm-opts -dwarf-version,N. // Android has the same issue (#22398) llvm::add_module_flag_u32( - self.llmod, + llmod, // In the case where multiple CGUs with different dwarf version // values are being merged together, such as with cross-crate // LTO, then we want to use the highest version of dwarf @@ -102,7 +86,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { DebuginfoKind::Pdb => { // Indicate that we want CodeView debug information llvm::add_module_flag_u32( - self.llmod, + llmod, llvm::ModuleFlagMergeBehavior::Warning, "CodeView", 1, @@ -112,28 +96,26 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { // Prevent bitcode readers from deleting the debug info. llvm::add_module_flag_u32( - self.llmod, + llmod, llvm::ModuleFlagMergeBehavior::Warning, "Debug Info Version", unsafe { llvm::LLVMRustDebugMetadataVersion() }, ); - } -} -/// Creates any deferred debug metadata nodes -pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { - if let Some(dbg_cx) = &cx.dbg_cx { - debug!("finalize"); - - if gdb::needs_gdb_debug_scripts_section(cx) { - // Add a .debug_gdb_scripts section to this compile-unit. This will - // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, - // which activates the Rust pretty printers for binary this section is - // contained in. - gdb::get_or_insert_gdb_debug_scripts_section_global(cx); + let builder = DIBuilderBox::new(llmod); + // DIBuilder inherits context from the module, so we'd better use the same one + CodegenUnitDebugContext { + builder, + created_files: Default::default(), + type_map: Default::default(), + adt_stack: Default::default(), + namespace_map: RefCell::new(Default::default()), + recursion_marker_type: OnceCell::new(), } + } - dbg_cx.finalize(cx.sess()); + pub(crate) fn finalize(&self) { + unsafe { llvm::LLVMDIBuilderFinalize(self.builder.as_ref()) }; } } @@ -144,381 +126,109 @@ impl<'ll> Builder<'_, 'll, '_> { } impl<'ll, 'tcx> DebugInfoBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { - // FIXME(eddyb) find a common convention for all of the debuginfo-related - // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). - fn dbg_var_addr( + fn dbg_scope_fn( &mut self, - dbg_var: &'ll DIVariable, - dbg_loc: &'ll DILocation, - variable_alloca: Self::Value, - direct_offset: Size, - indirect_offsets: &[Size], - fragment: &Option>, - ) { - use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst}; + instance: Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + maybe_definition_llfn: Option<&'ll Value>, + ) -> &'ll DIScope { + let tcx = self.tcx; - // Convert the direct and indirect offsets and fragment byte range to address ops. - let mut addr_ops = SmallVec::<[u64; 8]>::new(); + let def_id = instance.def_id(); + let (containing_scope, is_method) = get_containing_scope(self, instance); + let span = tcx.def_span(def_id); + let loc = self.lookup_debug_loc(span.lo()); + let file_metadata = file_metadata(self, &loc.file); - if direct_offset.bytes() > 0 { - addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(direct_offset.bytes()); - } - for &offset in indirect_offsets { - addr_ops.push(DW_OP_deref); - if offset.bytes() > 0 { - addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(offset.bytes()); - } - } - if let Some(fragment) = fragment { - // `DW_OP_LLVM_fragment` takes as arguments the fragment's - // offset and size, both of them in bits. - addr_ops.push(DW_OP_LLVM_fragment); - addr_ops.push(fragment.start.bits()); - addr_ops.push((fragment.end - fragment.start).bits()); - } + let function_type_metadata = + create_subroutine_type(self, &get_function_signature(self, fn_abi)); - let di_builder = DIB(self.cx()); - let addr_expr = di_builder.create_expression(&addr_ops); - unsafe { - llvm::LLVMDIBuilderInsertDeclareRecordAtEnd( - di_builder, - variable_alloca, - dbg_var, - addr_expr, - dbg_loc, - self.llbb(), - ) - }; - } + let mut name = String::with_capacity(64); + type_names::push_item_name(tcx, def_id, false, &mut name); - fn dbg_var_value( - &mut self, - dbg_var: &'ll DIVariable, - dbg_loc: &'ll DILocation, - value: Self::Value, - direct_offset: Size, - indirect_offsets: &[Size], - fragment: &Option>, - ) { - use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value}; + // Find the enclosing function, in case this is a closure. + let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); - // Convert the direct and indirect offsets and fragment byte range to address ops. - let mut addr_ops = SmallVec::<[u64; 8]>::new(); + // We look up the generics of the enclosing function and truncate the args + // to their length in order to cut off extra stuff that might be in there for + // closures or coroutines. + let generics = tcx.generics_of(enclosing_fn_def_id); + let args = instance.args.truncate_to(tcx, generics); - if direct_offset.bytes() > 0 { - addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(direct_offset.bytes() as u64); - addr_ops.push(DW_OP_stack_value); - } - for &offset in indirect_offsets { - addr_ops.push(DW_OP_deref); - if offset.bytes() > 0 { - addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(offset.bytes() as u64); - } - } - if let Some(fragment) = fragment { - // `DW_OP_LLVM_fragment` takes as arguments the fragment's - // offset and size, both of them in bits. - addr_ops.push(DW_OP_LLVM_fragment); - addr_ops.push(fragment.start.bits() as u64); - addr_ops.push((fragment.end - fragment.start).bits() as u64); - } + type_names::push_generic_args( + tcx, + tcx.normalize_erasing_regions(self.typing_env(), Unnormalized::new_wip(args)), + &mut name, + ); - let di_builder = DIB(self.cx()); - let addr_expr = unsafe { - llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) - }; - unsafe { - llvm::LLVMDIBuilderInsertDbgValueRecordAtEnd( - di_builder, - value, - dbg_var, - addr_expr, - dbg_loc, - self.llbb(), - ); - } - } + let template_parameters = get_template_parameters(self, generics, args); - fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { - unsafe { - llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc); - } - } + let linkage_name = &mangled_name_of_instance(self, instance).name; + // Omit the linkage_name if it is the same as subprogram name. + let linkage_name = if &name == linkage_name { "" } else { linkage_name }; - fn clear_dbg_loc(&mut self) { - unsafe { - llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, ptr::null()); - } - } + // FIXME(eddyb) does this need to be separate from `loc.line` for some reason? + let scope_line = loc.line; - fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { - gdb::insert_reference_to_gdb_debug_scripts_section_global(self) - } + let mut flags = DIFlags::FlagPrototyped; - fn set_var_name(&mut self, value: &'ll Value, name: &str) { - // Avoid wasting time if LLVM value names aren't even enabled. - if self.sess().fewer_names() { - return; + if fn_abi.ret.layout.is_uninhabited() { + flags |= DIFlags::FlagNoReturn; } - // Only function parameters and instructions are local to a function, - // don't change the name of anything else (e.g. globals). - let param_or_inst = unsafe { - llvm::LLVMIsAArgument(value).is_some() || llvm::LLVMIsAInstruction(value).is_some() - }; - if !param_or_inst { - return; + let mut spflags = DISPFlags::SPFlagDefinition; + if is_node_local_to_unit(self, def_id) { + spflags |= DISPFlags::SPFlagLocalToUnit; } - - // Avoid replacing the name if it already exists. - // While we could combine the names somehow, it'd - // get noisy quick, and the usefulness is dubious. - if llvm::get_value_name(value).is_empty() { - llvm::set_value_name(value, name.as_bytes()); + if self.sess().opts.optimize != config::OptLevel::No { + spflags |= DISPFlags::SPFlagOptimized; + } + if let Some((id, _)) = tcx.entry_fn(()) { + if id == def_id { + spflags |= DISPFlags::SPFlagMainSubprogram; + } } - } - /// Annotate move/copy operations with debug info for profiling. - /// - /// This creates a temporary debug scope that makes the move/copy appear as an inlined call to - /// `compiler_move()` or `compiler_copy()`. The provided closure is executed - /// with this temporary debug location active. - /// - /// The `instance` parameter should be the monomorphized instance of the `compiler_move` or - /// `compiler_copy` function with the actual type and size. - fn with_move_annotation( - &mut self, - instance: ty::Instance<'tcx>, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - // Save the current debug location - let saved_loc = self.get_dbg_loc(); + // When we're adding a method to a type DIE, we only want a DW_AT_declaration there, because + // LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition. + // When we use this `decl` below, the subprogram definition gets created at the CU level + // with a DW_AT_specification pointing back to the type's declaration. + let decl = is_method.then(|| unsafe { + llvm::LLVMRustDIBuilderCreateMethod( + DIB(self), + containing_scope, + name.as_c_char_ptr(), + name.len(), + linkage_name.as_c_char_ptr(), + linkage_name.len(), + file_metadata, + loc.line, + function_type_metadata, + flags, + spflags & !DISPFlags::SPFlagDefinition, + template_parameters, + ) + }); - // Create a DIScope for the compiler_move/compiler_copy function - // We use the function's FnAbi for debug info generation - let fn_abi = self - .cx() - .tcx - .fn_abi_of_instance( - self.cx().typing_env().as_query_input((instance, ty::List::empty())), + return unsafe { + llvm::LLVMRustDIBuilderCreateFunction( + DIB(self), + containing_scope, + name.as_c_char_ptr(), + name.len(), + linkage_name.as_c_char_ptr(), + linkage_name.len(), + file_metadata, + loc.line, + function_type_metadata, + scope_line, + flags, + spflags, + maybe_definition_llfn, + template_parameters, + decl, ) - .unwrap(); - - let di_scope = self.cx().dbg_scope_fn(instance, fn_abi, None); - - // Create an inlined debug location: - // - scope: the compiler_move/compiler_copy function - // - inlined_at: the current location (where the move/copy actually occurs) - // - span: use the function's definition span - let fn_span = self.cx().tcx.def_span(instance.def_id()); - let inlined_loc = self.cx().dbg_loc(di_scope, saved_loc, fn_span); - - // Set the temporary debug location - self.set_dbg_loc(inlined_loc); - - // Execute the closure (which will generate the memcpy) - let result = f(self); - - // Restore the original debug location - if let Some(loc) = saved_loc { - self.set_dbg_loc(loc); - } else { - self.clear_dbg_loc(); - } - - result - } -} - -/// A source code location used to generate debug information. -// FIXME(eddyb) rename this to better indicate it's a duplicate of -// `rustc_span::Loc` rather than `DILocation`, perhaps by making -// `lookup_char_pos` return the right information instead. -struct DebugLoc { - /// Information about the original source file. - file: Arc, - /// The (1-based) line number. - line: u32, - /// The (1-based) column number. - col: u32, -} - -impl<'ll> CodegenCx<'ll, '_> { - /// Looks up debug source information about a `BytePos`. - // FIXME(eddyb) rename this to better indicate it's a duplicate of - // `lookup_char_pos` rather than `dbg_loc`, perhaps by making - // `lookup_char_pos` return the right information instead. - fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc { - let (file, line, col) = match self.sess().source_map().lookup_line(pos) { - Ok(SourceFileAndLine { sf: file, line }) => { - let line_pos = file.lines()[line]; - - // Use 1-based indexing. - let line = (line + 1) as u32; - let col = (file.relative_position(pos) - line_pos).to_u32() + 1; - - (file, line, col) - } - Err(file) => (file, UNKNOWN_LINE_NUMBER, UNKNOWN_COLUMN_NUMBER), - }; - - // For MSVC, omit the column number. - // Otherwise, emit it. This mimics clang behaviour. - // See discussion in https://github.com/rust-lang/rust/issues/42921 - if self.sess().target.is_like_msvc { - DebugLoc { file, line, col: UNKNOWN_COLUMN_NUMBER } - } else { - DebugLoc { file, line, col } - } - } - - fn create_template_type_parameter( - &self, - name: &str, - actual_type_metadata: &'ll DIType, - ) -> &'ll DITemplateTypeParameter { - unsafe { - llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( - DIB(self), - None, - name.as_c_char_ptr(), - name.len(), - actual_type_metadata, - ) - } - } -} - -impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { - fn dbg_create_lexical_block(&self, pos: BytePos, parent_scope: &'ll DIScope) -> &'ll DIScope { - let loc = self.lookup_debug_loc(pos); - let file_metadata = file_metadata(self, &loc.file); - unsafe { - llvm::LLVMDIBuilderCreateLexicalBlock( - DIB(self), - parent_scope, - file_metadata, - loc.line, - loc.col, - ) - } - } - - fn dbg_location_clone_with_discriminator( - &self, - loc: &'ll DILocation, - discriminator: u32, - ) -> Option<&'ll DILocation> { - unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, discriminator) } - } - - fn dbg_scope_fn( - &self, - instance: Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - maybe_definition_llfn: Option<&'ll Value>, - ) -> &'ll DIScope { - let tcx = self.tcx; - - let def_id = instance.def_id(); - let (containing_scope, is_method) = get_containing_scope(self, instance); - let span = tcx.def_span(def_id); - let loc = self.lookup_debug_loc(span.lo()); - let file_metadata = file_metadata(self, &loc.file); - - let function_type_metadata = - create_subroutine_type(self, &get_function_signature(self, fn_abi)); - - let mut name = String::with_capacity(64); - type_names::push_item_name(tcx, def_id, false, &mut name); - - // Find the enclosing function, in case this is a closure. - let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); - - // We look up the generics of the enclosing function and truncate the args - // to their length in order to cut off extra stuff that might be in there for - // closures or coroutines. - let generics = tcx.generics_of(enclosing_fn_def_id); - let args = instance.args.truncate_to(tcx, generics); - - type_names::push_generic_args( - tcx, - tcx.normalize_erasing_regions(self.typing_env(), Unnormalized::new_wip(args)), - &mut name, - ); - - let template_parameters = get_template_parameters(self, generics, args); - - let linkage_name = &mangled_name_of_instance(self, instance).name; - // Omit the linkage_name if it is the same as subprogram name. - let linkage_name = if &name == linkage_name { "" } else { linkage_name }; - - // FIXME(eddyb) does this need to be separate from `loc.line` for some reason? - let scope_line = loc.line; - - let mut flags = DIFlags::FlagPrototyped; - - if fn_abi.ret.layout.is_uninhabited() { - flags |= DIFlags::FlagNoReturn; - } - - let mut spflags = DISPFlags::SPFlagDefinition; - if is_node_local_to_unit(self, def_id) { - spflags |= DISPFlags::SPFlagLocalToUnit; - } - if self.sess().opts.optimize != config::OptLevel::No { - spflags |= DISPFlags::SPFlagOptimized; - } - if let Some((id, _)) = tcx.entry_fn(()) { - if id == def_id { - spflags |= DISPFlags::SPFlagMainSubprogram; - } - } - - // When we're adding a method to a type DIE, we only want a DW_AT_declaration there, because - // LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition. - // When we use this `decl` below, the subprogram definition gets created at the CU level - // with a DW_AT_specification pointing back to the type's declaration. - let decl = is_method.then(|| unsafe { - llvm::LLVMRustDIBuilderCreateMethod( - DIB(self), - containing_scope, - name.as_c_char_ptr(), - name.len(), - linkage_name.as_c_char_ptr(), - linkage_name.len(), - file_metadata, - loc.line, - function_type_metadata, - flags, - spflags & !DISPFlags::SPFlagDefinition, - template_parameters, - ) - }); - - return unsafe { - llvm::LLVMRustDIBuilderCreateFunction( - DIB(self), - containing_scope, - name.as_c_char_ptr(), - name.len(), - linkage_name.as_c_char_ptr(), - linkage_name.len(), - file_metadata, - loc.line, - function_type_metadata, - scope_line, - flags, - spflags, - maybe_definition_llfn, - template_parameters, - decl, - ) - }; + }; fn get_function_signature<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, @@ -658,8 +368,34 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } + fn dbg_create_lexical_block( + &mut self, + pos: BytePos, + parent_scope: &'ll DIScope, + ) -> &'ll DIScope { + let loc = self.lookup_debug_loc(pos); + let file_metadata = file_metadata(self, &loc.file); + unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(self), + parent_scope, + file_metadata, + loc.line, + loc.col, + ) + } + } + + fn dbg_location_clone_with_discriminator( + &mut self, + loc: &'ll DILocation, + discriminator: u32, + ) -> Option<&'ll DILocation> { + unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, discriminator) } + } + fn dbg_loc( - &self, + &mut self, scope: &'ll DIScope, inlined_at: Option<&'ll DILocation>, span: Span, @@ -679,31 +415,18 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMDIBuilderCreateDebugLocation(self.llcx, line, col, scope, inlined_at) } } - fn create_vtable_debuginfo( - &self, - ty: Ty<'tcx>, - trait_ref: Option>, - vtable: Self::Value, - ) { - metadata::create_vtable_di_node(self, ty, trait_ref, vtable) - } - fn extend_scope_to_file( - &self, + &mut self, scope_metadata: &'ll DIScope, file: &rustc_span::SourceFile, ) -> &'ll DILexicalBlock { metadata::extend_scope_to_file(self, scope_metadata, file) } - fn debuginfo_finalize(&self) { - finalize(self) - } - // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). fn create_dbg_var( - &self, + &mut self, variable_name: Symbol, variable_type: Ty<'tcx>, scope_metadata: &'ll DIScope, @@ -750,4 +473,280 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { }, } } + + // FIXME(eddyb) find a common convention for all of the debuginfo-related + // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). + fn dbg_var_addr( + &mut self, + dbg_var: &'ll DIVariable, + dbg_loc: &'ll DILocation, + variable_alloca: Self::Value, + direct_offset: Size, + indirect_offsets: &[Size], + fragment: &Option>, + ) { + use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst}; + + // Convert the direct and indirect offsets and fragment byte range to address ops. + let mut addr_ops = SmallVec::<[u64; 8]>::new(); + + if direct_offset.bytes() > 0 { + addr_ops.push(DW_OP_plus_uconst); + addr_ops.push(direct_offset.bytes()); + } + for &offset in indirect_offsets { + addr_ops.push(DW_OP_deref); + if offset.bytes() > 0 { + addr_ops.push(DW_OP_plus_uconst); + addr_ops.push(offset.bytes()); + } + } + if let Some(fragment) = fragment { + // `DW_OP_LLVM_fragment` takes as arguments the fragment's + // offset and size, both of them in bits. + addr_ops.push(DW_OP_LLVM_fragment); + addr_ops.push(fragment.start.bits()); + addr_ops.push((fragment.end - fragment.start).bits()); + } + + let di_builder = DIB(self.cx()); + let addr_expr = di_builder.create_expression(&addr_ops); + unsafe { + llvm::LLVMDIBuilderInsertDeclareRecordAtEnd( + di_builder, + variable_alloca, + dbg_var, + addr_expr, + dbg_loc, + self.llbb(), + ) + }; + } + + fn dbg_var_value( + &mut self, + dbg_var: &'ll DIVariable, + dbg_loc: &'ll DILocation, + value: Self::Value, + direct_offset: Size, + indirect_offsets: &[Size], + fragment: &Option>, + ) { + use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value}; + + // Convert the direct and indirect offsets and fragment byte range to address ops. + let mut addr_ops = SmallVec::<[u64; 8]>::new(); + + if direct_offset.bytes() > 0 { + addr_ops.push(DW_OP_plus_uconst); + addr_ops.push(direct_offset.bytes() as u64); + addr_ops.push(DW_OP_stack_value); + } + for &offset in indirect_offsets { + addr_ops.push(DW_OP_deref); + if offset.bytes() > 0 { + addr_ops.push(DW_OP_plus_uconst); + addr_ops.push(offset.bytes() as u64); + } + } + if let Some(fragment) = fragment { + // `DW_OP_LLVM_fragment` takes as arguments the fragment's + // offset and size, both of them in bits. + addr_ops.push(DW_OP_LLVM_fragment); + addr_ops.push(fragment.start.bits() as u64); + addr_ops.push((fragment.end - fragment.start).bits() as u64); + } + + let di_builder = DIB(self.cx()); + let addr_expr = unsafe { + llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) + }; + unsafe { + llvm::LLVMDIBuilderInsertDbgValueRecordAtEnd( + di_builder, + value, + dbg_var, + addr_expr, + dbg_loc, + self.llbb(), + ); + } + } + + fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { + unsafe { + llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc); + } + } + + fn clear_dbg_loc(&mut self) { + unsafe { + llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, ptr::null()); + } + } + + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { + gdb::insert_reference_to_gdb_debug_scripts_section_global(self) + } + + fn set_var_name(&mut self, value: &'ll Value, name: &str) { + // Avoid wasting time if LLVM value names aren't even enabled. + if self.sess().fewer_names() { + return; + } + + // Only function parameters and instructions are local to a function, + // don't change the name of anything else (e.g. globals). + let param_or_inst = unsafe { + llvm::LLVMIsAArgument(value).is_some() || llvm::LLVMIsAInstruction(value).is_some() + }; + if !param_or_inst { + return; + } + + // Avoid replacing the name if it already exists. + // While we could combine the names somehow, it'd + // get noisy quick, and the usefulness is dubious. + if llvm::get_value_name(value).is_empty() { + llvm::set_value_name(value, name.as_bytes()); + } + } + + /// Annotate move/copy operations with debug info for profiling. + /// + /// This creates a temporary debug scope that makes the move/copy appear as an inlined call to + /// `compiler_move()` or `compiler_copy()`. The provided closure is executed + /// with this temporary debug location active. + /// + /// The `instance` parameter should be the monomorphized instance of the `compiler_move` or + /// `compiler_copy` function with the actual type and size. + fn with_move_annotation( + &mut self, + instance: ty::Instance<'tcx>, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + // Save the current debug location + let saved_loc = self.get_dbg_loc(); + + // Create a DIScope for the compiler_move/compiler_copy function + // We use the function's FnAbi for debug info generation + let fn_abi = self + .cx() + .tcx + .fn_abi_of_instance( + self.cx().typing_env().as_query_input((instance, ty::List::empty())), + ) + .unwrap(); + + let di_scope = self.dbg_scope_fn(instance, fn_abi, None); + + // Create an inlined debug location: + // - scope: the compiler_move/compiler_copy function + // - inlined_at: the current location (where the move/copy actually occurs) + // - span: use the function's definition span + let fn_span = self.cx().tcx.def_span(instance.def_id()); + let inlined_loc = self.dbg_loc(di_scope, saved_loc, fn_span); + + // Set the temporary debug location + self.set_dbg_loc(inlined_loc); + + // Execute the closure (which will generate the memcpy) + let result = f(self); + + // Restore the original debug location + if let Some(loc) = saved_loc { + self.set_dbg_loc(loc); + } else { + self.clear_dbg_loc(); + } + + result + } +} + +/// A source code location used to generate debug information. +// FIXME(eddyb) rename this to better indicate it's a duplicate of +// `rustc_span::Loc` rather than `DILocation`, perhaps by making +// `lookup_char_pos` return the right information instead. +struct DebugLoc { + /// Information about the original source file. + file: Arc, + /// The (1-based) line number. + line: u32, + /// The (1-based) column number. + col: u32, +} + +impl<'ll> CodegenCx<'ll, '_> { + /// Looks up debug source information about a `BytePos`. + // FIXME(eddyb) rename this to better indicate it's a duplicate of + // `lookup_char_pos` rather than `dbg_loc`, perhaps by making + // `lookup_char_pos` return the right information instead. + fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc { + let (file, line, col) = match self.sess().source_map().lookup_line(pos) { + Ok(SourceFileAndLine { sf: file, line }) => { + let line_pos = file.lines()[line]; + + // Use 1-based indexing. + let line = (line + 1) as u32; + let col = (file.relative_position(pos) - line_pos).to_u32() + 1; + + (file, line, col) + } + Err(file) => (file, UNKNOWN_LINE_NUMBER, UNKNOWN_COLUMN_NUMBER), + }; + + // For MSVC, omit the column number. + // Otherwise, emit it. This mimics clang behaviour. + // See discussion in https://github.com/rust-lang/rust/issues/42921 + if self.sess().target.is_like_msvc { + DebugLoc { file, line, col: UNKNOWN_COLUMN_NUMBER } + } else { + DebugLoc { file, line, col } + } + } + + fn create_template_type_parameter( + &self, + name: &str, + actual_type_metadata: &'ll DIType, + ) -> &'ll DITemplateTypeParameter { + unsafe { + llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( + DIB(self), + None, + name.as_c_char_ptr(), + name.len(), + actual_type_metadata, + ) + } + } + + /// Creates any deferred debug metadata nodes + pub(crate) fn debuginfo_finalize(&self) { + if let Some(dbg_cx) = &self.dbg_cx { + debug!("finalize"); + + if gdb::needs_gdb_debug_scripts_section(self) { + // Add a .debug_gdb_scripts section to this compile-unit. This will + // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, + // which activates the Rust pretty printers for binary this section is + // contained in. + gdb::get_or_insert_gdb_debug_scripts_section_global(self); + } + + dbg_cx.finalize(); + } + } +} + +impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { + fn create_vtable_debuginfo( + &self, + ty: Ty<'tcx>, + trait_ref: Option>, + vtable: Self::Value, + ) { + metadata::create_vtable_di_node(self, ty, trait_ref, vtable) + } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0b03d6862ca84..7bd604bdbbd76 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -28,7 +28,7 @@ use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::Arch; +use rustc_target::spec::{Arch, LlvmAbi}; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -37,13 +37,15 @@ use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::builder::gpu_offload::{ OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload, }; +use crate::common::pauth_fn_attrs; use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, IntrinsicWrongArch, OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic, }; -use crate::llvm::{self, Type, Value}; +use crate::intrinsic::ty::typetree::fnc_typetrees; +use crate::llvm::{self, Attribute, AttributePlace, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -113,17 +115,17 @@ fn call_simple_intrinsic<'ll, 'tcx>( sym::fmuladdf64 => ("llvm.fmuladd", &[bx.type_f64()]), sym::fmuladdf128 => ("llvm.fmuladd", &[bx.type_f128()]), + sym::minimumf16 => ("llvm.minimum", &[bx.type_f16()]), + sym::minimumf32 => ("llvm.minimum", &[bx.type_f32()]), // FIXME: LLVM currently mis-compile those intrinsics, re-enable them // when llvm/llvm-project#{139380,139381,140445} are fixed. - //sym::minimumf16 => ("llvm.minimum", &[bx.type_f16()]), - //sym::minimumf32 => ("llvm.minimum", &[bx.type_f32()]), //sym::minimumf64 => ("llvm.minimum", &[bx.type_f64()]), //sym::minimumf128 => ("llvm.minimum", &[cx.type_f128()]), // + sym::maximumf16 => ("llvm.maximum", &[bx.type_f16()]), + sym::maximumf32 => ("llvm.maximum", &[bx.type_f32()]), // FIXME: LLVM currently mis-compile those intrinsics, re-enable them // when llvm/llvm-project#{139380,139381,140445} are fixed. - //sym::maximumf16 => ("llvm.maximum", &[bx.type_f16()]), - //sym::maximumf32 => ("llvm.maximum", &[bx.type_f32()]), //sym::maximumf64 => ("llvm.maximum", &[bx.type_f64()]), //sym::maximumf128 => ("llvm.maximum", &[cx.type_f128()]), // @@ -223,12 +225,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) } sym::autodiff => { - let result = PlaceRef { - val: result_place.unwrap(), - layout: result_layout, - }; - codegen_autodiff(self, tcx, instance, args, result); - return IntrinsicResult::WroteIntoPlace; + return codegen_autodiff(self, tcx, instance, args, result_layout, result_place); } sym::offload => { if tcx.sess.opts.unstable_opts.offload.is_empty() { @@ -358,25 +355,39 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } sym::volatile_load | sym::unaligned_volatile_load => { - let result = PlaceRef { - val: result_place.unwrap(), - layout: result_layout, - }; - + // Note that we cannot just load the `llvm_type` because we should never load non-scalars. + // Trying to do so blows up horribly in some cases -- for example loading a + // `MaybeUninint<&dyn Trait>` would load as `{ [i64x2] }` which gives assertions later + // (if we're lucky) from things not being pointers that ought to be. let ptr = args[0].immediate(); - let load = self.volatile_load(result_layout.llvm_type(self), ptr); - let align = if name == sym::unaligned_volatile_load { - 1 + let abi_align = result_layout.align.abi; + let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE }; + if result_layout.is_zst() { + return IntrinsicResult::Operand(OperandValue::ZeroSized); + } else if let BackendRepr::Scalar(scalar) = result_layout.backend_repr { + let load = self.volatile_load(self.type_from_scalar(scalar), ptr, ptr_align); + self.to_immediate_scalar(load, scalar) } else { - result_layout.align.bytes() as u32 - }; - unsafe { - llvm::LLVMSetAlignment(load, align); - } - if !result_layout.is_zst() { - self.store_to_place(load, result.val); + // One day Rust will probably want to define how we split up a volatile load + // of something that's *not* just an ordinary scalar, but for now we can just + // use an LLVM integer type of the correct width and let it split it however. + let llty = self.type_ix(result_layout.size.bits()); + let temp = if let Some(result_place) = result_place { + PlaceRef { + val: result_place, + layout: result_layout, + } + } else { + PlaceRef::alloca(self, result_layout) + }; + let llval = self.volatile_load(llty, ptr, ptr_align); + self.store(llval, temp.val.llval, abi_align); + return if result_place.is_none() { + IntrinsicResult::Operand(self.load_operand(temp).val) + } else { + IntrinsicResult::WroteIntoPlace + }; } - return IntrinsicResult::WroteIntoPlace; } sym::volatile_store => { let dst = args[0].deref(self.cx()); @@ -402,7 +413,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let ptr = args[0].immediate(); let locality = fn_args.const_at(1).to_leaf().to_i32(); self.call_intrinsic( - "llvm.prefetch", + "llvm.prefetch.p0", &[self.val_ty(ptr)], &[ ptr, @@ -1701,6 +1712,13 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( hir::Safety::Unsafe, )); let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen); + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { + let attrs: Vec<&Attribute> = + pauth_fn_attrs().iter().map(|name| llvm::CreateAttrString(cx.llcx, name)).collect(); + let (_ty, rust_try_fn) = rust_try; + crate::attributes::apply_to_llfn(rust_try_fn, AttributePlace::Function, &attrs); + } + cx.rust_try_fn.set(Some(rust_try)); rust_try } @@ -1728,8 +1746,9 @@ fn codegen_autodiff<'ll, 'tcx>( tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, &'ll Value>], - result: PlaceRef<'tcx, &'ll Value>, -) { + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>, +) -> IntrinsicResult<'tcx, &'ll Value> { if !tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) { let _ = tcx.dcx().emit_almost_fatal(AutoDiffWithoutEnable); } @@ -1769,9 +1788,9 @@ fn codegen_autodiff<'ll, 'tcx>( diff_id, diff_args ), - Err(_) => { + Err(err) => { // An error has already been emitted - return; + return IntrinsicResult::Err(err); } }; @@ -1791,7 +1810,7 @@ fn codegen_autodiff<'ll, 'tcx>( &mut diff_attrs.input_activity, ); - let fnc_tree = rustc_middle::ty::fnc_typetrees(tcx, source_fn_ptr_ty); + let fnc_tree = fnc_typetrees(tcx, source_fn_ptr_ty); // Build body generate_enzyme_call( @@ -1802,9 +1821,10 @@ fn codegen_autodiff<'ll, 'tcx>( llret_ty, &val_arr, &diff_attrs, - result, + result_layout, + result_place, fnc_tree, - ); + ) } // Generates the LLVM code to offload a Rust function to a target device (e.g., GPU). diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index b14a6206ae682..bd770d286851b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2139,6 +2139,8 @@ unsafe extern "C" { IsVolatile: bool, ) -> &'a Value; + pub(crate) fn LLVMRustBuildVScale<'a>(B: &Builder<'a>, Ty: &'a Type) -> &'a Value; + pub(crate) fn LLVMRustTimeTraceProfilerInitialize(); pub(crate) fn LLVMRustTimeTraceProfilerFinishThread(); @@ -2645,4 +2647,12 @@ unsafe extern "C" { Aliasee: &Value, Name: *const c_char, ) -> &'ll Value; + + pub(crate) fn LLVMRustConstPtrAuth( + ptr: *const Value, + key: u32, + disc: u64, + addr_diversity: *const Value, + deactivation_symbol: *const Value, + ) -> *const Value; } diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2ec19b1795b5a..e9bc6ae0e80ef 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -475,3 +475,19 @@ pub(crate) fn add_alias<'ll>( ) -> &'ll Value { unsafe { LLVMAddAlias2(module, ty, address_space.0, aliasee, name.as_ptr()) } } + +/// Safe wrapper for `LLVMRustConstPtrAuth`. +pub(crate) fn const_ptr_auth<'ll>( + ptr: &'ll Value, + key: u32, + disc: u64, + addr_diversity: Option<&'ll Value>, +) -> &'ll Value { + unsafe { + let addr_div_ptr = addr_diversity.map_or(std::ptr::null(), |v| v as *const Value); + let deactivation_symbol = std::ptr::null(); + let result = + LLVMRustConstPtrAuth(ptr as *const Value, key, disc, addr_div_ptr, deactivation_symbol); + &*result + } +} diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index c7d7b7429f34a..7996e808b2779 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -9,7 +9,7 @@ ar_archive_writer = "0.5" bitflags = "2.4.1" bstr = "1.11.3" find-msvc-tools = "0.1.2" -itertools = "0.12" +itertools = "0.15" pathdiff = "0.2.0" regex = "1.4" rustc_abi = { path = "../rustc_abi" } diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index bdf346d8e69d2..4fc516b244857 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -22,7 +22,7 @@ use rustc_target::spec::Arch; use tracing::trace; use super::metadata::{create_compressed_metadata_file, search_for_section}; -use super::rmeta_link; +use super::rmeta_link::{self, RmetaLinkCache}; use super::symbol_edit::{apply_edits, collect_internal_names}; use crate::common; // Public for ArchiveBuilderBuilder::extract_bundled_libs @@ -311,7 +311,7 @@ fn find_binutils_dlltool(sess: &Session) -> OsString { } pub enum AddArchiveKind<'a> { - Rlib(/*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), + Rlib(&'a mut RmetaLinkCache, /*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), Other, } @@ -466,7 +466,11 @@ pub fn try_extract_macho_fat_archive( } impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { - fn add_archive(&mut self, archive_path: &Path, ar_kind: AddArchiveKind<'_>) -> io::Result<()> { + fn add_archive( + &mut self, + archive_path: &Path, + mut ar_kind: AddArchiveKind<'_>, + ) -> io::Result<()> { let mut archive_path = archive_path.to_path_buf(); if self.sess.target.llvm_target.contains("-apple-macosx") && let Some(new_archive_path) = try_extract_macho_fat_archive(self.sess, &archive_path)? @@ -481,8 +485,14 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? }; let archive = ArchiveFile::parse(&*archive_map) .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - let metadata_link = match ar_kind { - AddArchiveKind::Rlib(..) => rmeta_link::read(&archive, &archive_map, &archive_path), + let skip = match &ar_kind { + AddArchiveKind::Rlib(_, skip) => Some(*skip), + AddArchiveKind::Other => None, + }; + let metadata_link = match &mut ar_kind { + AddArchiveKind::Rlib(cache, _) => cache.get_or_insert_with(&archive_path, || { + rmeta_link::read(&archive, &archive_map, &archive_path) + }), AddArchiveKind::Other => None, }; let archive_index = self.src_archives.len(); @@ -512,9 +522,9 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { } else { ArchiveEntryKind::Other }; - let drop = match ar_kind { - AddArchiveKind::Rlib(skip) => skip(&file_name, kind), - AddArchiveKind::Other => false, + let drop = match skip { + Some(skip) => skip(&file_name, kind), + None => false, }; if !drop { let source = if entry.is_thin() { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 2c3ee1bae09f8..c41f6f30a1da3 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -35,8 +35,8 @@ use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{ - self, CFGuard, CrateType, DebugInfo, LinkerFeaturesCli, OutFileName, OutputFilenames, - OutputType, PrintKind, SplitDwarfKind, Strip, + self, CFGuard, CrateType, DebugInfo, InstrumentMcount, LinkerFeaturesCli, OutFileName, + OutputFilenames, OutputType, PrintKind, SplitDwarfKind, Strip, }; use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; @@ -47,7 +47,7 @@ use rustc_session::{Session, filesearch}; use rustc_span::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ - BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents, + Arch, BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, Os, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, }; @@ -59,10 +59,14 @@ use super::archive::{ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; +use super::rmeta_link::RmetaLinkCache; use super::rpath::{self, RPathConfig}; use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; -use crate::{CodegenLintLevelSpecs, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors}; +use crate::{ + CodegenLintLevelSpecs, CompiledModule, CompiledModules, CrateInfo, NativeLib, SymbolExport, + errors, +}; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { if let Err(e) = fs::remove_file(path) { @@ -86,6 +90,7 @@ pub fn link_binary( let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec = Vec::new(); + let mut rmeta_link_cache = RmetaLinkCache::default(); for &crate_type in &crate_info.crate_types { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) @@ -139,6 +144,7 @@ pub fn link_binary( link_staticlib( sess, archive_builder_builder, + &mut rmeta_link_cache, &compiled_modules, &crate_info, &metadata, @@ -150,6 +156,7 @@ pub fn link_binary( link_natively( sess, archive_builder_builder, + &mut rmeta_link_cache, crate_type, &out_filename, &compiled_modules, @@ -502,6 +509,7 @@ fn link_rlib<'a>( fn link_staticlib( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, compiled_modules: &CompiledModules, crate_info: &CrateInfo, metadata: &EncodedMetadata, @@ -531,7 +539,7 @@ fn link_staticlib( let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); ab.add_archive( path, - AddArchiveKind::Rlib(&|fname: &str, entry_kind| { + AddArchiveKind::Rlib(rmeta_link_cache, &|fname: &str, entry_kind| { // Ignore metadata and rmeta-link files. if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME { return true; @@ -588,7 +596,7 @@ fn link_staticlib( crate_info .exported_symbols .get(&CrateType::StaticLib) - .map(|symbols| symbols.iter().map(|(s, _)| s.clone()).collect()) + .map(|symbols| symbols.iter().map(|symbol| symbol.name.clone()).collect()) } } else { None @@ -939,6 +947,7 @@ fn report_linker_output( fn link_natively( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_type: CrateType, out_filename: &Path, compiled_modules: &CompiledModules, @@ -965,6 +974,7 @@ fn link_natively( flavor, sess, archive_builder_builder, + rmeta_link_cache, crate_type, tmpdir, temp_filename, @@ -2189,9 +2199,15 @@ fn add_linked_symbol_object( cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, - symbols: &[(String, SymbolExportKind)], + crate_type: CrateType, + linked_symbols: &[(String, SymbolExportKind)], + exported_symbols: &[SymbolExport], ) { - if symbols.is_empty() { + let should_export_symbols = sess.target.is_like_msvc + && !exported_symbols.is_empty() + && (crate_type != CrateType::Executable + || sess.opts.unstable_opts.export_executable_symbols); + if linked_symbols.is_empty() && !should_export_symbols { return; } @@ -2228,7 +2244,7 @@ fn add_linked_symbol_object( None }; - for (sym, kind) in symbols.iter() { + for (sym, kind) in linked_symbols.iter() { let symbol = file.add_symbol(object::write::Symbol { name: sym.clone().into(), value: 0, @@ -2286,6 +2302,37 @@ fn add_linked_symbol_object( } } + if should_export_symbols { + // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to + // export symbols from a dynamic library. When building a dynamic library, + // however, we're going to want some symbols exported, so this adds a + // `.drectve` section which lists all the symbols using /EXPORT arguments. + // + // The linker will read these arguments from the `.drectve` section and + // export all the symbols from the dynamic library. Note that this is not + // as simple as just exporting all the symbols in the current crate (as + // specified by `codegen.reachable`) but rather we also need to possibly + // export the symbols of upstream crates. Upstream rlibs may be linked + // statically to this dynamic library, in which case they may continue to + // transitively be used and hence need their symbols exported. + fn msvc_drectve_export(symbol: &SymbolExport) -> String { + let data = if symbol.kind == SymbolExportKind::Data { ",DATA" } else { "" }; + + if let Some(link_name) = symbol.link_name.as_deref() { + // The first name is the decorated symbol used by the import library, while + // EXPORTAS gives the public name written to the DLL export table. + format!(" /EXPORT:\"{link_name}\"{data},EXPORTAS,\"{}\"", symbol.name) + } else { + format!(" /EXPORT:\"{}\"{data}", symbol.name) + } + } + + let drectve = exported_symbols.iter().map(msvc_drectve_export).collect::(); + + let section = file.add_section(vec![], b".drectve".to_vec(), object::SectionKind::Linker); + file.append_section_data(section, drectve.as_bytes(), 1); + } + let path = tmpdir.join("symbols.o"); let result = std::fs::write(&path, file.write().unwrap()); if let Err(error) = result { @@ -2413,10 +2460,73 @@ fn add_rpath_args( } } +fn strip_numeric_suffix<'a>(base: &'a str, suffix: impl AsRef, fallback: &'a str) -> &'a str { + if suffix.as_ref().parse::().is_ok() { base } else { fallback } +} + +fn undecorate_c_symbol<'a>( + name: &'a str, + sess: &Session, + kind: SymbolExportKind, +) -> Option<&'a str> { + match sess.target.binary_format { + BinaryFormat::MachO => { + // Mach-O: strip the leading underscore that all external symbols have. + // The Darwin linker's export_symbols will add it back. + name.strip_prefix('_') + } + BinaryFormat::Coff => { + // MSVC C++ mangled names start with '?' and use a completely different + // decorating scheme that includes '@@' as structural delimiters. + // They must not be subjected to C calling-convention undecoration. + if name.starts_with('?') { + return Some(name); + } + Some(match sess.target.arch { + Arch::X86 => { + // COFF 32-bit: strip calling-convention decorations. + if let Some(rest) = name.strip_prefix('@') { + // fastcall: @foo@N -> foo + rest.rsplit_once('@') + .map(|(base, suffix)| strip_numeric_suffix(base, suffix, name)) + .unwrap_or(name) + } else if let Some(stripped) = name.strip_prefix('_') { + if let Some((base, suffix)) = stripped.rsplit_once('@') { + // stdcall: _foo@N -> foo + strip_numeric_suffix(base, suffix, stripped) + } else { + // cdecl: _foo -> foo + stripped + } + } else { + // vectorcall: foo@@N -> foo + name.rsplit_once("@@") + .map(|(base, suffix)| strip_numeric_suffix(base, suffix, name)) + .unwrap_or(name) + } + } + Arch::X86_64 => { + // COFF 64-bit: vectorcall mangling (foo@@N -> foo) also applies on x86_64. + name.rsplit_once("@@") + .map(|(base, suffix)| strip_numeric_suffix(base, suffix, name)) + .unwrap_or(name) + } + Arch::Arm64EC if kind == SymbolExportKind::Text => { + // Arm64EC: `#` prefix distinguishes ARM64EC text symbols from x64 thunks. + name.strip_prefix('#').unwrap_or(name) + } + _ => name, + }) + } + // ELF: no decoration + _ => Some(name), + } +} + fn add_c_staticlib_symbols( sess: &Session, lib: &NativeLib, - out: &mut Vec<(String, SymbolExportKind)>, + out: &mut Vec, ) -> io::Result<()> { let file_path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); @@ -2454,7 +2564,14 @@ fn add_c_staticlib_symbols( } for symbol in object.symbols() { - if symbol.scope() != object::SymbolScope::Dynamic { + // The `object` crate returns `Dynamic` for ELF/Mach-O global symbols, + // but always returns `Linkage` for COFF external symbols. + // Accept both for COFF (Windows and UEFI). + let scope = symbol.scope(); + if scope != object::SymbolScope::Dynamic + && !(sess.target.binary_format == BinaryFormat::Coff + && scope == object::SymbolScope::Linkage) + { continue; } @@ -2469,9 +2586,14 @@ fn add_c_staticlib_symbols( _ => continue, }; - // FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. - // Need to be resolved. - out.push((name.to_string(), export_kind)); + let Some(undecorated) = undecorate_c_symbol(name, sess, export_kind) else { + continue; + }; + out.push(SymbolExport::with_link_name( + undecorated.to_string(), + export_kind, + name.to_string(), + )); } } @@ -2491,6 +2613,7 @@ fn linker_with_args( flavor: LinkerFlavor, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_type: CrateType, tmpdir: &Path, out_filename: &Path, @@ -2550,7 +2673,14 @@ fn linker_with_args( // Pre-link CRT objects. add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects); - add_linked_symbol_object(cmd, sess, tmpdir, &crate_info.linked_symbols[&crate_type]); + add_linked_symbol_object( + cmd, + sess, + tmpdir, + crate_type, + &crate_info.linked_symbols[&crate_type], + &export_symbols, + ); // Sanitizer libraries. add_sanitizer_libraries(sess, flavor, crate_type, cmd); @@ -2619,6 +2749,7 @@ fn linker_with_args( cmd, sess, archive_builder_builder, + rmeta_link_cache, crate_info, crate_type, tmpdir, @@ -2882,7 +3013,7 @@ fn add_order_independent_options( cmd.pgo_gen(); } - if sess.opts.unstable_opts.instrument_mcount { + if sess.opts.unstable_opts.instrument_mcount != InstrumentMcount::Disabled { cmd.enable_profiling(); } @@ -3055,6 +3186,7 @@ fn add_upstream_rust_crates( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_info: &CrateInfo, crate_type: CrateType, tmpdir: &Path, @@ -3107,6 +3239,7 @@ fn add_upstream_rust_crates( cmd, sess, archive_builder_builder, + rmeta_link_cache, crate_info, tmpdir, cnum, @@ -3238,6 +3371,7 @@ fn add_static_crate( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_info: &CrateInfo, tmpdir: &Path, cnum: CrateNum, @@ -3268,7 +3402,7 @@ fn add_static_crate( let mut archive = archive_builder_builder.new_archive_builder(sess); if let Err(error) = archive.add_archive( cratepath, - AddArchiveKind::Rlib(&|f, entry_kind| { + AddArchiveKind::Rlib(rmeta_link_cache, &|f, entry_kind| { if f == METADATA_FILENAME || f == rmeta_link::FILENAME { return true; } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 4041bfaa24cf0..8c20db5791774 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -15,7 +15,7 @@ use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::{ self, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, }; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{SymbolName, TyCtxt}; use rustc_session::Session; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; use rustc_target::spec::{Arch, Cc, CfgAbi, LinkOutputKind, LinkerFlavor, Lld, Os}; @@ -25,7 +25,7 @@ use super::command::Command; use super::symbol_export; use crate::back::symbol_export::allocator_shim_symbols; use crate::base::needs_allocator_shim_for_linking; -use crate::errors; +use crate::{SymbolExport, errors}; #[cfg(test)] mod tests; @@ -338,12 +338,7 @@ pub(crate) trait Linker { fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]); fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ); + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[SymbolExport]); fn windows_subsystem(&mut self, subsystem: WindowsSubsystemKind); fn linker_plugin_lto(&mut self); fn add_eh_frame_header(&mut self) {} @@ -794,12 +789,7 @@ impl<'a> Linker for GccLinker<'a> { } } - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[SymbolExport]) { // Symbol visibility in object files typically takes care of this. if crate_type == CrateType::Executable { let should_export_executable_symbols = @@ -826,9 +816,9 @@ impl<'a> Linker for GccLinker<'a> { // Write a plain, newline-separated list of symbols let res = try { let mut f = File::create_buffered(&path)?; - for (sym, _) in symbols { - debug!(" _{sym}"); - writeln!(f, "_{sym}")?; + for sym in symbols { + debug!(" _{}", sym.name); + writeln!(f, "_{}", sym.name)?; } }; if let Err(error) = res { @@ -842,12 +832,13 @@ impl<'a> Linker for GccLinker<'a> { // .def file similar to MSVC one but without LIBRARY section // because LD doesn't like when it's empty writeln!(f, "EXPORTS")?; - for (symbol, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; - debug!(" _{symbol}"); + for symbol in symbols { + let kind_marker = + if symbol.kind == SymbolExportKind::Data { " DATA" } else { "" }; + debug!(" _{}", symbol.name); // Quote the name in case it's reserved by linker in some way // (this accounts for names with dots in particular). - writeln!(f, " \"{symbol}\"{kind_marker}")?; + writeln!(f, " \"{}\"{kind_marker}", symbol.name)?; } }; if let Err(error) = res { @@ -856,16 +847,16 @@ impl<'a> Linker for GccLinker<'a> { self.link_arg(path); } else if self.sess.target.is_like_wasm { self.link_arg("--no-export-dynamic"); - for (sym, _) in symbols { - self.link_arg("--export").link_arg(sym); + for sym in symbols { + self.link_arg("--export").link_arg(&sym.name); } } else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris { let res = try { let mut f = File::create_buffered(&path)?; writeln!(f, "{{")?; - for (sym, _) in symbols { - debug!(sym); - writeln!(f, " {sym};")?; + for sym in symbols { + debug!("{}", sym.name); + writeln!(f, " {};", sym.name)?; } writeln!(f, "}};")?; }; @@ -880,9 +871,9 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "{{")?; if !symbols.is_empty() { writeln!(f, " global:")?; - for (sym, _) in symbols { - debug!(" {sym};"); - writeln!(f, " {sym};")?; + for sym in symbols { + debug!(" {};", sym.name); + writeln!(f, " {};", sym.name)?; } } writeln!(f, "\n local:\n *;\n}};")?; @@ -1126,25 +1117,10 @@ impl<'a> Linker for MsvcLinker<'a> { } } - // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to - // export symbols from a dynamic library. When building a dynamic library, - // however, we're going to want some symbols exported, so this function - // generates a DEF file which lists all the symbols. - // - // The linker will read this `*.def` file and export all the symbols from - // the dynamic library. Note that this is not as simple as just exporting - // all the symbols in the current crate (as specified by `codegen.reachable`) - // but rather we also need to possibly export the symbols of upstream - // crates. Upstream rlibs may be linked statically to this dynamic library, - // in which case they may continue to transitively be used and hence need - // their symbols exported. - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { - // Symbol visibility takes care of this typically + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, _symbols: &[SymbolExport]) { + // We already add /EXPORT arguments to the .drectve section of symbols.o. + // Keep passing an empty .def file: link.exe otherwise skips the import + // library for DLLs with no exports. if crate_type == CrateType::Executable { let should_export_executable_symbols = self.sess.opts.unstable_opts.export_executable_symbols; @@ -1156,16 +1132,8 @@ impl<'a> Linker for MsvcLinker<'a> { let path = tmpdir.join("lib.def"); let res = try { let mut f = File::create_buffered(&path)?; - - // Start off with the standard module name header and then go - // straight to exports. writeln!(f, "LIBRARY")?; writeln!(f, "EXPORTS")?; - for (symbol, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; - debug!(" _{symbol}"); - writeln!(f, " {symbol}{kind_marker}")?; - } }; if let Err(error) = res { self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); @@ -1313,19 +1281,14 @@ impl<'a> Linker for EmLinker<'a> { self.cc_arg("-nodefaultlibs"); } - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { debug!("EXPORTED SYMBOLS:"); self.cc_arg("-s"); let mut arg = OsString::from("EXPORTED_FUNCTIONS="); let encoded = serde_json::to_string( - &symbols.iter().map(|(sym, _)| "_".to_owned() + sym).collect::>(), + &symbols.iter().map(|sym| "_".to_owned() + &sym.name).collect::>(), ) .unwrap(); debug!("{encoded}"); @@ -1453,14 +1416,9 @@ impl<'a> Linker for WasmLd<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { - for (sym, _) in symbols { - self.link_args(&["--export", sym]); + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { + for sym in symbols { + self.link_args(&["--export", &sym.name]); } } @@ -1581,7 +1539,7 @@ impl<'a> Linker for L4Bender<'a> { self.cc_arg("-nostdlib"); } - fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[(String, SymbolExportKind)]) { + fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[SymbolExport]) { // ToDo, not implemented, copy from GCC self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented); } @@ -1735,19 +1693,14 @@ impl<'a> Linker for AixLinker<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols( - &mut self, - tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { let path = tmpdir.join("list.exp"); let res = try { let mut f = File::create_buffered(&path)?; // FIXME: use llvm-nm to generate export list. - for (symbol, _) in symbols { - debug!(" _{symbol}"); - writeln!(f, " {symbol}")?; + for symbol in symbols { + debug!(" _{}", symbol.name); + writeln!(f, " {}", symbol.name)?; } }; if let Err(e) = res { @@ -1792,15 +1745,36 @@ fn for_each_exported_symbols_include_dep<'tcx>( } } -pub(crate) fn exported_symbols( +fn symbol_export_from_exported_symbol<'tcx>( + tcx: TyCtxt<'tcx>, + symbol: ExportedSymbol<'tcx>, + kind: SymbolExportKind, + cnum: CrateNum, +) -> SymbolExport { + let name = symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum); + let link_name = + symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, kind, cnum); + SymbolExport::with_link_name(name, kind, link_name) +} + +fn symbol_export_from_raw_name( tcx: TyCtxt<'_>, - crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { + name: String, + kind: SymbolExportKind, +) -> SymbolExport { + let symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &name)); + let link_name = + symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, kind, LOCAL_CRATE); + SymbolExport::with_link_name(name, kind, link_name) +} + +pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { if let Some(ref exports) = tcx.sess.target.override_export_symbols { return exports .iter() .map(|name| { - ( + symbol_export_from_raw_name( + tcx, name.to_string(), // FIXME use the correct export kind for this symbol. override_export_symbols // can't directly specify the SymbolExportKind as it is defined in rustc_middle @@ -1825,7 +1799,11 @@ pub(crate) fn exported_symbols( && !tcx.sess.target.is_like_wasm { let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx); - symbols.push((metadata_symbol_name, SymbolExportKind::Data)); + symbols.push(symbol_export_from_raw_name( + tcx, + metadata_symbol_name, + SymbolExportKind::Data, + )); } symbols @@ -1834,7 +1812,7 @@ pub(crate) fn exported_symbols( fn exported_symbols_for_non_proc_macro( tcx: TyCtxt<'_>, crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { +) -> Vec { let mut symbols = Vec::new(); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { @@ -1842,10 +1820,7 @@ fn exported_symbols_for_non_proc_macro( // from any dylib. The latter doesn't work anyway as we use hidden visibility for // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning. if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) { - symbols.push(( - symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum), - info.kind, - )); + symbols.push(symbol_export_from_exported_symbol(tcx, symbol, info.kind, cnum)); symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum); } }); @@ -1855,13 +1830,16 @@ fn exported_symbols_for_non_proc_macro( && needs_allocator_shim_for_linking(tcx.dependency_formats(()), crate_type) && let Some(kind) = tcx.allocator_kind(()) { - symbols.extend(allocator_shim_symbols(tcx, kind)); + symbols.extend( + allocator_shim_symbols(tcx, kind) + .map(|(name, kind)| symbol_export_from_raw_name(tcx, name, kind)), + ); } symbols } -fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, SymbolExportKind)> { +fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec { // `exported_symbols` will be empty when !should_codegen. if !tcx.sess.opts.output_types.should_codegen() { return Vec::new(); @@ -1870,7 +1848,7 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, Symbol let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); let proc_macro_decls_name = rustc_session::generate_proc_macro_decls_symbol(stable_crate_id); - vec![(proc_macro_decls_name, SymbolExportKind::Data)] + vec![symbol_export_from_raw_name(tcx, proc_macro_decls_name, SymbolExportKind::Data)] } pub(crate) fn linked_symbols( @@ -1984,16 +1962,11 @@ impl<'a> Linker for LlbcLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { match _crate_type { CrateType::Cdylib => { - for (sym, _) in symbols { - self.link_args(&["--export-symbol", sym]); + for sym in symbols { + self.link_args(&["--export-symbol", &sym.name]); } } _ => (), @@ -2064,17 +2037,12 @@ impl<'a> Linker for BpfLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[SymbolExport]) { let path = tmpdir.join("symbols"); let res = try { let mut f = File::create_buffered(&path)?; - for (sym, _) in symbols { - writeln!(f, "{sym}")?; + for sym in symbols { + writeln!(f, "{}", sym.name)?; } }; if let Err(error) = res { diff --git a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs index 68b23ca9ac5cb..58da783277dfe 100644 --- a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs +++ b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs @@ -2,9 +2,10 @@ //! and potentially other data collected and used when building or linking a rlib. //! See . -use std::path::Path; +use std::path::{Path, PathBuf}; use object::read::archive::ArchiveFile; +use rustc_data_structures::fx::FxHashMap; use rustc_serialize::opaque::mem_encoder::MemEncoder; use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; use rustc_serialize::{Decodable, Encodable}; @@ -54,3 +55,18 @@ pub fn read_from_data(archive_data: &[u8], rlib_path: &Path) -> Option>, +} + +impl RmetaLinkCache { + pub fn get_or_insert_with( + &mut self, + rlib_path: &Path, + load: impl FnOnce() -> Option, + ) -> Option<&RmetaLink> { + self.cache.entry(rlib_path.to_path_buf()).or_insert_with(load).as_ref() + } +} diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 014fd5cf3a0e5..42e2b646e4793 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -11,7 +11,9 @@ use rustc_middle::middle::exported_symbols::{ ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, }; use rustc_middle::query::LocalCrate; -use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, Instance, SymbolName, Ty, TyCtxt}; +use rustc_middle::ty::{ + self, GenericArgKind, GenericArgsRef, Instance, ShimKind, SymbolName, Ty, TyCtxt, +}; use rustc_middle::util::Providers; use rustc_session::config::CrateType; use rustc_span::Span; @@ -19,6 +21,7 @@ use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{Arch, Os, TlsModel}; use tracing::debug; +use crate::SymbolExport; use crate::back::symbol_export; use crate::base::allocator_shim_contents; @@ -332,7 +335,10 @@ fn exported_generic_symbols_provider_local<'tcx>( )); } } - MonoItem::Fn(Instance { def: InstanceKind::DropGlue(_, Some(ty)), args }) => { + MonoItem::Fn(Instance { + def: InstanceKind::Shim(ShimKind::DropGlue(_, Some(ty))), + args, + }) => { // A little sanity-check assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); @@ -356,7 +362,7 @@ fn exported_generic_symbols_provider_local<'tcx>( } } MonoItem::Fn(Instance { - def: InstanceKind::AsyncDropGlueCtorShim(_, ty), + def: InstanceKind::Shim(ShimKind::AsyncDropGlueCtor(_, ty)), args, }) => { // A little sanity-check @@ -371,7 +377,10 @@ fn exported_generic_symbols_provider_local<'tcx>( }, )); } - MonoItem::Fn(Instance { def: InstanceKind::AsyncDropGlue(def, ty), args: _ }) => { + MonoItem::Fn(Instance { + def: InstanceKind::Shim(ShimKind::AsyncDropGlue(def, ty)), + args: _, + }) => { symbols.push(( ExportedSymbol::AsyncDropGlue(def, ty), SymbolExportInfo { @@ -578,7 +587,7 @@ pub(crate) fn symbol_name_for_instance_in_crate<'tcx>( rustc_symbol_mangling::symbol_name_for_instance_in_crate( tcx, ty::Instance { - def: ty::InstanceKind::ThreadLocalShim(def_id), + def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }, instantiating_crate, @@ -713,7 +722,7 @@ pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>( /// Add it to the symbols list for all kernel functions, so that it is exported in the linked /// object. pub(crate) fn extend_exported_symbols<'tcx>( - symbols: &mut Vec<(String, SymbolExportKind)>, + symbols: &mut Vec, tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, instantiating_crate: CrateNum, @@ -729,7 +738,7 @@ pub(crate) fn extend_exported_symbols<'tcx>( // Add the symbol for the kernel descriptor (with .kd suffix) // Per https://llvm.org/docs/AMDGPUUsage.html#symbols these will always be `STT_OBJECT` so // export as data. - symbols.push((format!("{undecorated}.kd"), SymbolExportKind::Data)); + symbols.push(SymbolExport::new(format!("{undecorated}.kd"), SymbolExportKind::Data)); } fn maybe_emutls_symbol_name<'tcx>( diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 112cf45ebbf2e..de1adf02a049d 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -15,9 +15,7 @@ use rustc_errors::{ }; use rustc_fs_util::link_or_copy; use rustc_hir::find_attr; -use rustc_incremental::{ - copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, -}; +use rustc_incremental::{copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir_sess}; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::bug; @@ -884,20 +882,24 @@ fn execute_copy_from_cache_work_item( let mut links_from_incr_cache = Vec::new(); let mut load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { - let source_file = in_incr_comp_dir(incr_comp_session_dir, saved_path); + let source_file_in_incr_comp_dir = incr_comp_session_dir.join(saved_path); debug!( "copying preexisting module `{}` from {:?} to {}", module.name, - source_file, + source_file_in_incr_comp_dir, output_path.display() ); - match link_or_copy(&source_file, &output_path) { + match link_or_copy(&source_file_in_incr_comp_dir, &output_path) { Ok(_) => { - links_from_incr_cache.push(source_file); + links_from_incr_cache.push(source_file_in_incr_comp_dir); Some(output_path) } Err(error) => { - dcx.emit_err(errors::CopyPathBuf { source_file, output_path, error }); + dcx.emit_err(errors::CopyPathBuf { + source_file: source_file_in_incr_comp_dir, + output_path, + error, + }); None } } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 2a5e421282f4e..4e979df471318 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -493,7 +493,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return None; } - let main_llfn = cx.get_fn_addr(instance); + let main_llfn = cx.get_fn_addr(instance, Some(PacMetadata::default())); let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); @@ -554,7 +554,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx.tcx().mk_args(&[main_ret_ty.into()]), DUMMY_SP, ); - let start_fn = cx.get_fn_addr(start_instance); + let start_fn = cx.get_fn_addr(start_instance, Some(PacMetadata::default())); let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4d271447746c1..46bb1182c298c 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -290,9 +290,11 @@ fn process_builtin_attrs( AttributeKind::RustcOffloadKernel => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL } - AttributeKind::PatchableFunctionEntry { prefix, entry } => { + AttributeKind::PatchableFunctionEntry { prefix, entry, section } => { codegen_fn_attrs.patchable_function_entry = - Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); + Some(PatchableFunctionEntry::from_prefix_entry_and_section( + *prefix, *entry, *section, + )); } AttributeKind::InstrumentFn(instrument_fn) => { codegen_fn_attrs.instrument_fn = match instrument_fn { diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index eca013c83cb4a..3a2ff3ab5d2ae 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -117,7 +117,11 @@ pub(crate) fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let tcx = bx.tcx(); let def_id = tcx.require_lang_item(li, span); let instance = ty::Instance::mono(tcx, def_id); - (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) + ( + bx.fn_abi_of_instance(instance, ty::List::empty()), + bx.get_fn_addr(instance, Some(PacMetadata::default())), + instance, + ) } pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 9b8d05c9f72c0..5ece363fcd8d9 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -375,6 +375,7 @@ fn push_debuginfo_type_name<'tcx>( output.push_str("fn("); } + // FIXME(splat): should debuginfo be de-tupled in the callee (and caller)? if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { push_debuginfo_type_name(tcx, parameter_type, true, output, visited); diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 10ae8a9ee0b38..f3f19f9e90d83 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -233,6 +233,28 @@ impl From<&cstore::NativeLib> for NativeLib { } } +/// A symbol to make visible from a linked artifact. +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct SymbolExport { + /// Name to make visible from the linked artifact. + pub name: String, + /// Kind of symbol, used for target-specific export directives and name decoration. + pub kind: SymbolExportKind, + /// Name of the symbol as seen by the linker, when it differs from `name`. + pub link_name: Option, +} + +impl SymbolExport { + pub fn new(name: String, kind: SymbolExportKind) -> SymbolExport { + SymbolExport { name, kind, link_name: None } + } + + pub fn with_link_name(name: String, kind: SymbolExportKind, link_name: String) -> SymbolExport { + let link_name = if link_name == name { None } else { Some(link_name) }; + SymbolExport { name, kind, link_name } + } +} + /// Misc info we load from metadata to persist beyond the tcx. /// /// Note: though `CrateNum` is only meaningful within the same tcx, information within `CrateInfo` @@ -247,7 +269,7 @@ pub struct CrateInfo { pub target_cpu: String, pub target_features: Vec, pub crate_types: Vec, - pub exported_symbols: UnordMap>, + pub exported_symbols: UnordMap>, pub linked_symbols: FxIndexMap>, pub local_crate_name: Symbol, pub compiler_builtins: Option, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index d4932adeda1f9..0173b84a4d9a1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,6 +1,9 @@ use std::cmp; +use std::ops::Range; -use rustc_abi::{Align, BackendRepr, ExternAbi, HasDataLayout, Reg, Size, WrappingRange}; +use rustc_abi::{ + Align, ArmCall, BackendRepr, CanonAbi, ExternAbi, HasDataLayout, Reg, Size, WrappingRange, +}; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::packed::Pu128; @@ -597,6 +600,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"), }; + + if self.fn_abi.conv == CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) { + // The return value of an `extern "cmse-nonsecure-entry"` function crosses the + // secure boundary. Zero padding bytes so information does not leak. + // + // This only zeroes "guaranteed" padding. There may be more bytes that are + // padding for some but not all variants of this type; those are not zeroed. + // + // Returning a value with value-dependent padding will instead trigger a lint. + let ret_layout = self.fn_abi.ret.layout; + let uninit_ranges = ret_layout.padding_ranges(bx.cx()); + self.zero_byte_ranges(bx, llslot, ret_layout.size, &uninit_ranges); + } + load_cast(bx, cast_ty, llslot, self.fn_abi.ret.layout.align.abi) } }; @@ -618,7 +635,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = self.monomorphize(ty); let drop_fn = Instance::resolve_drop_glue(bx.tcx(), ty); - if let ty::InstanceKind::DropGlue(_, None) = drop_fn.def { + if let ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None)) = drop_fn.def { // we don't actually need to drop anything. return helper.funclet_br(self, bx, target, mergeable_succ, &[]); } @@ -669,7 +686,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => ( false, - bx.get_fn_addr(drop_fn), + bx.get_fn_addr(drop_fn, Some(PacMetadata::default())), bx.fn_abi_of_instance(drop_fn, ty::List::empty()), drop_fn, ), @@ -934,7 +951,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match instance.def { // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, // it is `func returning noop future` - ty::InstanceKind::DropGlue(_, None) => { + ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None)) => { // Empty drop glue; a no-op. let target = target.unwrap(); return helper.funclet_br(self, bx, target, mergeable_succ, &[]); @@ -1080,7 +1097,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) .unwrap(); - (None, Some(bx.get_fn_addr(instance))) + (None, Some(bx.get_fn_addr(instance, Some(PacMetadata::default())))) } _ => (Some(instance), None), } @@ -1185,6 +1202,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; // Split the rust-call tupled arguments off. + // FIXME(splat): un-tuple splatted arguments in codegen, for performance let (first_args, untuple) = if sig.abi() == ExternAbi::RustCall && let Some((tup, args)) = args.split_last() { @@ -1317,7 +1335,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } LocalRef::Operand(arg) => { let Ref(place_value) = arg.val else { - bug!("only `Ref` should use `PassMode::Indirect`"); + bug!( + "only `Ref` should use `PassMode::Indirect`, but got {:?}", + arg.val + ); }; bx.typed_place_copy(place_value, tmp.val, fn_abi.args[i].layout); op.val = arg.val; @@ -1338,6 +1359,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_argument( bx, + fn_abi.conv, op, by_move, &mut llargs, @@ -1348,6 +1370,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let num_untupled = untuple.map(|tup| { self.codegen_arguments_untupled( bx, + fn_abi.conv, &tup.node, &mut llargs, &fn_abi.args[first_args.len()..], @@ -1377,6 +1400,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let last_arg = fn_abi.args.last().unwrap(); self.codegen_argument( bx, + fn_abi.conv, location, /* by_move */ false, &mut llargs, @@ -1386,7 +1410,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let fn_ptr = match (instance, llfn) { - (Some(instance), None) => bx.get_fn_addr(instance), + (Some(instance), None) => bx.get_fn_addr(instance, Some(PacMetadata::default())), (_, Some(llfn)) => llfn, _ => span_bug!(fn_span, "no instance or llfn for call"), }; @@ -1693,9 +1717,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + fn zero_byte_ranges( + &mut self, + bx: &mut Bx, + ptr: Bx::Value, + limit: Size, + ranges: &[Range], + ) { + let zero = bx.const_u8(0); + + for range in ranges { + let end = cmp::min(range.end, limit); + if range.start >= end { + continue; + } + let offset = bx.const_usize(range.start.bytes()); + let len = bx.const_usize((end - range.start).bytes()); + let ptr = bx.inbounds_ptradd(ptr, offset); + bx.memset(ptr, zero, len, Align::ONE, MemFlags::empty()); + } + } + fn codegen_argument( &mut self, bx: &mut Bx, + conv: CanonAbi, op: OperandRef<'tcx, Bx::Value>, by_move: bool, llargs: &mut Vec, @@ -1819,6 +1865,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { MemFlags::empty(), None, ); + + // The arguments of an `extern "cmse-nonsecure-call"` function cross the secure + // boundary. Zero padding bytes so information does not leak. + // + // This only zeroes "guaranteed" padding. There may be more bytes that are + // padding for some but not all variants of this type; those are not zeroed. + // + // Passing an argument with value-dependent padding will instead trigger a lint. + if conv == CanonAbi::Arm(ArmCall::CCmseNonSecureCall) { + self.zero_byte_ranges( + bx, + llscratch, + Size::from_bytes(copy_bytes), + &arg.layout.padding_ranges(bx.cx()), + ); + } + // ...and then load it with the ABI type. llval = load_cast(bx, cast, llscratch, scratch_align); bx.lifetime_end(llscratch, scratch_size); @@ -1845,6 +1908,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn codegen_arguments_untupled( &mut self, bx: &mut Bx, + conv: CanonAbi, operand: &mir::Operand<'tcx>, llargs: &mut Vec, args: &[ArgAbi<'tcx, Ty<'tcx>>], @@ -1864,6 +1928,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let field = bx.load_operand(field_ptr); self.codegen_argument( bx, + conv, field, by_move, llargs, @@ -1875,7 +1940,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // If the tuple is immediate, the elements are as well. for i in 0..tuple.layout.fields.count() { let op = tuple.extract_field(self, bx, i); - self.codegen_argument(bx, op, by_move, llargs, &args[i], lifetime_ends_after_call); + self.codegen_argument( + bx, + conv, + op, + by_move, + llargs, + &args[i], + lifetime_ends_after_call, + ); } } tuple.layout.fields.count() diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 2a55cbedfb3b8..71315acc4e5db 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -78,15 +78,18 @@ impl<'tcx, S: Copy, L: Copy> DebugScope { /// it may so happen that the current span belongs to a different file than the DIScope /// corresponding to span's containing source scope. If so, we need to create a DIScope /// "extension" into that file. - pub fn adjust_dbg_scope_for_span>( + pub fn adjust_dbg_scope_for_span< + 'a, + Bx: BuilderMethods<'a, 'tcx, DIScope = S, DILocation = L>, + >( &self, - cx: &Cx, + bx: &mut Bx, span: Span, ) -> S { let pos = span.lo(); if pos < self.file_start_pos || pos >= self.file_end_pos { - let sm = cx.sess().source_map(); - cx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file) + let sm = bx.sess().source_map(); + bx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file) } else { self.dbg_scope } @@ -217,23 +220,24 @@ fn calculate_debuginfo_offset< impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) { bx.set_span(source_info.span); - if let Some(dbg_loc) = self.dbg_loc(source_info) { + if let Some(dbg_loc) = self.dbg_loc(bx, source_info) { bx.set_dbg_loc(dbg_loc); } } - fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option { - let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?; - Some(self.cx.dbg_loc(dbg_scope, inlined_at, span)) + fn dbg_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) -> Option { + let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(bx, source_info)?; + Some(bx.dbg_loc(dbg_scope, inlined_at, span)) } fn adjusted_span_and_dbg_scope( &self, + bx: &mut Bx, source_info: mir::SourceInfo, ) -> Option<(Bx::DIScope, Option, Span)> { let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]; let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span); - Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span)) + Some((scope.adjust_dbg_scope_for_span(bx, span), scope.inlined_at, span)) } fn spill_operand_to_stack( @@ -279,7 +283,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let Some(dbg_var) = var.dbg_var else { continue; }; - let Some(dbg_loc) = self.dbg_loc(var.source_info) else { + let Some(dbg_loc) = self.dbg_loc(bx, var.source_info) else { continue; }; bx.dbg_var_value( @@ -334,14 +338,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let name = sym::empty; let decl = &self.mir.local_decls[local]; let dbg_var = if full_debug_info { - self.adjusted_span_and_dbg_scope(decl.source_info).map( + self.adjusted_span_and_dbg_scope(bx, decl.source_info).map( |(dbg_scope, _, span)| { // FIXME(eddyb) is this `+ 1` needed at all? let kind = VariableKind::ArgumentVariable(arg_index + 1); let arg_ty = self.monomorphize(decl.ty); - self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span) + bx.create_dbg_var(name, arg_ty, dbg_scope, kind, span) }, ) } else { @@ -483,7 +487,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>, ) { let Some(dbg_var) = var.dbg_var else { return }; - let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return }; + let Some(dbg_loc) = self.dbg_loc(bx, var.source_info) else { return }; let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } = calculate_debuginfo_offset(bx, var.projection, base.layout); @@ -579,7 +583,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default(); for var in &self.mir.var_debug_info { let dbg_scope_and_span = if full_debug_info { - self.adjusted_span_and_dbg_scope(var.source_info) + self.adjusted_span_and_dbg_scope(bx, var.source_info) } else { None }; @@ -625,13 +629,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match params_seen.entry((dbg_scope, arg_index)) { Entry::Occupied(o) => o.get().clone(), Entry::Vacant(v) => v - .insert( - self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span), - ) + .insert(bx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)) .clone(), } } else { - self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span) + bx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span) } }); @@ -669,7 +671,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::VarDebugInfoContents::Const(c) => { if let Some(dbg_var) = dbg_var { - let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue }; + let Some(dbg_loc) = self.dbg_loc(bx, var.source_info) else { continue }; let operand = self.eval_mir_constant_to_operand(bx, &c); constants.push(ConstDebugInfo { @@ -692,7 +694,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// /// Returns the FunctionDebugContext for the function which holds state needed /// for debug info creation, if it is enabled. - pub(super) fn fill_function_debug_context(&mut self) { + pub(super) fn fill_function_debug_context(&mut self, bx: &mut Bx) { if self.cx.sess().opts.debuginfo == DebugInfo::None { return; } @@ -722,7 +724,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Instantiate all scopes. let mut discriminators = FxHashMap::default(); for scope in self.mir.source_scopes.indices() { - let scope_data = self.make_mir_scope(&variables, &mut discriminators, scope); + let scope_data = self.make_mir_scope(bx, &variables, &mut discriminators, scope); let _s = self.debug_context.as_mut().unwrap().scopes.push(scope_data); debug_assert_eq!(_s, scope); } @@ -730,6 +732,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn make_mir_scope( &mut self, + bx: &mut Bx, variables: &Option>, discriminators: &mut FxHashMap, scope: mir::SourceScope, @@ -741,7 +744,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { // The root is the function itself. let file = self.cx.sess().source_map().lookup_source_file(self.mir.span.lo()); - let dbg_scope = self.cx.dbg_scope_fn(self.instance, self.fn_abi, Some(self.llfn)); + let dbg_scope = bx.dbg_scope_fn(self.instance, self.fn_abi, Some(self.llfn)); return DebugScope { dbg_scope, inlined_at: None, @@ -770,16 +773,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { .entry(callee) .or_insert_with(|| { let callee_fn_abi = self.cx.fn_abi_of_instance(callee, ty::List::empty()); - self.cx.dbg_scope_fn(callee, callee_fn_abi, None) + bx.dbg_scope_fn(callee, callee_fn_abi, None) }) } - None => self.cx.dbg_create_lexical_block(scope_data.span.lo(), parent_scope.dbg_scope), + None => bx.dbg_create_lexical_block(scope_data.span.lo(), parent_scope.dbg_scope), }; let inlined_at = scope_data.inlined.map(|(_, callsite_span)| { let callsite_span = hygiene::walk_chain_collapsed(callsite_span, self.mir.span); - let callsite_scope = parent_scope.adjust_dbg_scope_for_span(self.cx, callsite_span); - let loc = self.cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span); + let callsite_scope = parent_scope.adjust_dbg_scope_for_span(bx, callsite_span); + let loc = bx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span); // NB: In order to produce proper debug info for variables (particularly // arguments) in multiply-inlined functions, LLVM expects to see a single @@ -805,9 +808,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // NB: We have to emit *something* here or we'll fail LLVM IR verification // in at least some circumstances (see issue #135322) so if the required // discriminant cannot be encoded fall back to the dummy location. - self.cx.dbg_location_clone_with_discriminator(loc, *o.get()).unwrap_or_else( - || self.cx.dbg_loc(callsite_scope, parent_scope.inlined_at, DUMMY_SP), - ) + bx.dbg_location_clone_with_discriminator(loc, *o.get()).unwrap_or_else(|| { + bx.dbg_loc(callsite_scope, parent_scope.inlined_at, DUMMY_SP) + }) } Entry::Vacant(v) => { v.insert(0); diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 039346b1a51db..2fa283a6c2eb4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -136,7 +136,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.instance.instantiate_mir_and_normalize_erasing_regions( self.cx.tcx(), self.cx.typing_env(), - ty::EarlyBinder::bind(value), + ty::EarlyBinder::bind(self.cx.tcx(), value), ) } } @@ -219,7 +219,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions( tcx, ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(mir.clone()), + ty::EarlyBinder::bind(tcx, mir.clone()), ); mir = tcx.arena.alloc(optimize_use_clone::(cx, monomorphized_mir)); } @@ -269,7 +269,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // monomorphization, and if there is an error during collection then codegen never starts -- so // we don't have to do it again. - fx.fill_function_debug_context(); + fx.fill_function_debug_context(&mut start_bx); let (per_local_var_debug_info, consts_debug_info) = fx.compute_per_local_var_debug_info(&mut start_bx).unzip(); @@ -433,6 +433,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let arg_decl = &mir.local_decls[local]; let arg_ty = fx.monomorphize(arg_decl.ty); + // FIXME(splat): re-tuple splatted arguments that were un-tupled in the ABI if Some(local) == mir.spread_arg { // This argument (e.g., the last argument in the "rust-call" ABI) // is a tuple that was spread at the ABI level and now we have diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 32a74d7b70587..0b00b22b1a992 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -67,7 +67,7 @@ fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndL .instantiate_mir_and_normalize_erasing_regions( cx.tcx(), cx.typing_env(), - ty::EarlyBinder::bind(value.const_), + ty::EarlyBinder::bind(cx.tcx(), value.const_), ) .eval(cx.tcx(), cx.typing_env(), value.span) .expect("erroneous constant missed by mono item collection"); @@ -75,7 +75,7 @@ fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndL let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( cx.tcx(), cx.typing_env(), - ty::EarlyBinder::bind(value.ty()), + ty::EarlyBinder::bind(cx.tcx(), value.ty()), ); let string = common::asm_const_to_str( @@ -91,7 +91,7 @@ fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndL let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( cx.tcx(), cx.typing_env(), - ty::EarlyBinder::bind(value.ty()), + ty::EarlyBinder::bind(cx.tcx(), value.ty()), ); let instance = match mono_type.kind() { diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index c87ea83eacf62..a47b3443ea664 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -227,7 +227,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { b @ abi::Scalar::Initialized { .. }, ) => { let (a_size, b_size) = (a.size(bx), b.size(bx)); - let b_offset = (offset + a_size).align_to(b.align(bx).abi); + let b_offset = (offset + a_size).align_to(b.default_align(bx).abi); assert!(b_offset.bytes() > 0); let a_val = read_scalar( offset, @@ -388,7 +388,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { assert_eq!(field.size, a.size(bx.cx())); (Some(a), a_llval) } else { - assert_eq!(offset, a.size(bx.cx()).align_to(b.align(bx.cx()).abi)); + assert_eq!(offset, a.size(bx.cx()).align_to(b.default_align(bx.cx()).abi)); assert_eq!(field.size, b.size(bx.cx())); (Some(b), b_llval) } @@ -962,7 +962,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { let BackendRepr::ScalarPair(a_scalar, b_scalar) = dest.layout.backend_repr else { bug!("store_with_flags: invalid ScalarPair layout: {:#?}", dest.layout); }; - let b_offset = a_scalar.size(bx).align_to(b_scalar.align(bx).abi); + let b_offset = a_scalar.size(bx).align_to(b_scalar.default_align(bx).abi); let val = bx.from_immediate(a); let align = dest.val.align; diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index deb8ca12b059d..49f03fe1376e2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -434,7 +434,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ) .unwrap(); - OperandValue::Immediate(bx.get_fn_addr(instance)) + OperandValue::Immediate( + bx.get_fn_addr( + instance, + Some(PacMetadata::default()), + ), + ) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } @@ -448,7 +453,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ty::ClosureKind::FnOnce, ); - OperandValue::Immediate(bx.cx().get_fn_addr(instance)) + OperandValue::Immediate( + bx.cx().get_fn_addr( + instance, + Some(PacMetadata::default()), + ), + ) } _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), } @@ -665,10 +675,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let static_ = if !def_id.is_local() && bx.cx().tcx().needs_thread_local_shim(def_id) { let instance = ty::Instance { - def: ty::InstanceKind::ThreadLocalShim(def_id), + def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }; - let fn_ptr = bx.get_fn_addr(instance); + let fn_ptr = bx.get_fn_addr(instance, Some(PacMetadata::default())); let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() { diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 363e47aa60fa1..39c2529152566 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -242,7 +242,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn alloca_with_ty(&mut self, layout: TyAndLayout<'tcx>) -> Self::Value; fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; - fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value; + fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; fn atomic_load( &mut self, ty: Self::Type, @@ -476,6 +476,11 @@ pub trait BuilderMethods<'a, 'tcx>: flags: MemFlags, ); + // Produce a value from calling the `vscale` intrinsic (containing the `vscale` multiplier that + // a scalable vector's element size and count can be multiplied by to get the real size of the + // vector) + fn vscale(&mut self, ty: Self::Type) -> Self::Value; + /// *Typed* copy for non-overlapping places. /// /// Has a default implementation in terms of `memcpy`, but specific backends @@ -513,6 +518,12 @@ pub trait BuilderMethods<'a, 'tcx>: temp.val.store_with_flags(self, dst.with_type(layout), flags); } else if !layout.is_zst() { let bytes = self.const_usize(layout.size.bytes()); + let bytes = if layout.peel_transparent_wrappers(self).ty.is_scalable_vector() { + let vscale = self.vscale(self.type_i64()); + self.mul(vscale, bytes) + } else { + bytes + }; self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags, None); } } diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 22784a8868ab5..20738ba79e9fb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -2,6 +2,7 @@ use rustc_abi as abi; use rustc_middle::mir::interpret::Scalar; use super::BackendTypes; +use crate::traits::PacMetadata; pub trait ConstCodegenMethods: BackendTypes { // Constant constructors @@ -38,7 +39,16 @@ pub trait ConstCodegenMethods: BackendTypes { fn const_to_opt_uint(&self, v: Self::Value) -> Option; fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option; - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value; + fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value { + self.scalar_to_backend_with_pac(cv, layout, llty, None) + } + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + llty: Self::Type, + pac: Option, + ) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index d3af9e52f1b91..5e407ad13d859 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -15,51 +15,54 @@ pub trait DebugInfoCodegenMethods<'tcx>: BackendTypes { trait_ref: Option>, vtable: Self::Value, ); +} - fn dbg_create_lexical_block(&self, pos: BytePos, parent_scope: Self::DIScope) -> Self::DIScope; - - fn dbg_location_clone_with_discriminator( - &self, - loc: Self::DILocation, - discriminator: u32, - ) -> Option; - +pub trait DebugInfoBuilderMethods<'tcx>: BackendTypes { // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). fn dbg_scope_fn( - &self, + &mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, maybe_definition_llfn: Option, ) -> Self::DIScope; + fn dbg_create_lexical_block( + &mut self, + pos: BytePos, + parent_scope: Self::DIScope, + ) -> Self::DIScope; + + fn dbg_location_clone_with_discriminator( + &mut self, + loc: Self::DILocation, + discriminator: u32, + ) -> Option; + fn dbg_loc( - &self, + &mut self, scope: Self::DIScope, inlined_at: Option, span: Span, ) -> Self::DILocation; fn extend_scope_to_file( - &self, + &mut self, scope_metadata: Self::DIScope, file: &SourceFile, ) -> Self::DIScope; - fn debuginfo_finalize(&self); // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). fn create_dbg_var( - &self, + &mut self, variable_name: Symbol, variable_type: Ty<'tcx>, scope_metadata: Self::DIScope, variable_kind: VariableKind, span: Span, ) -> Self::DIVariable; -} -pub trait DebugInfoBuilderMethods<'tcx>: BackendTypes { // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). fn dbg_var_addr( diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 92ddc1f347994..ecef986b55855 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -7,6 +7,35 @@ use rustc_span::Symbol; use super::BackendTypes; +/// Strategy for incorporating address-based diversity into PAC computation. +#[derive(Default)] +pub enum AddressDiversity { + /// No address diversity is applied. + #[default] + None, + /// Use the actual memory address for diversification. + Real, + /// Use a fixed synthetic value instead of the real address, + /// i.e. `1` is used for `.init_array` / `.fini_array`. + Synthetic(u64), +} + +/// Metadata used for pointer authentication. +pub struct PacMetadata { + /// The PAC key to use. + pub key: u32, + /// Discriminator value used to diversify the PAC. + pub disc: u64, + /// Controls how address diversity is applied when computing the PAC. + pub addr_diversity: AddressDiversity, +} + +impl Default for PacMetadata { + fn default() -> Self { + PacMetadata { key: 0, disc: 0, addr_diversity: AddressDiversity::default() } + } +} + pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn vtables( &self, @@ -19,7 +48,7 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { ) { } fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function; - fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value; + fn get_fn_addr(&self, instance: Instance<'tcx>, pac: Option) -> Self::Value; fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; fn set_frame_pointer_type(&self, llfn: Self::Function); diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index f46d07ea5008e..ed2dad5a510cd 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -42,7 +42,7 @@ pub use self::coverageinfo::CoverageInfoBuilderMethods; pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; pub use self::declare::PreDefineCodegenMethods; pub use self::intrinsic::IntrinsicCallBuilderMethods; -pub use self::misc::MiscCodegenMethods; +pub use self::misc::{AddressDiversity, MiscCodegenMethods, PacMetadata}; pub use self::statics::{StaticBuilderMethods, StaticCodegenMethods}; pub use self::type_::{ ArgAbiBuilderMethods, BaseTypeCodegenMethods, DerivedTypeCodegenMethods, diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index b7d2051f61741..d6578def58692 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -344,13 +344,13 @@ where let uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(_) | ty::ConstKind::Error(_) => None, - // Unevaluated consts in MIR bodies don't have associated MIR (e.g. `type const`). - ty::ConstKind::Unevaluated(_) => None, + // Alias consts in MIR bodies don't have associated MIR (e.g. `type const`). + ty::ConstKind::Alias(_, _) => None, // FIXME(mgca): Investigate whether using `None` for `ConstKind::Value` is overly // strict, and if instead we should be doing some kind of value-based analysis. ty::ConstKind::Value(_) => None, _ => bug!( - "expected ConstKind::Param, ConstKind::Value, ConstKind::Unevaluated, or ConstKind::Error here, found {:?}", + "expected ConstKind::Param, ConstKind::Value, ConstKind::Alias, or ConstKind::Error here, found {:?}", ct ), }, 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 388b6e11f7e5e..3ee11c3f5fbdd 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -97,8 +97,8 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( body: &'tcx mir::Body<'tcx>, ) -> InterpResult<'tcx, R> { let tcx = *ecx.tcx; - let layout = - ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args).skip_norm_wip())?; + let layout = ecx + .layout_of(body.bound_return_ty(tcx).instantiate(tcx, cid.instance.args).skip_norm_wip())?; let (intern_kind, ret) = setup_for_eval(ecx, cid, layout)?; trace!( diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 3d8d9592459c4..8f58e39d419ce 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -7,7 +7,7 @@ use rustc_ast::Mutability; use rustc_hir::LangItem; use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{self, Const, FnHeader, FnSigTys, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, Const, FnHeader, FnSigKind, FnSigTys, ScalarInt, Ty, TyCtxt}; use rustc_span::{Symbol, sym}; use crate::const_eval::CompileTimeMachine; @@ -436,6 +436,22 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { sym::variadic => { self.write_scalar(Scalar::from_bool(fn_sig_kind.c_variadic()), &field_place)?; } + sym::is_splatted => { + self.write_scalar( + Scalar::from_bool(fn_sig_kind.splatted().is_some()), + &field_place, + )?; + } + sym::splatted_index => { + self.write_scalar( + Scalar::from_u8( + // Currently the same encoding as FnSigKind.splatted + // FIXME(splat): make these two fields into a single Option, or choose a stable encoding + fn_sig_kind.splatted().unwrap_or(FnSigKind::NO_SPLATTED_ARG_INDEX), + ), + &field_place, + )?; + } other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 85da687d8af37..e2fe8c3a79690 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -428,7 +428,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Determine whether this is a non-capturing closure. That's relevant as their first // argument can be skipped (and that's the only kind of argument skipping we allow). let is_non_capturing_closure = - (matches!(instance.def, ty::InstanceKind::ClosureOnceShim { .. }) + (matches!(instance.def, ty::InstanceKind::Shim(ty::ShimKind::ClosureOnce { .. })) || self.tcx.is_closure_like(def_id)) && { let arg = &callee_fn_abi.args[0]; @@ -652,18 +652,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } } - ty::InstanceKind::VTableShim(..) - | ty::InstanceKind::ReifyShim(..) - | ty::InstanceKind::ClosureOnceShim { .. } - | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } - | ty::InstanceKind::FnPtrShim(..) - | ty::InstanceKind::DropGlue(..) - | ty::InstanceKind::CloneShim(..) - | ty::InstanceKind::FnPtrAddrShim(..) - | ty::InstanceKind::ThreadLocalShim(..) - | ty::InstanceKind::AsyncDropGlueCtorShim(..) - | ty::InstanceKind::AsyncDropGlue(..) - | ty::InstanceKind::FutureDropPollShim(..) + ty::InstanceKind::Shim(ty::ShimKind::VTable(..)) + | ty::InstanceKind::Shim(ty::ShimKind::Reify(..)) + | ty::InstanceKind::Shim(ty::ShimKind::ClosureOnce { .. }) + | ty::InstanceKind::Shim(ty::ShimKind::ConstructCoroutineInClosure { .. }) + | ty::InstanceKind::Shim(ty::ShimKind::FnPtr(..)) + | ty::InstanceKind::Shim(ty::ShimKind::DropGlue(..)) + | ty::InstanceKind::Shim(ty::ShimKind::Clone(..)) + | ty::InstanceKind::Shim(ty::ShimKind::FnPtrAddr(..)) + | ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(..)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(..)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(..)) + | ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(..)) | ty::InstanceKind::Item(_) => { // We need MIR for this fn. // Note that this can be an intrinsic, if we are executing its fallback body. @@ -681,6 +681,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // Special handling for the closure ABI: untuple the last argument. + // FIXME(splat): un-tuple splatted arguments that were tupled in typecheck let args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = if caller_abi == ExternAbi::RustCall && !args.is_empty() { // Untuple diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7ff9a4a1006a8..855ff1a318ed6 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -362,7 +362,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .try_instantiate_mir_and_normalize_erasing_regions( *self.tcx, self.typing_env, - ty::EarlyBinder::bind(value), + ty::EarlyBinder::bind(self.tcx.tcx, value), ) .map_err(|_| ErrorHandled::TooGeneric(self.cur_span())) } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 7b2983620d420..1f67b896fc8ec 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -419,7 +419,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { Immediate::from(if offset.bytes() == 0 { a_val } else { - assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi)); + assert_eq!(offset, a.size(cx).align_to(b.default_align(cx).abi)); b_val }) } @@ -614,7 +614,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. let (a_size, b_size) = (a.size(self), b.size(self)); - let b_offset = a_size.align_to(b.align(self).abi); + let b_offset = a_size.align_to(b.default_align(self).abi); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields let a_val = alloc.read_scalar( alloc_range(Size::ZERO, a_size), diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index b4e6a6b99b7a8..d6d73b2d8da81 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -733,7 +733,7 @@ where ) }; let a_size = a_val.size(); - let b_offset = a_size.align_to(b.align(&tcx).abi); + let b_offset = a_size.align_to(b.default_align(&tcx).abi); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index b865cafa38647..1aeaaee3a251c 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -607,7 +607,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { enter_trace_span!(M, resolve::resolve_drop_glue, ty = ?place.layout.ty); Instance::resolve_drop_glue(*self.tcx, place.layout.ty) }; - if let ty::InstanceKind::DropGlue(_, None) = instance.def { + if let ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None)) = instance.def { // This is the branch we enter if and only if the dropped type has no drop glue // whatsoever. This can happen as a result of monomorphizing a drop of a // generic. In order to make sure that generic and non-generic code behaves diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 224337e68cfbf..262ef6ba74ed0 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -345,55 +345,7 @@ fn write_path(out: &mut String, path: &[PathElem<'_>]) { } } -/// Represents a set of `Size` values as a sorted list of ranges. -// These are (offset, length) pairs, and they are sorted and mutually disjoint, -// and never adjacent (i.e. there's always a gap between two of them). -#[derive(Debug, Clone)] -pub struct RangeSet(Vec<(Size, Size)>); - -impl RangeSet { - fn add_range(&mut self, offset: Size, size: Size) { - if size.bytes() == 0 { - // No need to track empty ranges. - return; - } - let v = &mut self.0; - // We scan for a partition point where the left partition is all the elements that end - // strictly before we start. Those are elements that are too "low" to merge with us. - let idx = - v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset); - // Now we want to either merge with the first element of the second partition, or insert ourselves before that. - if let Some(&(other_offset, other_size)) = v.get(idx) - && offset + size >= other_offset - { - // Their end is >= our start (otherwise it would not be in the 2nd partition) and - // our end is >= their start. This means we can merge the ranges. - let new_start = other_offset.min(offset); - let mut new_end = (other_offset + other_size).max(offset + size); - // We grew to the right, so merge with overlapping/adjacent elements. - // (We also may have grown to the left, but that can never make us adjacent with - // anything there since we selected the first such candidate via `partition_point`.) - let mut scan_right = 1; - while let Some(&(next_offset, next_size)) = v.get(idx + scan_right) - && new_end >= next_offset - { - // Increase our size to absorb the next element. - new_end = new_end.max(next_offset + next_size); - // Look at the next element. - scan_right += 1; - } - // Update the element we grew. - v[idx] = (new_start, new_end - new_start); - // Remove the elements we absorbed (if any). - if scan_right > 1 { - drop(v.drain((idx + 1)..(idx + scan_right))); - } - } else { - // Insert new element. - v.insert(idx, (offset, size)); - } - } -} +pub type RangeSet = rustc_data_structures::range_set::RangeSet; struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> { /// The `path` may be pushed to, but the part that is present when a function @@ -1194,7 +1146,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { assert!(layout.is_sized(), "there are no unsized unions"); let layout_cx = LayoutCx::new(*ecx.tcx, ecx.typing_env); return M::cached_union_data_range(ecx, layout.ty, || { - let mut out = RangeSet(Vec::new()); + let mut out = RangeSet::new(); union_data_range_uncached(&layout_cx, layout, Size::ZERO, &mut out); out }); @@ -1644,7 +1596,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ctfe_mode, ecx, reset_provenance_and_padding, - data_bytes: reset_padding.then_some(RangeSet(Vec::new())), + data_bytes: reset_padding.then_some(RangeSet::new()), may_dangle: start_in_may_dangle, }; v.visit_value(val)?; diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index f1bca03218e07..56c41d7697e0c 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -53,20 +53,21 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { // Types with identity (print the module path). ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did: def_id, .. }, _)), args) | ty::FnDef(def_id, args) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { def_id } | ty::Opaque { def_id }, - args, - .. - }) + | ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { def_id } | ty::Opaque { def_id }, args, .. + }, + ) | ty::Closure(def_id, args) | ty::CoroutineClosure(def_id, args) | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args), ty::Foreign(def_id) => self.print_def_path(def_id, &[]), - ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => { bug!("type_name: unexpected free alias") } - ty::Alias(ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { bug!("type_name: unexpected inherent projection") } ty::CoroutineWitness(..) => bug!("type_name: unexpected `CoroutineWitness`"), diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index be8538acd30eb..73b0dbd1ceeba 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -68,6 +68,7 @@ pub mod obligation_forest; pub mod owned_slice; pub mod packed; pub mod profiling; +pub mod range_set; pub mod sharded; pub mod small_c_str; pub mod snapshot_map; diff --git a/compiler/rustc_data_structures/src/range_set.rs b/compiler/rustc_data_structures/src/range_set.rs new file mode 100644 index 0000000000000..514946a0fb2de --- /dev/null +++ b/compiler/rustc_data_structures/src/range_set.rs @@ -0,0 +1,59 @@ +/// Represents a set of `Size` values as a sorted list of ranges. +/// +/// These are (offset, length) pairs, and they are sorted and mutually disjoint, +/// and never adjacent (i.e. there's always a gap between two of them). +#[derive(Debug, Clone)] +pub struct RangeSet(pub Vec<(T, T)>); + +impl RangeSet +where + T: Copy + Ord + Default, + T: core::ops::Add, + T: core::ops::Sub, +{ + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn add_range(&mut self, offset: T, size: T) { + if size == T::default() { + // No need to track empty ranges. + return; + } + let v = &mut self.0; + // We scan for a partition point where the left partition is all the elements that end + // strictly before we start. Those are elements that are too "low" to merge with us. + let idx = + v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset); + // Now we want to either merge with the first element of the second partition, or insert ourselves before that. + if let Some(&(other_offset, other_size)) = v.get(idx) + && offset + size >= other_offset + { + // Their end is >= our start (otherwise it would not be in the 2nd partition) and + // our end is >= their start. This means we can merge the ranges. + let new_start = other_offset.min(offset); + let mut new_end = (other_offset + other_size).max(offset + size); + // We grew to the right, so merge with overlapping/adjacent elements. + // (We also may have grown to the left, but that can never make us adjacent with + // anything there since we selected the first such candidate via `partition_point`.) + let mut scan_right = 1; + while let Some(&(next_offset, next_size)) = v.get(idx + scan_right) + && new_end >= next_offset + { + // Increase our size to absorb the next element. + new_end = new_end.max(next_offset + next_size); + // Look at the next element. + scan_right += 1; + } + // Update the element we grew. + v[idx] = (new_start, new_end - new_start); + // Remove the elements we absorbed (if any). + if scan_right > 1 { + drop(v.drain((idx + 1)..(idx + scan_right))); + } + } else { + // Insert new element. + v.insert(idx, (offset, size)); + } + } +} diff --git a/compiler/rustc_error_codes/src/error_codes/E0718.md b/compiler/rustc_error_codes/src/error_codes/E0718.md index 1fe62ecf1f4e0..2ecf3081d2ed6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0718.md +++ b/compiler/rustc_error_codes/src/error_codes/E0718.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + A `#[lang = ".."]` attribute was placed on the wrong item type. Erroneous code example: -```compile_fail,E0718 +```compile_fail #![feature(lang_items)] #[lang = "owned_box"] diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md index bd93626a8db4d..b194e11b7f85e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0755.md +++ b/compiler/rustc_error_codes/src/error_codes/E0755.md @@ -20,7 +20,7 @@ side effects or infinite loops: extern "C" { #[unsafe(ffi_pure)] // ok! - pub fn strlen(s: *const i8) -> isize; + pub fn strlen(s: *const std::ffi::c_char) -> usize; } # fn main() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0756.md b/compiler/rustc_error_codes/src/error_codes/E0756.md index daafc2a5ac092..74233a6ad7a7a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0756.md +++ b/compiler/rustc_error_codes/src/error_codes/E0756.md @@ -21,7 +21,7 @@ which have no side effects except for their return value: extern "C" { #[unsafe(ffi_const)] // ok! - pub fn strlen(s: *const i8) -> i32; + pub fn strlen(s: *const std::ffi::c_char) -> usize; } # fn main() {} ``` diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 31c776f3fef00..821f541170f62 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1199,7 +1199,7 @@ pub trait LintStoreExpand { type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Default)] pub struct ModuleData { /// Path to the module starting from the crate name, like `my_crate::foo::bar`. pub mod_path: Vec, diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 41e550b8d6a3a..65af690611919 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -490,9 +490,11 @@ impl server::Server for Rustc<'_, '_> { fn literal_from_str(&mut self, s: &str) -> Result, String> { let name = FileName::proc_macro_source_code(s); - let mut parser = + let mut parser = rustc_errors::catch_fatal_errors(|| { new_parser_from_source_str(self.psess(), name, s.to_owned(), StripTokens::Nothing) - .map_err(cancel_diags_into_string)?; + }) + .map_err(|_| String::from("failed to parse to literal"))? + .map_err(cancel_diags_into_string)?; let first_span = parser.token.span.data(); let minus_present = parser.eat(exp!(Minus)); @@ -569,12 +571,15 @@ impl server::Server for Rustc<'_, '_> { } fn ts_from_str(&mut self, src: &str) -> Result { - source_str_to_stream( - self.psess(), - FileName::proc_macro_source_code(src), - src.to_string(), - Some(self.call_site), - ) + rustc_errors::catch_fatal_errors(|| { + source_str_to_stream( + self.psess(), + FileName::proc_macro_source_code(src), + src.to_string(), + Some(self.call_site), + ) + }) + .map_err(|_| String::from("failed to parse to tokenstream"))? .map_err(cancel_diags_into_string) } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 7c125c3a8983e..17d00863d99d5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -515,19 +515,66 @@ pub enum HideOrShow { Show, } -#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] -pub struct CfgInfo { - pub name: Symbol, - pub name_span: Span, - pub value: Option<(Symbol, Span)>, +#[derive(Clone, Copy, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] +pub struct DocCfgHideShowValue { + pub span: Span, + /// If `value` is `None`, then it's a `none()` value. + pub value: Option, +} + +impl DocCfgHideShowValue { + pub fn new(value: Symbol, span: Span) -> Self { + Self { span, value: Some(value) } + } + + pub fn new_none(span: Span) -> Self { + Self { span, value: None } + } +} + +#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] +pub enum DocCfgHideShow { + Any(Span), + List(ThinVec), } -impl CfgInfo { - pub fn span_for_name_and_value(&self) -> Span { - if let Some((_, value_span)) = self.value { - self.name_span.with_hi(value_span.hi()) - } else { - self.name_span +impl DocCfgHideShow { + pub fn new() -> Self { + Self::List(ThinVec::new()) + } + + pub fn new_with_only_key(span: Span) -> Self { + let mut values = ThinVec::with_capacity(1); + values.push(DocCfgHideShowValue { span, value: None }); + Self::List(values) + } + + pub fn push_none(&mut self, span: Span) { + if let Self::List(values) = self + && !values.iter().any(|v| v.value.is_none()) + { + values.push(DocCfgHideShowValue { span, value: None }); + } + } + + pub fn merge_with(&mut self, other: &Self) { + match (self, other) { + (Self::Any(_), Self::Any(_) | Self::List(_)) => { + // Nothing to do. + } + (s, Self::Any(span)) => { + // We "upgrade" the list values to "all". + *s = Self::Any(*span); + } + (Self::List(values), Self::List(other_values)) => { + // Having duplicates is not an issue, we simply ignore them. Would be more + // convenient to have a `set` type though. T_T + for other in other_values { + if !values.iter().any(|value| value.value == other.value) { + values.push(*other); + } + } + } } } } @@ -535,7 +582,7 @@ impl CfgInfo { #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub struct CfgHideShow { pub kind: HideOrShow, - pub values: ThinVec, + pub values: FxIndexMap, } #[derive(Clone, Debug, Default, StableHash, Decodable, PrintAttribute)] @@ -1224,8 +1271,9 @@ pub enum AttributeKind { /// Represents `#[patchable_function_entry]` PatchableFunctionEntry { - prefix: u8, - entry: u8, + prefix: Option, + entry: Option, + section: Option, }, /// Represents `#[path]` diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 9d14f9de3078d..d826aa363f349 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -81,7 +81,7 @@ impl PrintAttribute for ThinVec { p.word("]"); } } -impl PrintAttribute for FxIndexMap { +impl PrintAttribute for FxIndexMap { fn should_render(&self) -> bool { self.is_empty() || self[0].should_render() } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index e473db48b04c2..d0300d63d1213 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -448,43 +448,6 @@ impl DefKind { | DefKind::ExternCrate => false, } } - - /// Returns `true` if `self` is a kind of definition that does not have its own - /// type-checking context, i.e. closure, coroutine or inline const. - #[inline] - pub fn is_typeck_child(self) -> bool { - match self { - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => true, - DefKind::Mod - | DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Variant - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::Fn - | DefKind::Const { .. } - | DefKind::ConstParam - | DefKind::Static { .. } - | DefKind::Ctor(_, _) - | DefKind::AssocFn - | DefKind::AssocConst { .. } - | DefKind::Macro(_) - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::OpaqueTy - | DefKind::Field - | DefKind::LifetimeParam - | DefKind::GlobalAsm - | DefKind::Impl { .. } => false, - } - } } /// The resolution of a path or export. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2a10245d3d2e8..254d5fdd5f80c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -387,12 +387,24 @@ pub struct PathSegment<'hir> { /// out of those only the segments with no type parameters /// to begin with, e.g., `Vec::new` is `>::new::<..>`. pub infer_args: bool, + + /// Whether this segment is a delegation's child segment: + /// `reuse Trait::foo`, in this case `foo` is a delegation's child segment. + /// Used for faster check during generic args lowering. + pub delegation_child_segment: bool, } impl<'hir> PathSegment<'hir> { /// Converts an identifier to the corresponding segment. pub fn new(ident: Ident, hir_id: HirId, res: Res) -> PathSegment<'hir> { - PathSegment { ident, hir_id, res, infer_args: true, args: None } + PathSegment { + ident, + hir_id, + res, + infer_args: true, + args: None, + delegation_child_segment: false, + } } pub fn invalid() -> Self { @@ -3852,14 +3864,34 @@ pub enum OpaqueTyOrigin { }, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] +pub enum DelegationSelfTyPropagationKind { + /// Used when self type is explicitly specified in free-to-trait reuse + /// `reuse <() as Trait>::foo;`. + SelfTy(HirId /* Self ty id */), + /// Used when infer instead of a self type is specified or self type + /// is not specified at all: `reuse Trait::foo; reuse <_ as Trait>::foo;`. + SelfParam, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] pub struct DelegationInfo { pub call_expr_id: HirId, pub call_path_res: Option, - pub parent_args_segment_id: Option, - pub child_args_segment_id: Option, - pub self_ty_id: Option, - pub propagate_self_ty: bool, + + /// Id of the child segment in delegation: `reuse Trait::foo`, + /// `child_seg_id` points to `foo`. + pub child_seg_id: HirId, + + /// Ids of parent and child segments, `Some` when we need to take + /// generic args of those segments for signature/predicates inheritance. + /// `None` in trait impl case or when error delegation is generated, meaning + /// we should not access those segments for generic args lowering. + /// When `child_seg_id_for_sig` is Some it always equals `child_seg_id`. + pub parent_seg_id_for_sig: Option, + pub child_seg_id_for_sig: Option, + + pub self_ty_propagation_kind: Option, pub group_id: Option<(LocalExpnId, bool /* unused_target_expr */)>, } @@ -4025,12 +4057,12 @@ pub struct Param<'hir> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SplattedArgIndexError { /// The splatted argument index is invalid. - /// Currently the only unsupported index is `u16::MAX`, which is used to indicate that no argument - /// is splatted. - InvalidIndex { splatted_arg_index: u16 }, + /// A `u8::MAX` argument index used to indicate that no argument is splatted. + /// Higher values are also not supported, for performance reasons. + InvalidIndex { splatted_arg_index: u8 }, /// The splatted argument index is outside the bounds of the function arguments. - OutOfBounds { splatted_arg_index: u16, args_len: u16 }, + OutOfBounds { splatted_arg_index: u8, args_len: u16 }, } /// Contains the packed non-type fields of a function declaration. @@ -4041,9 +4073,9 @@ pub struct FnDeclFlags { flags: u8, /// Which function argument is splatted into multiple arguments in callers, if any? - /// Splatting functions with `u16::MAX` arguments is not supported, see `FnSigKind` for + /// Splatting functions with `>= u8::MAX` arguments is not supported, see `FnSigKind` for /// details. - splatted: u16, + splatted: u8, } impl fmt::Debug for FnDeclFlags { @@ -4081,13 +4113,13 @@ impl FnDeclFlags { /// Marker index for "no splatted argument". /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`. - const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX; + const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; /// Create a new FnDeclKind with no implicit self, no lifetime elision, no C-style variadic /// argument, and no splatting. /// To modify these flags, use the `set_*` methods, for readability. // FIXME: use Default instead when that trait is const stable. - pub const fn default() -> Self { + pub fn default() -> Self { Self { flags: 0, splatted: 0 } .set_implicit_self(ImplicitSelfKind::None) .set_lifetime_elision_allowed(false) @@ -4097,7 +4129,7 @@ impl FnDeclFlags { /// Set the implicit self kind. #[must_use = "this method does not modify the receiver"] - pub const fn set_implicit_self(mut self, implicit_self: ImplicitSelfKind) -> Self { + pub fn set_implicit_self(mut self, implicit_self: ImplicitSelfKind) -> Self { self.flags &= !Self::IMPLICIT_SELF_MASK; match implicit_self { @@ -4113,7 +4145,7 @@ impl FnDeclFlags { /// Set the C-style variadic argument flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_c_variadic(mut self, c_variadic: bool) -> Self { + pub fn set_c_variadic(mut self, c_variadic: bool) -> Self { if c_variadic { self.flags |= Self::C_VARIADIC_FLAG; } else { @@ -4125,7 +4157,7 @@ impl FnDeclFlags { /// Set the lifetime elision allowed flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_lifetime_elision_allowed(mut self, allowed: bool) -> Self { + pub fn set_lifetime_elision_allowed(mut self, allowed: bool) -> Self { if allowed { self.flags |= Self::LIFETIME_ELISION_ALLOWED_FLAG; } else { @@ -4138,16 +4170,17 @@ impl FnDeclFlags { /// Set the splatted argument index. /// The number of function arguments is used for error checking. #[must_use = "this method does not modify the receiver"] - pub const fn set_splatted( + pub fn set_splatted( mut self, - splatted: Option, + splatted: Option, args_len: usize, ) -> Result { if let Some(splatted_arg_index) = splatted { if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX { // This index value is used as a marker for "no splatting", so it is unsupported. + // Higher values are also not supported, for performance reasons. return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index }); - } else if splatted_arg_index as usize >= args_len { + } else if usize::from(splatted_arg_index) >= args_len { return Err(SplattedArgIndexError::OutOfBounds { splatted_arg_index, args_len: args_len as u16, @@ -4164,14 +4197,14 @@ impl FnDeclFlags { /// Set "no splatted arguments" for the function declaration. #[must_use = "this method does not modify the receiver"] - pub const fn set_no_splatted_args(mut self) -> Self { + pub fn set_no_splatted_args(mut self) -> Self { self.splatted = Self::NO_SPLATTED_ARG_INDEX; self } /// Get the implicit self kind. - pub const fn implicit_self(self) -> ImplicitSelfKind { + pub fn implicit_self(self) -> ImplicitSelfKind { match self.flags & Self::IMPLICIT_SELF_MASK { 0 => ImplicitSelfKind::None, 1 => ImplicitSelfKind::Imm, @@ -4183,17 +4216,17 @@ impl FnDeclFlags { } /// Do the function arguments end with a C-style variadic argument? - pub const fn c_variadic(self) -> bool { + pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } /// Is lifetime elision allowed? - pub const fn lifetime_elision_allowed(self) -> bool { + pub fn lifetime_elision_allowed(self) -> bool { self.flags & Self::LIFETIME_ELISION_ALLOWED_FLAG != 0 } /// Get the splatted argument index, if any. - pub const fn splatted(self) -> Option { + pub fn splatted(self) -> Option { if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) } } } @@ -4243,7 +4276,7 @@ impl<'hir> FnDecl<'hir> { self.fn_decl_kind.lifetime_elision_allowed() } - pub fn splatted(&self) -> Option { + pub fn splatted(&self) -> Option { self.fn_decl_kind.splatted() } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9e0eaef596420..f1f542e9ce1e9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1468,7 +1468,8 @@ pub fn walk_path_segment<'v, V: Visitor<'v>>( visitor: &mut V, segment: &'v PathSegment<'v>, ) -> V::Result { - let PathSegment { ident, hir_id, res: _, args, infer_args: _ } = segment; + let PathSegment { ident, hir_id, res: _, args, infer_args: _, delegation_child_segment: _ } = + segment; try_visit!(visitor.visit_ident(*ident)); try_visit!(visitor.visit_id(*hir_id)); visit_opt!(visitor, visit_generic_args, *args); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 2069b746b5a7f..9c3f7789f9ed9 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -447,6 +447,14 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); + + // Runtime symbols + MemCpy, sym::memcpy_fn, memcpy_fn, Target::Fn, GenericRequirement::None; + MemMove, sym::memmove_fn, memmove_fn, Target::Fn, GenericRequirement::None; + MemSet, sym::memset_fn, memset_fn, Target::Fn, GenericRequirement::None; + MemCmp, sym::memcmp_fn, memcmp_fn, Target::Fn, GenericRequirement::None; + Bcmp, sym::bcmp_fn, bcmp_fn, Target::Fn, GenericRequirement::None; + StrLen, sym::strlen_fn, strlen_fn, Target::Fn, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 85b0e3958c2d6..f9d94bd505724 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -23,7 +23,28 @@ macro_rules! weak_lang_items { } } +macro_rules! weak_only_lang_items { + ($($item:ident,)*) => { + pub static WEAK_ONLY_LANG_ITEMS: &[LangItem] = &[$(LangItem::$item,)*]; + + impl LangItem { + pub fn is_weak_only(self) -> bool { + matches!(self, $(LangItem::$item)|*) + } + } + } +} + weak_lang_items! { PanicImpl, rust_begin_unwind; EhPersonality, rust_eh_personality; } + +weak_only_lang_items! { + MemCpy, + MemMove, + MemSet, + MemCmp, + Bcmp, + StrLen, +} diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 0dbd4333ed3ca..955629d40cee2 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 3742999d05b5d..20223b66405cd 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -165,8 +165,9 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { return None; } - let (normalized_ty, obligations) = self - .normalize_ty(Unnormalized::new(Ty::new_projection(tcx, trait_target_def_id, [ty])))?; + let (normalized_ty, obligations) = self.normalize_ty(Unnormalized::new( + Ty::new_projection(tcx, ty::IsRigid::No, trait_target_def_id, [ty]), + ))?; debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); self.state.obligations.extend(obligations); diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index 7a12b1bf8142a..7a509dcd738ed 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -192,8 +192,9 @@ fn ensure_all_fields_are_const_destruct<'tcx>( let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let impl_span = tcx.def_span(impl_def_id.to_def_id()); - let env = - ty::EarlyBinder::bind(tcx.param_env(impl_def_id)).instantiate_identity().skip_norm_wip(); + let env = ty::EarlyBinder::bind(tcx, tcx.param_env(impl_def_id)) + .instantiate_identity() + .skip_norm_wip(); let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id); let destruct_trait = tcx.lang_items().destruct_trait().unwrap(); for field in tcx.adt_def(adt_def_id).all_fields() { @@ -281,7 +282,7 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>( // reference the params from the ADT instead of from the impl which is bad UX. To resolve // this we "rename" the ADT's params to be the impl's params which should not affect behaviour. let impl_adt_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args); - let adt_env = ty::EarlyBinder::bind(tcx.param_env(adt_def_id)) + let adt_env = ty::EarlyBinder::bind(tcx, tcx.param_env(adt_def_id)) .instantiate(tcx, adt_to_impl_args) .skip_norm_wip(); diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 917360ab8119e..aa204a20e2d57 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -337,7 +337,7 @@ fn check_opaque_meets_bounds<'tcx>( }), }; - let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + let opaque_ty = Ty::new_opaque(tcx, ty::IsRigid::No, def_id.to_def_id(), args); // `ReErased` regions appear in the "parent_args" of closures/coroutines. // We're ignoring them here and replacing them with fresh region variables. @@ -534,7 +534,7 @@ fn sanity_check_found_hidden_type<'tcx>( // Nothing was actually constrained. return Ok(()); } - if let &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = ty.ty.kind() { + if let &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = ty.ty.kind() { if def_id == key.def_id.to_def_id() && args == key.args { // Nothing was actually constrained, this is an opaque usage that was // only discovered to be opaque after inference vars resolved. @@ -778,8 +778,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), if has_default { // need to store default and type of default let ct = tcx.const_param_default(param.def_id).skip_binder(); - if let ty::ConstKind::Unevaluated(uv) = ct.kind() - && let Some(def_id) = uv.kind.opt_def_id() + if let ty::ConstKind::Alias(_, alias_const) = ct.kind() + && let Some(def_id) = alias_const.kind.opt_def_id() { tcx.ensure_ok().type_of(def_id); } @@ -839,9 +839,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().associated_items(def_id); if of_trait { let impl_trait_header = tcx.impl_trait_header(def_id); - res = res.and(tcx.ensure_result().coherent_trait( - impl_trait_header.trait_ref.instantiate_identity().skip_norm_wip().def_id, - )); + res = res + .and(tcx.ensure_result().coherent_trait(impl_trait_header.trait_ref.def_id())); if res.is_ok() { // Checking this only makes sense if the all trait impls satisfy basic @@ -2191,7 +2190,7 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, opaque_def_id: LocalDefId) -> ErrorG impl<'tcx> ty::TypeVisitor> for OpaqueTypeCollector { fn visit_ty(&mut self, t: Ty<'tcx>) { match *t.kind() { - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: def }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: def }, .. }) => { self.opaques.push(def); } ty::Closure(def_id, ..) | ty::Coroutine(def_id, ..) => { @@ -2224,10 +2223,13 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, opaque_def_id: LocalDefId) -> ErrorG let mut label_match = |ty: Ty<'_>, span| { for arg in ty.walk() { if let ty::GenericArgKind::Type(ty) = arg.kind() - && let ty::Alias(ty::AliasTy { - kind: ty::Opaque { def_id: captured_def_id }, - .. - }) = *ty.kind() + && let ty::Alias( + _, + ty::AliasTy { + kind: ty::Opaque { def_id: captured_def_id }, + .. + }, + ) = *ty.kind() && captured_def_id == opaque_def_id.to_def_id() { err.span_label( diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index b3d1f2a1489a5..ce4c21ff8d86e 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -761,7 +761,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( Ok(ty) => ty, Err(guar) => Ty::new_error(tcx, guar), }; - remapped_types.insert(def_id, ty::EarlyBinder::bind(ty)); + remapped_types.insert(def_id, ty::EarlyBinder::bind(tcx, ty)); } Err(err) => { // This code path is not reached in any tests, but may be @@ -783,11 +783,14 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( if !remapped_types.contains_key(assoc_item) { remapped_types.insert( *assoc_item, - ty::EarlyBinder::bind(Ty::new_error_with_message( + ty::EarlyBinder::bind( tcx, - return_span, - "missing synthetic item for RPITIT", - )), + Ty::new_error_with_message( + tcx, + return_span, + "missing synthetic item for RPITIT", + ), + ), ); } } @@ -826,7 +829,7 @@ where } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let &ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, args: proj_args, .. }) = + if let &ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, args: proj_args, .. }) = ty.kind() && self.cx().is_impl_trait_in_trait(def_id) { @@ -930,10 +933,10 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { } else { let guar = match region.opt_param_def_id(self.tcx, self.impl_m_def_id) { Some(def_id) => { - let return_span = if let &ty::Alias(ty::AliasTy { - kind: ty::Opaque { def_id: opaque_ty_def_id }, - .. - }) = self.ty.kind() + let return_span = if let &ty::Alias( + _, + ty::AliasTy { kind: ty::Opaque { def_id: opaque_ty_def_id }, .. }, + ) = self.ty.kind() { self.tcx.def_span(opaque_ty_def_id) } else { @@ -2721,7 +2724,7 @@ fn param_env_with_gat_bounds<'tcx>( let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars); match normalize_impl_ty.kind() { - &ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) + &ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) if def_id == trait_ty.def_id && args == rebased_args => { // Don't include this predicate if the projected type is @@ -2764,7 +2767,7 @@ fn try_report_async_mismatch<'tcx>( return Ok(()); } - let ty::Alias(ty::AliasTy { kind: ty::Projection { def_id: async_future_def_id }, .. }) = + let ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id: async_future_def_id }, .. }) = *tcx.fn_sig(trait_m.def_id).skip_binder().skip_binder().output().kind() else { bug!("expected `async fn` to return an RPITIT"); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 539328f801666..074196b0f959e 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -87,7 +87,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>( hidden_tys[&trait_projection.kind].instantiate(tcx, impl_opaque_args).skip_norm_wip(); // If the hidden type is not an opaque, then we have "refined" the trait signature. - let impl_opaque = if let ty::Alias(alias) = *hidden_ty.kind() + let impl_opaque = if let ty::Alias(_, alias) = *hidden_ty.kind() && let Some(impl_opaque) = alias.try_to_opaque() { impl_opaque @@ -267,7 +267,7 @@ struct ImplTraitInTraitCollector<'tcx> { impl<'tcx> TypeVisitor> for ImplTraitInTraitCollector<'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) { - if let ty::Alias(alias) = *ty.kind() + if let ty::Alias(_, alias) = *ty.kind() && let Some(proj) = alias.try_to_projection() && self.tcx.is_impl_trait_in_trait(proj.kind) { @@ -318,9 +318,10 @@ fn report_mismatched_rpitit_signature<'tcx>( let mut return_ty = trait_m_sig.output().fold_with(&mut super::RemapLateParam { tcx, mapping }); if tcx.asyncness(impl_m_def_id).is_async() && tcx.asyncness(trait_m_def_id).is_async() { - let &ty::Alias(ty::AliasTy { - kind: ty::Projection { def_id: future_ty_def_id }, args, .. - }) = return_ty.kind() + let &ty::Alias( + _, + ty::AliasTy { kind: ty::Projection { def_id: future_ty_def_id }, args, .. }, + ) = return_ty.kind() else { span_bug!( tcx.def_span(trait_m_def_id), diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 09a3cf4d182f8..9dd7bb8058afc 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -631,6 +631,7 @@ pub(crate) fn check_intrinsic_type( vec![Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0))], Ty::new_projection_from_args( tcx, + ty::IsRigid::No, discriminant_def_id, tcx.mk_args(&[param(0).into()]), ), diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index e1bbc8f0641de..9d8e0a512ab66 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -444,11 +444,13 @@ fn fn_sig_suggestion<'tcx>( predicates: impl IntoIterator, Span)>, assoc: ty::AssocItem, ) -> String { + let splatted_arg_index = sig.splatted().map(usize::from); let args = sig .inputs() .iter() .enumerate() .map(|(i, ty)| { + let splat = if splatted_arg_index == Some(i) { "#[splat] " } else { "" }; let arg_ty = match ty.kind() { ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(), ty::Ref(reg, ref_ty, mutability) if i == 0 => { @@ -477,7 +479,7 @@ fn fn_sig_suggestion<'tcx>( } } }; - Some(format!("{arg_ty}")) + Some(format!("{splat}{arg_ty}")) }) .chain(std::iter::once(if sig.c_variadic() { Some("...".to_string()) } else { None })) .flatten() diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index e31beca390837..e60318756d5c7 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -148,7 +148,7 @@ fn resolve_block<'tcx>( if !terminating && !visitor .tcx - .lints_that_dont_need_to_run(()) + .skippable_lints(()) .contains(&lint::LintId::of(lint::builtin::TAIL_EXPR_DROP_ORDER)) { // If this temporary scope will be changing once the codebase adopts Rust 2024, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 51c345a455f90..1d17621be217f 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -782,7 +782,7 @@ impl<'tcx> GATArgsCollector<'tcx> { impl<'tcx> TypeVisitor> for GATArgsCollector<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) { match t.kind() { - &ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) + &ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) if def_id == self.gat => { for (idx, arg) in args.iter().enumerate() { @@ -1488,7 +1488,7 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: // be sure if it will error or not as user might always specify the other. // FIXME(generic_const_exprs): This is incorrect when dealing with unused const params. // E.g: `struct Foo;`. Here, we should - // eagerly error but we don't as we have `ConstKind::Unevaluated(.., [N, M])`. + // eagerly error but we don't as we have `ConstKind::Alias(.., [N, M])`. if !default.has_param() { wfcx.register_wf_obligation( tcx.def_span(param.def_id), @@ -1509,7 +1509,9 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: | ty::ConstKind::Bound(_, _) => unreachable!(), ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue, ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(wfcx.param_env) } @@ -1585,7 +1587,7 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: } let mut param_count = CountParams::default(); let has_region = pred.visit_with(&mut param_count).is_break(); - let instantiated_pred = ty::EarlyBinder::bind(pred).instantiate(tcx, args); + let instantiated_pred = ty::EarlyBinder::bind(tcx, pred).instantiate(tcx, args); // Don't check non-defaulted params, dependent defaults (including lifetimes) // or preds with multiple params. if instantiated_pred.skip_normalization().has_non_region_param() @@ -1714,7 +1716,16 @@ fn check_fn_or_method<'tcx>( let span = tcx.def_span(def_id); let has_implicit_self = hir_decl.implicit_self().has_implicit_self(); let mut inputs = sig.inputs().iter().skip(if has_implicit_self { 1 } else { 0 }); - // FIXME(splat): use `sig.splatted()` once FnSig has it + // FIXME(splat): support the rest of closure splatting, or replace this code with an error + if let Some(mut splatted_arg_index) = sig.splatted() { + let mut inputs_count = sig.inputs().len(); + if has_implicit_self { + splatted_arg_index = splatted_arg_index.strict_sub(1); + inputs_count = inputs_count.strict_sub(1); + } + debug!(?splatted_arg_index, ?inputs_count, ?has_implicit_self, ?sig); + sig = sig.set_splatted(Some(splatted_arg_index), inputs_count).unwrap(); + } // Check that the argument is a tuple and is sized if let Some(ty) = inputs.next() { wfcx.register_bound( diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index d154503f4ec04..4e3aa41942845 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -203,10 +203,13 @@ impl<'tcx> InherentCollect<'tcx> { | ty::FnPtr(..) | ty::Tuple(..) | ty::UnsafeBinder(_) => self.check_primitive_impl(id, self_ty), - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, - .. - }) + ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, + .. + }, + ) | ty::Param(_) => { Err(self.tcx.dcx().emit_err(diagnostics::InherentNominal { span: item_span })) } @@ -215,7 +218,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) - | ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) + | ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) => { diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index d49326a089c90..c3bd09f2a4767 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -194,7 +194,7 @@ pub(crate) fn orphan_check_impl( NonlocalImpl::DisallowOther, ), - ty::Alias(ty::AliasTy { kind, .. }) => { + ty::Alias(_, ty::AliasTy { kind, .. }) => { let problematic_kind = match kind { // trait Id { type This: ?Sized; } // impl Id for T { @@ -440,7 +440,7 @@ fn emit_orphan_check_error<'tcx>( }); } } - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { diag.subdiagnostic(diagnostics::OnlyCurrentTraitsOpaque { span }); } ty::RawPtr(ptr_ty, mutbl) => { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index c8f6e5da69b56..05648bf7c2ece 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -15,7 +15,7 @@ //! crate as a kind of pass. This should eventually be factored away. use std::cell::Cell; -use std::{assert_matches, iter}; +use std::{assert_matches, debug_assert_matches, iter}; use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; @@ -1085,7 +1085,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn bug!("unexpected sort of node in fn_sig(): {:?}", x); } }; - ty::EarlyBinder::bind(output) + ty::EarlyBinder::bind(tcx, output) } fn lower_fn_sig_recovering_infer_ret_ty<'tcx>( @@ -1363,7 +1363,12 @@ pub fn suggest_impl_trait<'tcx>( let item_ty = ocx.normalize( &ObligationCause::dummy(), param_env, - Unnormalized::new(Ty::new_projection_from_args(infcx.tcx, assoc_item_def_id, args)), + Unnormalized::new(Ty::new_projection_from_args( + infcx.tcx, + ty::IsRigid::No, + assoc_item_def_id, + args, + )), ); // FIXME(compiler-errors): We may benefit from resolving regions here. if ocx.try_evaluate_obligations().is_empty() @@ -1405,7 +1410,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader let trait_ref = icx.lowerer().lower_impl_trait_ref(&of_trait.trait_ref, selfty); ty::ImplTraitHeader { - trait_ref: ty::EarlyBinder::bind(trait_ref), + trait_ref: ty::EarlyBinder::bind(tcx, trait_ref), safety: of_trait.safety, polarity: polarity_of_impl(tcx, of_trait, is_rustc_reservation), constness: impl_.constness, @@ -1626,14 +1631,16 @@ fn const_param_default<'tcx>( default_ct, tcx.type_of(def_id).instantiate(tcx, identity_args).skip_norm_wip(), ); - ty::EarlyBinder::bind(ct) + ty::EarlyBinder::bind(tcx, ct) } fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKind { + debug_assert_matches!(tcx.def_kind(def), DefKind::AnonConst | DefKind::InlineConst); let hir_id = tcx.local_def_id_to_hir_id(def); let const_arg_id = tcx.parent_hir_id(hir_id); match tcx.hir_node(const_arg_id) { - hir::Node::ConstArg(_) => { + hir::Node::ConstArg(const_arg) => { + debug_assert_matches!(const_arg.kind, hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) if *def_id == def); let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id)); if tcx.features().generic_const_exprs() { ty::AnonConstKind::GCE @@ -1676,7 +1683,7 @@ fn const_of_item<'tcx>( tcx.def_span(def_id), "cannot call const_of_item on a non-type_const", ); - return ty::EarlyBinder::bind(Const::new_error(tcx, e)); + return ty::EarlyBinder::bind(tcx, Const::new_error(tcx, e)); } }; let icx = ItemCtxt::new(tcx, def_id); @@ -1688,8 +1695,8 @@ fn const_of_item<'tcx>( if let Err(e) = icx.check_tainted_by_errors() && !ct.references_error() { - ty::EarlyBinder::bind(Const::new_error(tcx, e)) + ty::EarlyBinder::bind(tcx, Const::new_error(tcx, e)) } else { - ty::EarlyBinder::bind(ct) + ty::EarlyBinder::bind(tcx, ct) } } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 64f12fd6644c5..811ed83e0bf48 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -124,11 +124,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // ^ parent_def_id // // then we only want to return generics for params to the left of `N`. If we don't do that we - // end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, args: [N#0])`. + // end up with that const looking like: `ty::ConstKind::Alias(def_id, args: [N#0])`. // // This causes ICEs (#86580) when building the args for Foo in `fn foo() -> Foo { .. }` as // we instantiate the defaults with the partially built args when we build the args. Instantiating - // the `N#0` on the unevaluated const indexes into the empty args we're in the process of building. + // the `N#0` on the alias const indexes into the empty args we're in the process of building. // // We fix this by having this function return the parent's generics ourselves and truncating the // generics to only include non-forward declared params (with the exception of the `Self` ty) diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 4409f2c068eb8..46007503539ca 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -33,6 +33,7 @@ fn associated_type_bounds<'tcx>( ty::print::with_reduced_queries!({ let item_ty = Ty::new_projection_from_args( tcx, + ty::IsRigid::No, assoc_item_def_id.to_def_id(), GenericArgs::identity_for_item(tcx, assoc_item_def_id), ); @@ -144,6 +145,7 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>( let gat_vars = loop { if let ty::Alias( + _, alias_ty @ ty::AliasTy { kind: ty::Projection { def_id: alias_ty_def_id }, .. }, ) = *clause_ty.kind() { @@ -430,7 +432,7 @@ pub(super) fn explicit_item_bounds_with_filter( let opaque_ty = tcx.hir_node_by_def_id(opaque_def_id.expect_local()).expect_opaque_ty(); let bounds = associated_type_bounds(tcx, def_id, opaque_ty.bounds, opaque_ty.span, filter); - return ty::EarlyBinder::bind(bounds); + return ty::EarlyBinder::bind_iter(bounds); } Some(ty::ImplTraitInTraitData::Impl { .. }) => { span_bug!(tcx.def_span(def_id), "RPITIT in impl should not have item bounds") @@ -457,7 +459,7 @@ pub(super) fn explicit_item_bounds_with_filter( in_trait_or_impl: Some(hir::RpitContext::Trait), } => { let args = GenericArgs::identity_for_item(tcx, def_id); - let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + let item_ty = Ty::new_opaque(tcx, ty::IsRigid::No, def_id.to_def_id(), args); let bounds = &*tcx.arena.alloc_slice( &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) .to_vec() @@ -476,7 +478,7 @@ pub(super) fn explicit_item_bounds_with_filter( } | rustc_hir::OpaqueTyOrigin::TyAlias { parent: _, .. } => { let args = GenericArgs::identity_for_item(tcx, def_id); - let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + let item_ty = Ty::new_opaque(tcx, ty::IsRigid::No, def_id.to_def_id(), args); let bounds = opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter); assert_only_contains_predicates_from(filter, bounds, item_ty); bounds @@ -486,7 +488,7 @@ pub(super) fn explicit_item_bounds_with_filter( node => bug!("item_bounds called on {def_id:?} => {node:?}"), }; - ty::EarlyBinder::bind(bounds) + ty::EarlyBinder::bind_iter(bounds) } pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { @@ -515,9 +517,12 @@ pub(super) fn item_non_self_bounds( let all_bounds: FxIndexSet<_> = tcx.item_bounds(def_id).skip_binder().iter().collect(); let own_bounds: FxIndexSet<_> = tcx.item_self_bounds(def_id).skip_binder().iter().collect(); if all_bounds.len() == own_bounds.len() { - ty::EarlyBinder::bind(ty::ListWithCachedTypeInfo::empty()) + ty::EarlyBinder::bind(tcx, ty::ListWithCachedTypeInfo::empty()) } else { - ty::EarlyBinder::bind(tcx.mk_clauses_from_iter(all_bounds.difference(&own_bounds).copied())) + ty::EarlyBinder::bind( + tcx, + tcx.mk_clauses_from_iter(all_bounds.difference(&own_bounds).copied()), + ) } } @@ -549,11 +554,10 @@ impl<'tcx> TypeFolder> for AssocTyToOpaque<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let &ty::Alias(ty::AliasTy { - kind: ty::Projection { def_id: projection_ty_def_id }, - args, - .. - }) = ty.kind() + if let &ty::Alias( + _, + ty::AliasTy { kind: ty::Projection { def_id: projection_ty_def_id }, args, .. }, + ) = ty.kind() && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx.opt_rpitit_info(projection_ty_def_id) && fn_def_id == self.fn_def_id diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index db22b57ad22a6..0dc89381c5eb6 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -415,8 +415,8 @@ fn const_evaluatable_predicates_of<'tcx>( preds: FxIndexSet<(ty::Clause<'tcx>, Span)>, } - fn is_const_param_default(tcx: TyCtxt<'_>, kind: ty::UnevaluatedConstKind<'_>) -> bool { - let ty::UnevaluatedConstKind::Anon { def_id } = kind else { return false }; + fn is_const_param_default(tcx: TyCtxt<'_>, kind: ty::AliasConstKind<'_>) -> bool { + let ty::AliasConstKind::Anon { def_id } = kind else { return false }; let Some(local) = def_id.as_local() else { return false }; let hir_id = tcx.local_def_id_to_hir_id(local); @@ -433,8 +433,8 @@ fn const_evaluatable_predicates_of<'tcx>( impl<'tcx> TypeVisitor> for ConstCollector<'tcx> { fn visit_const(&mut self, c: ty::Const<'tcx>) { - if let ty::ConstKind::Unevaluated(uv) = c.kind() { - if is_const_param_default(self.tcx, uv.kind) { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { + if is_const_param_default(self.tcx, alias_const.kind) { // Do not look into const param defaults, // these get checked when they are actually instantiated. // @@ -446,11 +446,11 @@ fn const_evaluatable_predicates_of<'tcx>( } // Skip type consts as mGCA doesn't support evaluatable clauses. - if uv.kind.is_type_const(self.tcx) { + if alias_const.kind.is_type_const(self.tcx) { return; } - let span = uv.kind.def_span(self.tcx); + let span = alias_const.kind.def_span(self.tcx); self.preds.insert((ty::ClauseKind::ConstEvaluatable(c).upcast(self.tcx), span)); } } @@ -519,11 +519,10 @@ pub(super) fn explicit_predicates_of<'tcx>( // identity args of the trait. // * It must be an associated type for this trait (*not* a // supertrait). - if let &ty::Alias(ty::AliasTy { - kind: ty::Projection { def_id: projection_def_id }, - args, - .. - }) = ty.kind() + if let &ty::Alias( + _, + ty::AliasTy { kind: ty::Projection { def_id: projection_def_id }, args, .. }, + ) = ty.kind() { args == trait_identity_args // FIXME(return_type_notation): This check should be more robust @@ -745,7 +744,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( assert_only_contains_predicates_from(filter, implied_bounds, tcx.types.self_param); - ty::EarlyBinder::bind(implied_bounds) + ty::EarlyBinder::bind_iter(implied_bounds) } // Make sure when elaborating supertraits, probing for associated types, etc., @@ -919,7 +918,7 @@ pub(super) fn type_param_predicates<'tcx>( let icx = ItemCtxt::new(tcx, parent); icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_ident) } else { - ty::EarlyBinder::bind(&[] as &[_]) + ty::EarlyBinder::bind_iter(&[] as &[_]) }; let mut extend = None; @@ -967,7 +966,7 @@ pub(super) fn type_param_predicates<'tcx>( self_ty, ); - ty::EarlyBinder::bind(bounds) + ty::EarlyBinder::bind_iter(bounds) } impl<'tcx> ItemCtxt<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 70129192faa2c..3c7cc160a4b3d 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -2574,10 +2574,10 @@ fn is_late_bound_map( ty::Param(param_ty) => { self.arg_is_constrained[param_ty.index as usize] = true; } - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, - .. - }) => return, + ty::Alias( + _, + ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }, + ) => return, _ => (), } t.super_visit_with(self) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index d729a4adacec2..9c8e61d89da97 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -33,21 +33,28 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ return map[&trait_item_def_id]; } Err(_) => { - return ty::EarlyBinder::bind(Ty::new_error_with_message( + return ty::EarlyBinder::bind( tcx, - DUMMY_SP, - "Could not collect return position impl trait in trait tys", - )); + Ty::new_error_with_message( + tcx, + DUMMY_SP, + "Could not collect return position impl trait in trait tys", + ), + ); } } } // For an RPITIT in a trait, just return the corresponding opaque. Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => { - return ty::EarlyBinder::bind(Ty::new_opaque( + return ty::EarlyBinder::bind( tcx, - opaque_def_id, - ty::GenericArgs::identity_for_item(tcx, opaque_def_id), - )); + Ty::new_opaque( + tcx, + ty::IsRigid::No, + opaque_def_id, + ty::GenericArgs::identity_for_item(tcx, opaque_def_id), + ), + ); } None => {} } @@ -240,9 +247,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ if let Err(e) = icx.check_tainted_by_errors() && !output.references_error() { - ty::EarlyBinder::bind(Ty::new_error(tcx, e)) + ty::EarlyBinder::bind(tcx, Ty::new_error(tcx, e)) } else { - ty::EarlyBinder::bind(output) + ty::EarlyBinder::bind(tcx, output) } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index aae06aedc7b7a..88936b375a12f 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -49,7 +49,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( name: tcx.item_ident(parent_def_id.to_def_id()), what: "impl", }); - EarlyBinder::bind(Ty::new_error(tcx, guar)) + EarlyBinder::bind(tcx, Ty::new_error(tcx, guar)) } } @@ -94,7 +94,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait( name: tcx.item_ident(parent_def_id.to_def_id()), what: "crate", }); - EarlyBinder::bind(Ty::new_error(tcx, guar)) + EarlyBinder::bind(tcx, Ty::new_error(tcx, guar)) } } @@ -247,14 +247,14 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( let guar = tcx .dcx() .span_delayed_bug(opaque_type_span, "cannot infer type for stranded opaque type"); - return EarlyBinder::bind(Ty::new_error(tcx, guar)); + return EarlyBinder::bind(tcx, Ty::new_error(tcx, guar)); } match opaque_types_from { DefiningScopeKind::HirTypeck => { let tables = tcx.typeck(owner_def_id); if let Some(guar) = tables.tainted_by_errors { - EarlyBinder::bind(Ty::new_error(tcx, guar)) + EarlyBinder::bind(tcx, Ty::new_error(tcx, guar)) } else if let Some(hidden_ty) = tables.hidden_types.get(&def_id) { hidden_ty.ty } else { @@ -265,7 +265,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( // so we can just make the hidden type be `!`. // For backwards compatibility reasons, we fall back to // `()` until we the diverging default is changed. - EarlyBinder::bind(tcx.types.unit) + EarlyBinder::bind(tcx, tcx.types.unit) } } DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(owner_def_id) { @@ -275,14 +275,14 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( } else { let hir_ty = tcx.type_of_opaque_hir_typeck(def_id); if let Err(guar) = hir_ty.skip_binder().error_reported() { - EarlyBinder::bind(Ty::new_error(tcx, guar)) + EarlyBinder::bind(tcx, Ty::new_error(tcx, guar)) } else { assert!(!tcx.next_trait_solver_globally()); hir_ty } } } - Err(guar) => EarlyBinder::bind(Ty::new_error(tcx, guar)), + Err(guar) => EarlyBinder::bind(tcx, Ty::new_error(tcx, guar)), }, } } diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index efd3cb2acb422..a392ff5d3a552 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -63,14 +63,17 @@ impl<'tcx> TypeVisitor> for ParameterCollector { fn visit_ty(&mut self, t: Ty<'tcx>) { match *t.kind() { // Projections are not injective in general. - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, - .. - }) if !self.include_nonconstraining => { + ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, + .. + }, + ) if !self.include_nonconstraining => { return; } // All free alias types should've been expanded beforehand. - ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) if !self.include_nonconstraining => { bug!("unexpected free alias type") @@ -90,7 +93,7 @@ impl<'tcx> TypeVisitor> for ParameterCollector { fn visit_const(&mut self, c: ty::Const<'tcx>) { match c.kind() { - ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => { + ty::ConstKind::Alias(..) if !self.include_nonconstraining => { // Constant expressions are not injective in general. return; } diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 7f3edd7f09231..58474f0ee2e38 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -5,16 +5,15 @@ use std::debug_assert_matches; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::PathSegment; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{DelegationSelfTyPropagationKind, PathSegment}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_span::{ErrorGuaranteed, Span, kw}; use crate::collect::ItemCtxt; -use crate::diagnostics::DelegationSelfTypeNotSpecified; use crate::hir_ty_lowering::HirTyLowerer; type RemapTable = FxHashMap; @@ -65,8 +64,9 @@ impl<'tcx> TypeFolder> for ParamIndexRemapper<'tcx> { } } +#[derive(Debug)] enum SelfPositionKind { - AfterLifetimes(bool /* Should propagate self ty */), + AfterLifetimes(Option), Zero, None, } @@ -83,8 +83,8 @@ fn create_self_position_kind( | (FnKind::AssocTrait, FnKind::Free) => SelfPositionKind::Zero, (FnKind::Free, FnKind::AssocTrait) => { - let propagate_self_ty = tcx.hir_delegation_info(delegation_id).propagate_self_ty; - SelfPositionKind::AfterLifetimes(propagate_self_ty) + let kind = tcx.hir_delegation_info(delegation_id).self_ty_propagation_kind; + SelfPositionKind::AfterLifetimes(kind) } _ => SelfPositionKind::None, @@ -268,28 +268,6 @@ fn get_parent_and_inheritance_kind<'tcx>( } } -fn get_delegation_self_ty_or_err(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> Ty<'_> { - tcx.hir_delegation_info(delegation_id) - .self_ty_id - .map(|id| { - let ctx = ItemCtxt::new(tcx, delegation_id); - ctx.lower_ty(tcx.hir_node(id).expect_ty()) - }) - .unwrap_or_else(|| { - // It is possible to attempt to get self type when it is used in signature - // (i.e., `fn default() -> Self`), so emit error here in addition to possible - // `mismatched types` error (see #156388). - let err = DelegationSelfTypeNotSpecified { span: tcx.def_span(delegation_id) }; - tcx.dcx().emit_err(err); - - Ty::new_error_with_message( - tcx, - tcx.def_span(delegation_id), - "the self type must be specified", - ) - }) -} - fn get_delegation_self_ty<'tcx>(tcx: TyCtxt<'tcx>, delegation_id: LocalDefId) -> Option> { let sig_id = tcx.hir_opt_delegation_sig_id(delegation_id).expect("Delegation must have sig_id"); let (caller_kind, callee_kind) = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)); @@ -302,14 +280,24 @@ fn get_delegation_self_ty<'tcx>(tcx: TyCtxt<'tcx>, delegation_id: LocalDefId) -> | (FnKind::AssocTrait, FnKind::AssocTrait) => { match create_self_position_kind(tcx, delegation_id, sig_id) { SelfPositionKind::None => None, - SelfPositionKind::AfterLifetimes(propagate_self_ty) => { - if propagate_self_ty { - Some(get_delegation_self_ty_or_err(tcx, delegation_id)) - } else { - // Both sig parent and child lifetimes are in included in this count. - let index = tcx.generics_of(delegation_id).own_counts().lifetimes; - Some(Ty::new_param(tcx, index as u32, kw::SelfUpper)) - } + SelfPositionKind::AfterLifetimes(propagation_kind) => { + Some(match propagation_kind { + Some(kind) => match kind { + DelegationSelfTyPropagationKind::SelfTy(self_ty_id) => { + let ctx = ItemCtxt::new(tcx, delegation_id); + ctx.lower_ty(tcx.hir_node(self_ty_id).expect_ty()) + } + DelegationSelfTyPropagationKind::SelfParam => { + let index = tcx.generics_of(delegation_id).own_counts().lifetimes; + Ty::new_param(tcx, index as u32, kw::SelfUpper) + } + }, + None => Ty::new_error_with_message( + tcx, + tcx.def_span(delegation_id), + "self propagation kind must be specified for `AfterLifetimes` variant", + ), + }) } SelfPositionKind::Zero => Some(Ty::new_param(tcx, 0, kw::SelfUpper)), } @@ -348,137 +336,69 @@ fn create_generic_args<'tcx>( sig_id: DefId, delegation_id: LocalDefId, mut parent_args: &[ty::GenericArg<'tcx>], - child_args: &[ty::GenericArg<'tcx>], + mut child_args: &[ty::GenericArg<'tcx>], ) -> Vec> { - let (caller_kind, callee_kind) = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)); - + let delegation_generics = tcx.generics_of(delegation_id); let delegation_args = ty::GenericArgs::identity_for_item(tcx, delegation_id); - let deleg_parent_args_without_self_count = - get_delegation_parent_args_count_without_self(tcx, delegation_id, sig_id); - - let delegation_generics = tcx.generics_of(delegation_id); let real_args_count = delegation_args.len() - delegation_generics.own_synthetic_params_count(); let synth_args = &delegation_args[real_args_count..]; - let delegation_args = &delegation_args[..real_args_count]; - - let args = match (caller_kind, callee_kind) { - (FnKind::Free, FnKind::Free) - | (FnKind::Free, FnKind::AssocTrait) - | (FnKind::AssocInherentImpl, FnKind::Free) - | (FnKind::AssocTrait, FnKind::Free) - | (FnKind::AssocTrait, FnKind::AssocTrait) => delegation_args, - (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { - // Special case, as user specifies Trait args in trait impl header, we want to treat - // them as parent args. We always generate a function whose generics match - // child generics in trait. - let parent = tcx.local_parent(delegation_id); - parent_args = - tcx.impl_trait_header(parent).trait_ref.instantiate_identity().skip_norm_wip().args; - - assert!(child_args.is_empty(), "Child args can not be used in trait impl case"); - - tcx.mk_args(&delegation_args[delegation_generics.parent_count..]) - } - - (FnKind::AssocInherentImpl, FnKind::AssocTrait) => { - let self_ty = - tcx.type_of(tcx.local_parent(delegation_id)).instantiate_identity().skip_norm_wip(); + let mut delegation_parent_args = + &delegation_args[delegation_generics.has_self as usize..delegation_generics.parent_count]; - tcx.mk_args_from_iter( - std::iter::once(ty::GenericArg::from(self_ty)) - .chain(delegation_args.iter().copied()), - ) - } + let delegation_args = &delegation_args[delegation_generics.parent_count..]; - // For trait impl's `sig_id` is always equal to the corresponding trait method. - // For inherent methods delegation is not yet supported. - (FnKind::AssocTraitImpl, _) - | (_, FnKind::AssocTraitImpl) - | (_, FnKind::AssocInherentImpl) => unreachable!(), - }; + let kinds = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)); + if matches!(kinds, (FnKind::AssocTraitImpl, FnKind::AssocTrait)) { + // Special case, as user specifies Trait args in trait impl header, we want to treat + // them as parent args. We always generate a function whose generics match + // child generics in trait. + let parent = tcx.local_parent(delegation_id); - let mut new_args = vec![]; + parent_args = + tcx.impl_trait_header(parent).trait_ref.instantiate_identity().skip_norm_wip().args; - let self_pos_kind = create_self_position_kind(tcx, delegation_id, sig_id); - let mut lifetimes_end_pos; + child_args = + &delegation_args[delegation_args.len() - delegation_generics.own_params.len()..]; - if !parent_args.is_empty() { - let parent_args_lifetimes_count = - parent_args.iter().filter(|a| a.as_region().is_some()).count(); - - match self_pos_kind { - SelfPositionKind::AfterLifetimes { .. } => { - new_args.extend(&parent_args[1..1 + parent_args_lifetimes_count]); + delegation_parent_args = &[]; + } - lifetimes_end_pos = parent_args_lifetimes_count; + let self_type = get_delegation_self_ty(tcx, delegation_id).map(|t| t.into()); - new_args.push(parent_args[0]); + // Remove `Self` from parent args (it is always at the `0th` index) as it is + // added manually. + if self_type.is_some() && !parent_args.is_empty() { + parent_args = &parent_args[1..]; + } - new_args.extend(&parent_args[1 + parent_args_lifetimes_count..]); + let (zero_self, after_lifetimes_self) = + match create_self_position_kind(tcx, delegation_id, sig_id) { + SelfPositionKind::AfterLifetimes(_) => { + assert!(self_type.is_some()); + (None, self_type) } SelfPositionKind::Zero => { - lifetimes_end_pos = 1 /* Self */ + parent_args_lifetimes_count; - new_args.extend_from_slice(parent_args); - - for i in 0..deleg_parent_args_without_self_count { - new_args.insert(1 + i, args[1 + i]); - } - - lifetimes_end_pos += deleg_parent_args_without_self_count; + assert!(self_type.is_some()); + (self_type, None) } - // If we have parent args then we obtained them from trait, then self must be somewhere - SelfPositionKind::None => unreachable!(), + SelfPositionKind::None => (None, None), }; - } else { - let self_impact = matches!(self_pos_kind, SelfPositionKind::Zero) as usize; - - lifetimes_end_pos = self_impact - + deleg_parent_args_without_self_count - + &args[self_impact + deleg_parent_args_without_self_count..] - .iter() - .filter(|a| a.as_region().is_some()) - .count(); - - new_args.extend_from_slice(args); - - // Parent args are empty, then if we should propagate self ty (meaning Self generic - // param was not generated) then we should insert it, as it won't be in `args`. - if matches!(self_pos_kind, SelfPositionKind::AfterLifetimes(true)) { - new_args.insert( - lifetimes_end_pos, - ty::GenericArg::from(get_delegation_self_ty_or_err(tcx, delegation_id)), - ); - } - } - - if !child_args.is_empty() { - let child_lifetimes_count = child_args.iter().filter(|a| a.as_region().is_some()).count(); - - for i in 0..child_lifetimes_count { - new_args.insert(lifetimes_end_pos + i, child_args[i]); - } - new_args.extend_from_slice(&child_args[child_lifetimes_count..]); - } else if !parent_args.is_empty() { - let child_args = &delegation_args[delegation_generics.parent_count..]; - - let child_lifetimes_count = - child_args.iter().take_while(|a| a.as_region().is_some()).count(); - - for i in 0..child_lifetimes_count { - new_args.insert(lifetimes_end_pos + i, child_args[i]); - } - - // If self_ty is propagated it means that Self generic param was not generated. - let skip_self = matches!(self_pos_kind, SelfPositionKind::AfterLifetimes(false)); - new_args.extend(&child_args[child_lifetimes_count + skip_self as usize..]); - } - - new_args.extend(synth_args); - - new_args + let zero_self = zero_self.as_ref().into_iter(); + let after_lifetimes_self = after_lifetimes_self.as_ref().into_iter(); + + zero_self + .chain(delegation_parent_args) + .chain(parent_args.iter().filter(|a| a.as_region().is_some())) + .chain(child_args.iter().filter(|a| a.as_region().is_some())) + .chain(after_lifetimes_self) + .chain(parent_args.iter().filter(|a| a.as_region().is_none())) + .chain(child_args.iter().filter(|a| a.as_region().is_none())) + .chain(synth_args) + .copied() + .collect::>() } pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( @@ -516,7 +436,9 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( let new_pred = pred.0.fold_with(&mut self.folder); self.preds.push(( - EarlyBinder::bind(new_pred).instantiate(self.tcx, args).skip_norm_wip(), + EarlyBinder::bind(self.tcx, new_pred) + .instantiate(self.tcx, args) + .skip_norm_wip(), pred.1, )); } @@ -541,7 +463,10 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( let (parent_args, child_args) = tcx.delegation_user_specified_args(def_id); let (folder, args) = create_folder_and_args(tcx, def_id, sig_id, parent_args, child_args); let self_pos_kind = create_self_position_kind(tcx, def_id, sig_id); - let filter_self_preds = matches!(self_pos_kind, SelfPositionKind::AfterLifetimes(true)); + let filter_self_preds = matches!( + self_pos_kind, + SelfPositionKind::AfterLifetimes(Some(DelegationSelfTyPropagationKind::SelfTy(..))) + ); let collector = PredicatesCollector { tcx, preds: vec![], args, folder, filter_self_preds }; let (parent, inh_kind) = get_parent_and_inheritance_kind(tcx, def_id, sig_id); @@ -616,7 +541,7 @@ pub(crate) fn inherit_sig_for_delegation_item<'tcx>( let (parent_args, child_args) = tcx.delegation_user_specified_args(def_id); let (mut folder, args) = create_folder_and_args(tcx, def_id, sig_id, parent_args, child_args); - let caller_sig = EarlyBinder::bind(caller_sig.skip_binder().fold_with(&mut folder)); + let caller_sig = EarlyBinder::bind(tcx, caller_sig.skip_binder().fold_with(&mut folder)); let sig = caller_sig.instantiate(tcx, args.as_slice()).skip_binder(); let sig_iter = sig.inputs().iter().cloned().chain(std::iter::once(sig.output())); @@ -640,18 +565,21 @@ pub(crate) fn delegation_user_specified_args<'tcx>( let ctx = ItemCtxt::new_for_delegation(tcx, delegation_id); let lowerer = ctx.lowerer(); + let parent_args = info + .parent_seg_id_for_sig + .and_then(get_segment) + .filter(|(_, def_id)| matches!(tcx.def_kind(*def_id), DefKind::Trait)) + .map(|(segment, def_id)| { + let self_ty = get_delegation_self_ty(tcx, delegation_id); - let parent_args = info.parent_args_segment_id.and_then(get_segment).map(|(segment, def_id)| { - let self_ty = get_delegation_self_ty(tcx, delegation_id); - - lowerer - .lower_generic_args_of_path(segment.ident.span, def_id, &[], segment, self_ty) - .0 - .as_slice() - }); + lowerer + .lower_generic_args_of_path(segment.ident.span, def_id, &[], segment, self_ty) + .0 + .as_slice() + }); let child_args = info - .child_args_segment_id + .child_seg_id_for_sig .and_then(get_segment) .filter(|(_, def_id)| matches!(tcx.def_kind(*def_id), DefKind::Fn | DefKind::AssocFn)) .map(|(segment, def_id)| { diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 5997f16b42917..1bfc495071284 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -1582,14 +1582,6 @@ pub(crate) struct UnsupportedDelegation<'a> { pub callee_span: Span, } -#[derive(Diagnostic)] -#[diag("delegation self type is not specified")] -#[help("consider explicitly specifying self type: `reuse ::function`")] -pub(crate) struct DelegationSelfTypeNotSpecified { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("inferred lifetimes are not allowed in delegations as we need to inherit signature")] pub(crate) struct ElidedLifetimesAreNotAllowedInDelegations { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index edfab4a777f33..d730683b132d4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -467,6 +467,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( @@ -589,7 +590,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map_bound(|projection_term| projection_term.expect_ty()); // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` // parameter to have a skipped binder. - let param_ty = Ty::new_alias(tcx, projection_ty.skip_binder()); + let param_ty = + Ty::new_alias(tcx, ty::IsRigid::No, projection_ty.skip_binder()); self.lower_bounds( param_ty, hir_bounds, @@ -679,7 +681,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Binder::bind_with_vars(trait_ref, tcx.late_bound_vars(item_segment.hir_id)); match self.lower_return_type_notation_ty(candidate, item_def_id, hir_ty.span) { - Ok(ty) => Ty::new_alias(tcx, ty), + Ok(ty) => Ty::new_alias(tcx, ty::IsRigid::No, ty), Err(guar) => Ty::new_error(tcx, guar), } } @@ -727,7 +729,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } match self.lower_return_type_notation_ty(bound, item_def_id, hir_ty.span) { - Ok(ty) => Ty::new_alias(tcx, ty), + Ok(ty) => Ty::new_alias(tcx, ty::IsRigid::No, ty), Err(guar) => Ty::new_error(tcx, guar), } } @@ -791,7 +793,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Next, we need to check that the return-type notation is being used on // an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait). let output = tcx.fn_sig(item_def_id).skip_binder().output(); - let output = if let ty::Alias(alias_ty) = *output.skip_binder().kind() + let output = if let ty::Alias(_, alias_ty) = *output.skip_binder().kind() && let ty::AliasTy { kind: ty::Projection { def_id: projection_def_id }, .. } = alias_ty && tcx.is_impl_trait_in_trait(projection_def_id) { @@ -810,7 +812,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // `rustc_middle::ty::predicate::Clause::instantiate_supertrait` // and it's no coincidence why. let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output); - Ok(ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args).skip_norm_wip()) + Ok(ty::EarlyBinder::bind(tcx, shifted_output).instantiate(tcx, args).skip_norm_wip()) } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 39402cfc35dce..9d22d3060e1ff 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -409,9 +409,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(super) fn report_ambiguous_assoc_item( &self, - bound1: ty::PolyTraitRef<'tcx>, - bound2: ty::PolyTraitRef<'tcx>, - matching_candidates: impl Iterator>, + matching_candidates: &[ty::PolyTraitRef<'tcx>], qself: AssocItemQSelf, assoc_tag: ty::AssocTag, assoc_ident: Ident, @@ -443,7 +441,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // predicates!). // FIXME: Turn this into a structured, translatable & more actionable suggestion. let mut where_bounds = vec![]; - for bound in [bound1, bound2].into_iter().chain(matching_candidates) { + for &bound in matching_candidates { let bound_id = bound.def_id(); let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( tcx, @@ -473,6 +471,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index a26d84291e42d..45c2ed205c74d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -430,8 +430,14 @@ pub(crate) fn check_generic_arg_count( prohibit_assoc_item_constraint(cx, c, None); } - let explicit_late_bound = - prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos); + let tcx = cx.tcx(); + + // Suppress this warning for delegations as it is compiler generated and lifetimes are + // propagated while late-bound lifetimes may be present. + let explicit_late_bound = match seg.delegation_child_segment { + true => ExplicitLateBound::No, + false => prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos), + }; let mut invalid_args = vec![]; @@ -458,7 +464,7 @@ pub(crate) fn check_generic_arg_count( }; let reported = cx.dcx().emit_err(WrongNumberOfGenericArgs::new( - cx.tcx(), + tcx, gen_args_info, seg, gen_params, @@ -536,20 +542,19 @@ pub(crate) fn check_generic_arg_count( .map(|param| param.name) .collect(); if constraint_names == param_names { - let has_assoc_ty_with_same_name = - if let DefKind::Trait = cx.tcx().def_kind(def_id) { - gen_args.constraints.iter().any(|constraint| { - traits::supertrait_def_ids(cx.tcx(), def_id).any(|trait_did| { - cx.probe_trait_that_defines_assoc_item( - trait_did, - ty::AssocTag::Type, - constraint.ident, - ) - }) + let has_assoc_ty_with_same_name = if let DefKind::Trait = tcx.def_kind(def_id) { + gen_args.constraints.iter().any(|constraint| { + traits::supertrait_def_ids(tcx, def_id).any(|trait_did| { + cx.probe_trait_that_defines_assoc_item( + trait_did, + ty::AssocTag::Type, + constraint.ident, + ) }) - } else { - false - }; + }) + } else { + false + }; // We set this to true and delay emitting `WrongNumberOfGenericArgs` // to provide a succinct error for cases like issue #113073, // but only if when we don't have any assoc type with the same name with a @@ -573,7 +578,7 @@ pub(crate) fn check_generic_arg_count( let reported = gen_args.has_err().unwrap_or_else(|| { cx.dcx() .create_err(WrongNumberOfGenericArgs::new( - cx.tcx(), + tcx, gen_args_info, seg, gen_params, 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 87f88a16b2c42..2b0219f356c3c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -24,6 +24,7 @@ use std::{assert_matches, slice}; use rustc_abi::FIRST_VARIANT; use rustc_ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::sso::SsoHashSet; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, StashKey, @@ -482,7 +483,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let parent_def_id = self.item_def_id(); if let Res::Def(DefKind::ConstParam, _) = res - && tcx.def_kind(parent_def_id) == DefKind::AnonConst + && matches!(tcx.def_kind(parent_def_id), DefKind::AnonConst | DefKind::InlineConst) && let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id) { let folder = ForbidParamUsesFolder { @@ -511,15 +512,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Inline consts and closures can be nested inside anon consts that forbid generic // params (e.g. an enum discriminant). Walk up the def parent chain to find the // nearest enclosing AnonConst and use that to determine the context. + let parent_def_id = tcx.typeck_root_def_id(parent_def_id.into()); + let anon_const_def_id = match tcx.def_kind(parent_def_id) { DefKind::AnonConst => parent_def_id, - DefKind::InlineConst | DefKind::Closure => { - let root = tcx.typeck_root_def_id(parent_def_id.into()); - match tcx.def_kind(root) { - DefKind::AnonConst => root.expect_local(), - _ => return None, - } - } + DefKind::InlineConst if tcx.is_type_system_inline_const(parent_def_id) => parent_def_id, _ => return None, }; @@ -716,6 +713,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { generic_args: &'a GenericArgs<'tcx>, span: Span, infer_args: bool, + create_synth_args: bool, incorrect_args: &'a Result<(), GenericArgCountMismatch>, } @@ -828,7 +826,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .instantiate(tcx, preceding_args) .skip_norm_wip() .into() - } else if synthetic { + } else if self.create_synth_args && synthetic { Ty::new_param(tcx, param.index, param.name).into() } else if infer_args { self.lowerer.ty_infer(Some(param), self.span).into() @@ -868,6 +866,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, generic_args: segment.args(), infer_args: segment.infer_args, + create_synth_args: segment.delegation_child_segment, incorrect_args: &arg_count.correct, }; @@ -1216,7 +1215,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // feature `lazy_type_alias` enabled get encoded as a type alias that normalization will // then actually instantiate the where bounds of. let alias_ty = ty::AliasTy::new_from_args(tcx, ty::Free { def_id }, args); - Ty::new_alias(tcx, alias_ty) + Ty::new_alias(tcx, ty::IsRigid::No, alias_ty) } else { tcx.at(span).type_of(def_id).instantiate(tcx, args).skip_norm_wip() } @@ -1260,6 +1259,74 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) } + /// When there are multiple traits which contain an identically named + /// associated item, this function eliminates any traits which are a + /// supertrait of another candidate trait. + /// + /// This is the type-level analogue of + /// `rustc_hir_typeck::method::probe::ProbeContext::collapse_candidates_to_subtrait_pick`; + /// keep both implementations in sync. + /// + /// This implements RFC #3624. + fn collapse_candidates_to_subtrait_pick( + &self, + matching_candidates: &[ty::PolyTraitRef<'tcx>], + ) -> Option> { + if !self.tcx().features().supertrait_item_shadowing() { + return None; + } + + let mut child_trait = matching_candidates[0]; + let mut supertraits: SsoHashSet<_> = + traits::supertrait_def_ids(self.tcx(), child_trait.def_id()).collect(); + + let mut remaining_candidates: Vec<_> = matching_candidates[1..].iter().copied().collect(); + while !remaining_candidates.is_empty() { + let mut made_progress = false; + let mut next_round = vec![]; + + for remaining_trait in remaining_candidates { + if supertraits.contains(&remaining_trait.def_id()) { + made_progress = true; + continue; + } + + // This candidate is not a supertrait of the `child_trait`. + // Check if it's a subtrait of the `child_trait`, instead. + // If it is, then it must have been a subtrait of every + // other pick we've eliminated at this point. It will + // take over at this point. + let remaining_trait_supertraits: SsoHashSet<_> = + traits::supertrait_def_ids(self.tcx(), remaining_trait.def_id()).collect(); + if remaining_trait_supertraits.contains(&child_trait.def_id()) { + child_trait = remaining_trait; + supertraits = remaining_trait_supertraits; + made_progress = true; + continue; + } + + // Neither `child_trait` or the current candidate are + // supertraits of each other. + // Don't bail here, since we may be comparing two supertraits + // of a common subtrait. These two supertraits won't be related + // at all, but we will pick them up next round when we find their + // child as we continue iterating in this round. + next_round.push(remaining_trait); + } + + if made_progress { + // If we've made progress, iterate again. + remaining_candidates = next_round; + } else { + // Otherwise, we must have at least two candidates which + // are not related to each other at all. + return None; + } + } + + Some(child_trait) + } + /// Search for a single trait bound whose trait defines the associated item given by /// `assoc_ident`. /// @@ -1294,10 +1361,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; if let Some(bound2) = matching_candidates.next() { + let all_matching_candidates: Vec<_> = + [bound1, bound2].into_iter().chain(matching_candidates).collect(); + if let Some(bound) = self.collapse_candidates_to_subtrait_pick(&all_matching_candidates) + { + return Ok(bound); + } + return Err(self.report_ambiguous_assoc_item( - bound1, - bound2, - matching_candidates, + &all_matching_candidates, qself, assoc_tag, assoc_ident, @@ -1361,7 +1433,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::AliasTyKind::Inherent { def_id } => def_id, kind => bug!("expected projection or inherent alias, got {kind:?}"), }; - let ty = alias_ty.to_ty(tcx); + let ty = alias_ty.to_ty(tcx, ty::IsRigid::No); let ty = self.check_param_uses_if_mcg(ty, span, false); Ok((ty, tcx.def_kind(def_id), def_id)) } @@ -1400,7 +1472,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(def_id) = alias_ct.kind.opt_def_id() { self.require_type_const_attribute(def_id, span)?; } - let ct = Const::new_unevaluated(tcx, alias_ct); + let ct = Const::new_alias(tcx, ty::IsRigid::No, alias_ct); let ct = self.check_param_uses_if_mcg(ct, span, false); Ok(ct) } @@ -1635,8 +1707,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .inherent_impls(adt_did) .iter() .filter_map(|&impl_| { - let (item, scope) = - self.probe_assoc_item_unchecked(name, assoc_tag, block, impl_)?; + let (item, scope) = self.probe_assoc_item_unchecked(name, assoc_tag, impl_)?; Some(InherentAssocCandidate { impl_, assoc_item: item.def_id, scope }) }) .collect(); @@ -1712,7 +1783,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, scope: DefId, ) -> Option { - let (item, scope) = self.probe_assoc_item_unchecked(ident, assoc_tag, block, scope)?; + let (item, scope) = self.probe_assoc_item_unchecked(ident, assoc_tag, scope)?; self.check_assoc_item(item.def_id, ident, scope, block, span); Some(item) } @@ -1725,12 +1796,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, ident: Ident, assoc_tag: ty::AssocTag, - block: HirId, scope: DefId, ) -> Option<(ty::AssocItem, /*scope*/ DefId)> { let tcx = self.tcx(); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, self.item_def_id()); // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` // instead of calling `filter_by_name_and_kind` which would needlessly normalize the // `ident` again and again. @@ -1837,7 +1907,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::AssocTag::Type, ) { Ok((item_def_id, item_args)) => { - Ty::new_projection_from_args(self.tcx(), item_def_id, item_args) + Ty::new_projection_from_args(self.tcx(), ty::IsRigid::No, item_def_id, item_args) } Err(guar) => Ty::new_error(self.tcx(), guar), } @@ -1863,12 +1933,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::AssocTag::Const, )?; self.require_type_const_attribute(item_def_id, span)?; - let uv = ty::UnevaluatedConst::new( + let alias_const = ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, item_def_id), + ty::AliasConstKind::new_from_def_id(tcx, item_def_id), item_args, ); - Ok(Const::new_unevaluated(tcx, uv)) + Ok(Const::new_alias(tcx, ty::IsRigid::No, alias_const)) } /// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path. @@ -2122,7 +2192,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { GenericsArgsErrExtend::OpaqueTy, ); let args = self.lower_generic_args_of_path_segment(span, did, segment); - Ty::new_opaque(tcx, did, args) + Ty::new_opaque(tcx, ty::IsRigid::No, did, args) } Res::Def( DefKind::Enum @@ -2321,7 +2391,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { const_arg.span, "anonymous constants with lifetimes in their type are not yet supported", ); - tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); + tcx.feed_anon_const_type( + anon.def_id, + ty::EarlyBinder::bind(tcx, Ty::new_error(tcx, e)), + ); return ty::Const::new_error(tcx, e); } // We must error if the instantiated type has any inference variables as we will @@ -2332,7 +2405,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { const_arg.span, "anonymous constants with inferred types are not yet supported", ); - tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); + tcx.feed_anon_const_type( + anon.def_id, + ty::EarlyBinder::bind(tcx, Ty::new_error(tcx, e)), + ); return ty::Const::new_error(tcx, e); } // We error when the type contains unsubstituted generics since we do not currently @@ -2342,11 +2418,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { const_arg.span, "anonymous constants referencing generics are not yet supported", ); - tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); + tcx.feed_anon_const_type( + anon.def_id, + ty::EarlyBinder::bind(tcx, Ty::new_error(tcx, e)), + ); return ty::Const::new_error(tcx, e); } - tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(ty)); + tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(tcx, ty)); } let hir_id = const_arg.hir_id; @@ -2522,15 +2601,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Const<'tcx> { let tcx = self.tcx(); + let found_tuple = || { + tcx.sess + .source_map() + .span_to_snippet(span) + .map(|snippet| format!("`{snippet}`")) + .unwrap_or_else(|_| "const tuple".to_string()) + }; + let tys = match ty.kind() { ty::Tuple(tys) => tys, ty::Error(e) => return Const::new_error(tcx, *e), _ => { - let e = tcx.dcx().span_err(span, format!("expected `{}`, found const tuple", ty)); + let e = + tcx.dcx().span_err(span, format!("expected `{}`, found {}", ty, found_tuple())); return Const::new_error(tcx, e); } }; + if exprs.len() != tys.len() { + let e = tcx.dcx().span_err(span, format!("expected `{}`, found {}", ty, found_tuple())); + return Const::new_error(tcx, e); + } + let exprs = exprs .iter() .zip(tys.iter()) @@ -2747,13 +2840,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let _ = self .prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None); let args = self.lower_generic_args_of_path_segment(span, did, segment); - ty::Const::new_unevaluated( + ty::Const::new_alias( tcx, - ty::UnevaluatedConst::new( - tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, did), - args, - ), + ty::IsRigid::No, + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, did), args), ) } Res::Def(kind @ DefKind::Ctor(ctor_of, CtorKind::Const), did) => { @@ -2840,7 +2930,27 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { did, path.segments.last().unwrap(), ); - ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + + if self.tcx().generics_of(did).own_synthetic_params_count() == 0 { + ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + } else { + let tcx = self.tcx(); + let generics = tcx.generics_of(did); + + // Use infer tys for synthetic params; otherwise the impl header's trait ref may + // contain callee-owned synthetic params and fail when instantiated with impl args. + // See issue #155834 + let args = args.iter().enumerate().map(|(index, arg)| { + let param = generics.param_at(index, tcx); + if param.kind.is_synthetic() { + self.ty_infer(Some(param), span).into() + } else { + arg + } + }); + + ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + } } // Exhaustive match to be clear about what exactly we're considering to be @@ -2889,7 +2999,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.check_param_uses_if_mcg(ct, span, false) } - /// Literals are eagerly converted to a constant, everything else becomes `Unevaluated`. + /// Literals are eagerly converted to a constant, everything else becomes `ConstKind::Alias`. #[instrument(skip(self), level = "debug")] fn lower_const_arg_anon(&self, anon: &AnonConst) -> Const<'tcx> { let tcx = self.tcx(); @@ -2904,11 +3014,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { match self.try_lower_anon_const_lit(ty, expr) { Some(v) => v, - None => ty::Const::new_unevaluated( + None => ty::Const::new_alias( tcx, - ty::UnevaluatedConst::new( + ty::IsRigid::No, + ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::Anon { def_id: anon.def_id.to_def_id() }, + ty::AliasConstKind::Anon { def_id: anon.def_id.to_def_id() }, ty::GenericArgs::identity_for_item(tcx, anon.def_id.to_def_id()), ), ), @@ -3384,8 +3495,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } (FIRST_VARIANT, def.non_enum_variant()) }; - let block = tcx.local_def_id_to_hir_id(item_def_id); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(field, def.did(), block); + let (ident, def_scope) = + tcx.adjust_ident_and_get_scope(field, def.did(), item_def_id); if let Some((field_idx, field)) = variant .fields .iter_enumerated() @@ -3529,9 +3640,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?args); if in_trait.is_some() { - Ty::new_projection_from_args(tcx, def_id, args) + Ty::new_projection_from_args(tcx, ty::IsRigid::No, def_id, args) } else { - Ty::new_opaque(tcx, def_id, args) + Ty::new_opaque(tcx, ty::IsRigid::No, def_id, args) } } @@ -3555,11 +3666,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?output_ty); debug!(?abi, ?safety, ?decl.fn_decl_kind, input_tys_len = ?input_tys.len()); - // FIXME(splat): use `set_splatted()` once FnSig has it let fn_sig_kind = FnSigKind::default() .set_abi(abi) .set_safety(safety) - .set_c_variadic(decl.fn_decl_kind.c_variadic()); + .set_c_variadic(decl.fn_decl_kind.c_variadic()) + .set_splatted(decl.splatted(), input_tys.len()) + .unwrap(); let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, fn_sig_kind); let fn_ptr_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index c5a6ce75c0e41..ff00e18783706 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -186,9 +186,13 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } _ => (), } - // Skip `AnonConst`s because we feed their `type_of`. + // Skip `AnonConst`s and type system `InlineConst`s because we feed their `type_of` in + // `feed_anon_const_type`. // Also skip items for which typeck forwards to parent typeck. - if !(matches!(def_kind, DefKind::AnonConst) || def_kind.is_typeck_child()) { + if !(def_kind == DefKind::AnonConst + || def_kind == DefKind::InlineConst && tcx.is_type_system_inline_const(item_def_id) + || tcx.is_typeck_child(item_def_id.to_def_id())) + { tcx.ensure_ok().typeck(item_def_id); } // Ensure we generate the new `DefId` before finishing `check_crate`. diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index d3a57a4d8e5d3..97ef6a2461495 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -59,7 +59,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { } } - ty::EarlyBinder::bind(required_predicates) + ty::EarlyBinder::bind_iter(required_predicates) }) } } diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index 174f702dcd0c4..b2deea63dfd16 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -81,8 +81,10 @@ pub(super) fn infer_predicates( .map_or(0, |p| p.as_ref().skip_binder().len()); if item_required_predicates.len() > item_predicates_len { predicates_added.push(item_did); - global_inferred_outlives - .insert(item_did.to_def_id(), ty::EarlyBinder::bind(item_required_predicates)); + global_inferred_outlives.insert( + item_did.to_def_id(), + ty::EarlyBinder::bind_iter(item_required_predicates), + ); } } @@ -154,7 +156,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( ); } - ty::Alias(ty::AliasTy { kind: ty::Free { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Free { def_id }, args, .. }) => { // This corresponds to a type like `Type<'a, T>`. // We check inferred and explicit predicates. debug!("Free"); @@ -204,7 +206,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( } } - ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) => { // This corresponds to a type like `<() as Trait<'a, T>>::Type`. // We only use the explicit predicates of the trait but // not the ones of the associated type itself. @@ -220,7 +222,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( } // FIXME(inherent_associated_types): Use the explicit predicates from the parent impl. - ty::Alias(ty::AliasTy { kind: ty::Inherent { .. }, .. }) => {} + ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }) => {} _ => {} } diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 99a633e2b7d1f..ac58a37cc8e81 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -102,7 +102,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( // // Here we want to add an explicit `where ::Item: 'a` // or `Opaque: 'a` depending on the alias kind. - let ty = alias_ty.to_ty(tcx); + let ty = alias_ty.to_ty(tcx, ty::IsRigid::No); required_predicates .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) .or_insert(span); diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 3e7f5b73e4569..a2e3883b0684e 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -260,15 +260,18 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_args(current, def.did(), args, variance); } - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, - args, - .. - }) => { + ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, + args, + .. + }, + ) => { self.add_constraints_from_invariant_args(current, args, variance); } - ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => { let ty = self.tcx().expand_free_alias_tys(ty); self.add_constraints_from_ty(current, ty, variance); } @@ -405,8 +408,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { debug!("add_constraints_from_const(c={:?}, variance={:?})", c, variance); match &c.kind() { - ty::ConstKind::Unevaluated(uv) => { - self.add_constraints_from_invariant_args(current, uv.args, variance); + ty::ConstKind::Alias(_, alias_const) => { + self.add_constraints_from_invariant_args(current, alias_const.args, variance); } _ => {} } diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index c4287a8d0bb10..5c42d79621524 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -139,7 +139,7 @@ fn variance_of_opaque( #[instrument(level = "trace", skip(self), ret)] fn visit_ty(&mut self, t: Ty<'tcx>) { match t.kind() { - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { self.visit_opaque(*def_id, args); } _ => t.super_visit_with(self), diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 43db09b0eab8f..9d185a565fe00 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index e043e1afaf61f..3f3373d372ddd 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.may_coerce(arm_ty, ret_ty) && prior_arm.is_none_or(|(_, ty, _)| self.may_coerce(ty, ret_ty)) // The match arms need to unify for the case of `impl Trait`. - && !matches!(ret_ty.kind(), ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. })) + && !matches!(ret_ty.kind(), ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. })) } _ => false, }; @@ -532,7 +532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expected_ty = expectation.to_option(self)?; let (def_id, args) = match *expected_ty.kind() { // FIXME: Could also check that the RPIT is not defined - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { (def_id.as_local()?, args) } // FIXME(-Znext-solver=no): Remove this branch once `replace_opaque_types_with_infer` is gone. diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 57ab29ac752ad..bb822f8d0f68d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -9,11 +9,11 @@ use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; +use rustc_middle::bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::{self, FnSig, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; -use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; @@ -58,9 +58,13 @@ pub(crate) fn check_legal_trait_for_method_call( tcx.ensure_result().coherent_trait(trait_id) } +/// State machine for typechecking a call, based on the callee type. #[derive(Debug)] enum CallStep<'tcx> { + /// Typecheck a call to a function definition or pointer. + /// Includes functions with splatted arguments. Builtin(Ty<'tcx>), + /// Deferred closure Fn* trait typechecking, when the callee is a closure. DeferredClosure(LocalDefId, ty::FnSig<'tcx>), /// Call overloading when callee implements one of the Fn* traits. Overloaded(MethodCallee<'tcx>), @@ -544,7 +548,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let (fn_sig, def_id) = match *callee_ty.kind() { + let (fn_sig, def_id, callee_generic_args) = match *callee_ty.kind() { ty::FnDef(def_id, args) => { self.enforce_context_effects(Some(call_expr.hir_id), call_expr.span, def_id, args); let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args).skip_norm_wip(); @@ -573,11 +577,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } } - (fn_sig, Some(def_id)) + (fn_sig, Some(def_id), Some(args)) } // FIXME(const_trait_impl): these arms should error because we can't enforce them - ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None), + ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None, None), _ => unreachable!(), }; @@ -595,12 +599,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = self.normalize(call_expr.span, Unnormalized::new_wip(fn_sig)); self.check_argument_types_maybe_method_like( - &fn_sig, call_expr, arg_exprs, expected, def_id, + &fn_sig, + call_expr, + arg_exprs, + expected, + TupleArgumentsFlag::with_fn_sig_kind(fn_sig.fn_sig_kind, false), + def_id, + callee_generic_args, ); + // Splatting is currently incompatible with RustCall. if fn_sig.abi() == rustc_abi::ExternAbi::RustCall { let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span); - if let Some(ty) = fn_sig.inputs().last().copied() { + if let Some(ty) = fn_sig.inputs().last().copied() + && fn_sig.splatted().is_none() + { self.register_bound( ty, self.tcx.require_lang_item(hir::LangItem::Tuple, sp), @@ -627,7 +640,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, + tuple_arguments_flag: TupleArgumentsFlag, def_id: Option, + callee_generic_args: Option>, ) { let do_check = || { self.check_argument_types( @@ -638,8 +653,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, arg_exprs, fn_sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, + tuple_arguments_flag, def_id, + callee_generic_args, ); }; @@ -1009,9 +1025,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig.output(), expected, arg_exprs, - fn_sig.c_variadic(), - TupleArgumentsFlag::TupleArguments, + fn_sig.fn_sig_kind.c_variadic(), + TupleArgumentsFlag::rust_fn_trait_call(), Some(closure_def_id.to_def_id()), + None, ); fn_sig.output() @@ -1092,6 +1109,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, method: MethodCallee<'tcx>, ) -> Ty<'tcx> { + // FIXME(splat): if we ever support splatting here, decrement the splatted index, because + // the receiver argument is removed below. + assert_eq!( + method.sig.fn_sig_kind.splatted(), + None, + "splatting is not supported on RustCall tuples", + ); self.check_argument_types( call_expr.span, call_expr, @@ -1099,9 +1123,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method.sig.output(), expected, arg_exprs, - method.sig.c_variadic(), - TupleArgumentsFlag::TupleArguments, + method.sig.fn_sig_kind.c_variadic(), + TupleArgumentsFlag::rust_fn_trait_call(), Some(method.def_id), + None, ); self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method); @@ -1161,11 +1186,16 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { ); } None => { - span_bug!( - self.call_expr.span, - "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", - self.closure_ty - ) + let guar = fcx.tainted_by_errors().unwrap_or_else(|| { + fcx.dcx().span_delayed_bug( + self.call_expr.span, + format!( + "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", + self.closure_ty + ), + ) + }); + fcx.write_resolution(self.call_expr.hir_id, Err(guar)); } } } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index c8680be6de718..f7f0b69873e1e 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -123,7 +123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Pointers to foreign types are thin, despite being unsized ty::Foreign(..) => Some(PointerKind::Thin), // We should really try to normalize here. - ty::Alias(pi) => Some(PointerKind::OfAlias(pi)), + ty::Alias(_, pi) => Some(PointerKind::OfAlias(pi)), ty::Param(p) => Some(PointerKind::OfParam(p)), // Insufficient type information. ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, @@ -739,9 +739,21 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } + fn expr_span_for_type_resolution(&self, fcx: &FnCtxt<'a, 'tcx>) -> Span { + if let hir::ExprKind::Index(_, idx, _) = self.expr.kind + && fcx.resolve_vars_if_possible(self.expr_ty).is_ty_var() + && fcx.resolve_vars_if_possible(fcx.node_ty(idx.hir_id)).is_ty_var() + { + index_operand_ambiguity_span(idx) + } else { + self.expr_span + } + } + #[instrument(skip(fcx), level = "debug")] pub(crate) fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { - self.expr_ty = fcx.structurally_resolve_type(self.expr_span, self.expr_ty); + let expr_span = self.expr_span_for_type_resolution(fcx); + self.expr_ty = fcx.structurally_resolve_type(expr_span, self.expr_ty); self.cast_ty = fcx.structurally_resolve_type(self.cast_span, self.cast_ty); debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); @@ -1169,3 +1181,10 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } } + +fn index_operand_ambiguity_span(expr: &hir::Expr<'_>) -> Span { + match expr.kind { + hir::ExprKind::MethodCall(segment, ..) => segment.ident.span, + _ => expr.span, + } +} diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 5fe1f4191c93f..1780430567a80 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>( } // Check that argument is Sized. - if !params_can_be_unsized { + if !params_can_be_unsized || fn_sig.abi() == rustc_abi::ExternAbi::RustTail { fcx.require_type_is_sized( param_ty, param.ty_span, diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 570d33aaf20e0..95e67b135e411 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -294,7 +294,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_kind: hir::ClosureKind, ) -> (Option>, Option) { match *expected_ty.kind() { - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => self + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => self .deduce_closure_signature_from_predicates( expected_ty, closure_kind, @@ -720,6 +720,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in this binder we are creating. assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST)); let bound_sig = expected_sig.sig.map_bound(|sig| { + // Ignore splatting, it is unsupported on closures. let fn_sig_kind = FnSigKind::default() .set_abi(ExternAbi::RustCall) .set_safety(hir::Safety::Safe) @@ -990,14 +991,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { get_future_output(obligation.predicate, obligation.cause.span) })? } - ty::Alias(ty::AliasTy { kind: ty::Projection { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }) => { return Some(Ty::new_error_with_message( self.tcx, closure_span, "this projection should have been projected to an opaque type", )); } - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => self + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => self .tcx .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 6e4d6aa80a63a..bd8295ba3a328 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -3,11 +3,11 @@ use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, find_attr}; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_middle::bug; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, BottomUpFolder, Ty, TypeFoldable, TypeVisitableExt}; +use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; @@ -402,9 +402,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unify the method signature with our incompatible arg, to // do inference in the *opposite* direction and to find out // what our ideal rcvr ty would look like. + let Some(input_arg) = method.sig.inputs().get(idx + 1) else { + if method.sig.splatted().is_some() { + // FIXME(splat): when the arg is splatted, adjust its index, to handle the type mismatch properly + return None; + } else { + span_bug!( + self.tcx.def_span(method.def_id), + "arg index {} out of bounds for method with {} inputs", + idx + 1, + method.sig.inputs().len(), + ); + } + }; let _ = self .at(&ObligationCause::dummy(), self.param_env) - .eq(DefineOpaqueTypes::Yes, method.sig.inputs()[idx + 1], arg_ty) + .eq(DefineOpaqueTypes::Yes, *input_arg, arg_ty) .ok()?; self.select_obligations_where_possible(|errs| { // Yeet the errors, we're already reporting errors. diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index a9a819935287c..a5c39bd1584a1 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -100,6 +100,7 @@ impl IntoDiagArg for ReturnLikeStatementKind { } } +// FIXME(splat): add "non-splatted" to all 4 instances of this error message #[derive(Diagnostic)] #[diag("functions with the \"rust-call\" ABI must take a single non-self tuple argument")] pub(crate) struct RustCallIncorrectArgs { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index fa3fc32984a22..0cd80a6a83c87 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1463,16 +1463,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) => { self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method); + // Handle splatted method arguments + // self is already handled as `rcvr`, so it's never splatted here + let method_inputs = &method.sig.inputs()[1..]; + let method_tuple_args_flag = + TupleArgumentsFlag::with_fn_sig_kind(method.sig.fn_sig_kind, true); + self.check_argument_types( segment.ident.span, expr, - &method.sig.inputs()[1..], + method_inputs, method.sig.output(), expected, args, - method.sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, + method.sig.fn_sig_kind.c_variadic(), + method_tuple_args_flag, Some(method.def_id), + Some(method.args), ); self.check_call_abi(method.sig.abi(), expr.span); @@ -1495,6 +1502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false, TupleArgumentsFlag::DontTupleArguments, None, + Some(GenericArgsRef::default()), ); err_output @@ -2756,9 +2764,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ty::new_error(self.tcx(), guar); } - let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id); let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, base_def.did(), fn_body_hir_id); + self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id); if let Some((idx, field)) = self.find_adt_field(*base_def, ident) { self.write_field_index(expr.hir_id, idx); @@ -2976,7 +2983,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(ident.span, "unknown field"); self.point_at_param_definition(&mut err, param_ty); } - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs()); } _ => { @@ -3159,6 +3166,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_first_deref_field(&self, err: &mut Diag<'_>, base: &hir::Expr<'_>, field: Ident) { err.span_label(field.span, "unknown field"); + if base.span.from_expansion() || field.span.from_expansion() { + return; + } let val = if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) && base.len() < 20 { @@ -3578,6 +3588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.param_env, Unnormalized::new(Ty::new_projection_from_args( self.tcx, + ty::IsRigid::No, index_trait_output_def_id, impl_trait_ref.args, )), @@ -3756,9 +3767,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match container.kind() { ty::Adt(container_def, args) if container_def.is_enum() => { - let block = self.tcx.local_def_id_to_hir_id(self.body_id); - let (ident, _def_scope) = - self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block); + let ident = self.tcx.adjust_ident(field, container_def.did()); if !self.tcx.features().offset_of_enum() { rustc_session::errors::feature_err( @@ -3794,7 +3803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { break; }; let (subident, sub_def_scope) = - self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, block); + self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, self.body_id); let Some((subindex, field)) = variant .fields @@ -3842,9 +3851,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } ty::Adt(container_def, args) => { - let block = self.tcx.local_def_id_to_hir_id(self.body_id); - let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block); + let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope( + field, + container_def.did(), + self.body_id, + ); let fields = &container_def.non_enum_variant().fields; if let Some((index, field)) = fields diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index c8c6de4f99c03..9a471d3ea007b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, - SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, Unnormalized, - UserArgs, UserSelfTy, + SizedTraitKind, SplattedDef, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, + Unnormalized, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -143,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// also select obligations if it seems useful, in an effort /// to get more type information. // FIXME(-Znext-solver): A lot of the calls to this method should - // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead. + // probably be `resolve_vars_with_obligations` or `structurally_resolve_type` instead. #[instrument(skip(self), level = "debug", ret)] pub(crate) fn resolve_vars_with_obligations>>( &self, @@ -236,6 +236,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); } + #[instrument(level = "debug", skip(self))] + pub(crate) fn write_splatted_resolution( + &self, + hir_id: HirId, + r: Result, + ) { + self.typeck_results.borrow_mut().splatted_defs_mut().insert(hir_id, r); + } + #[instrument(level = "debug", skip(self))] pub(crate) fn write_method_call_and_enforce_effects( &self, @@ -248,6 +257,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_args(hir_id, method.args); } + #[instrument(level = "debug", skip(self))] + pub(crate) fn write_splatted_call( + &self, + hir_id: HirId, + span: Span, + callee_def_id: Option, + callee_generic_args: Option>, + first_tupled_arg_index: u16, + tupled_args_count: u16, + ) { + // FIXME(const_trait_impl): enforce constness using enforce_context_effects() and add + // _and_enforce_effects to this method's name + + self.write_splatted_resolution( + hir_id, + Ok(SplattedDef { + def_id: callee_def_id, + arg_index: first_tupled_arg_index, + arg_count: tupled_args_count, + }), + ); + if let Some(callee_generic_args) = callee_generic_args { + self.write_args(hir_id, callee_generic_args); + } + } + fn write_args(&self, node_id: HirId, args: GenericArgsRef<'tcx>) { if !args.is_empty() { debug!("write_args({:?}, {:?}) in fcx {}", node_id, args, self.tag()); @@ -1453,7 +1488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ct = self.resolve_vars_with_obligations(ct); if self.next_trait_solver() - && let ty::ConstKind::Unevaluated(..) = ct.kind() + && let ty::ConstKind::Alias(..) = ct.kind() { // We need to use a separate variable here as otherwise the temporary for // `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index 825aa37065e15..5a1bab56b91f2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -4,8 +4,11 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_infer::traits::ObligationCauseCode; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, +}; use rustc_span::{Span, kw}; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; use crate::FnCtxt; @@ -37,6 +40,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, error: &mut traits::FulfillmentError<'tcx>, ) -> bool { + if self.adjust_binop_index_operand(error) { + return true; + } + let (def_id, hir_id, idx, flavor) = match *error.obligation.cause.code().peel_derives() { ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) => { (def_id, hir_id, idx, ClauseFlavor::Where) @@ -165,6 +172,119 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn adjust_binop_index_operand(&self, error: &mut traits::FulfillmentError<'tcx>) -> bool { + let ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } = + *error.obligation.cause.code().peel_derives() + else { + return false; + }; + if !matches!(error.code, traits::FulfillmentErrorCode::Ambiguity { .. }) + || !error.obligation.predicate.has_infer() + { + return false; + } + + let hir::Node::Expr(lhs_expr) = self.tcx.hir_node(lhs_hir_id) else { + return false; + }; + let hir::Node::Expr(rhs_expr) = self.tcx.hir_node(rhs_hir_id) else { + return false; + }; + let Some(binop) = self.binop_for_operands(lhs_hir_id, rhs_hir_id) else { + return false; + }; + let hir::ExprKind::Index(indexed_expr, idx, _) = rhs_expr.kind else { + return false; + }; + if !self.resolve_vars_if_possible(self.node_ty(idx.hir_id)).is_ty_var() { + return false; + } + let lhs_ty = self.resolve_vars_if_possible(self.node_ty(lhs_expr.hir_id)); + let indexed_ty = self.resolve_vars_if_possible(self.node_ty(indexed_expr.hir_id)); + let rhs_ty = match *indexed_ty.kind() { + ty::Array(element_ty, _) | ty::Slice(element_ty) => element_ty, + ty::Ref(_, pointee_ty, _) => match *pointee_ty.kind() { + ty::Array(element_ty, _) | ty::Slice(element_ty) => element_ty, + _ => self.resolve_vars_if_possible(self.node_ty(rhs_expr.hir_id)), + }, + _ => self.resolve_vars_if_possible(self.node_ty(rhs_expr.hir_id)), + }; + if !self.binop_accepts_types(binop.node, lhs_ty, rhs_ty) { + return false; + } + + error.obligation.cause.span = match idx.kind { + hir::ExprKind::MethodCall(segment, ..) => segment.ident.span, + _ => idx.span, + }; + true + } + + fn binop_for_operands( + &self, + lhs_hir_id: hir::HirId, + rhs_hir_id: hir::HirId, + ) -> Option { + let hir::Node::Expr(parent_expr) = self.tcx.parent_hir_node(rhs_hir_id) else { + return None; + }; + let hir::ExprKind::Binary(binop, lhs_expr, rhs_expr) = parent_expr.kind else { + return None; + }; + (lhs_expr.hir_id == lhs_hir_id && rhs_expr.hir_id == rhs_hir_id).then_some(binop) + } + + fn binop_accepts_types( + &self, + binop: hir::BinOpKind, + lhs_ty: Ty<'tcx>, + rhs_ty: Ty<'tcx>, + ) -> bool { + let lhs_ty = self.deref_ty_if_possible(lhs_ty); + let rhs_ty = self.deref_ty_if_possible(rhs_ty); + if lhs_ty.references_error() || rhs_ty.references_error() { + return true; + } + + match binop { + hir::BinOpKind::Shl | hir::BinOpKind::Shr => { + lhs_ty.is_integral() && rhs_ty.is_integral() + } + hir::BinOpKind::Add + | hir::BinOpKind::Sub + | hir::BinOpKind::Mul + | hir::BinOpKind::Div + | hir::BinOpKind::Rem => { + self.can_eq(self.param_env, lhs_ty, rhs_ty) + && (lhs_ty.is_integral() || lhs_ty.is_floating_point()) + && (rhs_ty.is_integral() || rhs_ty.is_floating_point()) + } + hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => { + self.can_eq(self.param_env, lhs_ty, rhs_ty) + && ((lhs_ty.is_integral() && rhs_ty.is_integral()) + || (lhs_ty.is_bool() && rhs_ty.is_bool())) + } + hir::BinOpKind::Eq + | hir::BinOpKind::Ne + | hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt => { + self.can_eq(self.param_env, lhs_ty, rhs_ty) + && lhs_ty.is_scalar() + && rhs_ty.is_scalar() + } + hir::BinOpKind::And | hir::BinOpKind::Or => lhs_ty.is_bool() && rhs_ty.is_bool(), + } + } + + fn deref_ty_if_possible(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.kind() { + ty::Ref(_, ty, hir::Mutability::Not) => *ty, + _ => ty, + } + } + fn point_at_expr_if_possible( &self, error: &mut traits::FulfillmentError<'tcx>, @@ -1058,9 +1178,10 @@ fn find_param_in_ty<'tcx>( return true; } if let ty::GenericArgKind::Type(ty) = arg.kind() - && let ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, .. - }) = ty.kind() + && let ty::Alias( + _, + ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }, + ) = ty.kind() { // This logic may seem a bit strange, but typically when // we have a projection type in a function signature, the diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a295cc4127b67..7d85a5a4ded63 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -50,6 +50,19 @@ rustc_index::newtype_index! { pub(crate) struct GenericIdx {} } +/// Outcome of checking arguments that are tupled by "rust-call" or `#[splat]`. +#[derive(Debug, Clone, Eq, PartialEq)] +struct TupledArgCheckOutcome<'tcx> { + /// The error code to emit if the arguments are not compatible. + new_err_code: Option, + + /// The formal input types after checking. + untupled_formal_input_tys: Vec>, + + /// The expected input types after checking. + untupled_expected_input_tys: Option>>, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { let mut deferred_cast_checks = self.root_ctxt.deferred_cast_checks.borrow_mut(); @@ -161,7 +174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_) => enforce_copy_bound(element, element_ty), + | ty::ConstKind::Alias(_, _) => enforce_copy_bound(element, element_ty), ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => { unreachable!() @@ -185,13 +198,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expectation: Expectation<'tcx>, // The expressions for each provided argument provided_args: &'tcx [hir::Expr<'tcx>], - // Whether the function is variadic, for example when imported from C - // FIXME(splat): maybe change this to FnSigKind? + // Whether the function is variadic (e.g. from C) c_variadic: bool, - // Whether the arguments have been bundled in a tuple (ex: closures) + // Whether all the arguments have been bundled in a tuple (ex: closures), or one has been splatted tuple_arguments: TupleArgumentsFlag, // The DefId for the function being called, for better error messages fn_def_id: Option, + // The generics of the function being called. Only used for splatting + callee_generic_args: Option>, ) { let tcx = self.tcx; @@ -220,11 +234,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // First, let's unify the formal method signature with the expectation eagerly. - // We use this to guide coercion inference; it's output is "fudged" which means + // We use this to guide coercion inference; its output is "fudged" which means // any remaining type variables are assigned to new, unrelated variables. This // is because the inference guidance here is only speculative. + // FIXME(splat): do we need to splat arguments before this type inference? let formal_output = self.resolve_vars_with_obligations(formal_output); - let expected_input_tys: Option> = expectation + let mut expected_input_tys: Option> = expectation .only_has_type(self) .and_then(|expected_output| { // FIXME(#149379): This operation results in expected input @@ -272,45 +287,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err_code = E0061; - // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]); - match tuple_type.kind() { - // We expected a tuple and got a tuple - ty::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - err_code = E0057; - } - let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.get(0) { - Some(ty) => match ty.kind() { - ty::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // Otherwise, there's a mismatch, so clear out what we're expecting, and set - // our input types to err_args so we don't blow up the error messages - let guar = struct_span_code_err!( - self.dcx(), - call_span, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); - (self.err_args(provided_args.len(), guar), None) - } + let mut formal_input_tys = formal_input_tys.to_vec(); + + // If the arguments should be wrapped in a tuple (ex: closures, splats), unwrap them here + if tuple_arguments.is_tupled() { + // Caller arguments are tupled before typechecking, starting at the given index. + // Tupling makes the callee and caller argument counts match. + let outcome = self.check_tupled_arguments( + call_span, + call_expr, + formal_input_tys, + provided_args, + expected_input_tys, + c_variadic, + tuple_arguments, + fn_def_id, + callee_generic_args, + ); + let TupledArgCheckOutcome { + new_err_code, + untupled_formal_input_tys, + untupled_expected_input_tys, + } = outcome; + if let Some(new_err_code) = new_err_code { + err_code = new_err_code; } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; + formal_input_tys = untupled_formal_input_tys; + expected_input_tys = untupled_expected_input_tys; + } // If there are no external expectations at the call site, just use the types from the function defn let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { @@ -556,6 +560,257 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Check arguments that are tupled by "rust-call" or `#[splat]`. + fn check_tupled_arguments( + &self, + // Span enclosing the call site + call_span: Span, + // Expression of the call site + call_expr: &'tcx hir::Expr<'tcx>, + // Types (as defined in the *signature* of the target function) + mut formal_input_tys: Vec>, + // The expressions for each provided argument + provided_args: &'tcx [hir::Expr<'tcx>], + // The expected input types from the context of the call site + mut expected_input_tys: Option>>, + // Whether the function is variadic (e.g. from C) + c_variadic: bool, + // Whether all the arguments have been bundled in a tuple (ex: closures). + // Splatting is handled separately. + tuple_arguments: TupleArgumentsFlag, + // The DefId for the function being called, for better error messages + fn_def_id: Option, + // The generics of the function being called. Only used for splatting + callee_generic_args: Option>, + ) -> TupledArgCheckOutcome<'tcx> { + let (first_tupled_arg_index, is_self_splatted) = tuple_arguments.tupled_arg_index(); + let Some(first_tupled_arg_index) = first_tupled_arg_index else { + // If we're not tupling any of the current arguments, we're done. + return TupledArgCheckOutcome { + new_err_code: None, + untupled_formal_input_tys: formal_input_tys, + untupled_expected_input_tys: expected_input_tys, + }; + }; + + // The argument difference can range from -1 to u16::MAX - 1, so we count the number + // of tupled arguments instead. + // (An empty argument list becomes a unit tuple in the callee.) + // 0: f() -> f(#[splat] _: ()) + // 1: f(a) -> f(#[splat] _: (A,)) + // 2: f(a, b) -> f(#[splat] _: (A, B)) + // The Fn* traits ensure this by construction, and `#[splat]` can only be applied to + // an actual argument. + let tupled_args_count = (1 + provided_args.len()).checked_sub(formal_input_tys.len()); + debug!( + ?first_tupled_arg_index, ?is_self_splatted, + ?tupled_args_count, ?tuple_arguments, ?c_variadic, + provided_args_len = ?provided_args.len(), formal_input_tys_len = ?formal_input_tys.len() + ); + + // If earlier code has modified the FnSig argument list without adjusting the splatted + // argument, indexing into the formal input types will panic. + if first_tupled_arg_index >= formal_input_tys.len() { + span_bug!( + call_span, + "splatted argument index is out of bounds: {first_tupled_arg_index:?} >= {}, \ + is_self_splatted = {is_self_splatted:?}, \ + tupled_args_count = {tupled_args_count:?}, {tuple_arguments:?}, \ + c_variadic = {c_variadic:?}, provided_args: {}", + formal_input_tys.len(), + provided_args.len(), + ); + } + + // Keep the type variable if the argument is splatted, so we can force it to be a tuple later. + let tuple_type = if tuple_arguments.is_splatted() { + let callee_tuple_type = + self.resolve_vars_with_obligations(formal_input_tys[first_tupled_arg_index]); + if callee_tuple_type.is_ty_var() + && let Some(tupled_args_count) = tupled_args_count + { + // Make the original type variable resolve to a tuple containing new type variables + let ocx = ObligationCtxt::new(self); + let origin = self.misc(call_span); + + let new_tupled_type = Ty::new_tup_from_iter( + self.tcx, + iter::repeat_with(|| self.next_ty_var(call_span)).take(tupled_args_count), + ); + + // FIXME(splat): should this be a sub/super type relationship? + let ocx_error = ocx.eq(&origin, self.param_env, callee_tuple_type, new_tupled_type); + if let Err(ocx_error) = ocx_error { + // FIXME(splat): add a test for this error and the one below, if they are reachable + struct_span_code_err!( + self.dcx(), + call_span, + // FIXME(splat): add a new error code before stabilization (and below as well) + E0277, + "cannot resolve splatted arguments; splatted type parameters \ + must be a tuple or unit type: {:?}", + ocx_error, + ) + .emit(); + } + + let type_errors = ocx.try_evaluate_obligations(); + if type_errors.is_empty() { + new_tupled_type + } else { + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0277, + "cannot resolve splatted arguments; splatted type parameters \ + must be a tuple or unit type: {:?}", + type_errors, + ) + .emit(); + Ty::new_error(self.tcx, guar) + } + } else { + // Otherwise, just let the argument type checker make a suggestion + callee_tuple_type + } + } else { + self.structurally_resolve_type(call_span, formal_input_tys[first_tupled_arg_index]) + }; + + // We expected a tuple and got a tuple (or made one ourselves). + // If it's not a tuple, we error out in the next block. + let mut err_code = None; + if let ty::Tuple(detup_formal_arg_tys) = tuple_type.kind() { + // Argument length differs + // FIXME(splat): update the error code E0057 docs when splat is stabilized + if Some(detup_formal_arg_tys.len()) != tupled_args_count { + err_code = Some(E0057); + } + if let Some(ref mut expected_input_tys) = expected_input_tys + && let Some(ty) = expected_input_tys.get(first_tupled_arg_index) + && let ty::Tuple(detup_expected_arg_tys) = ty.kind() + { + let substitute_tys = if Some(detup_expected_arg_tys.len()) == tupled_args_count { + detup_expected_arg_tys.iter() + } else { + // Just fall back to the formal argument types + detup_formal_arg_tys.iter() + }; + + expected_input_tys + .splice(first_tupled_arg_index..=first_tupled_arg_index, substitute_tys); + } else { + expected_input_tys = None; + } + // If splatting, record this call in a side-table, so MIR lowering can tuple the caller's arguments + if tuple_arguments.is_splatted() { + // FIXME(const_trait_impl): does not enforce constness yet + self.write_splatted_call( + call_expr.hir_id, + call_span, + fn_def_id, + callee_generic_args, + first_tupled_arg_index.try_into().unwrap(), + tupled_args_count.unwrap().try_into().unwrap(), + ); + } + + formal_input_tys.splice( + first_tupled_arg_index..=first_tupled_arg_index, + detup_formal_arg_tys.iter(), + ); + if let Some(ref expected_input_tys) = expected_input_tys { + assert_eq!( + formal_input_tys.len(), + expected_input_tys.len(), + "incorrectly constructed input type tuples, argument counts must match: \ + tuple_arguments: {tuple_arguments:?}", + ) + } + } + + // Otherwise, there's a mismatch during splatting or a rust-call. + // So clear out what we're expecting, and set our input types to err_args so we don't + // blow up the error messages. + let guar = + if tuple_arguments == TupleAllCallArgs && !matches!(tuple_type.kind(), ty::Tuple(_)) { + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0059, + "cannot use call notation; the first type parameter \ + for the function trait is neither a tuple nor unit" + ) + .emit(); + + Some(guar) + } else if tuple_arguments.is_splatted() { + // If we don't check argument counts here, and there's a subtle bug in the code above, + // later compilation stages can fail in unrelated places with confusing errors. + if !matches!(tuple_type.kind(), ty::Tuple(_)) { + let spans = if let Some(def_id) = fn_def_id + && let Some(hir_node) = self.tcx.hir_get_if_local(def_id) + && let Some(fn_decl) = hir_node.fn_decl() + && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index) + { + let arg_def_span = arg_ty.span; + vec![call_span, arg_def_span] + } else { + vec![call_span] + }; + let guar = struct_span_code_err!( + self.dcx(), + spans, + // FIXME(splat): add a new error code before stabilization + E0277, + "cannot use splat attribute; the splatted argument type \ + must be a tuple or unit, not a {:?} ({:?})", + tuple_type.kind(), + self.structurally_resolve_type( + call_span, + formal_input_tys[first_tupled_arg_index] + ) + .kind(), + ) + .emit(); + + Some(guar) + } else if formal_input_tys.len() != provided_args.len() { + // FIXME(splat): suggest alternative argument counts, if there are any + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0057, + "this splatted function takes {} arguments, but {} {} provided", + formal_input_tys.len(), + provided_args.len(), + if provided_args.len() == 1 { "was" } else { "were" }, + ) + .emit(); + + Some(guar) + } else { + None + } + } else { + None + }; + + if let Some(guar) = guar { + TupledArgCheckOutcome { + new_err_code: err_code, + untupled_formal_input_tys: self.err_args(provided_args.len(), guar), + untupled_expected_input_tys: None, + } + } else { + TupledArgCheckOutcome { + new_err_code: err_code, + untupled_formal_input_tys: formal_input_tys, + untupled_expected_input_tys: expected_input_tys, + } + } + } + /// If `unsized_fn_params` is active, check that unsized values are place expressions. Since /// the removal of `unsized_locals` in we can't /// store them in MIR locals as temporaries. @@ -581,6 +836,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_def_id: Option, call_span: Span, call_expr: &'tcx hir::Expr<'tcx>, + // FIXME(splat): when the feature design is settled, improve the errors here tuple_arguments: TupleArgumentsFlag, ) -> ErrorGuaranteed { // Next, let's construct the error @@ -1354,8 +1610,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we're calling a method of a Fn/FnMut/FnOnce trait object implicitly // (eg invoking a closure) we want to point at the underlying callable, // not the method implicitly invoked (eg call_once). - // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) - if tuple_arguments == TupleArguments + // TupleAllCallArgs is set only when this is an implicit call `my_closure(...)` rather + // than explicit `my_closure.call(...)`. + if tuple_arguments == TupleAllCallArgs && let Some(assoc_item) = self.tcx.opt_associated_item(def_id) // Since this is an associated item, it might point at either an impl or a trait item. // We want it to always point to the trait item. @@ -1396,7 +1653,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: new_def_id }, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: new_def_id }, .. }) | ty::Closure(new_def_id, _) | ty::FnDef(new_def_id, _) => { def_id = new_def_id; @@ -1442,25 +1699,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deps: SmallVec<[ExpectedIdx; 4]>, } - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // FIXME(splat): fix the generic mismatch earlier, so it doesn't reach here + if !tuple_arguments.is_splatted() { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + } // Gather all mismatched parameters with generics. let mut mismatched_params = Vec::>::new(); + let mut use_splat_fallback = false; if let Some(expected_idx) = expected_idx { let expected_idx = ExpectedIdx::from_usize(expected_idx); - let &(expected_generic, ref expected_param) = - ¶ms_with_generics[expected_idx]; - if let Some(expected_generic) = expected_generic { - mismatched_params.push(MismatchedParam { - idx: expected_idx, - generic: expected_generic, - param: expected_param, - deps: SmallVec::new(), - }); - } else { - // Still mark the mismatched parameter - spans.push_span_label(expected_param.span(), ""); - } - } else { + match params_with_generics.get(expected_idx) { + Some(&(Some(expected_generic), ref expected_param)) => mismatched_params + .push(MismatchedParam { + idx: expected_idx, + generic: expected_generic, + param: expected_param, + deps: SmallVec::new(), + }), + Some((None, expected_param)) => { + // Still mark the mismatched parameter + spans.push_span_label(expected_param.span(), ""); + } + None => { + if tuple_arguments.is_splatted() { + // FIXME(splat): when the arg is splatted, adjust its index, to handle the type mismatch properly + use_splat_fallback = true; + } else { + span_bug!( + self.tcx.def_span(def_id), + "arg index {} out of bounds for method with {} inputs", + expected_idx.as_usize(), + params_with_generics.len(), + ); + } + } + }; + } + + if expected_idx.is_none() || use_splat_fallback { mismatched_params.extend( params_with_generics.iter_enumerated().zip(matched_inputs).filter_map( |((idx, &(generic, ref param)), matched_idx)| { @@ -1671,15 +1947,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_arg_tys: &IndexVec, Span)>, formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, is_method: bool, + is_splat: bool, ) { let Some(def_id) = callable_def_id else { return; }; if let Some((params_with_generics, _)) = self.get_hir_param_info(def_id, is_method) { - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // FIXME(splat): fix the generic mismatch earlier, so it doesn't reach here + if !is_splat { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + } for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { - if matched_inputs[idx].is_none() { + if matched_inputs.get(idx).flatten_ref().is_none() { continue; } @@ -1701,7 +1981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(other_generic_param) = other_generic_param else { return false; }; - if matched_inputs[other_idx].is_some() { + if matched_inputs.get(other_idx).flatten_ref().is_some() { return false; } other_generic_param == generic_param @@ -2004,7 +2284,11 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { self.arg_matching_ctxt.args_ctxt.call_metadata.full_call_span, format!( "{call_name} takes {}{} but {} {} supplied", - if self.c_variadic { "at least " } else { "" }, + if self.arg_matching_ctxt.args_ctxt.c_variadic { + "at least " + } else { + "" + }, potentially_plural_count( self.formal_and_expected_inputs.len(), "argument" @@ -2147,14 +2431,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { format!("arguments to this {call_name} are incorrect"), ); - self.fn_ctxt.label_generic_mismatches( - &mut err, - self.fn_def_id, - &self.matched_inputs, - &self.provided_arg_tys, - &self.formal_and_expected_inputs, - self.call_metadata.is_method, - ); + self.label_generic_mismatches(&mut err); if let hir::ExprKind::MethodCall(_, rcvr, _, _) = self.arg_matching_ctxt.args_ctxt.call_ctxt.call_expr.kind @@ -2237,7 +2514,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { format!( "this {} takes {}{} but {} {} supplied", self.call_metadata.call_name, - if self.c_variadic { "at least " } else { "" }, + if self.arg_matching_ctxt.args_ctxt.c_variadic { "at least " } else { "" }, potentially_plural_count(self.formal_and_expected_inputs.len(), "argument"), potentially_plural_count(self.provided_args.len(), "argument"), pluralize!("was", self.provided_args.len()) @@ -2614,6 +2891,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { &self.provided_arg_tys, &self.formal_and_expected_inputs, self.call_metadata.is_method, + self.arg_matching_ctxt.tuple_arguments.is_splatted(), ); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 922a9d7e95bda..c4a1b56320ff1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -294,7 +294,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { // HACK(eddyb) should get the original `Span`. let span = tcx.def_span(def_id); - ty::EarlyBinder::bind(tcx.arena.alloc_from_iter( + ty::EarlyBinder::bind_iter(tcx.arena.alloc_from_iter( self.param_env.caller_bounds().iter().filter_map(|predicate| { match predicate.kind().skip_binder() { ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => { @@ -399,10 +399,13 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { match ty.kind() { ty::Adt(adt_def, _) => Some(*adt_def), // FIXME(#104767): Should we handle bound regions here? - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, - .. - }) if !ty.has_escaping_bound_vars() => { + ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, + .. + }, + ) if !ty.has_escaping_bound_vars() => { self.normalize(span, Unnormalized::new_wip(ty)).ty_adt_def() } _ => None, @@ -416,11 +419,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { // WF obligations that are registered elsewhere, but they have a // better cause code assigned to them in `add_required_obligations_for_hir`. // This means that they should shadow obligations with worse spans. - if let ty::Alias(ty::AliasTy { - kind: ty::Projection { def_id } | ty::Free { def_id }, - args, - .. - }) = ty.kind() + if let ty::Alias( + _, + ty::AliasTy { kind: ty::Projection { def_id } | ty::Free { def_id }, args, .. }, + ) = ty.kind() { self.add_required_obligations_for_hir(span, *def_id, args, hir_id); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 84480fd57e3b5..b3fec86229fae 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -294,7 +294,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; }; - let item_type = Ty::new_projection(tcx, iterator_item_id, [found]); + let item_type = Ty::new_projection(tcx, ty::IsRigid::No, iterator_item_id, [found]); let item_type = self.normalize(expr.span, rustc_middle::ty::Unnormalized::new_wip(item_type)); diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 3941f06c3e7d3..3b3f5caa85c09 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -3,6 +3,7 @@ #![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(never_type)] +#![feature(option_reference_flattening)] #![feature(trim_prefix_suffix)] // tidy-alphabetical-end @@ -50,7 +51,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, FnSigKind, Ty, TyCtxt, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -589,12 +590,10 @@ fn report_unexpected_variant_res( .emit() } -/// Controls whether the arguments are tupled. This is used for the call -/// operator. +/// Controls whether all arguments are tupled. This is used for the call operator only. /// -/// Tupling means that all call-side arguments are packed into a tuple and -/// passed as a single parameter. For example, if tupling is enabled, this -/// function: +/// Tupling means that all call-side arguments are packed into a tuple and passed as a single +/// parameter. For example, if tupling is enabled, this function: /// ``` /// fn f(x: (isize, isize)) {} /// ``` @@ -608,10 +607,72 @@ fn report_unexpected_variant_res( /// # fn f(x: (isize, isize)) {} /// f((1, 2)); /// ``` -#[derive(Copy, Clone, Eq, PartialEq)] +/// +/// Note: splatted arguments are handled separately. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] enum TupleArgumentsFlag { + /// Arguments are typechecked unchanged. DontTupleArguments, - TupleArguments, + /// This is a call operator: all caller arguments are tupled before typechecking. + /// Set based on the "rust-call" ABI and Fn* traits. + TupleAllCallArgs, + /// The `self` method argument is splatted, so `Self` should be tupled before typechecking. + TupleSplattedSelfArg, + /// A non-self argument is splatted, so that argument should be tupled before typechecking. + TupleSplattedArg(u8), +} + +impl TupleArgumentsFlag { + /// Returns the TupleArgumentsFlag for a known RustCall function. + fn rust_fn_trait_call() -> Self { + Self::TupleAllCallArgs + } + + /// Returns the appropriate TupleArgumentsFlag for the given FnSigKind and method flag. + fn with_fn_sig_kind<'tcx>(fn_sig_kind: FnSigKind<'tcx>, is_method: bool) -> Self { + if let Some(splatted_arg_index) = fn_sig_kind.splatted() { + if is_method { + if let Some(splatted_arg_index) = splatted_arg_index.checked_sub(1) { + return Self::TupleSplattedArg(splatted_arg_index); + } else { + // In `check_argument_types`, this is effectively `TupleSplattedArg(-1)` + return Self::TupleSplattedSelfArg; + } + } + + return Self::TupleSplattedArg(splatted_arg_index); + } + + Self::DontTupleArguments + } + + /// Returns true if the arguments are tupled through "rust-call" or splatting. + fn is_tupled(self) -> bool { + match self { + Self::DontTupleArguments => false, + Self::TupleAllCallArgs | Self::TupleSplattedSelfArg | Self::TupleSplattedArg(_) => true, + } + } + + /// Returns true if the arguments are tupled through splatting. + /// (But false if they are "rust-call" or not tupled.) + fn is_splatted(self) -> bool { + match self { + Self::TupleSplattedSelfArg | Self::TupleSplattedArg(_) => true, + Self::DontTupleArguments | Self::TupleAllCallArgs => false, + } + } + + /// Returns the tupled argument index, and whether the `self` argument is splatted. + /// Returns `None` if the arguments are not tupled, or if the `self` argument is splatted. + fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { + match self { + Self::TupleSplattedArg(index) => (Some(usize::from(index)), false), + Self::TupleAllCallArgs => (Some(0), false), + Self::TupleSplattedSelfArg => (None, true), + Self::DontTupleArguments => (None, false), + } + } } fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs index 21aad64f58d38..ebd8842bd753f 100644 --- a/compiler/rustc_hir_typeck/src/loops.rs +++ b/compiler/rustc_hir_typeck/src/loops.rs @@ -84,6 +84,11 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir CheckLoopVisitor { tcx, cx_stack: vec![Normal], block_breaks: Default::default() }; let cx = match tcx.def_kind(def_id) { DefKind::AnonConst => AnonConst, + DefKind::InlineConst => { + // only type system inline consts are typeck roots + debug_assert!(tcx.is_type_system_inline_const(def_id)); + ConstBlock + } _ => Fn, }; check.with_context(cx, |v| v.visit_body(body)); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4258896deec70..ce22ab937391e 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -810,9 +810,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) { let is_accessible = if let Some(name) = self.method_name { let item = candidate.item; - let hir_id = self.tcx.local_def_id_to_hir_id(self.body_id); - let def_scope = - self.tcx.adjust_ident_and_get_scope(name, item.container_id(self.tcx), hir_id).1; + let container_id = item.container_id(self.tcx); + let def_scope = self.tcx.adjust_ident_and_get_scope(name, container_id, self.body_id).1; item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx) } else { true @@ -2050,7 +2049,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // HACK: opaque types will match anything for which their bounds hold. // Thus we need to prevent them from trying to match the `&_` autoref // candidates that get created for `&self` trait methods. - &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) + &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) if !self.next_trait_solver() && self.infcx.can_define_opaque_ty(def_id) && !xform_self_ty.is_ty_var() => @@ -2359,6 +2358,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// multiple conflicting picks if there is one pick whose trait container is a subtrait /// of the trait containers of all of the other picks. /// + /// This is the method-probe analogue of + /// `rustc_hir_analysis::hir_ty_lowering::HirTyLowerer::collapse_candidates_to_subtrait_pick`; + /// keep both implementations in sync. + /// /// This implements RFC #3624. fn collapse_candidates_to_subtrait_pick( &self, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 54779a90cc2bb..e7116b7492584 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -178,7 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Slice(..) | ty::Adt(..) - | ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { + | ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { for unsatisfied in unsatisfied_predicates.iter() { if is_iterator_predicate(unsatisfied.0) { return true; @@ -3759,10 +3759,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Adt(_, _) | ty::Str - | ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, - .. - }) + | ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. }, .. + }, + ) | ty::Param(_) => format!("{deref_ty}"), // we need to test something like <&[_]>::len or <(&[u32])>::len // and Vec::function(); diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index ad35dc3f7a04b..fd52c7f4b4d26 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -313,6 +313,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method); + method.sig.output() } // error types are considered "builtin" diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 9e0ca938a438d..ad9d0df78b333 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2745,6 +2745,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The expected type for the deref pat's inner pattern is `::Target`. let target_ty = Ty::new_projection( tcx, + ty::IsRigid::No, tcx.require_lang_item(hir::LangItem::DerefTarget, span), [source_ty], ); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index ba5b55b43049f..3d50befe042f1 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -348,7 +348,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) { intravisit::walk_ty(self, hir_ty); // If there are type checking errors, Type privacy pass will stop, - // so we may not get the type from hid_id, see #104513 + // so we may not get the type from hir_id, see #104513 if let Some(ty) = self.fcx.node_ty_opt(hir_ty.hir_id) { let ty = self.resolve(ty, &hir_ty.span); self.write_ty_to_typeck_results(hir_ty.hir_id, ty); @@ -567,7 +567,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { for (opaque_type_key, hidden_type) in opaque_types { let hidden_type = self.resolve(hidden_type, &hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span); - if let &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = + if let &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = hidden_type.ty.kind() && def_id == opaque_type_key.def_id.to_def_id() && args == opaque_type_key.args @@ -663,6 +663,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().insert(hir_id, def); } + // Export splatted function call resolutions. + if let Some(def) = self.fcx.typeck_results.borrow_mut().splatted_defs_mut().remove(hir_id) { + self.typeck_results.splatted_defs_mut().insert(hir_id, def); + } + // Resolve any borrowings for the node with id `node_id` self.visit_adjustments(span, hir_id); @@ -1055,7 +1060,7 @@ impl<'tcx> TypeVisitor> for HasRecursiveOpaque<'_, 'tcx> { type Result = ControlFlow<()>; fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *t.kind() + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *t.kind() && let Some(def_id) = def_id.as_local() { if self.def_id == def_id { diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 0f5d9c5389667..83646cb086d8d 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -10,8 +10,8 @@ mod diagnostics; mod persist; pub use persist::{ - copy_cgu_workproduct_to_incr_comp_cache_dir, finalize_session_directory, in_incr_comp_dir, - in_incr_comp_dir_sess, load_query_result_cache, save_work_product_index, setup_dep_graph, + copy_cgu_workproduct_to_incr_comp_cache_dir, finalize_session_directory, in_incr_comp_dir_sess, + load_query_result_cache, save_work_product_index, setup_dep_graph, }; use rustc_middle::util::Providers; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 80134d5ff2202..562481e51188b 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -184,15 +184,7 @@ fn lock_file_path(session_dir: &Path) -> PathBuf { /// Returns the path for a given filename within the incremental compilation directory /// in the current session. pub fn in_incr_comp_dir_sess(sess: &Session, file_name: &str) -> PathBuf { - in_incr_comp_dir(&sess.incr_comp_session_dir(), file_name) -} - -/// Returns the path for a given filename within the incremental compilation directory, -/// not necessarily from the current session. -/// -/// To ensure the file is part of the current session, use [`in_incr_comp_dir_sess`]. -pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBuf { - incr_comp_session_dir.join(file_name) + sess.incr_comp_session_dir().join(file_name) } /// Allocates the private session directory. diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index c25c5f1ea47fb..7d486cc394b80 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -10,7 +10,7 @@ mod load; mod save; mod work_product; -pub use fs::{finalize_session_directory, in_incr_comp_dir, in_incr_comp_dir_sess}; +pub use fs::{finalize_session_directory, in_incr_comp_dir_sess}; pub use load::{load_query_result_cache, setup_dep_graph}; pub(crate) use save::save_dep_graph; pub use save::save_work_product_index; diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 5641523c304c9..733cf757a17cb 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -519,7 +519,7 @@ impl<'tcx> InferCtxt<'tcx> { self.at(cause, param_env) .eq( DefineOpaqueTypes::Yes, - Ty::new_opaque(self.tcx, a.def_id.to_def_id(), a.args), + Ty::new_opaque(self.tcx, ty::IsRigid::No, a.def_id.to_def_id(), a.args), b, )? .obligations, diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index c058bebddcf03..f0fd46e6cac74 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -158,7 +158,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Value(_) - | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Alias(..) | ty::ConstKind::Expr(..) | ty::ConstKind::Error(_) => ct.super_fold_with(self), } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 0b5add10200de..aa2fe2ee21dfa 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -960,10 +960,20 @@ impl<'tcx> InferCtxt<'tcx> { ty::Region::new_var(self.tcx, region_var) } - pub fn next_term_var_of_kind(&self, term: ty::Term<'tcx>, span: Span) -> ty::Term<'tcx> { - match term.kind() { - ty::TermKind::Ty(_) => self.next_ty_var(span).into(), - ty::TermKind::Const(_) => self.next_const_var(span).into(), + pub fn next_term_var_of_alias_kind( + &self, + alias_term: ty::AliasTerm<'tcx>, + span: Span, + ) -> ty::Term<'tcx> { + match alias_term.kind { + ty::AliasTermKind::ProjectionTy { .. } + | ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::OpaqueTy { .. } + | ty::AliasTermKind::FreeTy { .. } => self.next_ty_var(span).into(), + ty::AliasTermKind::FreeConst { .. } + | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::AnonConst { .. } + | ty::AliasTermKind::ProjectionConst { .. } => self.next_const_var(span).into(), } } @@ -1252,10 +1262,11 @@ impl<'tcx> InferCtxt<'tcx> { .unwrap_or(ct), InferConst::Fresh(_) => ct, }, + ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Alias(_, _) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => ct, diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index afb4c3dcecaaa..f381146726734 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -45,7 +45,7 @@ impl<'tcx> InferCtxt<'tcx> { lt_op: |lt| lt, ct_op: |ct| ct, ty_op: |ty| match *ty.kind() { - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) if self.can_define_opaque_ty(def_id) && !ty.has_escaping_bound_vars() => { let def_span = self.tcx.def_span(def_id); @@ -85,7 +85,7 @@ impl<'tcx> InferCtxt<'tcx> { ) -> Result>>, TypeError<'tcx>> { debug_assert!(!self.next_trait_solver()); let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) if def_id.is_local() => { let def_id = def_id.expect_local(); @@ -136,7 +136,7 @@ impl<'tcx> InferCtxt<'tcx> { return None; } - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }) = + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }) = *b.kind() { // We could accept this, but there are various ways to handle this situation, @@ -324,6 +324,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(RPITIT): Don't replace RPITITs with inference vars. // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. ty::Alias( + _, projection_ty @ ty::AliasTy { kind: ty::Projection { def_id }, .. }, ) if !projection_ty.has_escaping_bound_vars() && !tcx.is_impl_trait_in_trait(def_id) @@ -342,11 +343,10 @@ impl<'tcx> InferCtxt<'tcx> { } // Replace all other mentions of the same opaque type with the hidden type, // as the bounds must hold on the hidden type after all. - ty::Alias(ty::AliasTy { - kind: ty::Opaque { def_id: def_id2 }, - args: args2, - .. - }) if def_id == def_id2 && args == args2 => hidden_ty, + ty::Alias( + _, + ty::AliasTy { kind: ty::Opaque { def_id: def_id2 }, args: args2, .. }, + ) if def_id == def_id2 && args == args2 => hidden_ty, _ => ty, }, lt_op: |lt| lt, diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index bd07bb7f91a27..15756f03f25f9 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -56,7 +56,7 @@ where // either `'static` or a unique outlives region, and if one is // found, we just need to prove that that region is still live. // If one is not found, then we continue to walk through the alias. - ty::Alias(ty::AliasTy { kind, args, .. }) => { + ty::Alias(_, alias_ty @ ty::AliasTy { kind, args, .. }) => { let tcx = self.tcx; let param_env = self.param_env; let def_id = match kind { @@ -82,7 +82,11 @@ where &outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| { VerifyIfEq { ty, bound } }), - ty, + // FIXME(#155345): Region handling should generally only + // deal with rigid aliases, making sure we do so correctly + // everywhere is effort, so we're just using `No` everywhere + // for now. This should change soon. + alias_ty.to_ty(tcx, ty::IsRigid::No), ) } }) diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 479daf67a8ba3..6166560a671a0 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -44,7 +44,13 @@ pub fn extract_verify_if_eq<'tcx>( assert!(!verify_if_eq_b.has_escaping_bound_vars()); let mut m = MatchAgainstHigherRankedOutlives::new(tcx); let verify_if_eq = verify_if_eq_b.skip_binder(); - m.relate(verify_if_eq.ty, test_ty).ok()?; + // FIXME(#155345): Region handling should generally only + // deal with rigid aliases, making sure we do so correctly + // everywhere is effort, so we're just using `No` everywhere + // for now. This should change soon. + let (verify_ty, test_ty) = + ty::set_aliases_to_non_rigid(tcx, (verify_if_eq.ty, test_ty)).skip_norm_wip(); + m.relate(verify_ty, test_ty).ok()?; if let ty::RegionKind::ReBound(index_kind, br) = verify_if_eq.bound.kind() { assert!(matches!(index_kind, ty::BoundVarIndexKind::Bound(ty::INNERMOST))); @@ -80,6 +86,11 @@ pub(super) fn can_match_erased_ty<'tcx>( assert!(!outlives_predicate.has_escaping_bound_vars()); let erased_outlives_predicate = tcx.erase_and_anonymize_regions(outlives_predicate); let outlives_ty = erased_outlives_predicate.skip_binder().0; + // FIXME(#155345): Region handling should generally only + // deal with rigid aliases, making sure we do so correctly + // everywhere is effort, so we're just using `No` everywhere + // for now. This should change soon. + let outlives_ty = ty::set_aliases_to_non_rigid(tcx, outlives_ty).skip_normalization(); if outlives_ty == erased_ty { // pointless micro-optimization true diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 2c51128933c41..9a9801aa3f3f7 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -96,7 +96,15 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { &self, alias_ty: ty::AliasTy<'tcx>, ) -> Vec> { - let erased_alias_ty = self.tcx.erase_and_anonymize_regions(alias_ty.to_ty(self.tcx)); + // FIXME(#155345): Region handling should generally only + // deal with rigid aliases, making sure we do so correctly + // everywhere is effort, so we're just using `No` everywhere + // for now. This should change soon. + let erased_alias_ty = self.tcx.erase_and_anonymize_regions( + ty::set_aliases_to_non_rigid(self.tcx, alias_ty) + .skip_norm_wip() + .to_ty(self.tcx, ty::IsRigid::No), + ); self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty) } @@ -104,8 +112,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { pub(crate) fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> { // Search the env for where clauses like `P: 'a`. let env_bounds = self.approx_declared_bounds_from_env(alias_ty).into_iter().map(|binder| { + // FIXME(#155345): We probably want to assert the alias is rigid here. if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() - && let ty::Alias(alias_ty_from_bound) = *ty.kind() + && let ty::Alias(_, alias_ty_from_bound) = *ty.kind() && alias_ty_from_bound == alias_ty { // Micro-optimize if this is an exact match (this @@ -236,12 +245,19 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // And therefore we can safely use structural equality for alias types. (GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {} (GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {} - (GenericKind::Alias(a1), ty::Alias(a2)) if a1.kind == a2.kind => {} + // FIXME(#155345): We probably want to assert that the rhs is rigid. + (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.kind == a2.kind => {} _ => return None, } let p_ty = p.to_ty(tcx); - let erased_p_ty = self.tcx.erase_and_anonymize_regions(p_ty); + // FIXME(#155345): Region handling should generally only + // deal with rigid aliases, making sure we do so correctly + // everywhere is effort, so we're just using `No` everywhere + // for now. This should change soon. + let erased_p_ty = self.tcx.erase_and_anonymize_regions( + ty::set_aliases_to_non_rigid(self.tcx, p_ty).skip_norm_wip(), + ); (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) })); diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 03bcb5215ee1a..da6fb0e64f727 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -805,7 +805,11 @@ impl<'tcx> GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => p.to_ty(tcx), GenericKind::Placeholder(ref p) => Ty::new_placeholder(tcx, *p), - GenericKind::Alias(ref p) => p.to_ty(tcx), + // FIXME(#155345): Region handling should generally only + // deal with rigid aliases, making sure we do so correctly + // everywhere is effort, so we're just using `No` everywhere + // for now. This should change soon. + GenericKind::Alias(ref p) => p.to_ty(tcx, ty::IsRigid::No), } } } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 8c296289fef0c..7881b85997e8c 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -77,7 +77,7 @@ impl<'tcx> InferCtxt<'tcx> { /// would result in an infinite type as we continuously replace an inference variable /// in `ct` with `ct` itself. /// - /// This is especially important as unevaluated consts use their parents generics. + /// This is especially important as alias consts use their parents generics. /// They therefore often contain unused args, making these errors far more likely. /// /// A good example of this is the following: @@ -95,7 +95,7 @@ impl<'tcx> InferCtxt<'tcx> { /// } /// ``` /// - /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// Here `3 + 4` ends up as `ConstKind::Alias` which uses the generics /// of `fn bind` (meaning that its args contain `N`). /// /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. @@ -112,8 +112,8 @@ impl<'tcx> InferCtxt<'tcx> { target_vid: ty::ConstVid, source_ct: ty::Const<'tcx>, ) -> RelateResult<'tcx, ()> { - // FIXME(generic_const_exprs): Occurs check failures for unevaluated - // constants and generic expressions are not yet handled correctly. + // FIXME(generic_const_exprs): Occurs check failures for alias consts + // and generic expressions are not yet handled correctly. debug_assert!( self.inner.borrow_mut().const_unification_table().probe_value(target_vid).is_unknown() ); @@ -433,13 +433,16 @@ impl<'tcx> Generalizer<'_, 'tcx> { } } + /// We only handle potentially normalizable aliases via this method. For rigid alias, + /// we always generalize structurally. + /// /// An occurs check failure inside of an alias does not mean /// that the types definitely don't unify. We may be able /// to normalize the alias after all. /// - /// We handle this by lazily equating the alias and generalizing - /// it to an inference variable. In the new solver, we always - /// generalize to an infer var unless the alias contains escaping + /// We handle this by lazily equating the normalizable alias + /// and generalizing it to an inference variable. In the new solver, + /// we always generalize to an infer var unless the alias contains escaping /// bound variables. /// /// Correctly handling aliases with escaping bound variables is @@ -467,7 +470,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { let is_nested_alias = mem::replace(&mut self.in_alias, true); let result = match self.relate(alias, alias) { - Ok(alias) => Ok(alias.to_term(self.cx())), + Ok(alias) => Ok(alias.to_term(self.cx(), ty::IsRigid::No)), Err(e) => { if is_nested_alias { return Err(e); @@ -636,7 +639,9 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { } } - ty::Alias(data) => match self.structurally_relate_aliases { + // We only need to be careful with potentially normalizeable + // aliases here. See `generalize_alias_term` for more information. + ty::Alias(ty::IsRigid::No, data) => match self.structurally_relate_aliases { StructurallyRelateAliases::No => { self.generalize_alias_term(data.into()).map(|v| v.expect_type()) } @@ -747,29 +752,38 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { } } } - // FIXME: Unevaluated constants are also not rigid, so the current + // FIXME: Alias consts are also not rigid, so the current // approach of always relating them structurally is incomplete. // // FIXME: replace the StructurallyRelateAliases::Yes branch with // `structurally_relate_consts` once it is fully structural. - ty::ConstKind::Unevaluated(uv) => match self.structurally_relate_aliases { - // Hack: Fall back to old behavior if GCE is enabled (it used to just be the Yes - // path), as doing this new No path breaks some GCE things. I expect GCE to be - // ripped out soon so this shouldn't matter soon. - StructurallyRelateAliases::No if !tcx.features().generic_const_exprs() => { - self.generalize_alias_term(uv.into()).map(|v| v.expect_const()) - } - _ => { - let ty::UnevaluatedConst { kind, args, .. } = uv; - let args = self.relate_with_variance( - ty::Invariant, - ty::VarianceDiagInfo::default(), - args, - args, - )?; - Ok(ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(tcx, kind, args))) + // + // We only need to be careful with potentially normalizeable + // aliases here. See `generalize_alias_term` for more information. + ty::ConstKind::Alias(ty::IsRigid::No, alias_const) => { + match self.structurally_relate_aliases { + // Hack: Fall back to old behavior if GCE is enabled (it used to just be the Yes + // path), as doing this new No path breaks some GCE things. I expect GCE to be + // ripped out soon so this shouldn't matter soon. + StructurallyRelateAliases::No if !tcx.features().generic_const_exprs() => { + self.generalize_alias_term(alias_const.into()).map(|v| v.expect_const()) + } + _ => { + let ty::AliasConst { kind, args, .. } = alias_const; + let args = self.relate_with_variance( + ty::Invariant, + ty::VarianceDiagInfo::default(), + args, + args, + )?; + Ok(ty::Const::new_alias( + tcx, + ty::IsRigid::No, + ty::AliasConst::new(tcx, kind, args), + )) + } } - }, + } ty::ConstKind::Placeholder(placeholder) => { if self.for_universe.can_name(placeholder.universe) { Ok(c) diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index bb09b50fd8ae2..2c14a7d44e8c6 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -161,12 +161,12 @@ impl<'tcx> TypeRelation> for LatticeOp<'_, 'tcx> { } ( - &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: a_def_id }, .. }), - &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }), + &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: a_def_id }, .. }), + &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }), ) if a_def_id == b_def_id => super_combine_tys(infcx, self, a, b), - (&ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }), _) - | (_, &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. })) + (&ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }), _) + | (_, &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. })) if def_id.is_local() && !infcx.next_trait_solver() => { self.register_goals(infcx.handle_opaque_type( diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 595392fcfb524..d5e98f262db39 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -2,7 +2,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::combine::{combine_ty_args, super_combine_consts, super_combine_tys}; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation, relate_args_invariantly}; -use rustc_middle::ty::{self, DelayedSet, Ty, TyCtxt, TyVar}; +use rustc_middle::ty::{self, DelayedSet, Ty, TyCtxt, TyVar, TypeVisitableExt}; use rustc_span::Span; use tracing::{debug, instrument}; @@ -118,6 +118,10 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, 'tcx> { #[instrument(skip(self), level = "trace")] fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + // We don't use the rigid marker in the old solver. + debug_assert!(!a.has_rigid_aliases()); + debug_assert!(!b.has_rigid_aliases()); + if a == b { return Ok(a); } @@ -184,14 +188,14 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, 'tcx> { } ( - &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: a_def_id }, .. }), - &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }), + &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: a_def_id }, .. }), + &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }), ) if a_def_id == b_def_id => { super_combine_tys(infcx, self, a, b)?; } - (&ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }), _) - | (_, &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. })) + (&ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }), _) + | (_, &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. })) if self.define_opaque_types == DefineOpaqueTypes::Yes && def_id.is_local() => { self.register_goals(infcx.handle_opaque_type( @@ -261,6 +265,10 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, 'tcx> { a: ty::Const<'tcx>, b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { + // We don't use the rigid marker in the old solver. + debug_assert!(!a.has_rigid_aliases()); + debug_assert!(!b.has_rigid_aliases()); + super_combine_consts(self.infcx, self, a, b) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 38ab1ef8dedf7..ca6e48cb67fe5 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -101,7 +101,6 @@ fn pre_expansion_lint<'a>( lint_store, registered_tools, None, - rustc_lint::BuiltinCombinedPreExpansionLintPass::new(), check_node, ); }, @@ -479,7 +478,6 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { lint_store, tcx.registered_tools(()), Some(lint_buffer), - rustc_lint::BuiltinCombinedEarlyLintPass::new(), EarlyCheckNode::CrateRoot(&*krate, &*krate.attrs), ) } @@ -805,8 +803,8 @@ fn resolver_for_lowering_raw<'tcx>( ); let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver); - // Make sure we don't mutate the cstore from here on. - tcx.untracked().cstore.freeze(); + // Don't mutate the cstore or stable crate id map from here on. + tcx.untracked().freeze_cstore(); let ResolverOutputs { global_ctxt: untracked_resolutions, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index ce21a79a02dfd..a3264d3cc3311 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -13,11 +13,11 @@ use rustc_session::config::{ AnnotateMoves, AutoDiff, BranchProtection, CFGuard, Cfg, CodegenRetagOptions, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, FmtDebug, FunctionReturn, IncrementalStateAssertion, InliningThreshold, Input, - InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, - MirIncludeSpans, NextSolverConfig, Offload, Options, OutFileName, OutputType, OutputTypes, - PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, - SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, build_configuration, - build_session_options, rustc_optgroups, + InstrumentCoverage, InstrumentMcount, InstrumentXRay, LinkSelfContained, LinkerPluginLto, + LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, OutFileName, + OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius, + ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + build_configuration, build_session_options, rustc_optgroups, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -834,7 +834,7 @@ fn test_unstable_options_tracking_hash() { tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); - tracked!(instrument_mcount, true); + tracked!(instrument_mcount, InstrumentMcount::Mcount); tracked!(instrument_xray, Some(InstrumentXRay::default())); tracked!(link_directives, false); tracked!(link_only, true); @@ -866,7 +866,7 @@ fn test_unstable_options_tracking_hash() { tracked!(panic_in_drop, PanicStrategy::Abort); tracked!( patchable_function_entry, - PatchableFunctionEntry::from_total_and_prefix_nops(10, 5) + PatchableFunctionEntry::from_parts(10, 5, None) .expect("total must be greater than or equal to prefix") ); tracked!(plt, Some(true)); diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 758d2762a6af4..176890bab85f2 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -22,6 +22,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_lint/src/c_void_returns.rs b/compiler/rustc_lint/src/c_void_returns.rs new file mode 100644 index 0000000000000..f4dd260dd5ae4 --- /dev/null +++ b/compiler/rustc_lint/src/c_void_returns.rs @@ -0,0 +1,89 @@ +use rustc_abi::ExternAbi; +use rustc_hir::def::Res; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{self as hir, LangItem}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::Span; + +use crate::lints::{CVoidReturn, ExternCVoidReturn}; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `c_void_returns` lint detects the use of [`core::ffi::c_void`] as a return type. + /// + /// ### Example + /// + /// ```rust + /// use std::ffi::c_void; + /// + /// unsafe extern "C" { + /// fn foo() -> c_void; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `c_void` is designed for use through a [`pointer`], equivalent to C's `void*` type. It is a + /// mistake to use it directly as a return type, and calling `extern` functions declared as such + /// may result in undefined behavior. C functions that return `void` must be declared to return + /// [`()`] in Rust (omitting the return type implicitly returns `()`). + /// + /// [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html + /// [`pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + pub C_VOID_RETURNS, + Warn, + "detects use of `c_void` as a return type" +} + +declare_lint_pass!(CVoidReturns => [C_VOID_RETURNS]); + +impl<'tcx> LateLintPass<'tcx> for CVoidReturns { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + _: &'tcx hir::Body<'tcx>, + _: Span, + _: LocalDefId, + ) { + check_decl( + cx, + decl, + !matches!(fn_kind, FnKind::ItemFn(.., hir::FnHeader { abi: ExternAbi::Rust, .. })), + ); + } + + fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'tcx>) { + if let hir::ForeignItemKind::Fn(sig, ..) = item.kind { + check_decl(cx, sig.decl, true); + } + } + + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { + if let hir::TyKind::FnPtr(fn_ptr_ty) = ty.kind { + check_decl(cx, fn_ptr_ty.decl, fn_ptr_ty.abi != ExternAbi::Rust); + } + } +} + +fn check_decl(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is_extern: bool) { + if let hir::FnRetTy::Return(output_ty) = decl.output + && let hir::TyKind::Path(qpath) = output_ty.kind + && let Res::Def(.., def_id) = cx.qpath_res(&qpath, output_ty.hir_id) + && cx.tcx.is_lang_item(def_id, LangItem::CVoid) + { + let suggestion = + cx.sess().source_map().span_extend_to_prev_char(decl.output.span(), ')', true); + + if is_extern { + cx.emit_span_lint(C_VOID_RETURNS, decl.output.span(), ExternCVoidReturn { suggestion }); + } else { + cx.emit_span_lint(C_VOID_RETURNS, decl.output.span(), CVoidReturn { suggestion }); + } + } +} diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 5599e3cdafd63..e992f1ed385e7 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -40,26 +40,45 @@ use self::TargetLint::*; use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; -type EarlyLintPassFactory = Box EarlyLintPassObject + sync::DynSend + sync::DynSync>; +pub(crate) type EarlyLintPassFactory = + Box EarlyLintPassObject + sync::DynSend + sync::DynSync>; type LateLintPassFactory = Box Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync>; /// Information about the registered lints. +// +// About the pass factories: these should only be called once, but since we +// want to avoid locks or interior mutability, we don't enforce this. Lints +// should, in theory, be compatible with being constructed more than once, +// though not necessarily in a sane manner. This is safe though. pub struct LintStore { /// Registered lints. lints: Vec<&'static Lint>, - /// Constructor functions for each variety of lint pass. + /// This lint pass kind is softly deprecated. It misses expanded code and has caused a few + /// errors in the past. Currently, it is only used in Clippy. New implementations + /// should avoid using this interface, as it might be removed in the future. + /// + /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838) + /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) + pub(crate) pre_expansion_lint_passes: Vec, + + /// These lint passes run on AST nodes. + pub(crate) early_lint_passes: Vec, + + /// These lint passes run on HIR nodes. Each one processes an entire crate. They don't benefit + /// from incremental compilation. `late_lint_mod_passes` should be used in preference where + /// possible; only use `late_lint_passes` for lints that implement `check_crate` and/or + /// `check_crate_post` and accumulate cross-module state. /// - /// These should only be called once, but since we want to avoid locks or - /// interior mutability, we don't enforce this (and lints should, in theory, - /// be compatible with being constructed more than once, though not - /// necessarily in a sane manner. This is safe though.) - pub pre_expansion_passes: Vec, - pub early_passes: Vec, - pub late_passes: Vec, - /// This is unique in that we construct them per-module, so not once. - pub late_module_passes: Vec, + /// The exception is Clippy, which uses `late_lint_passes` for all late lint passes. It needs + /// `check_crate`/`check_crate_post` for some of its lints and uses late lint passes throughout + /// for consistency. This is ok because Clippy isn't wired for incremental compilation. + pub(crate) late_lint_passes: Vec, + + /// These lint passes run on HIR nodes, and are constructed per-module (i.e. multiple times). + /// They benefit from incremental compilation. + pub(crate) late_lint_mod_passes: Vec, /// Lints indexed by name. by_name: UnordMap, @@ -135,10 +154,10 @@ impl LintStore { pub fn new() -> LintStore { LintStore { lints: vec![], - pre_expansion_passes: vec![], - early_passes: vec![], - late_passes: vec![], - late_module_passes: vec![], + pre_expansion_lint_passes: vec![], + early_lint_passes: vec![], + late_lint_passes: vec![], + late_lint_mod_passes: vec![], by_name: Default::default(), lint_groups: Default::default(), } @@ -165,26 +184,24 @@ impl LintStore { self.lint_groups.keys().copied() } - pub fn register_early_pass(&mut self, pass: EarlyLintPassFactory) { - self.early_passes.push(pass); + /// See the comment on `LintStore::pre_expansion_lint_passes`. + pub fn register_pre_expansion_lint_pass(&mut self, pass: EarlyLintPassFactory) { + self.pre_expansion_lint_passes.push(pass); } - /// This lint pass is softly deprecated. It misses expanded code and has caused a few - /// errors in the past. Currently, it is only used in Clippy. New implementations - /// should avoid using this interface, as it might be removed in the future. - /// - /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838) - /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) - pub fn register_pre_expansion_pass(&mut self, pass: EarlyLintPassFactory) { - self.pre_expansion_passes.push(pass); + /// See the comment on `LintStore::early_lint_passes`. + pub fn register_early_lint_pass(&mut self, pass: EarlyLintPassFactory) { + self.early_lint_passes.push(pass); } - pub fn register_late_pass(&mut self, pass: LateLintPassFactory) { - self.late_passes.push(pass); + /// See the comment on `LintStore::late_lint_passes`. + pub fn register_late_lint_pass(&mut self, pass: LateLintPassFactory) { + self.late_lint_passes.push(pass); } - pub fn register_late_mod_pass(&mut self, pass: LateLintPassFactory) { - self.late_module_passes.push(pass); + /// See the comment on `LintStore::late_lint_mod_passes`. + pub fn register_late_lint_mod_pass(&mut self, pass: LateLintPassFactory) { + self.late_lint_mod_passes.push(pass); } /// Helper method for register_early/late_pass @@ -830,7 +847,7 @@ impl<'tcx> LateContext<'tcx> { tcx.associated_items(trait_id) .find_by_ident_and_kind(tcx, Ident::with_dummy_span(name), ty::AssocTag::Type, trait_id) .and_then(|assoc| { - let proj = Ty::new_projection(tcx, assoc.def_id, [self_ty]); + let proj = Ty::new_projection(tcx, ty::IsRigid::No, assoc.def_id, [self_ty]); tcx.try_normalize_erasing_regions(self.typing_env(), Unnormalized::new_wip(proj)) .ok() }) diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index e9afbcbbbaf70..9c123f67abd5f 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -16,7 +16,7 @@ use rustc_span::{Ident, Span}; use tracing::debug; use crate::DiagAndSess; -use crate::context::{EarlyContext, LintContext, LintStore}; +use crate::context::{EarlyContext, EarlyLintPassFactory, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; pub(super) mod diagnostics; @@ -316,7 +316,6 @@ pub fn check_ast_node<'a>( lint_store: &LintStore, registered_tools: &RegisteredTools, lint_buffer: Option, - builtin_lints: impl EarlyLintPass + 'static, check_node: EarlyCheckNode<'a>, ) { let context = EarlyContext::new( @@ -328,35 +327,20 @@ pub fn check_ast_node<'a>( lint_buffer.unwrap_or_default(), ); - // Note: `passes` is often empty. In that case, it's faster to run - // `builtin_lints` directly rather than bundling it up into the - // `RuntimeCombinedEarlyLintPass`. - let passes = - if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; - if passes.is_empty() { - check_ast_node_inner(sess, check_node, context, builtin_lints); + let context = if pre_expansion { + let builtin_lints = crate::BuiltinCombinedPreExpansionLintPass::new(); + let passes = &lint_store.pre_expansion_lint_passes; + run_passes(check_node, context, builtin_lints, passes) } else { - let mut passes: Vec<_> = passes.iter().map(|mk_pass| (mk_pass)()).collect(); - passes.push(Box::new(builtin_lints)); - let pass = RuntimeCombinedEarlyLintPass { passes }; - check_ast_node_inner(sess, check_node, context, pass); - } -} - -fn check_ast_node_inner<'a, T: EarlyLintPass>( - sess: &Session, - check_node: EarlyCheckNode<'a>, - context: EarlyContext<'_>, - pass: T, -) { - let mut cx = EarlyContextAndPass { context, pass }; - - cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); + let builtin_lints = crate::BuiltinCombinedEarlyLintPass::new(); + let passes = &lint_store.early_lint_passes; + run_passes(check_node, context, builtin_lints, passes) + }; // All of the buffered lints should have been emitted at this point. // If not, that means that we somehow buffered a lint for a node id // that was not lint-checked (perhaps it doesn't exist?). This is a bug. - for (id, lints) in cx.context.buffered.map { + for (id, lints) in context.buffered.map { if !lints.is_empty() { assert!( sess.dcx().has_errors().is_some(), @@ -367,3 +351,32 @@ fn check_ast_node_inner<'a, T: EarlyLintPass>( } } } + +fn run_passes<'a, 'ecx, T: EarlyLintPass + 'static>( + check_node: EarlyCheckNode<'a>, + context: EarlyContext<'ecx>, + builtin_lints: T, + passes: &[EarlyLintPassFactory], +) -> EarlyContext<'ecx> { + // Note: `passes` is often empty. In that case, it's faster to run + // `builtin_lints` directly rather than bundling it up into the + // `RuntimeCombinedEarlyLintPass`. + if passes.is_empty() { + run_pass(check_node, context, builtin_lints) + } else { + let mut passes: Vec<_> = passes.iter().map(|mk_pass| mk_pass()).collect(); + passes.push(Box::new(builtin_lints)); + let pass = RuntimeCombinedEarlyLintPass { passes }; + run_pass(check_node, context, pass) + } +} + +fn run_pass<'a, 'ecx, T: EarlyLintPass>( + check_node: EarlyCheckNode<'a>, + context: EarlyContext<'ecx>, + pass: T, +) -> EarlyContext<'ecx> { + let mut cx = EarlyContextAndPass { context, pass }; + cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); + cx.context +} diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 53a84d1285298..3010eadb61057 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -329,6 +329,14 @@ fn structurally_same_type_impl<'tcx>( let a_sig = tcx.instantiate_bound_regions_with_erased(a_poly_sig); let b_sig = tcx.instantiate_bound_regions_with_erased(b_poly_sig); + // FIXME(splat): Is splatting ever repr(C)? + // Can two splatted functions to have the same structure? + // Can a splatted and non-splatted function have the same structure? + // For now, we require splatting to match exactly. + if a_sig.splatted() != b_sig.splatted() { + return false; + } + (a_sig.abi(), a_sig.safety(), a_sig.c_variadic()) == (b_sig.abi(), b_sig.safety(), b_sig.c_variadic()) && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { @@ -355,16 +363,16 @@ fn structurally_same_type_impl<'tcx>( | (ty::Coroutine(..), ty::Coroutine(..)) | (ty::CoroutineWitness(..), ty::CoroutineWitness(..)) | ( - ty::Alias(ty::AliasTy { kind: ty::Projection { .. }, .. }), - ty::Alias(ty::AliasTy { kind: ty::Projection { .. }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }), ) | ( - ty::Alias(ty::AliasTy { kind: ty::Inherent { .. }, .. }), - ty::Alias(ty::AliasTy { kind: ty::Inherent { .. }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }), ) | ( - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), ) => false, // These definitely should have been caught above. diff --git a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs deleted file mode 100644 index 699004a3b37e0..0000000000000 --- a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs +++ /dev/null @@ -1,79 +0,0 @@ -use rustc_hir as hir; -use rustc_session::{declare_lint, declare_lint_pass}; - -use crate::lints::{LossyProvenanceInt2Ptr, LossyProvenanceInt2PtrSuggestion}; -use crate::{LateContext, LateLintPass}; - -declare_lint! { - /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer - /// and a pointer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(fuzzy_provenance_casts)] - /// - /// fn main() { - /// let _dangling = 16_usize as *const u8; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting an integer to a pointer is considered bad style, as a pointer - /// contains, besides the *address* also a *provenance*, indicating what - /// memory the pointer is allowed to read/write. Casting an integer, which - /// doesn't have provenance, to a pointer requires the compiler to assign - /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::with_exposed_provenance`] for more information about this - /// "exposing"). This penalizes the optimiser and is not well suited for - /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI - /// platforms). - /// - /// It is much better to use [`ptr::with_addr`] instead to specify the - /// provenance you want. If using this function is not possible because the - /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::with_exposed_provenance`]. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html - pub FUZZY_PROVENANCE_CASTS, - Allow, - "a fuzzy integer to pointer cast is used", - @feature_gate = strict_provenance_lints; -} - -declare_lint_pass!( - /// Lint for `as` casts between an integer and a pointer. - FuzzyProvenanceCasts => [FUZZY_PROVENANCE_CASTS] -); - -impl<'tcx> LateLintPass<'tcx> for FuzzyProvenanceCasts { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; - - let typeck_results = cx.typeck_results(); - // Only lint casts from integer to pointer - let cast_from_ty = typeck_results.expr_ty(cast_from_expr); - if !cast_from_ty.is_integral() { - return; - } - let cast_to_ty = typeck_results.expr_ty(expr); - if !cast_to_ty.is_raw_ptr() { - return; - } - - let sugg = - expr.span.can_be_used_for_suggestions().then(|| LossyProvenanceInt2PtrSuggestion { - lo: cast_from_expr.span.shrink_to_lo(), - hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), - }); - let lint = LossyProvenanceInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; - cx.tcx.emit_node_span_lint(FUZZY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) - } -} diff --git a/compiler/rustc_lint/src/gpukernel_abi.rs b/compiler/rustc_lint/src/gpukernel_abi.rs index d24e934fc06fe..e09687bc49114 100644 --- a/compiler/rustc_lint/src/gpukernel_abi.rs +++ b/compiler/rustc_lint/src/gpukernel_abi.rs @@ -115,7 +115,7 @@ impl<'tcx> TypeFolder> for CheckGpuKernelTypes<'tcx> { } ty::Adt(_, _) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Array(_, _) | ty::Bound(_, _) | ty::Closure(_, _) diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index cc8229b3f387b..97b7cdc135142 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -269,7 +269,7 @@ impl_lint_pass!( impl<'tcx> LateLintPass<'tcx> for IfLetRescope { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if expr.span.edition().at_least_rust_2024() - || cx.tcx.lints_that_dont_need_to_run(()).contains(&LintId::of(IF_LET_RESCOPE)) + || cx.tcx.skippable_lints(()).contains(&LintId::of(IF_LET_RESCOPE)) { return; } diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 842a9be7093a7..3859b0cab2520 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -243,12 +243,12 @@ where return; } - if let ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) = *t.kind() + if let ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) = *t.kind() && self.tcx.is_impl_trait_in_trait(def_id) { // visit the opaque of the RPITIT self.tcx.type_of(def_id).instantiate(self.tcx, args).skip_norm_wip().visit_with(self) - } else if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args: opaque_ty_args, .. }) = *t.kind() + } else if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args: opaque_ty_args, .. }) = *t.kind() && let Some(opaque_def_id) = def_id.as_local() // Don't recurse infinitely on an opaque && self.seen.insert(opaque_def_id) diff --git a/compiler/rustc_lint/src/implicit_provenance_casts.rs b/compiler/rustc_lint/src/implicit_provenance_casts.rs new file mode 100644 index 0000000000000..c77ecbe273387 --- /dev/null +++ b/compiler/rustc_lint/src/implicit_provenance_casts.rs @@ -0,0 +1,122 @@ +use rustc_ast::util::parser::ExprPrecedence; +use rustc_hir as hir; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{ + ImplicitProvenanceCastsInt2Ptr, ImplicitProvenanceCastsPtr2Int, Int2PtrSuggestion, + Ptr2IntSuggestion, +}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `implicit_provenance_casts` lint detects integer-to-pointer and pointer-to-integer + /// casts. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(implicit_provenance_casts)] + /// + /// fn main() { + /// let x: u8 = 37; + /// let addr: usize = &x as *const u8 as usize; + /// let _ptr = addr as *const u8; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint exists to help migrate code to [*Strict Provenance* APIs][strict-provenance] where + /// possible, and make remaining uses of [*Exposed Provenance*][exposed-provenance] explicit. + /// For more information on pointer provenance, see the [`std::ptr` documentation][provenance]. + /// + /// Earlier versions of Rust did not have a clear answer how integer-to-pointer and + /// pointer-to-integer casts interact with provenance. Such casts are now defined to use the + /// exposed provenance model, but in many cases the code can be updated to strict provenance + /// APIs, which is preferable as it enables more precise reasoning about unsafe code, both by + /// humans and by tools like [Miri]. + /// + /// However, there are situations where exposed provenance is required or following the strict + /// provenance model requires major refactorings. In those cases, it's still useful to replace + /// the `as` casts with equivalent explicit use of exposed provenance APIs and a comment + /// explaining why they are needed. + /// + /// [provenance]: https://doc.rust-lang.org/core/ptr/index.html#provenance + /// [strict-provenance]: https://doc.rust-lang.org/core/ptr/index.html#strict-provenance + /// [exposed-provenance]: https://doc.rust-lang.org/core/ptr/index.html#exposed-provenance + /// [Miri]: https://github.com/rust-lang/miri + pub IMPLICIT_PROVENANCE_CASTS, + Allow, + "an `as` cast relying on exposed provenance is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for int2ptr and ptr2int `as` casts. + ImplicitProvenanceCasts => [IMPLICIT_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for ImplicitProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if cast_from_ty.is_raw_ptr() { + let cast_to_ty = typeck_results.expr_ty(expr); + if cast_to_ty.is_integral() { + lint_ptr2int(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty) + } + } else if cast_from_ty.is_integral() { + let cast_to_ty = typeck_results.expr_ty(expr); + if cast_to_ty.is_raw_ptr() { + lint_int2ptr(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty) + } + } + } +} + +fn lint_ptr2int<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + cast_from_expr: &'tcx hir::Expr<'tcx>, + cast_from_ty: Ty<'tcx>, + cast_to_hir: &'tcx hir::Ty<'tcx>, + cast_to_ty: Ty<'tcx>, +) { + let sugg = expr.span.can_be_used_for_suggestions().then(|| { + let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; + let needs_cast = !cast_to_ty.is_usize(); + let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); + let expr_span = cast_from_expr.span.shrink_to_lo(); + match (needs_parens, needs_cast) { + (true, true) => Ptr2IntSuggestion::NeedsParensCast { expr_span, cast_span, cast_to_ty }, + (true, false) => Ptr2IntSuggestion::NeedsParens { expr_span, cast_span }, + (false, true) => Ptr2IntSuggestion::NeedsCast { cast_span, cast_to_ty }, + (false, false) => Ptr2IntSuggestion::Other { cast_span }, + } + }); + + let lint = ImplicitProvenanceCastsPtr2Int { cast_from_ty, cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); +} + +fn lint_int2ptr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + cast_from_expr: &'tcx hir::Expr<'tcx>, + cast_from_ty: Ty<'tcx>, + cast_to_hir: &'tcx hir::Ty<'tcx>, + cast_to_ty: Ty<'tcx>, +) { + let sugg = expr.span.can_be_used_for_suggestions().then(|| Int2PtrSuggestion { + lo: cast_from_expr.span.shrink_to_lo(), + hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), + }); + let lint = ImplicitProvenanceCastsInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) +} diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 1f37ac2d3e0bc..c17fe6f2e510f 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -18,7 +18,7 @@ use rustc_span::Span; use tracing::debug; use crate::passes::LateLintPassObject; -use crate::{LateContext, LateLintPass, LintId, LintStore}; +use crate::{LateContext, LateLintPass, LintStore, is_lint_pass_required}; /// Extract the [`LintStore`] from [`Session`]. /// @@ -349,31 +349,26 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( only_module: true, }; + let skippable_lints = tcx.skippable_lints(()); + // Note: `passes` is often empty. In that case, it's faster to run // `builtin_lints` directly rather than bundling it up into the // `RuntimeCombinedLateLintPass`. - let store = unerased_lint_store(tcx.sess); - - if store.late_module_passes.is_empty() { - // If all builtin lints can be skipped, there is no point in running `late_lint_mod_inner` - // at all. This happens often for dependencies built with `--cap-lints=allow`. - let dont_need_to_run = tcx.lints_that_dont_need_to_run(()); - let can_skip_lints = builtin_lints - .get_lints() - .iter() - .all(|lint| dont_need_to_run.contains(&LintId::of(lint))); - if !can_skip_lints { + let mut passes: Vec<_> = unerased_lint_store(tcx.sess) + .late_lint_mod_passes + .iter() + .map(|mk_pass| mk_pass(tcx)) + .filter(|pass| is_lint_pass_required(skippable_lints, &pass.get_lints())) + .collect(); + let builtin_lints_must_run = is_lint_pass_required(skippable_lints, &builtin_lints.get_lints()); + if passes.is_empty() { + if builtin_lints_must_run { late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); } } else { - let builtin_lints = Box::new(builtin_lints) as Box>; - let passes = store - .late_module_passes - .iter() - .map(|mk_pass| (mk_pass)(tcx)) - .chain(std::iter::once(builtin_lints)) - .collect::>(); - + if builtin_lints_must_run { + passes.push(Box::new(builtin_lints) as Box>); + } let pass = RuntimeCombinedLateLintPass { passes }; late_lint_mod_inner(tcx, module_def_id, context, pass); } @@ -404,18 +399,15 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( } fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { - let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(()); + let skippable_lints = tcx.skippable_lints(()); // Note: `passes` is often empty after filtering. - let mut passes: Vec<_> = - unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); - passes.retain(|pass| { - let lints = pass.get_lints(); - // Lintless passes are always in - lints.is_empty() || - // If the pass doesn't have a single needed lint, omit it - !lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint))) - }); + let passes: Vec<_> = unerased_lint_store(tcx.sess) + .late_lint_passes + .iter() + .map(|mk_pass| mk_pass(tcx)) + .filter(|pass| is_lint_pass_required(skippable_lints, &pass.get_lints())) + .collect(); if passes.is_empty() { return; } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index b7c24b9f2d5b5..492131ca8b32e 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -114,11 +114,11 @@ impl LintLevelSets { } } -fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet { +fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet { let store = unerased_lint_store(&tcx.sess); let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID); - let mut dont_need_to_run: FxHashSet = store + let mut skippable: FxHashSet = store .get_lints() .into_iter() .filter(|lint| { @@ -145,13 +145,13 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet { for (_, specs) in map.specs.iter() { for (lint, level_spec) in specs.iter() { if !level_spec.is_allow() { - dont_need_to_run.remove(lint); + skippable.remove(lint); } } } } - dont_need_to_run.into() + skippable.into() } #[instrument(level = "trace", skip(tcx), ret)] @@ -1035,7 +1035,7 @@ where } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers }; + *providers = Providers { shallow_lint_levels_on, skippable_lints, ..*providers }; } pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option, &str) { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 91267c2445ac4..0e96b9f118790 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -32,6 +32,7 @@ mod async_closures; mod async_fn_in_trait; mod autorefs; pub mod builtin; +mod c_void_returns; mod context; mod dangling; mod default_could_be_derived; @@ -45,10 +46,10 @@ mod expect; mod for_loops_over_fallibles; mod foreign_modules; mod function_cast_as_integer; -mod fuzzy_provenance_casts; mod gpukernel_abi; mod if_let_rescope; mod impl_trait_overcaptures; +mod implicit_provenance_casts; mod interior_mutable_consts; mod internal; mod invalid_from_utf8; @@ -57,7 +58,6 @@ mod let_underscore; mod levels; pub mod lifetime_syntax; mod lints; -mod lossy_provenance_casts; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; mod multiple_supertrait_upcastable; @@ -72,6 +72,7 @@ mod precedence; mod ptr_nulls; mod redundant_semicolon; mod reference_casting; +mod runtime_symbols; mod shadowed_into_iter; mod static_mut_refs; mod traits; @@ -86,6 +87,7 @@ use async_closures::AsyncClosureUsage; use async_fn_in_trait::AsyncFnInTrait; use autorefs::*; use builtin::*; +use c_void_returns::*; use dangling::*; use default_could_be_derived::DefaultCouldBeDerived; use deref_into_dyn_supertrait::*; @@ -94,16 +96,15 @@ use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; use function_cast_as_integer::*; -use fuzzy_provenance_casts::FuzzyProvenanceCasts; use gpukernel_abi::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; +use implicit_provenance_casts::ImplicitProvenanceCasts; use interior_mutable_consts::*; use internal::*; use invalid_from_utf8::*; use let_underscore::*; use lifetime_syntax::*; -use lossy_provenance_casts::LossyProvenanceCasts; use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; use multiple_supertrait_upcastable::*; @@ -117,6 +118,8 @@ use precedence::*; use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; +use runtime_symbols::*; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LocalModDefId; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -151,7 +154,7 @@ pub fn provide(providers: &mut Providers) { } fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { - late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); + late_lint_mod(tcx, module_def_id, BuiltinCombinedLateLintModPass::new()); } early_lint_methods!( @@ -204,7 +207,7 @@ early_lint_methods!( late_lint_methods!( declare_combined_late_lint_pass, [ - BuiltinCombinedModuleLateLintPass, + BuiltinCombinedLateLintModPass, [ ForLoopsOverFallibles: ForLoopsOverFallibles, DefaultCouldBeDerived: DefaultCouldBeDerived, @@ -258,6 +261,7 @@ late_lint_methods!( AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), InteriorMutableConsts: InteriorMutableConsts, + RuntimeSymbols: RuntimeSymbols, ImplTraitOvercaptures: ImplTraitOvercaptures, IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, @@ -266,8 +270,8 @@ late_lint_methods!( CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, - FuzzyProvenanceCasts: FuzzyProvenanceCasts, - LossyProvenanceCasts: LossyProvenanceCasts, + ImplicitProvenanceCasts: ImplicitProvenanceCasts, + CVoidReturns: CVoidReturns, ] ] ); @@ -275,7 +279,7 @@ late_lint_methods!( late_lint_methods!( declare_combined_late_lint_pass, [ - InternalCombinedModuleLateLintPass, + InternalCombinedLateLintModPass, [ DefaultHashTypes: DefaultHashTypes, QueryStability: QueryStability, @@ -313,7 +317,7 @@ fn register_builtins(store: &mut LintStore) { store.register_lints(&BuiltinCombinedPreExpansionLintPass::lint_vec()); store.register_lints(&BuiltinCombinedEarlyLintPass::lint_vec()); - store.register_lints(&BuiltinCombinedModuleLateLintPass::lint_vec()); + store.register_lints(&BuiltinCombinedLateLintModPass::lint_vec()); store.register_lints(&foreign_modules::lint_vec()); store.register_lints(&hardwired::lint_vec()); @@ -406,6 +410,8 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("static_mut_ref", "static_mut_refs"); store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes"); + store.register_renamed("fuzzy_provenance_casts", "implicit_provenance_casts"); + store.register_renamed("lossy_provenance_casts", "implicit_provenance_casts"); // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use @@ -694,10 +700,12 @@ fn register_builtins(store: &mut LintStore) { fn register_internals(store: &mut LintStore) { store.register_lints(&InternalCombinedEarlyLintPass::lint_vec()); - store.register_early_pass(Box::new(|| Box::new(InternalCombinedEarlyLintPass::new()))); + store.register_early_lint_pass(Box::new(|| Box::new(InternalCombinedEarlyLintPass::new()))); - store.register_lints(&InternalCombinedModuleLateLintPass::lint_vec()); - store.register_late_mod_pass(Box::new(|_| Box::new(InternalCombinedModuleLateLintPass::new()))); + store.register_lints(&InternalCombinedLateLintModPass::lint_vec()); + store.register_late_lint_mod_pass(Box::new(|_| { + Box::new(InternalCombinedLateLintModPass::new()) + })); store.register_group( false, @@ -742,5 +750,21 @@ fn register_internals(store: &mut LintStore) { ); } +/// Is a pass (which contains `lints`) required to run? Maybe not, e.g. for dependencies built with +/// `--cap-lints=allow`. +/// +/// Note: this is a conservative estimate intended for optimization purposes. It might return +/// `true` for a pass that need not run, but it will never return `false` for a pass that must run. +pub fn is_lint_pass_required(skippable: &UnordSet, lints: &LintVec) -> bool { + // A pass without any lints? Clippy sometimes does this, to collect things while traversing. + // Such a pass must always run. + if lints.is_empty() { + return true; + } + + // Otherwise, the pass must run unless all lints within are skippable. + !lints.iter().all(|lint| skippable.contains(&LintId::of(lint))) +} + #[cfg(test)] mod tests; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index d3048d08ebf77..eb82afab13186 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -614,6 +614,33 @@ pub(crate) enum BuiltinSpecialModuleNameUsed { Main, } +// c_void_return.rs +#[derive(Diagnostic)] +#[diag("`c_void` should not be used as a return type")] +#[help("returning `()` in Rust is equivalent to returning `void` in C")] +pub(crate) struct CVoidReturn { + #[suggestion( + "remove the return type to implicitly return `()`", + code = "", + applicability = "maybe-incorrect" + )] + pub suggestion: Span, +} + +// c_void_return.rs +#[derive(Diagnostic)] +#[diag("declarations returning `c_void` are not compatible with C functions returning `void`")] +#[help("returning `()` in Rust is equivalent to returning `void` in C")] +#[note("`c_void` is only used through raw pointers for compatibility with `void` pointers")] +pub(crate) struct ExternCVoidReturn { + #[suggestion( + "remove the return type to implicitly return `()`", + code = "", + applicability = "maybe-incorrect" + )] + pub suggestion: Span, +} + // deref_into_dyn_supertrait.rs #[derive(Diagnostic)] #[diag("this `Deref` implementation is covered by an implicit supertrait coercion")] @@ -808,6 +835,45 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion { }, } +// runtime_symbols.rs +#[derive(Diagnostic)] +pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { + #[diag( + "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `{$found_fn_sig}`" + )] + #[help( + "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" + )] + FnDefInvalid { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, + #[diag( + "suspicious definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `{$found_fn_sig}`" + )] + #[help( + "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" + )] + #[help("allow this lint if the signature is compatible")] + FnDefSuspicious { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, + #[diag( + "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `static {$symbol_name}: {$static_ty}`" + )] + #[help( + "either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = \"{$symbol_name}\")]`" + )] + Static { symbol_name: String, static_ty: Ty<'tcx>, expected_fn_sig: Ty<'tcx> }, +} + // drop_forget_useless.rs #[derive(Diagnostic)] #[diag("calls to `std::mem::drop` with a reference instead of an owned value does nothing")] @@ -2895,23 +2961,24 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { pub(crate) struct EqInternalMethodImplemented; #[derive(Diagnostic)] -#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] +#[diag("cast from `{$expr_ty}` to `{$cast_ty}` implicitly relies on exposed provenance")] #[help( - "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" + "if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()`" )] -pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { +#[note("for more information, visit ")] +pub(crate) struct ImplicitProvenanceCastsInt2Ptr<'tcx> { pub expr_ty: Ty<'tcx>, pub cast_ty: Ty<'tcx>, #[subdiagnostic] - pub sugg: Option, + pub sugg: Option, } #[derive(Subdiagnostic)] #[multipart_suggestion( - "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", + "use `.with_addr()` to adjust the address of a valid pointer in the same allocation", applicability = "has-placeholders" )] -pub(crate) struct LossyProvenanceInt2PtrSuggestion { +pub(crate) struct Int2PtrSuggestion { #[suggestion_part(code = "(...).with_addr(")] pub lo: Span, #[suggestion_part(code = ")")] @@ -2919,21 +2986,18 @@ pub(crate) struct LossyProvenanceInt2PtrSuggestion { } #[derive(Diagnostic)] -#[diag( - "under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`" -)] -#[help( - "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" -)] -pub(crate) struct LossyProvenancePtr2Int<'tcx> { +#[diag("cast from `{$cast_from_ty}` to `{$cast_to_ty}` implicitly exposes pointer provenance")] +#[help("if conforming to strict provenance is not possible, use `.expose_provenance()`")] +#[note("for more information, visit ")] +pub(crate) struct ImplicitProvenanceCastsPtr2Int<'tcx> { pub cast_from_ty: Ty<'tcx>, pub cast_to_ty: Ty<'tcx>, #[subdiagnostic] - pub sugg: Option>, + pub sugg: Option>, } #[derive(Subdiagnostic)] -pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { +pub(crate) enum Ptr2IntSuggestion<'tcx> { #[multipart_suggestion( "use `.addr()` to obtain the address of a pointer", applicability = "maybe-incorrect" diff --git a/compiler/rustc_lint/src/lossy_provenance_casts.rs b/compiler/rustc_lint/src/lossy_provenance_casts.rs deleted file mode 100644 index bdb5bdf6bb0e3..0000000000000 --- a/compiler/rustc_lint/src/lossy_provenance_casts.rs +++ /dev/null @@ -1,98 +0,0 @@ -use rustc_ast::util::parser::ExprPrecedence; -use rustc_hir as hir; -use rustc_session::{declare_lint, declare_lint_pass}; - -use crate::lints::{LossyProvenancePtr2Int, LossyProvenancePtr2IntSuggestion}; -use crate::{LateContext, LateLintPass}; - -declare_lint! { - /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer - /// and an integer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(lossy_provenance_casts)] - /// - /// fn main() { - /// let x: u8 = 37; - /// let _addr: usize = &x as *const u8 as usize; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting a pointer to an integer is a lossy operation, because beyond - /// just an *address* a pointer may be associated with a particular - /// *provenance*. This information is used by the optimiser and for dynamic - /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). - /// - /// Since this cast is lossy, it is considered good style to use the - /// [`ptr::addr`] method instead, which has a similar effect, but doesn't - /// "expose" the pointer provenance. This improves optimisation potential. - /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information - /// about exposing pointer provenance. - /// - /// If your code can't comply with strict provenance and needs to expose - /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, - /// which preserves the behaviour of `as usize` casts while being explicit - /// about the semantics. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr - /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance - pub LOSSY_PROVENANCE_CASTS, - Allow, - "a lossy pointer to integer cast is used", - @feature_gate = strict_provenance_lints; -} - -declare_lint_pass!( - /// Lint for `as` casts between a pointer and an integer. - LossyProvenanceCasts => [LOSSY_PROVENANCE_CASTS] -); - -impl<'tcx> LateLintPass<'tcx> for LossyProvenanceCasts { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; - - let typeck_results = cx.typeck_results(); - // Only lint casts from pointer to integer - let cast_from_ty = typeck_results.expr_ty(cast_from_expr); - if !cast_from_ty.is_raw_ptr() { - return; - } - let cast_to_ty = typeck_results.expr_ty(expr); - if !cast_to_ty.is_integral() { - return; - } - - let sugg = expr.span.can_be_used_for_suggestions().then(|| { - let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; - let needs_cast = !cast_to_ty.is_usize(); - let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); - let expr_span = cast_from_expr.span.shrink_to_lo(); - match (needs_parens, needs_cast) { - (true, true) => LossyProvenancePtr2IntSuggestion::NeedsParensCast { - expr_span, - cast_span, - cast_to_ty, - }, - (true, false) => { - LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } - } - (false, true) => { - LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_to_ty } - } - (false, false) => LossyProvenancePtr2IntSuggestion::Other { cast_span }, - } - }); - - let lint = LossyProvenancePtr2Int { cast_from_ty, cast_to_ty, sugg }; - cx.tcx.emit_node_span_lint(LOSSY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); - } -} diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 3fb929aa92216..ed71ff6a463db 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { let Some(proj_term) = proj.term.as_type() else { return }; // HACK: `impl Trait` from an RPIT is "ok"... - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: opaque_def_id }, .. }) = + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: opaque_def_id }, .. }) = *proj_term.kind() && cx.tcx.parent(opaque_def_id) == def_id && matches!( @@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { return; } - let proj_ty = proj.projection_term.expect_ty().to_ty(cx.tcx); + let proj_ty = proj.projection_term.expect_ty().to_ty(cx.tcx,ty::IsRigid::No); // For every instance of the projection type in the bounds, // replace them with the term we're assigning to the associated // type in our opaque type. @@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // then we can emit a suggestion to add the bound. let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) { ( - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }), ty::ClauseKind::Trait(trait_pred), ) => Some(AddBound { suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(), @@ -192,6 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { OpaqueHiddenInferredBoundLint { ty: Ty::new_opaque( cx.tcx, + ty::IsRigid::No, def_id, ty::GenericArgs::identity_for_item(cx.tcx, def_id), ), diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 5c101048b8010..12cf58907566d 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -203,7 +203,7 @@ macro_rules! expand_combined_early_lint_pass_methods { /// Combines multiple lints passes into a single lint pass, at compile time, /// for maximum speed. Each `check_foo` method in `$methods` within this pass /// simply calls `check_foo` once per `$pass`. Compare with -/// `EarlyLintPassObjects`, which is similar, but combines lint passes at +/// `RuntimeCombinedEarlyLintPass`, which is similar, but combines lint passes at /// runtime. #[macro_export] macro_rules! declare_combined_early_lint_pass { diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs new file mode 100644 index 0000000000000..a583ee70074b6 --- /dev/null +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -0,0 +1,255 @@ +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, FnSig, ForeignItemKind, LanguageItems}; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_middle::ty::{self, Instance, Ty}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::Span; +use rustc_trait_selection::infer::TyCtxtInferExt; + +use crate::lints::RedefiningRuntimeSymbolsDiag; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `invalid_runtime_symbol_definitions` lint checks the signature of items whose + /// symbol name is a runtime symbol expected by `core` differs significantly from the + /// expected signature (like mismatch ABI, mismatch C variadics, mismatch argument count, + /// missing return type, ...). + /// + /// ### Example + /// + #[cfg_attr(bootstrap, doc = "```rust")] + #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] + #[cfg_attr(not(bootstrap), doc = "#[unsafe(no_mangle)]")] + /// pub fn strlen() {} // invalid definition of the `strlen` function + #[doc = "```"] + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Up-most care is required when defining runtime symbols assumed and + /// used by the standard library. They must follow the C specification, not use any + /// standard-library facility or undefined behavior may occur. + /// + /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, + /// `bcmp` and `strlen`. + /// + /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library + pub INVALID_RUNTIME_SYMBOL_DEFINITIONS, + Deny, + "invalid definition of a symbol used by the standard library" +} + +declare_lint! { + /// The `suspicious_runtime_symbol_definitions` lint checks the signature of items whose + /// symbol name is a runtime symbol expected by `core`. + /// + /// ### Example + /// + /// ```rust,no_run,standalone_crate + #[cfg_attr(not(bootstrap), doc = "#[unsafe(no_mangle)]")] + /// pub extern "C" fn strlen(ptr: *mut f32) -> usize { 0 } + /// // suspicious definition of the `strlen` function + /// // `ptr` should be `*const std::ffi::c_char` + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Up-most care is required when defining runtime symbols assumed and + /// used by the standard library. They must follow the C specification, not use any + /// standard-library facility or undefined behavior may occur. + /// + /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, + /// `bcmp` and `strlen`. + /// + /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library + pub SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + Warn, + "suspicious definition of a symbol used by the standard library" +} + +declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS, SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS]); + +static EXPECTED_SYMBOLS: &[ExpectedSymbol] = &[ + ExpectedSymbol { symbol: "memcpy", lang: LanguageItems::memcpy_fn }, + ExpectedSymbol { symbol: "memmove", lang: LanguageItems::memmove_fn }, + ExpectedSymbol { symbol: "memset", lang: LanguageItems::memset_fn }, + ExpectedSymbol { symbol: "memcmp", lang: LanguageItems::memcmp_fn }, + ExpectedSymbol { symbol: "bcmp", lang: LanguageItems::bcmp_fn }, + ExpectedSymbol { symbol: "strlen", lang: LanguageItems::strlen_fn }, +]; + +#[derive(Copy, Clone, Debug)] +struct ExpectedSymbol { + symbol: &'static str, + lang: fn(&LanguageItems) -> Option, +} + +impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + // Bail-out if the item is not a function/method or static. + match item.kind { + hir::ItemKind::Fn { sig, ident: _, generics, body: _, has_body: _ } => { + // Generic functions cannot have the same runtime symbol as we do not allow + // any symbol attributes. + if !generics.params.is_empty() { + return; + } + + // Try to get the overridden symbol name of this function (our mangling + // cannot ever conflict with runtime symbols, so no need to check for those). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + check_fn(cx, &symbol_name, sig, item.owner_id.def_id); + } + hir::ItemKind::Static(..) => { + // Compute the symbol name of this static (without mangling, as our mangling + // cannot ever conflict with runtime symbols). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + let def_id = item.owner_id.def_id; + + check_static(cx, &symbol_name, def_id, item.span); + } + hir::ItemKind::ForeignMod { abi: _, items } => { + for item in items { + let item = cx.tcx.hir_foreign_item(*item); + + let did = item.owner_id.def_id; + let instance = Instance::new_raw( + did.to_def_id(), + ty::List::identity_for_item(cx.tcx, did), + ); + let symbol_name = cx.tcx.symbol_name(instance); + + match item.kind { + ForeignItemKind::Fn(fn_sig, _idents, _generics) => { + check_fn(cx, &symbol_name.name, fn_sig, did); + } + ForeignItemKind::Static(..) => { + check_static(cx, &symbol_name.name, did, item.span); + } + ForeignItemKind::Type => return, + } + } + } + _ => return, + } + } +} + +fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalDefId) { + let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + // The symbol name does not correspond to a runtime symbols, bail out + return; + }; + + let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { + // Can't find the corresponding language item, bail out + return; + }; + + // Get the two function signatures + let lang_sig = cx.tcx.normalize_erasing_regions( + cx.typing_env(), + cx.tcx.fn_sig(expected_def_id).instantiate_identity(), + ); + let user_sig = cx + .tcx + .normalize_erasing_regions(cx.typing_env(), cx.tcx.fn_sig(did).instantiate_identity()); + + // Compare the two signatures with an inference context + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + let cause = rustc_middle::traits::ObligationCause::misc(sig.span, did); + let result = infcx.at(&cause, cx.param_env).eq(DefineOpaqueTypes::No, lang_sig, user_sig); + + // If they don't match, emit our own mismatch signatures + if let Err(_terr) = result { + // Create fn pointers for diagnostics purpose + let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); + let actual = Ty::new_fn_ptr(cx.tcx, user_sig); + + if lang_sig.abi() != user_sig.abi() + || lang_sig.c_variadic() != user_sig.c_variadic() + || lang_sig.inputs().skip_binder().len() != user_sig.inputs().skip_binder().len() + || (!lang_sig.output().skip_binder().is_unit() + && user_sig.output().skip_binder().is_unit()) + { + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sig.span, + RedefiningRuntimeSymbolsDiag::FnDefInvalid { + symbol_name: symbol_name.to_string(), + found_fn_sig: actual, + expected_fn_sig: expected, + }, + ); + } else { + cx.emit_span_lint( + SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + sig.span, + RedefiningRuntimeSymbolsDiag::FnDefSuspicious { + symbol_name: symbol_name.to_string(), + found_fn_sig: actual, + expected_fn_sig: expected, + }, + ); + }; + } +} + +fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, did: LocalDefId, sp: Span) { + let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + // The symbol name does not correspond to a runtime symbols, bail out + return; + }; + + let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { + // Can't find the corresponding language item, bail out + return; + }; + + // Get the static type + let static_ty = cx.tcx.type_of(did).instantiate_identity().skip_norm_wip(); + + // Peel Option<...> and get the inner type (see std weak! macro with #[linkage = "extern_weak"]) + let inner_static_ty: Ty<'_> = match static_ty.kind() { + ty::Adt(def, args) if Some(def.did()) == cx.tcx.lang_items().option_type() => { + args.type_at(0) + } + _ => static_ty, + }; + + // Get the expected symbol function signature + let lang_sig = cx.tcx.normalize_erasing_regions( + cx.typing_env(), + cx.tcx.fn_sig(expected_def_id).instantiate_identity(), + ); + + let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); + + // Compare the expected function signature with the static type, report an error if they don't match + if expected != inner_static_ty { + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sp, + RedefiningRuntimeSymbolsDiag::Static { + static_ty, + symbol_name: symbol_name.to_string(), + expected_fn_sig: expected, + }, + ); + } +} diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index b086d7e1fedb9..0c34d9da66f1d 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -358,6 +358,8 @@ struct VisitorState { /// Flags describing both the immediate context in which the current Ty is, /// linked to how it relates to its parent Ty (or lack thereof). outer_ty_kind: OuterTyKind, + /// Type recursion depth, to prevent infinite recursion + depth: usize, } impl RootUseFlags { @@ -385,6 +387,7 @@ impl VisitorState { VisitorState { root_use_flags: self.root_use_flags, outer_ty_kind: OuterTyKind::from_ty(current_ty), + depth: self.depth + 1, } } @@ -399,6 +402,7 @@ impl VisitorState { FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR, }, outer_ty_kind: OuterTyKind::from_ty(current_ty), + depth: self.depth + 1, } } @@ -410,12 +414,16 @@ impl VisitorState { (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION, (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION, }; - VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None } + VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None, depth: 0 } } /// Get the proper visitor state for a static variable's type fn static_entry_point() -> Self { - VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None } + VisitorState { + root_use_flags: RootUseFlags::STATIC_TY, + outer_ty_kind: OuterTyKind::None, + depth: 0, + } } /// Whether the type is used in a function. @@ -737,9 +745,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Protect against infinite recursion, for example // `struct S(*mut S);`. - // FIXME: A recursion limit is necessary as well, for irregular - // recursive types. - if !self.cache.insert(ty) { + if !(self.cache.insert(ty) && self.cx.tcx.recursion_limit().value_within_limit(state.depth)) + { return FfiSafe; } @@ -889,16 +896,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // While opaque types are checked for earlier, if a projection in a struct field // normalizes to an opaque type, then it will reach this branch. - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { FfiUnsafe { ty, reason: msg!("opaque types have no C equivalent"), help: None } } // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, // so they are currently ignored for the purposes of this lint. ty::Param(..) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, .. - }) if state.can_expect_ty_params() => FfiSafe, + | ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }) + if state.can_expect_ty_params() => + { + FfiSafe + } ty::UnsafeBinder(_) => FfiUnsafe { ty, @@ -920,10 +929,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }, ty::Param(..) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, - .. - }) + | ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, + .. + }, + ) | ty::Infer(..) | ty::Bound(..) | ty::Error(_) @@ -942,7 +954,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return ControlFlow::Continue(()); } - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) = ty.kind() { + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) = ty.kind() { ControlFlow::Break(ty) } else { ty.super_visit_with(self) @@ -989,6 +1001,8 @@ impl<'tcx> ImproperCTypesLint { fn_mode: CItemKind, ) { struct FnPtrFinder<'tcx> { + current_depth: usize, + depths: Vec, spans: Vec, tys: Vec>, } @@ -996,13 +1010,16 @@ impl<'tcx> ImproperCTypesLint { impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> { fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { debug!(?ty); + self.current_depth += 1; if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind && !abi.is_rustic_abi() { + self.depths.push(self.current_depth); self.spans.push(ty.span); } hir::intravisit::walk_ty(self, ty); + self.current_depth -= 1; } } @@ -1020,16 +1037,25 @@ impl<'tcx> ImproperCTypesLint { } } - let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() }; + let mut visitor = FnPtrFinder { + spans: Vec::new(), + tys: Vec::new(), + depths: Vec::new(), + current_depth: 0, + }; ty.visit_with(&mut visitor); visitor.visit_ty_unambig(hir_ty); - let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); - for (fn_ptr_ty, span) in all_types { + let all_types = iter::zip( + visitor.depths.drain(..), + iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)), + ); + for (depth, (fn_ptr_ty, span)) in all_types { let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty); let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); + let bridge_state = VisitorState { depth, ..state }; // FIXME(ctypes): make a check_for_fnptr - let ffi_res = visitor.check_type(state, fn_ptr_ty); + let ffi_res = visitor.check_type(bridge_state, fn_ptr_ty); self.process_ffi_result(cx, span, ffi_res, fn_mode); } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index dd2cb505cb1bc..5d1b82b0bb48c 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -338,7 +338,7 @@ trait UnusedDelimLint { && !snip.starts_with(' ') { " " - } else if let Ok(snip) = sm.span_to_prev_source(value_span) + } else if let Ok(snip) = sm.span_to_next_source(value_span) && snip.starts_with(|c: char| c.is_alphanumeric()) { " " diff --git a/compiler/rustc_lint/src/unused/must_use.rs b/compiler/rustc_lint/src/unused/must_use.rs index 293320d868a66..c7177f1829bbd 100644 --- a/compiler/rustc_lint/src/unused/must_use.rs +++ b/compiler/rustc_lint/src/unused/must_use.rs @@ -176,10 +176,12 @@ pub fn is_ty_must_use<'tcx>( ty::Adt(def, _) => { is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes) } - ty::Alias(ty::AliasTy { - kind: ty::Opaque { def_id: def } | ty::Projection { def_id: def }, - .. - }) => { + ty::Alias( + _, + ty::AliasTy { + kind: ty::Opaque { def_id: def } | ty::Projection { def_id: def }, .. + }, + ) => { elaborate( cx.tcx, cx.tcx @@ -296,7 +298,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind && let ty = cx.typeck_results().expr_ty(await_expr) - && let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: future_def_id }, .. }) = ty.kind() + && let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: future_def_id }, .. }) = ty.kind() && cx.tcx.ty_is_opaque_future(ty) && let async_fn_def_id = cx.tcx.parent(*future_def_id) && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 5dcaa5f6f84b8..45229e5399dd5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -288,7 +288,11 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, // Just print a bare list of target CPU names, and let Rust-side code handle // the full formatting of `--print=target-cpus`. for (auto &CPU : CPUTable) { +#if LLVM_VERSION_GE(23, 0) + OS << CPU.key() << "\n"; +#else OS << CPU.Key << "\n"; +#endif } } @@ -315,9 +319,15 @@ extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, #endif const ArrayRef FeatTable = MCInfo.getAllProcessorFeatures(); +#if LLVM_VERSION_GE(23, 0) + const SubtargetFeatureKV &Feat = FeatTable[Index]; + *Feature = Feat.key(); + *Desc = Feat.desc(); +#else const SubtargetFeatureKV Feat = FeatTable[Index]; *Feature = Feat.Key; *Desc = Feat.Desc; +#endif } extern "C" const char *LLVMRustGetHostCPUName(size_t *OutLen) { diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a50b06e5120b2..3b8e6f6415365 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1497,6 +1497,10 @@ LLVMRustBuildMemMove(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, unwrap(Size), IsVolatile)); } +extern "C" LLVMValueRef LLVMRustBuildVScale(LLVMBuilderRef B, LLVMTypeRef Ty) { + return wrap(unwrap(B)->CreateVScale(unwrap(Ty))); +} + extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Val, LLVMValueRef Size, @@ -1844,6 +1848,35 @@ extern "C" bool LLVMRustIsTargetIntrinsic(unsigned ID) { return Intrinsic::isTargetIntrinsic(ID); } +extern "C" LLVMValueRef LLVMRustConstPtrAuth(LLVMValueRef Ptr, uint32_t Key, + uint64_t Disc, + LLVMValueRef AddrDiversity, + LLVMValueRef DeactivationSymbol) { + auto *C = cast(unwrap(Ptr)); + assert(C->getType()->isPointerTy() && "Expected pointer type"); + assert(!isa(C) && "Unexpected undef in const_ptr_auth"); + assert(!isa(C) && "Unexpected null in const_ptr_auth"); + + LLVMContext &Ctx = C->getContext(); + auto *KeyC = ConstantInt::get(Type::getInt32Ty(Ctx), Key); + auto *DiscC = ConstantInt::get(Type::getInt64Ty(Ctx), Disc); + auto *PTy = cast(C->getType()); + Constant *AddrDiv = + AddrDiversity ? dyn_cast(unwrap(AddrDiversity)) + : ConstantPointerNull::get(cast(C->getType())); + assert(AddrDiv && "Failed to get Address Diversity"); +#if LLVM_VERSION_GE(22, 0) + Constant *DeactivationSym = + DeactivationSymbol ? dyn_cast(unwrap(DeactivationSymbol)) + : ConstantPointerNull::get(PTy); + assert(DeactivationSym && "Failed to get Deactivation Symbol"); + + return wrap(ConstantPtrAuth::get(C, KeyC, DiscC, AddrDiv, DeactivationSym)); +#else + return wrap(ConstantPtrAuth::get(C, KeyC, DiscC, AddrDiv)); +#endif +} + // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 3a70ee130c27b..2fce131e08b6a 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -18,7 +18,6 @@ rustc_feature = { path = "../rustc_feature" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } -rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_metadata/src/diagnostics.rs b/compiler/rustc_metadata/src/diagnostics.rs index 5c33fab5011d1..659406f8c84e9 100644 --- a/compiler/rustc_metadata/src/diagnostics.rs +++ b/compiler/rustc_metadata/src/diagnostics.rs @@ -704,3 +704,18 @@ pub(crate) struct MitigationLessStrictInDependency { pub mitigation_level: String, pub extern_crate: Symbol, } + +#[derive(Diagnostic)] +pub(crate) enum StaticLinkingNotSupported<'a> { + #[diag( + "static linking of `{$lib_name}` is not supported on `{$target}`; using dynamic linking instead" + )] + #[help("remove `kind = \"static\"` and ensure a shared library is available")] + UserRequested { lib_name: Symbol, target: &'a str }, + + #[diag( + "library `{$lib_name}` is linked statically by a dependency, but `{$target}` requires dynamic linking; using dynamic linking instead" + )] + #[help("ensure a shared library is available")] + FromDependency { lib_name: Symbol, target: &'a str }, +} diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index e467cf9bf105b..c9e4ce60e1a01 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -197,6 +197,31 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec } } collector.process_command_line(); + for lib in &mut collector.libs { + // FIXME(jchlanda) Pauthtest does not support static linking. It must be dynamically linked, + // with a dynamic linker acting as the ELF interpreter that can resolve pauth relocations + // and enforce pointer authentication constraints. + if tcx.sess.target.cfg_abi == CfgAbi::Pauthtest { + if let NativeLibKind::Static { .. } = lib.kind { + if !tcx.sess.opts.unstable_opts.ui_testing { + let diag = if lib.foreign_module.is_none() { + diagnostics::StaticLinkingNotSupported::UserRequested { + lib_name: lib.name, + target: tcx.sess.target.llvm_target.as_ref(), + } + } else { + diagnostics::StaticLinkingNotSupported::FromDependency { + lib_name: lib.name, + target: tcx.sess.target.llvm_target.as_ref(), + } + }; + tcx.dcx().emit_warn(diag); + } + + lib.kind = NativeLibKind::Dylib { as_needed: None }; + } + } + } collector.libs } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index cbd6afd68473a..dff8a4078da16 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -14,7 +14,7 @@ use rustc_middle::middle::stability::DeprecationEntry; use rustc_middle::queries::ExternProviders; use rustc_middle::query::LocalCrate; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_middle::util::Providers; use rustc_serialize::Decoder; use rustc_session::StableCrateId; @@ -39,10 +39,11 @@ impl ProcessQueryValue<'_, T> for T { } } -impl<'tcx, T> ProcessQueryValue<'tcx, ty::EarlyBinder<'tcx, T>> for T { +// The `TypeVisitable` bound here is merely for `EarlyBinder`'s rigidness check. +impl<'tcx, T: TypeVisitable>> ProcessQueryValue<'tcx, ty::EarlyBinder<'tcx, T>> for T { #[inline(always)] fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> ty::EarlyBinder<'tcx, T> { - ty::EarlyBinder::bind(self) + ty::EarlyBinder::bind_no_rigid_aliases(self) } } @@ -555,15 +556,15 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { ) }, crates: |tcx, ()| { - // The list of loaded crates is now frozen in query cache, - // so make sure cstore is not mutably accessed from here on. - tcx.untracked().cstore.freeze(); + // The loaded-crate list is now frozen in the query cache; stop + // mutating the cstore and stable crate id map from here on. + tcx.untracked().freeze_cstore(); tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum)) }, used_crates: |tcx, ()| { - // The list of loaded crates is now frozen in query cache, - // so make sure cstore is not mutably accessed from here on. - tcx.untracked().cstore.freeze(); + // The loaded-crate list is now frozen in the query cache; stop + // mutating the cstore and stable crate id map from here on. + tcx.untracked().freeze_cstore(); tcx.arena.alloc_from_iter( CStore::from_tcx(tcx) .iter_crate_data() diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c232f595d4229..16a5a6c8877a5 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1624,7 +1624,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } - if let DefKind::AnonConst = def_kind { + if let DefKind::AnonConst | DefKind::InlineConst = def_kind { record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); } if should_encode_const_of_item(self.tcx, def_id, def_kind) { @@ -2472,10 +2472,10 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { && tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { let saved_path = &work_product.saved_files["rmeta"]; - let incr_comp_session_dir = tcx.sess.incr_comp_session_dir_opt().unwrap(); - let source_file = rustc_incremental::in_incr_comp_dir(&incr_comp_session_dir, saved_path); - debug!("copying preexisting metadata from {source_file:?} to {path:?}"); - match rustc_fs_util::link_or_copy(&source_file, path) { + let incr_comp_session_dir = tcx.sess.incr_comp_session_dir(); + let source_file_in_incr_dir = &incr_comp_session_dir.join(saved_path); + debug!("copying preexisting metadata from {source_file_in_incr_dir:?} to {path:?}"); + match rustc_fs_util::link_or_copy(&source_file_in_incr_dir, path) { Ok(_) => {} Err(err) => tcx.dcx().emit_fatal(FailCreateFileEncoder { err }), }; diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 444433dcb6c54..6a97327504721 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -22,7 +22,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans}; use crate::hir::{ModuleItems, ProjectedMaybeOwner, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::{IntoQueryKey, LocalCrate}; -use crate::ty::TyCtxt; +use crate::ty::{self, TyCtxt}; /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir_parent_iter(hir_id)`. @@ -1108,6 +1108,12 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn is_type_system_inline_const(self, def_id: impl IntoQueryKey) -> bool { + let def_id = def_id.into_query_key(); + debug_assert_eq!(self.def_kind(def_id), DefKind::InlineConst); + self.anon_const_kind(def_id) != ty::AnonConstKind::NonTypeSystem + } + pub fn hir_maybe_get_struct_pattern_shorthand_field(self, expr: &Expr<'_>) -> Option { let local = match expr { Expr { diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 9b78c31871fd7..b6ae4a98a34e3 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -8,7 +8,7 @@ use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; use crate::mono::Visibility; -use crate::ty::{InstanceKind, TyCtxt}; +use crate::ty::{InstanceKind, ShimKind, TyCtxt}; impl<'tcx> TyCtxt<'tcx> { pub fn codegen_instance_attrs( @@ -33,7 +33,7 @@ impl<'tcx> TyCtxt<'tcx> { // // A `ClosureOnceShim` with the track_caller attribute does not have a symbol, // and therefore can be skipped here. - if let InstanceKind::ReifyShim(_, _) = instance_kind + if let InstanceKind::Shim(ShimKind::Reify(_, _)) = instance_kind && attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER) { if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { @@ -54,8 +54,11 @@ impl<'tcx> TyCtxt<'tcx> { } // Ensure closure shims have the optimization properties of their closure applied to them. - if let InstanceKind::ClosureOnceShim { call_once: _, closure, track_caller: _ } = - instance_kind + if let InstanceKind::Shim(ShimKind::ClosureOnce { + call_once: _, + closure, + track_caller: _, + }) = instance_kind { let closure_attrs = self.codegen_fn_attrs(closure); attrs.to_mut().optimize = closure_attrs.optimize; @@ -111,7 +114,7 @@ pub struct CodegenFnAttrs { // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity pub alignment: Option, /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around - /// the function entry. + /// the function entry, or override default section to record entry location. pub patchable_function_entry: Option, /// The `#[rustc_objc_class = "..."]` attribute. pub objc_class: Option, @@ -159,24 +162,33 @@ pub struct TargetFeature { #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)] pub struct PatchableFunctionEntry { /// Nops to prepend to the function - prefix: u8, + prefix: Option, /// Nops after entry, but before body - entry: u8, + entry: Option, + /// Optional, specific section to record entry location in + section: Option, } impl PatchableFunctionEntry { - pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self { - Self { prefix: config.prefix(), entry: config.entry() } + pub fn from_prefix_entry_and_section( + prefix: Option, + entry: Option, + section: Option, + ) -> Self { + Self { prefix, entry, section } } pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self { - Self { prefix, entry } + Self { prefix: Some(prefix), entry: Some(entry), section: None } } - pub fn prefix(&self) -> u8 { + pub fn prefix(&self) -> Option { self.prefix } - pub fn entry(&self) -> u8 { + pub fn entry(&self) -> Option { self.entry } + pub fn section(&self) -> Option { + self.section + } } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, StableHash)] diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index fb770076f0a26..e23ad3c832ef7 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -75,7 +75,7 @@ impl<'tcx> ExportedSymbol<'tcx> { tcx.symbol_name(ty::Instance::resolve_async_drop_in_place_poll(tcx, def_id, ty)) } ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance { - def: ty::InstanceKind::ThreadLocalShim(def_id), + def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }), ExportedSymbol::NoDefId(symbol_name) => symbol_name, diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 39c73350f7e41..44c5e66279c14 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -221,7 +221,7 @@ pub enum Const<'tcx> { /// An unevaluated mir constant which is not part of the type system. /// - /// Note that `Ty(ty::ConstKind::Unevaluated)` and this variant are *not* identical! `Ty` will + /// Note that `Ty(ty::ConstKind::Alias)` and this variant are *not* identical! `Ty` will /// always flow through a valtree, so all data not captured in the valtree is lost. This variant /// directly uses the evaluated result of the given constant, including e.g. data stored in /// padding. @@ -239,14 +239,17 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, def_id: DefId, ) -> ty::EarlyBinder<'tcx, Const<'tcx>> { - ty::EarlyBinder::bind(Const::Unevaluated( - UnevaluatedConst { - def: def_id, - args: ty::GenericArgs::identity_for_item(tcx, def_id), - promoted: None, - }, - tcx.type_of(def_id).skip_binder(), - )) + ty::EarlyBinder::bind( + tcx, + Const::Unevaluated( + UnevaluatedConst { + def: def_id, + args: ty::GenericArgs::identity_for_item(tcx, def_id), + promoted: None, + }, + tcx.type_of(def_id).skip_binder(), + ), + ) } #[inline(always)] @@ -317,7 +320,9 @@ impl<'tcx> Const<'tcx> { ) -> Result { match self { Const::Ty(_, c) => { - if c.has_non_region_param() { + // FIXME(generic_const_exprs): We shouldn't encounter placeholders here + // and could change this to ICE when encountering them instead. + if c.has_non_region_param() || c.has_non_region_placeholders() { return Err(ErrorHandled::TooGeneric(span)); } @@ -467,13 +472,9 @@ pub struct UnevaluatedConst<'tcx> { impl<'tcx> UnevaluatedConst<'tcx> { #[inline] - pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::UnevaluatedConst<'tcx> { + pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::AliasConst<'tcx> { assert_eq!(self.promoted, None); - ty::UnevaluatedConst::new( - tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, self.def), - self.args, - ) + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, self.def), self.args) } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 51cf856a2d477..accd00a8ebb51 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -90,7 +90,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn const_eval_resolve_for_typeck( self, typing_env: ty::TypingEnv<'tcx>, - ct: ty::UnevaluatedConst<'tcx>, + ct: ty::AliasConst<'tcx>, span: Span, ) -> ConstToValTreeResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference @@ -104,10 +104,10 @@ impl<'tcx> TyCtxt<'tcx> { } let def_id = match ct.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => def_id, + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => def_id, }; let cid = match ty::Instance::try_resolve(self, typing_env, def_id, ct.args) { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c12e72d1dce0f..1d809a2c84ca0 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -32,8 +32,8 @@ use crate::mir::interpret::{AllocRange, Scalar}; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths}; use crate::ty::{ - self, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypeVisitableExt, - TypingEnv, UserTypeAnnotationIndex, + self, GenericArg, GenericArgsRef, Instance, InstanceKind, List, ShimKind, Ty, TyCtxt, + TypeVisitableExt, TypingEnv, UserTypeAnnotationIndex, }; mod basic_blocks; @@ -127,8 +127,8 @@ impl<'tcx> MirSource<'tcx> { MirSource { instance: InstanceKind::Item(def_id), promoted: None } } - pub fn from_instance(instance: InstanceKind<'tcx>) -> Self { - MirSource { instance, promoted: None } + pub fn from_shim(shim: ShimKind<'tcx>) -> Self { + MirSource { instance: InstanceKind::Shim(shim), promoted: None } } #[inline] @@ -530,8 +530,8 @@ impl<'tcx> Body<'tcx> { /// Returns the return type; it always return first element from `local_decls` array. #[inline] - pub fn bound_return_ty(&self) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - ty::EarlyBinder::bind(self.local_decls[RETURN_PLACE].ty) + pub fn bound_return_ty(&self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { + ty::EarlyBinder::bind(tcx, self.local_decls[RETURN_PLACE].ty) } /// Gets the location of the terminator for the given block. @@ -624,7 +624,7 @@ impl<'tcx> Body<'tcx> { let mono_literal = instance.instantiate_mir_and_normalize_erasing_regions( tcx, typing_env, - crate::ty::EarlyBinder::bind(constant.const_), + crate::ty::EarlyBinder::bind(tcx, constant.const_), ); mono_literal.try_eval_bits(tcx, typing_env) }; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 942c36f343343..8e83d44fbb78b 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -218,7 +218,7 @@ impl<'a, 'tcx> MirDumper<'a, 'tcx> { // All drop shims have the same DefId, so we have to add the type // to get unique file names. let shim_disambiguator = match source.instance { - ty::InstanceKind::DropGlue(_, Some(ty)) => { + ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, Some(ty))) => { // Unfortunately, pretty-printed types are not very filename-friendly. // We do some filtering. let mut s = ".".to_owned(); @@ -229,7 +229,7 @@ impl<'a, 'tcx> MirDumper<'a, 'tcx> { })); s } - ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => { + ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(_, ty)) => { let mut s = ".".to_owned(); s.extend(ty.to_string().chars().filter_map(|c| match c { ' ' => None, @@ -238,7 +238,7 @@ impl<'a, 'tcx> MirDumper<'a, 'tcx> { })); s } - ty::InstanceKind::AsyncDropGlue(_, ty) => { + ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(_, ty)) => { let ty::Coroutine(_, args) = ty.kind() else { bug!(); }; @@ -251,7 +251,7 @@ impl<'a, 'tcx> MirDumper<'a, 'tcx> { })); s } - ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => { + ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(_, proxy_cor, impl_cor)) => { let mut s = ".".to_owned(); s.extend(proxy_cor.to_string().chars().filter_map(|c| match c { ' ' => None, @@ -1491,16 +1491,14 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { let val = match const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(p) => format!("ty::Param({p})"), - ty::ConstKind::Unevaluated(uv) => { - let kind = match uv.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { - self.tcx.def_path_str(def_id) - } + ty::ConstKind::Alias(_, alias_const) => { + let kind = match alias_const.kind { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => self.tcx.def_path_str(def_id), }; - format!("ty::Unevaluated({}, {:?})", kind, uv.args) + format!("ty::AliasConst({}, {:?})", kind, alias_const.args) } ty::ConstKind::Value(cv) => { format!("ty::Valtree({})", fmt_valtree(&cv)) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index fb89e9ac884a0..921c54b2828c2 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -347,27 +347,27 @@ macro_rules! make_mir_visitor { ty::InstanceKind::Item(_def_id) => {} ty::InstanceKind::Intrinsic(_def_id) - | ty::InstanceKind::VTableShim(_def_id) - | ty::InstanceKind::ReifyShim(_def_id, _) + | ty::InstanceKind::Shim(ty::ShimKind::VTable(_def_id)) + | ty::InstanceKind::Shim(ty::ShimKind::Reify(_def_id, _)) | ty::InstanceKind::Virtual(_def_id, _) - | ty::InstanceKind::ThreadLocalShim(_def_id) - | ty::InstanceKind::ClosureOnceShim { call_once: _def_id, closure: _, track_caller: _ } - | ty::InstanceKind::ConstructCoroutineInClosureShim { + | ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(_def_id)) + | ty::InstanceKind::Shim(ty::ShimKind::ClosureOnce { call_once: _def_id, closure: _, track_caller: _ }) + | ty::InstanceKind::Shim(ty::ShimKind::ConstructCoroutineInClosure { coroutine_closure_def_id: _def_id, receiver_by_ref: _, - } - | ty::InstanceKind::DropGlue(_def_id, None) => {} - - ty::InstanceKind::FnPtrShim(_def_id, ty) - | ty::InstanceKind::DropGlue(_def_id, Some(ty)) - | ty::InstanceKind::CloneShim(_def_id, ty) - | ty::InstanceKind::FnPtrAddrShim(_def_id, ty) - | ty::InstanceKind::AsyncDropGlue(_def_id, ty) - | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, ty) => { + }) + | ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_def_id, None)) => {} + + ty::InstanceKind::Shim(ty::ShimKind::FnPtr(_def_id, ty)) + | ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_def_id, Some(ty))) + | ty::InstanceKind::Shim(ty::ShimKind::Clone(_def_id, ty)) + | ty::InstanceKind::Shim(ty::ShimKind::FnPtrAddr(_def_id, ty)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(_def_id, ty)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(_def_id, ty)) => { // FIXME(eddyb) use a better `TyContext` here. self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } - ty::InstanceKind::FutureDropPollShim(_def_id, proxy_ty, impl_ty) => { + ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(_def_id, proxy_ty, impl_ty)) => { self.visit_ty($(& $mutability)? *proxy_ty, TyContext::Location(location)); self.visit_ty($(& $mutability)? *impl_ty, TyContext::Location(location)); } diff --git a/compiler/rustc_middle/src/mono.rs b/compiler/rustc_middle/src/mono.rs index 2c1a9d1ed7bfb..6df67257a2ea7 100644 --- a/compiler/rustc_middle/src/mono.rs +++ b/compiler/rustc_middle/src/mono.rs @@ -22,7 +22,7 @@ use tracing::debug; use crate::dep_graph::dep_node::{make_compile_codegen_unit, make_compile_mono_item}; use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::ty::{self, GenericArgs, Instance, InstanceKind, SymbolName, Ty, TyCtxt}; +use crate::ty::{self, GenericArgs, Instance, InstanceKind, ShimKind, SymbolName, Ty, TyCtxt}; /// Describes how a monomorphization will be instantiated in object files. #[derive(PartialEq)] @@ -173,7 +173,7 @@ impl<'tcx> MonoItem<'tcx> { // incrementality (which wants small CGUs with as many things GloballyShared as possible). // The heuristics implemented here do better than a completely naive approach in the // compiler benchmark suite, but there is no reason to believe they are optimal. - if let InstanceKind::DropGlue(_, Some(ty)) = instance.def { + if let InstanceKind::Shim(ShimKind::DropGlue(_, Some(ty))) = instance.def { if tcx.sess.opts.optimize == OptLevel::No { return InstantiationMode::GloballyShared { may_conflict: false }; } @@ -523,20 +523,20 @@ impl<'tcx> CodegenUnit<'tcx> { match item { MonoItem::Fn(ref instance) => match instance.def { InstanceKind::Item(def) => def.as_local().map(|_| def), - InstanceKind::VTableShim(..) - | InstanceKind::ReifyShim(..) - | InstanceKind::Intrinsic(..) - | InstanceKind::FnPtrShim(..) + InstanceKind::Intrinsic(..) | InstanceKind::Virtual(..) - | InstanceKind::ClosureOnceShim { .. } - | InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::DropGlue(..) - | InstanceKind::CloneShim(..) - | InstanceKind::ThreadLocalShim(..) - | InstanceKind::FnPtrAddrShim(..) - | InstanceKind::AsyncDropGlue(..) - | InstanceKind::FutureDropPollShim(..) - | InstanceKind::AsyncDropGlueCtorShim(..) => None, + | InstanceKind::Shim(ShimKind::VTable(..)) + | InstanceKind::Shim(ShimKind::Reify(..)) + | InstanceKind::Shim(ShimKind::FnPtr(..)) + | InstanceKind::Shim(ShimKind::ClosureOnce { .. }) + | InstanceKind::Shim(ShimKind::ConstructCoroutineInClosure { .. }) + | InstanceKind::Shim(ShimKind::DropGlue(..)) + | InstanceKind::Shim(ShimKind::Clone(..)) + | InstanceKind::Shim(ShimKind::ThreadLocal(..)) + | InstanceKind::Shim(ShimKind::FnPtrAddr(..)) + | InstanceKind::Shim(ShimKind::AsyncDropGlue(..)) + | InstanceKind::Shim(ShimKind::FutureDropPoll(..)) + | InstanceKind::Shim(ShimKind::AsyncDropGlueCtor(..)) => None, }, MonoItem::Static(def_id) => def_id.as_local().map(|_| def_id), MonoItem::GlobalAsm(item_id) => Some(item_id.owner_id.def_id.to_def_id()), diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index d4817888468fa..f0557c3d3381a 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -541,7 +541,7 @@ rustc_queries! { desc { "computing `#[expect]`ed lints in this crate" } } - query lints_that_dont_need_to_run(_: ()) -> &'tcx UnordSet { + query skippable_lints(_: ()) -> &'tcx UnordSet { arena_cache // This depends on the lint store, which includes internal lints when the // untracked `-Zunstable-options` flag is set. @@ -1400,10 +1400,10 @@ rustc_queries! { } /// Generates a MIR body for the shim. - query mir_shims(key: ty::InstanceKind<'tcx>) -> &'tcx mir::Body<'tcx> { + query mir_shims(key: ty::ShimKind<'tcx>) -> &'tcx mir::Body<'tcx> { arena_cache desc { - "generating MIR shim for `{}`, instance={:?}", + "generating MIR shim for `{}`, kind={:?}", tcx.def_path_str(key.def_id()), key } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index c290ba62a3930..5eedeb4f9ed2e 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -63,6 +63,12 @@ impl QueryKey for () { } } +impl<'tcx> QueryKey for ty::ShimKind<'tcx> { + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + tcx.def_span(self.def_id()) + } +} + impl<'tcx> QueryKey for ty::InstanceKind<'tcx> { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) @@ -385,7 +391,7 @@ fn def_id_of_type_cached<'a>(ty: Ty<'a>, visited: &mut SsoHashSet>) -> Op | ty::CoroutineWitness(def_id, _) | ty::Foreign(def_id) => Some(def_id), - ty::Alias(alias) => match alias.kind { + ty::Alias(_, alias) => match alias.kind { ty::AliasTyKind::Projection { def_id } | ty::AliasTyKind::Inherent { def_id } | ty::AliasTyKind::Opaque { def_id } diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index ac38053d153cc..43ed22927da56 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -52,11 +52,13 @@ impl<'tcx> TyCtxt<'tcx> { } fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> { let ct = match c.kind() { - ty::ConstKind::Unevaluated(uv) if let Some(def_id) = uv.kind.opt_def_id() => { + ty::ConstKind::Alias(_, alias_const) + if let Some(def_id) = alias_const.kind.opt_def_id() => + { match self.tcx.thir_abstract_const(def_id) { Err(e) => ty::Const::new_error(self.tcx, e), Ok(Some(bac)) => { - let args = self.tcx.erase_and_anonymize_regions(uv.args); + let args = self.tcx.erase_and_anonymize_regions(alias_const.args); let bac = bac.instantiate(self.tcx, args).skip_norm_wip(); return bac.fold_with(self); } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index e26dac94edd16..d380b7f522636 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -292,7 +292,7 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { self, tcx: TyCtxt<'tcx>, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - ty::EarlyBinder::bind( + ty::EarlyBinder::bind_iter( self.all_fields().map(move |field| tcx.type_of(field.did).skip_binder()), ) } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 30d9a9fb1650c..df25b08168609 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -21,8 +21,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed}; pub use valtree::*; pub type ConstKind<'tcx> = ir::ConstKind>; -pub type UnevaluatedConst<'tcx> = ir::UnevaluatedConst>; -pub type UnevaluatedConstKind<'tcx> = ir::UnevaluatedConstKind>; +pub type AliasConst<'tcx> = ir::AliasConst>; +pub type AliasConstKind<'tcx> = ir::AliasConstKind>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(ConstKind<'_>, 32); @@ -107,8 +107,12 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn new_unevaluated(tcx: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Unevaluated(uv)) + pub fn new_alias( + tcx: TyCtxt<'tcx>, + is_rigid: ty::IsRigid, + alias_const: ty::AliasConst<'tcx>, + ) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Alias(is_rigid, alias_const)) } #[inline] @@ -153,7 +157,7 @@ impl<'tcx> Const<'tcx> { true } ty::ConstKind::Infer(_) - | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Alias(..) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => false, @@ -190,8 +194,12 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { Const::new_placeholder(tcx, placeholder) } - fn new_unevaluated(interner: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Self { - Const::new_unevaluated(interner, uv) + fn new_alias( + interner: TyCtxt<'tcx>, + is_rigid: ty::IsRigid, + alias_const: ty::AliasConst<'tcx>, + ) -> Self { + Const::new_alias(interner, is_rigid, alias_const) } fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 32d4678b8ecb8..3d7aef0b273f1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -601,7 +601,14 @@ impl<'tcx> TyCtxt<'tcx> { /// effect. However, we do not want this as a general capability, so this interface restricts /// to the only allowed case. pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder<'tcx, Ty<'tcx>>) { - debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); + if cfg!(debug_assertions) { + match self.def_kind(key) { + DefKind::AnonConst => (), + DefKind::InlineConst => assert!(self.is_type_system_inline_const(key)), + def_kind => bug!("unexpected DefKind in feed_anon_const_type: {def_kind:?}"), + } + } + TyCtxtFeed { tcx: self, key }.type_of(value) } @@ -2058,7 +2065,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Given a `ty`, return whether it's an `impl Future<...>`. pub fn ty_is_opaque_future(self, ty: Ty<'_>) -> bool { - let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = *ty.kind() else { + let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = *ty.kind() else { return false; }; let future_trait = self.require_lang_item(LangItem::Future, DUMMY_SP); @@ -2085,6 +2092,8 @@ impl<'tcx> TyCtxt<'tcx> { ty::Tuple(params) => *params, _ => bug!(), }; + // Ignore splatting, it is unsupported on closures. + assert!(s.splatted().is_none()); self.mk_fn_sig( params, s.output(), @@ -2690,6 +2699,10 @@ impl<'tcx> TyCtxt<'tcx> { self.sess.opts.unstable_opts.disable_fast_paths } + pub fn renormalize_rigid_aliases(self) -> bool { + self.sess.opts.unstable_opts.renormalize_rigid_aliases + } + #[allow(rustc::bad_opt_access)] pub fn use_typing_mode_post_typeck_until_borrowck(self) -> bool { self.next_trait_solver_globally() @@ -2706,8 +2719,8 @@ impl<'tcx> TyCtxt<'tcx> { pub fn get_impl_future_output_ty(self, ty: Ty<'tcx>) -> Option> { let (def_id, args) = match *ty.kind() { - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => (def_id, args), - ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => (def_id, args), + ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) if self.is_impl_trait_in_trait(def_id) => { (def_id, args) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index cf570197ad968..ada3298a6ca6e 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -41,7 +41,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type CoroutineId = DefId; type AdtId = DefId; type ImplId = DefId; - type UnevaluatedConstId = DefId; + type AnonConstId = DefId; type TraitAssocTyId = DefId; type TraitAssocConstId = DefId; type TraitAssocTermId = DefId; @@ -208,23 +208,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } - fn unevaluated_const_kind_from_def_id( - self, - def_id: Self::DefId, - ) -> ty::UnevaluatedConstKind<'tcx> { + fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind<'tcx> { match self.def_kind(def_id) { DefKind::AssocConst { .. } => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - ty::UnevaluatedConstKind::Inherent { def_id } + ty::AliasConstKind::Inherent { def_id } } else { - ty::UnevaluatedConstKind::Projection { def_id } + ty::AliasConstKind::Projection { def_id } } } - DefKind::Const { .. } => ty::UnevaluatedConstKind::Free { def_id }, + DefKind::Const { .. } => ty::AliasConstKind::Free { def_id }, DefKind::AnonConst | DefKind::InlineConst | DefKind::Ctor(_, CtorKind::Const) => { - ty::UnevaluatedConstKind::Anon { def_id } + ty::AliasConstKind::Anon { def_id } } - kind => bug!("unexpected DefKind in UnevaluatedConst: {kind:?}"), + kind => bug!("unexpected DefKind in AliasConst: {kind:?}"), } } @@ -247,7 +244,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy { def_id }, DefKind::TyAlias => ty::AliasTermKind::FreeTy { def_id }, DefKind::Const { .. } => ty::AliasTermKind::FreeConst { def_id }, - DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { + DefKind::AnonConst | DefKind::InlineConst | DefKind::Ctor(_, CtorKind::Const) => { ty::AliasTermKind::AnonConst { def_id } } kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), @@ -340,6 +337,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.assumptions_on_binders() } + fn renormalize_rigid_aliases(self) -> bool { + self.renormalize_rigid_aliases() + } + fn coroutine_hidden_types( self, def_id: DefId, @@ -388,7 +389,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - ty::EarlyBinder::bind( + ty::EarlyBinder::bind_iter( self.predicates_of(def_id) .instantiate_identity(self) .predicates @@ -401,7 +402,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - ty::EarlyBinder::bind( + ty::EarlyBinder::bind_iter( self.predicates_of(def_id) .instantiate_own_identity() .map(|(clause, _)| clause.skip_normalization()), @@ -456,7 +457,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>>> { - ty::EarlyBinder::bind( + ty::EarlyBinder::bind_iter( self.const_conditions(def_id) .instantiate_identity(self) .into_iter() @@ -468,7 +469,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>>> { - ty::EarlyBinder::bind( + ty::EarlyBinder::bind_iter( self.explicit_implied_const_bounds(def_id) .iter_identity_copied() .map(Unnormalized::skip_normalization) @@ -540,8 +541,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { // only want to consider types that *actually* unify with float/int vars. fn for_each_relevant_impl( self, - trait_def_id: DefId, - self_ty: Ty<'tcx>, + trait_ref: ty::TraitRef<'tcx>, mut f: impl FnMut(DefId) -> R, ) -> R { macro_rules! ret { @@ -553,6 +553,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { }; } + let trait_def_id = trait_ref.def_id; + let self_ty = trait_ref.self_ty(); let tcx = self; let trait_impls = tcx.trait_impls_of(trait_def_id); let mut consider_impls_for_simplified_type = |simp| { @@ -645,7 +647,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { // // Impls which apply to an alias after normalization are handled by // `assemble_candidates_after_normalizing_self_ty`. - ty::Alias(_) | ty::Placeholder(..) | ty::Error(_) => (), + ty::Alias(ty::IsRigid::Yes, _) | ty::Placeholder(..) | ty::Error(_) => (), + // FIXME(-Znext-solver=no): Need to support aliases not marked as + // rigid for the old solver. + ty::Alias(ty::IsRigid::No, _) => (), // FIXME: These should ideally not exist as a self type. It would be nice for // the builtin auto trait impls of coroutines to instead directly recurse diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index a9e7425fdcbf6..fadf1ee2ee384 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -627,11 +627,11 @@ impl<'tcx> TypeVisitor> for IsSuggestableVisitor<'tcx> { return ControlFlow::Break(()); } - Alias(AliasTy { kind: Opaque { def_id }, .. }) => { + Alias(_, AliasTy { kind: Opaque { def_id }, .. }) => { let parent = self.tcx.parent(def_id); let parent_ty = self.tcx.type_of(parent).instantiate_identity().skip_norm_wip(); if let DefKind::TyAlias | DefKind::AssocTy = self.tcx.def_kind(parent) - && let Alias(AliasTy { kind: Opaque { def_id: parent_opaque_def_id }, .. }) = + && let Alias(_, AliasTy { kind: Opaque { def_id: parent_opaque_def_id }, .. }) = *parent_ty.kind() && parent_opaque_def_id == def_id { @@ -641,7 +641,7 @@ impl<'tcx> TypeVisitor> for IsSuggestableVisitor<'tcx> { } } - Alias(AliasTy { kind: Projection { def_id }, .. }) + Alias(_, AliasTy { kind: Projection { def_id }, .. }) if self.tcx.def_kind(def_id) != DefKind::AssocTy => { return ControlFlow::Break(()); @@ -715,12 +715,12 @@ impl<'tcx> FallibleTypeFolder> for MakeSuggestableFolder<'tcx> { placeholder } - Alias(AliasTy { kind: Opaque { def_id }, .. }) => { + Alias(_, AliasTy { kind: Opaque { def_id }, .. }) => { let parent = self.tcx.parent(def_id); let parent_ty = self.tcx.type_of(parent).instantiate_identity().skip_norm_wip(); if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent) - && let Alias(AliasTy { kind: Opaque { def_id: parent_opaque_def_id }, .. }) = + && let Alias(_, AliasTy { kind: Opaque { def_id: parent_opaque_def_id }, .. }) = *parent_ty.kind() && parent_opaque_def_id == def_id { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 8dda3603df9df..208e94270a45e 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -96,6 +96,20 @@ impl<'tcx> TypeError<'tcx> { if values.found { "variadic" } else { "non-variadic" } ) .into(), + TypeError::SplatMismatch(ref values) => format!( + "expected fn with {}, found fn with {}", + if let Some(index) = values.expected { + format!("arg {index} splatted") + } else { + "no splatted arg".to_string() + }, + if let Some(index) = values.found { + format!("arg {index} splatted") + } else { + "no splatted arg".to_string() + } + ) + .into(), TypeError::ProjectionMismatched(ref values) => format!( "expected `{}`, found `{}`", tcx.alias_term_kind_def_path_str(values.expected), @@ -148,11 +162,11 @@ impl<'tcx> Ty<'tcx> { ty::Infer(ty::FreshTy(_)) => "fresh type".into(), ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, .. - }) => "associated type".into(), + ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }) => { + "associated type".into() + } ty::Param(p) => format!("type parameter `{p}`").into(), - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { if tcx.ty_is_opaque_future(self) { "future".into() } else { "opaque type".into() } } ty::Error(_) => "type error".into(), @@ -207,12 +221,12 @@ impl<'tcx> Ty<'tcx> { ty::Tuple(..) => "tuple".into(), ty::Placeholder(..) => "higher-ranked type".into(), ty::Bound(..) => "bound type variable".into(), - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, .. - }) => "associated type".into(), - ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) => "type alias".into(), + ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }) => { + "associated type".into() + } + ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => "type alias".into(), ty::Param(_) => "type parameter".into(), - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) => "opaque type".into(), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => "opaque type".into(), } } } diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 6eb5e83005975..41adea64eeecb 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -402,8 +402,9 @@ impl<'tcx> GenericPredicates<'tcx> { args: GenericArgsRef<'tcx>, ) -> impl Iterator>, Span)> + DoubleEndedIterator - + ExactSizeIterator { - EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args).map(|u| { + + ExactSizeIterator + + Clone { + EarlyBinder::bind_iter(self.predicates).iter_instantiated_copied(tcx, args).map(|u| { let (clause, span) = u.unzip(); (clause, span.skip_normalization()) }) @@ -413,8 +414,9 @@ impl<'tcx> GenericPredicates<'tcx> { self, ) -> impl Iterator>, Span)> + DoubleEndedIterator - + ExactSizeIterator { - EarlyBinder::bind(self.predicates).iter_identity_copied().map(|u| { + + ExactSizeIterator + + Clone { + EarlyBinder::bind_iter(self.predicates).iter_identity_copied().map(|u| { let (clause, span) = u.unzip(); (clause, span.skip_normalization()) }) @@ -431,7 +433,7 @@ impl<'tcx> GenericPredicates<'tcx> { tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, args); } instantiated.predicates.extend( - self.predicates.iter().map(|(p, _)| EarlyBinder::bind(*p).instantiate(tcx, args)), + self.predicates.iter().map(|(p, _)| EarlyBinder::bind(tcx, *p).instantiate(tcx, args)), ); instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); } @@ -482,8 +484,9 @@ impl<'tcx> ConstConditions<'tcx> { args: GenericArgsRef<'tcx>, ) -> impl Iterator>, Span)> + DoubleEndedIterator - + ExactSizeIterator { - EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args).map(|u| { + + ExactSizeIterator + + Clone { + EarlyBinder::bind_iter(self.predicates).iter_instantiated_copied(tcx, args).map(|u| { let (trait_ref, span) = u.unzip(); (trait_ref, span.skip_normalization()) }) @@ -493,8 +496,9 @@ impl<'tcx> ConstConditions<'tcx> { self, ) -> impl Iterator>, Span)> + DoubleEndedIterator - + ExactSizeIterator { - EarlyBinder::bind(self.predicates).iter_identity_copied().map(|u| { + + ExactSizeIterator + + Clone { + EarlyBinder::bind_iter(self.predicates).iter_identity_copied().map(|u| { let (trait_ref, span) = u.unzip(); (trait_ref, span.skip_normalization()) }) @@ -511,7 +515,9 @@ impl<'tcx> ConstConditions<'tcx> { tcx.const_conditions(def_id).instantiate_into(tcx, instantiated, args); } instantiated.extend( - self.predicates.iter().map(|&(p, s)| (EarlyBinder::bind(p).instantiate(tcx, args), s)), + self.predicates + .iter() + .map(|&(p, s)| (EarlyBinder::bind(tcx, p).instantiate(tcx, args), s)), ); } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index 32625e6b3280e..03a6163b33ade 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -243,7 +243,7 @@ impl<'tcx> InhabitedPredicate<'tcx> { fn instantiate_opt(self, tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> Option { match self { Self::ConstIsZero(c) => { - let c = ty::EarlyBinder::bind(c).instantiate(tcx, args).skip_norm_wip(); + let c = ty::EarlyBinder::bind(tcx, c).instantiate(tcx, args).skip_norm_wip(); let pred = match c.try_to_target_usize(tcx) { Some(0) => Self::True, Some(1..) => Self::False, @@ -252,7 +252,7 @@ impl<'tcx> InhabitedPredicate<'tcx> { Some(pred) } Self::GenericType(t) => Some( - ty::EarlyBinder::bind(t) + ty::EarlyBinder::bind(tcx, t) .instantiate(tcx, args) .skip_norm_wip() .inhabited_predicate(tcx), diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 6aff85e77da5a..57e48d3993019 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -113,12 +113,16 @@ impl<'tcx> Ty<'tcx> { InhabitedPredicate::True } Never => InhabitedPredicate::False, + // FIXME(#155345): This should only encounter rigid aliases with the new solver. Param(_) - | Alias(ty::AliasTy { - kind: ty::Inherent { .. } | ty::Projection { .. } | ty::Free { .. }, - .. - }) => InhabitedPredicate::GenericType(self), - &Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + | Alias( + _, + ty::AliasTy { + kind: ty::Inherent { .. } | ty::Projection { .. } | ty::Free { .. }, + .. + }, + ) => InhabitedPredicate::GenericType(self), + &Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { match def_id.as_local() { // Foreign opaque is considered inhabited. None => InhabitedPredicate::True, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 8734d2cde8152..c4cccecec6548 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -75,12 +75,28 @@ pub enum InstanceKind<'tcx> { /// caller. Intrinsic(DefId), + /// Dynamic dispatch to `::fn`. + /// + /// This `InstanceKind` may have a callable MIR as the default implementation. + /// Calls to `Virtual` instances must be codegen'd as virtual calls through the vtable. + /// *This means we might not know exactly what is being called.* + /// + /// If this is reified to a `fn` pointer, a `ReifyShim` is used (see `ReifyShim` above for more + /// details on that). + Virtual(DefId, usize), + + Shim(ShimKind<'tcx>), +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable, Lift)] +pub enum ShimKind<'tcx> { /// `::method` where `method` receives unsizeable `self: Self` (part of the /// `unsized_fn_params` feature). /// /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` - /// and dereference the argument to call the original function. - VTableShim(DefId), + VTable(DefId), /// `fn()` pointer where the function itself cannot be turned into a pointer. /// @@ -99,22 +115,12 @@ pub enum InstanceKind<'tcx> { /// /// This field will only be populated if we are compiling in a mode that needs these shims /// to be separable, currently only when KCFI is enabled. - ReifyShim(DefId, Option), + Reify(DefId, Option), /// `::call_*` (generated `FnTrait` implementation for `fn()` pointers). /// /// `DefId` is `FnTrait::call_*`. - FnPtrShim(DefId, Ty<'tcx>), - - /// Dynamic dispatch to `::fn`. - /// - /// This `InstanceKind` may have a callable MIR as the default implementation. - /// Calls to `Virtual` instances must be codegen'd as virtual calls through the vtable. - /// *This means we might not know exactly what is being called.* - /// - /// If this is reified to a `fn` pointer, a `ReifyShim` is used (see `ReifyShim` above for more - /// details on that). - Virtual(DefId, usize), + FnPtr(DefId, Ty<'tcx>), /// `<[FnMut/Fn closure] as FnOnce>::call_once`. /// @@ -122,14 +128,14 @@ pub enum InstanceKind<'tcx> { /// /// This generates a body that will just borrow the (owned) self type, /// and dispatch to the `FnMut::call_mut` instance for the closure. - ClosureOnceShim { call_once: DefId, closure: DefId, track_caller: bool }, + ClosureOnce { call_once: DefId, closure: DefId, track_caller: bool }, /// `<[FnMut/Fn coroutine-closure] as FnOnce>::call_once` /// /// The body generated here differs significantly from the `ClosureOnceShim`, /// since we need to generate a distinct coroutine type that will move the /// closure's upvars *out* of the closure. - ConstructCoroutineInClosureShim { + ConstructCoroutineInClosure { coroutine_closure_def_id: DefId, // Whether the generated MIR body takes the coroutine by-ref. This is // because the signature of `<{async fn} as FnMut>::call_mut` is: @@ -142,10 +148,10 @@ pub enum InstanceKind<'tcx> { /// Compiler-generated accessor for thread locals which returns a reference to the thread local /// the `DefId` defines. This is used to export thread locals from dylibs on platforms lacking /// native support. - ThreadLocalShim(DefId), + ThreadLocal(DefId), /// Proxy shim for async drop of future (def_id, proxy_cor_ty, impl_cor_ty) - FutureDropPollShim(DefId, Ty<'tcx>, Ty<'tcx>), + FutureDropPoll(DefId, Ty<'tcx>, Ty<'tcx>), /// `core::ptr::drop_glue::`. /// @@ -162,20 +168,20 @@ pub enum InstanceKind<'tcx> { /// Additionally, arrays, tuples, and closures get a `Clone` shim even if they aren't `Copy`. /// /// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl. - CloneShim(DefId, Ty<'tcx>), + Clone(DefId, Ty<'tcx>), /// Compiler-generated `::addr` implementation. /// /// Automatically generated for all potentially higher-ranked `fn(I) -> R` types. /// /// The `DefId` is for `FnPtr::addr`, the `Ty` is the type `T`. - FnPtrAddrShim(DefId, Ty<'tcx>), + FnPtrAddr(DefId, Ty<'tcx>), /// `core::future::async_drop::async_drop_in_place::<'_, T>`. /// /// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty` /// is the type `T`. - AsyncDropGlueCtorShim(DefId, Ty<'tcx>), + AsyncDropGlueCtor(DefId, Ty<'tcx>), /// `core::future::async_drop::async_drop_in_place::<'_, T>::{closure}`. /// @@ -228,10 +234,12 @@ impl<'tcx> Instance<'tcx> { InstanceKind::Item(def) => tcx .upstream_monomorphizations_for(def) .and_then(|monos| monos.get(&self.args).cloned()), - InstanceKind::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args), - InstanceKind::AsyncDropGlue(_, _) => None, - InstanceKind::FutureDropPollShim(_, _, _) => None, - InstanceKind::AsyncDropGlueCtorShim(_, _) => { + InstanceKind::Shim(ShimKind::DropGlue(_, Some(_))) => { + tcx.upstream_drop_glue_for(self.args) + } + InstanceKind::Shim(ShimKind::AsyncDropGlue(_, _)) => None, + InstanceKind::Shim(ShimKind::FutureDropPoll(_, _, _)) => None, + InstanceKind::Shim(ShimKind::AsyncDropGlueCtor(_, _)) => { tcx.upstream_async_drop_glue_for(self.args) } _ => None, @@ -244,45 +252,18 @@ impl<'tcx> InstanceKind<'tcx> { pub fn def_id(self) -> DefId { match self { InstanceKind::Item(def_id) - | InstanceKind::VTableShim(def_id) - | InstanceKind::ReifyShim(def_id, _) - | InstanceKind::FnPtrShim(def_id, _) | InstanceKind::Virtual(def_id, _) - | InstanceKind::Intrinsic(def_id) - | InstanceKind::ThreadLocalShim(def_id) - | InstanceKind::ClosureOnceShim { call_once: def_id, closure: _, track_caller: _ } - | ty::InstanceKind::ConstructCoroutineInClosureShim { - coroutine_closure_def_id: def_id, - receiver_by_ref: _, - } - | InstanceKind::DropGlue(def_id, _) - | InstanceKind::CloneShim(def_id, _) - | InstanceKind::FnPtrAddrShim(def_id, _) - | InstanceKind::FutureDropPollShim(def_id, _, _) - | InstanceKind::AsyncDropGlue(def_id, _) - | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id, + | InstanceKind::Intrinsic(def_id) => def_id, + InstanceKind::Shim(shim) => shim.def_id(), } } /// Returns the `DefId` of instances which might not require codegen locally. pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option { match self { - ty::InstanceKind::Item(def) => Some(def), - ty::InstanceKind::DropGlue(def_id, Some(_)) - | InstanceKind::AsyncDropGlueCtorShim(def_id, _) - | InstanceKind::AsyncDropGlue(def_id, _) - | InstanceKind::FutureDropPollShim(def_id, ..) - | InstanceKind::ThreadLocalShim(def_id) => Some(def_id), - InstanceKind::VTableShim(..) - | InstanceKind::ReifyShim(..) - | InstanceKind::FnPtrShim(..) - | InstanceKind::Virtual(..) - | InstanceKind::Intrinsic(..) - | InstanceKind::ClosureOnceShim { .. } - | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::DropGlue(..) - | InstanceKind::CloneShim(..) - | InstanceKind::FnPtrAddrShim(..) => None, + InstanceKind::Item(def) => Some(def), + InstanceKind::Virtual(..) | InstanceKind::Intrinsic(..) => None, + InstanceKind::Shim(shim) => shim.def_id_if_not_guaranteed_local_codegen(), } } @@ -293,31 +274,28 @@ impl<'tcx> InstanceKind<'tcx> { /// `generates_cgu_internal_copy` for more information. pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool { use rustc_hir::definitions::DefPathData; - let def_id = match *self { - ty::InstanceKind::Item(def) => def, - ty::InstanceKind::DropGlue(_, Some(ty)) => return ty.is_array(), - ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => return ty.is_coroutine(), - ty::InstanceKind::FutureDropPollShim(_, _, _) => return false, - ty::InstanceKind::AsyncDropGlue(_, _) => return false, - ty::InstanceKind::ThreadLocalShim(_) => return false, - _ => return true, - }; - matches!( - tcx.def_key(def_id).disambiguated_data.data, - DefPathData::Ctor | DefPathData::Closure - ) + match *self { + InstanceKind::Item(def_id) => matches!( + tcx.def_key(def_id).disambiguated_data.data, + DefPathData::Ctor | DefPathData::Closure + ), + InstanceKind::Shim(shim) => shim.requires_inline(), + InstanceKind::Virtual(..) | InstanceKind::Intrinsic(..) => true, + } } pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { match *self { InstanceKind::Item(def_id) | InstanceKind::Virtual(def_id, _) - | InstanceKind::VTableShim(def_id) => { + | InstanceKind::Shim(ShimKind::VTable(def_id)) => { tcx.body_codegen_attrs(def_id).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) } - InstanceKind::ClosureOnceShim { call_once: _, closure: _, track_caller } => { - track_caller - } + InstanceKind::Shim(ShimKind::ClosureOnce { + call_once: _, + closure: _, + track_caller, + }) => track_caller, _ => false, } } @@ -330,22 +308,79 @@ impl<'tcx> InstanceKind<'tcx> { /// body should perform necessary instantiations. pub fn has_polymorphic_mir_body(&self) -> bool { match *self { - InstanceKind::CloneShim(..) - | InstanceKind::ThreadLocalShim(..) - | InstanceKind::FnPtrAddrShim(..) - | InstanceKind::FnPtrShim(..) - | InstanceKind::DropGlue(_, Some(_)) - | InstanceKind::FutureDropPollShim(..) - | InstanceKind::AsyncDropGlue(_, _) => false, - InstanceKind::AsyncDropGlueCtorShim(_, _) => false, - InstanceKind::ClosureOnceShim { .. } - | InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::DropGlue(..) - | InstanceKind::Item(_) - | InstanceKind::Intrinsic(..) - | InstanceKind::ReifyShim(..) - | InstanceKind::Virtual(..) - | InstanceKind::VTableShim(..) => true, + InstanceKind::Item(_) | InstanceKind::Intrinsic(..) | InstanceKind::Virtual(..) => true, + InstanceKind::Shim(shim) => shim.has_polymorphic_mir_body(), + } + } +} + +impl<'tcx> ShimKind<'tcx> { + #[inline] + pub fn def_id(self) -> DefId { + match self { + ShimKind::VTable(def_id) + | ShimKind::Reify(def_id, _) + | ShimKind::FnPtr(def_id, _) + | ShimKind::ThreadLocal(def_id) + | ShimKind::ClosureOnce { call_once: def_id, closure: _, track_caller: _ } + | ShimKind::ConstructCoroutineInClosure { + coroutine_closure_def_id: def_id, + receiver_by_ref: _, + } + | ShimKind::DropGlue(def_id, _) + | ShimKind::Clone(def_id, _) + | ShimKind::FnPtrAddr(def_id, _) + | ShimKind::FutureDropPoll(def_id, _, _) + | ShimKind::AsyncDropGlue(def_id, _) + | ShimKind::AsyncDropGlueCtor(def_id, _) => def_id, + } + } + + /// Returns the `DefId` of instances which might not require codegen locally. + pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option { + match self { + ShimKind::DropGlue(def_id, Some(_)) + | ShimKind::AsyncDropGlueCtor(def_id, _) + | ShimKind::AsyncDropGlue(def_id, _) + | ShimKind::FutureDropPoll(def_id, ..) + | ShimKind::ThreadLocal(def_id) => Some(def_id), + ShimKind::VTable(..) + | ShimKind::Reify(..) + | ShimKind::FnPtr(..) + | ShimKind::ClosureOnce { .. } + | ShimKind::ConstructCoroutineInClosure { .. } + | ShimKind::DropGlue(..) + | ShimKind::Clone(..) + | ShimKind::FnPtrAddr(..) => None, + } + } + + pub fn requires_inline(&self) -> bool { + match self { + ShimKind::DropGlue(_, Some(ty)) => ty.is_array(), + ShimKind::AsyncDropGlueCtor(_, ty) => ty.is_coroutine(), + ShimKind::FutureDropPoll(_, _, _) => false, + ShimKind::AsyncDropGlue(_, _) => false, + ShimKind::ThreadLocal(_) => false, + _ => true, + } + } + + pub fn has_polymorphic_mir_body(&self) -> bool { + match *self { + ShimKind::Clone(..) + | ShimKind::ThreadLocal(..) + | ShimKind::FnPtrAddr(..) + | ShimKind::FnPtr(..) + | ShimKind::DropGlue(_, Some(_)) + | ShimKind::FutureDropPoll(..) + | ShimKind::AsyncDropGlue(_, _) => false, + ShimKind::AsyncDropGlueCtor(_, _) => false, + ShimKind::ClosureOnce { .. } + | ShimKind::ConstructCoroutineInClosure { .. } + | ShimKind::DropGlue(..) + | ShimKind::Reify(..) + | ShimKind::VTable(..) => true, } } } @@ -416,7 +451,11 @@ fn resolve_async_drop_poll<'tcx>(mut cor_ty: Ty<'tcx>) -> Instance<'tcx> { continue; } else { return Instance { - def: ty::InstanceKind::FutureDropPollShim(poll_def_id, first_cor, cor_ty), + def: ty::InstanceKind::Shim(ShimKind::FutureDropPoll( + poll_def_id, + first_cor, + cor_ty, + )), args: proxy_args, }; } @@ -426,12 +465,16 @@ fn resolve_async_drop_poll<'tcx>(mut cor_ty: Ty<'tcx>) -> Instance<'tcx> { }; if first_cor != cor_ty { return Instance { - def: ty::InstanceKind::FutureDropPollShim(poll_def_id, first_cor, cor_ty), + def: ty::InstanceKind::Shim(ShimKind::FutureDropPoll( + poll_def_id, + first_cor, + cor_ty, + )), args: proxy_args, }; } else { return Instance { - def: ty::InstanceKind::AsyncDropGlue(poll_def_id, cor_ty), + def: ty::InstanceKind::Shim(ShimKind::AsyncDropGlue(poll_def_id, cor_ty)), args: child_args, }; } @@ -599,11 +642,11 @@ impl<'tcx> Instance<'tcx> { match resolved.def { InstanceKind::Item(def) if resolved.def.requires_caller_location(tcx) => { debug!(" => fn pointer created for function with #[track_caller]"); - resolved.def = InstanceKind::ReifyShim(def, reason); + resolved.def = InstanceKind::Shim(ShimKind::Reify(def, reason)); } InstanceKind::Virtual(def_id, _) => { debug!(" => fn pointer created for virtual call"); - resolved.def = InstanceKind::ReifyShim(def_id, reason); + resolved.def = InstanceKind::Shim(ShimKind::Reify(def_id, reason)); } _ if tcx.sess.is_sanitizer_kcfi_enabled() => { // Reify `::call`-like method implementations @@ -611,7 +654,10 @@ impl<'tcx> Instance<'tcx> { // Reroute through a reify via the *unresolved* instance. The resolved one can't // be directly reified because it's closure-like. The reify can handle the // unresolved instance. - resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } + resolved = Instance { + def: InstanceKind::Shim(ShimKind::Reify(def_id, reason)), + args, + } // Reify `Trait::method` implementations if the trait is dyn-compatible. } else if let Some(assoc) = tcx.opt_associated_item(def_id) && let AssocContainer::Trait | AssocContainer::TraitImpl(Ok(_)) = @@ -620,7 +666,8 @@ impl<'tcx> Instance<'tcx> { { // If this function could also go in a vtable, we need to `ReifyShim` it with // KCFI because it can only attach one type per function. - resolved.def = InstanceKind::ReifyShim(resolved.def_id(), reason) + resolved.def = + InstanceKind::Shim(ShimKind::Reify(resolved.def_id(), reason)) } } _ => {} @@ -645,7 +692,7 @@ impl<'tcx> Instance<'tcx> { if is_vtable_shim { debug!(" => associated item with unsizeable self: Self"); - return Instance { def: InstanceKind::VTableShim(def_id), args }; + return Instance { def: InstanceKind::Shim(ShimKind::VTable(def_id)), args }; } let mut resolved = Instance::expect_resolve(tcx, typing_env, def_id, args, span); @@ -688,19 +735,22 @@ impl<'tcx> Instance<'tcx> { // Create a shim for the `FnOnce/FnMut/Fn` method we are calling // - unlike functions, invoking a closure always goes through a // trait. - resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }; + resolved = Instance { + def: InstanceKind::Shim(ShimKind::Reify(def_id, reason)), + args, + }; } else { debug!( " => vtable fn pointer created for function with #[track_caller]: {:?}", def ); - resolved.def = InstanceKind::ReifyShim(def, reason); + resolved.def = InstanceKind::Shim(ShimKind::Reify(def, reason)); } } } InstanceKind::Virtual(def_id, _) => { debug!(" => vtable fn pointer created for virtual call"); - resolved.def = InstanceKind::ReifyShim(def_id, reason) + resolved.def = InstanceKind::Shim(ShimKind::Reify(def_id, reason)) } _ => {} } @@ -770,8 +820,11 @@ impl<'tcx> Instance<'tcx> { .def_id; let track_caller = tcx.codegen_fn_attrs(closure_did).flags.contains(CodegenFnAttrFlags::TRACK_CALLER); - let def = - ty::InstanceKind::ClosureOnceShim { call_once, closure: closure_did, track_caller }; + let def = ty::InstanceKind::Shim(ShimKind::ClosureOnce { + call_once, + closure: closure_did, + track_caller, + }); let self_ty = Ty::new_closure(tcx, closure_did, args); @@ -863,11 +916,10 @@ impl<'tcx> Instance<'tcx> { self.def.has_polymorphic_mir_body().then_some(self.args) } - pub fn instantiate_mir(&self, tcx: TyCtxt<'tcx>, v: EarlyBinder<'tcx, &T>) -> T + pub fn instantiate_mir(&self, tcx: TyCtxt<'tcx>, v: EarlyBinder<'tcx, T>) -> T where T: TypeFoldable> + Copy, { - let v = v.map_bound(|v| *v); if let Some(args) = self.args_for_mir_body() { v.instantiate(tcx, args).skip_norm_wip() } else { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index a0dd7b5c5cb5e..1f3d4e32714a0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -408,11 +408,13 @@ impl<'tcx> SizeSkeleton<'tcx> { ); match tail.kind() { + // FIXME(#155345): This should only handle rigid aliases if we're using + // the new solver. ty::Param(_) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, - .. - }) => { + | ty::Alias( + _, + ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }, + ) => { debug_assert!(tail.has_non_region_param()); Ok(SizeSkeleton::Pointer { non_zero, @@ -915,7 +917,12 @@ where { let metadata = tcx.normalize_erasing_regions( cx.typing_env(), - Unnormalized::new(Ty::new_projection(tcx, metadata_def_id, [pointee])), + Unnormalized::new(Ty::new_projection( + tcx, + ty::IsRigid::No, + metadata_def_id, + [pointee], + )), ); // Map `Metadata = DynMetadata` back to a vtable, since it diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 980f464cea17f..41079d431edf9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -27,7 +27,6 @@ pub use intrinsic::IntrinsicDef; use rustc_abi::{ Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, ScalableElt, VariantIdx, }; -use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast}; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; @@ -67,7 +66,7 @@ pub use rustc_type_ir::solve::{CandidatePreferenceMode, SizedTraitKind, VisibleF pub use rustc_type_ir::*; #[allow(hidden_glob_reexports, unused_imports)] use rustc_type_ir::{InferCtxtLike, Interner}; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument}; pub use vtable::*; pub use self::closure::{ @@ -77,15 +76,15 @@ pub use self::closure::{ place_to_string_for_capture, }; pub use self::consts::{ - AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr, ExprKind, - LitToConstInput, ScalarInt, SimdAlign, UnevaluatedConst, UnevaluatedConstKind, ValTree, - ValTreeKindExt, Value, const_lit_matches_ty, + AliasConst, AliasConstKind, AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, + Expr, ExprKind, LitToConstInput, ScalarInt, SimdAlign, ValTree, ValTreeKindExt, Value, + const_lit_matches_ty, }; pub use self::context::{ CtxtInterners, CurrentGcx, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls, }; pub use self::fold::*; -pub use self::instance::{Instance, InstanceKind, ReifyReason}; +pub use self::instance::{Instance, InstanceKind, ReifyReason, ShimKind}; pub(crate) use self::list::RawList; pub use self::list::{List, ListWithCachedTypeInfo}; pub use self::opaque_types::OpaqueTypeKey; @@ -114,7 +113,8 @@ pub use self::sty::{ pub use self::trait_def::TraitDef; pub use self::typeck_results::{ CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, - Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, + Rust2024IncompatiblePatInfo, SplattedDef, TypeckResults, UserType, UserTypeAnnotationIndex, + UserTypeKind, }; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::{AmbigModChild, ModChild}; @@ -144,6 +144,7 @@ pub mod print; pub mod relate; pub mod significant_drop_order; pub mod trait_def; +pub mod typetree; pub mod util; pub mod vtable; @@ -688,16 +689,29 @@ impl<'tcx> Term<'tcx> { pub fn to_alias_term(self) -> Option> { match self.kind() { TermKind::Ty(ty) => match *ty.kind() { - ty::Alias(alias_ty) => Some(alias_ty.into()), + ty::Alias(_, alias_ty) => Some(alias_ty.into()), _ => None, }, TermKind::Const(ct) => match ct.kind() { - ConstKind::Unevaluated(uv) => Some(uv.into()), + ConstKind::Alias(_, alias_const) => Some(alias_const.into()), _ => None, }, } } + pub fn is_non_rigid_alias(self) -> bool { + match self.kind() { + ty::TermKind::Ty(ty) => match ty.kind() { + ty::Alias(ty::IsRigid::No, _) => true, + _ => false, + }, + ty::TermKind::Const(ct) => match ct.kind() { + ty::ConstKind::Alias(ty::IsRigid::No, _) => true, + _ => false, + }, + } + } + pub fn is_infer(&self) -> bool { match self.kind() { TermKind::Ty(ty) => ty.is_ty_var(), @@ -926,7 +940,7 @@ impl<'tcx> ProvisionalHiddenType<'tcx> { if cfg!(debug_assertions) && matches!(defining_scope_kind, DefiningScopeKind::HirTypeck) { assert_eq!(result_ty, fold_regions(tcx, result_ty, |_, _| tcx.lifetimes.re_erased)); } - DefinitionSiteHiddenType { span: self.span, ty: ty::EarlyBinder::bind(result_ty) } + DefinitionSiteHiddenType { span: self.span, ty: ty::EarlyBinder::bind(tcx, result_ty) } } } @@ -954,7 +968,7 @@ impl<'tcx> DefinitionSiteHiddenType<'tcx> { pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> DefinitionSiteHiddenType<'tcx> { DefinitionSiteHiddenType { span: DUMMY_SP, - ty: ty::EarlyBinder::bind(Ty::new_error(tcx, guar)), + ty: ty::EarlyBinder::bind(tcx, Ty::new_error(tcx, guar)), } } @@ -1803,20 +1817,9 @@ impl<'tcx> TyCtxt<'tcx> { _ => self.optimized_mir(def), } } - ty::InstanceKind::VTableShim(..) - | ty::InstanceKind::ReifyShim(..) - | ty::InstanceKind::Intrinsic(..) - | ty::InstanceKind::FnPtrShim(..) - | ty::InstanceKind::Virtual(..) - | ty::InstanceKind::ClosureOnceShim { .. } - | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } - | ty::InstanceKind::FutureDropPollShim(..) - | ty::InstanceKind::DropGlue(..) - | ty::InstanceKind::CloneShim(..) - | ty::InstanceKind::ThreadLocalShim(..) - | ty::InstanceKind::FnPtrAddrShim(..) - | ty::InstanceKind::AsyncDropGlueCtorShim(..) - | ty::InstanceKind::AsyncDropGlue(..) => self.mir_shims(instance), + ty::InstanceKind::Intrinsic(..) => bug!("intrinsics have no instance MIR"), + ty::InstanceKind::Virtual(..) => bug!("virtual dispatches have no instance MIR"), + ty::InstanceKind::Shim(shim) => self.mir_shims(shim), }; assert!( @@ -1942,7 +1945,7 @@ impl<'tcx> TyCtxt<'tcx> { if args[0].has_placeholders() || args[0].has_non_region_param() { return Err(self.layout_error(LayoutError::TooGeneric(ty()))); } - let instance = InstanceKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args)); + let instance = ShimKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args)); self.mir_shims(instance) .coroutine_layout_raw() .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) @@ -2134,18 +2137,17 @@ impl<'tcx> TyCtxt<'tcx> { ident } - // FIXME(vincenzopalazzo): move the HirId to a LocalDefId pub fn adjust_ident_and_get_scope( self, mut ident: Ident, scope: DefId, - block: hir::HirId, + item_id: LocalDefId, ) -> (Ident, DefId) { let scope = ident .span .normalize_to_macros_2_0_and_adjust(self.expn_that_defined(scope)) .and_then(|actual_expansion| actual_expansion.expn_data().parent_module) - .unwrap_or_else(|| self.parent_module(block).to_def_id()); + .unwrap_or_else(|| self.parent_module_from_def_id(item_id).to_def_id()); (ident, scope) } @@ -2388,228 +2390,3 @@ pub struct DestructuredAdtConst<'tcx> { pub variant: VariantIdx, pub fields: &'tcx [ty::Const<'tcx>], } - -/// Generate TypeTree information for autodiff. -/// This function creates TypeTree metadata that describes the memory layout -/// of function parameters and return types for Enzyme autodiff. -pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { - // Check if TypeTrees are disabled via NoTT flag - if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) { - return FncTree { args: vec![], ret: TypeTree::new() }; - } - - // Check if this is actually a function type - if !fn_ty.is_fn() { - return FncTree { args: vec![], ret: TypeTree::new() }; - } - - // Get the function signature - let fn_sig = fn_ty.fn_sig(tcx); - let sig = tcx.instantiate_bound_regions_with_erased(fn_sig); - - // Create TypeTrees for each input parameter - let mut args = vec![]; - for ty in sig.inputs().iter() { - let type_tree = typetree_from_ty(tcx, *ty); - args.push(type_tree); - } - - // Create TypeTree for return type - let ret = typetree_from_ty(tcx, sig.output()); - - FncTree { args, ret } -} - -/// Generate TypeTree for a specific type. -/// This function analyzes a Rust type and creates appropriate TypeTree metadata. -pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { - let mut visited = Vec::new(); - typetree_from_ty_inner(tcx, ty, 0, &mut visited) -} - -/// Maximum recursion depth for TypeTree generation to prevent stack overflow -/// from pathological deeply nested types. Combined with cycle detection. -const MAX_TYPETREE_DEPTH: usize = 6; - -/// Internal recursive function for TypeTree generation with cycle detection and depth limiting. -fn typetree_from_ty_inner<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - depth: usize, - visited: &mut Vec>, -) -> TypeTree { - if depth >= MAX_TYPETREE_DEPTH { - trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty); - return TypeTree::new(); - } - - if visited.contains(&ty) { - return TypeTree::new(); - } - - visited.push(ty); - let result = typetree_from_ty_impl(tcx, ty, depth, visited); - visited.pop(); - result -} - -/// Implementation of TypeTree generation logic. -fn typetree_from_ty_impl<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - depth: usize, - visited: &mut Vec>, -) -> TypeTree { - typetree_from_ty_impl_inner(tcx, ty, depth, visited, false) -} - -/// Internal implementation with context about whether this is for a reference target. -fn typetree_from_ty_impl_inner<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - depth: usize, - visited: &mut Vec>, - is_reference_target: bool, -) -> TypeTree { - if ty.is_scalar() { - let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { - (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) - } else if ty.is_floating_point() { - match ty { - x if x == tcx.types.f16 => (Kind::Half, 2), - x if x == tcx.types.f32 => (Kind::Float, 4), - x if x == tcx.types.f64 => (Kind::Double, 8), - x if x == tcx.types.f128 => (Kind::F128, 16), - _ => (Kind::Integer, 0), - } - } else { - (Kind::Integer, 0) - }; - - // Use offset 0 for scalars that are direct targets of references (like &f64) - // Use offset -1 for scalars used directly (like function return types) - let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 }; - return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]); - } - - if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { - let Some(inner_ty) = ty.builtin_deref(true) else { - return TypeTree::new(); - }; - - let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true); - return TypeTree(vec![Type { - offset: -1, - size: tcx.data_layout.pointer_size().bytes_usize(), - kind: Kind::Pointer, - child, - }]); - } - - if ty.is_array() { - if let ty::Array(element_ty, len_const) = ty.kind() { - let len = len_const.try_to_target_usize(tcx).unwrap_or(0); - if len == 0 { - return TypeTree::new(); - } - let element_tree = - typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); - let mut types = Vec::new(); - for elem_type in &element_tree.0 { - types.push(Type { - offset: -1, - size: elem_type.size, - kind: elem_type.kind, - child: elem_type.child.clone(), - }); - } - - return TypeTree(types); - } - } - - if ty.is_slice() { - if let ty::Slice(element_ty) = ty.kind() { - let element_tree = - typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); - return element_tree; - } - } - - if let ty::Tuple(tuple_types) = ty.kind() { - if tuple_types.is_empty() { - return TypeTree::new(); - } - - let mut types = Vec::new(); - let mut current_offset = 0; - - for tuple_ty in tuple_types.iter() { - let element_tree = - typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false); - - let element_layout = tcx - .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty)) - .ok() - .map(|layout| layout.size.bytes_usize()) - .unwrap_or(0); - - for elem_type in &element_tree.0 { - types.push(Type { - offset: if elem_type.offset == -1 { - current_offset as isize - } else { - current_offset as isize + elem_type.offset - }, - size: elem_type.size, - kind: elem_type.kind, - child: elem_type.child.clone(), - }); - } - - current_offset += element_layout; - } - - return TypeTree(types); - } - - if let ty::Adt(adt_def, args) = ty.kind() { - if adt_def.is_struct() { - let struct_layout = - tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)); - if let Ok(layout) = struct_layout { - let mut types = Vec::new(); - - for (field_idx, field_def) in adt_def.all_fields().enumerate() { - let field_ty = field_def.ty(tcx, args); - let field_tree = typetree_from_ty_impl_inner( - tcx, - field_ty.skip_norm_wip(), - depth + 1, - visited, - false, - ); - - let field_offset = layout.fields.offset(field_idx).bytes_usize(); - - for elem_type in &field_tree.0 { - types.push(Type { - offset: if elem_type.offset == -1 { - field_offset as isize - } else { - field_offset as isize + elem_type.offset - }, - size: elem_type.size, - kind: elem_type.kind, - child: elem_type.child.clone(), - }); - } - } - - return TypeTree(types); - } - } - } - - TypeTree::new() -} diff --git a/compiler/rustc_middle/src/ty/offload_meta.rs b/compiler/rustc_middle/src/ty/offload_meta.rs index 6303520383fbd..9d0fcc50ef224 100644 --- a/compiler/rustc_middle/src/ty/offload_meta.rs +++ b/compiler/rustc_middle/src/ty/offload_meta.rs @@ -119,7 +119,8 @@ impl MappingFlags { MappingFlags::LITERAL | MappingFlags::IMPLICIT } - ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Alias(_) | ty::Param(_) => { + // FIXME: This should not treat aliases this way. + ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Alias(_, _) | ty::Param(_) => { MappingFlags::TO } diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 26108f7be6964..7928218876c8e 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -419,7 +419,7 @@ impl<'tcx> Clause<'tcx> { let shifted_pred = tcx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder()); // 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1> - let new = EarlyBinder::bind(shifted_pred) + let new = EarlyBinder::bind(tcx, shifted_pred) .instantiate(tcx, trait_ref.skip_binder().args) .skip_norm_wip(); // 3) ['x] + ['b] -> ['x, 'b] diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 29875a3530c15..bec1ffc642769 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -367,35 +367,42 @@ impl<'tcx, P: Printer<'tcx> + std::fmt::Write> Print

for ty::Instance<'tcx> { cx.print_def_path(self.def_id(), self.args)?; match self.def { ty::InstanceKind::Item(_) => {} - ty::InstanceKind::VTableShim(_) => cx.write_str(" - shim(vtable)")?, - ty::InstanceKind::ReifyShim(_, None) => cx.write_str(" - shim(reify)")?, - ty::InstanceKind::ReifyShim(_, Some(ty::ReifyReason::FnPtr)) => { - cx.write_str(" - shim(reify-fnptr)")? - } - ty::InstanceKind::ReifyShim(_, Some(ty::ReifyReason::Vtable)) => { - cx.write_str(" - shim(reify-vtable)")? - } - ty::InstanceKind::ThreadLocalShim(_) => cx.write_str(" - shim(tls)")?, ty::InstanceKind::Intrinsic(_) => cx.write_str(" - intrinsic")?, ty::InstanceKind::Virtual(_, num) => cx.write_str(&format!(" - virtual#{num}"))?, - ty::InstanceKind::FnPtrShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?, - ty::InstanceKind::ClosureOnceShim { .. } => cx.write_str(" - shim")?, - ty::InstanceKind::ConstructCoroutineInClosureShim { .. } => cx.write_str(" - shim")?, - ty::InstanceKind::DropGlue(_, None) => cx.write_str(" - shim(None)")?, - ty::InstanceKind::DropGlue(_, Some(ty)) => { - cx.write_str(&format!(" - shim(Some({ty}))"))? + ty::InstanceKind::Shim(shim) => { + cx.write_str(" - ")?; + shim.print(cx)?; + } + } + Ok(()) + } +} + +impl<'tcx, P: Printer<'tcx> + std::fmt::Write> Print

for ty::ShimKind<'tcx> { + fn print(&self, cx: &mut P) -> Result<(), PrintError> { + match self { + ty::ShimKind::VTable(_) => cx.write_str("shim(vtable)"), + ty::ShimKind::Reify(_, None) => cx.write_str("shim(reify)"), + ty::ShimKind::Reify(_, Some(ty::ReifyReason::FnPtr)) => { + cx.write_str("shim(reify-fnptr)") } - ty::InstanceKind::CloneShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?, - ty::InstanceKind::FnPtrAddrShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?, - ty::InstanceKind::FutureDropPollShim(_, proxy_ty, impl_ty) => { - cx.write_str(&format!(" - dropshim({proxy_ty}-{impl_ty})"))? + ty::ShimKind::Reify(_, Some(ty::ReifyReason::Vtable)) => { + cx.write_str("shim(reify-vtable)") } - ty::InstanceKind::AsyncDropGlue(_, ty) => cx.write_str(&format!(" - shim({ty})"))?, - ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => { - cx.write_str(&format!(" - shim(Some({ty}))"))? + ty::ShimKind::ThreadLocal(_) => cx.write_str("shim(tls)"), + ty::ShimKind::FnPtr(_, ty) => cx.write_str(&format!("shim({ty})")), + ty::ShimKind::ClosureOnce { .. } => cx.write_str("shim"), + ty::ShimKind::ConstructCoroutineInClosure { .. } => cx.write_str("shim"), + ty::ShimKind::DropGlue(_, None) => cx.write_str("shim(None)"), + ty::ShimKind::DropGlue(_, Some(ty)) => cx.write_str(&format!("shim(Some({ty}))")), + ty::ShimKind::Clone(_, ty) => cx.write_str(&format!("shim({ty})")), + ty::ShimKind::FnPtrAddr(_, ty) => cx.write_str(&format!("shim({ty})")), + ty::ShimKind::FutureDropPoll(_, proxy_ty, impl_ty) => { + cx.write_str(&format!("dropshim({proxy_ty}-{impl_ty})")) } - }; - Ok(()) + ty::ShimKind::AsyncDropGlue(_, ty) => cx.write_str(&format!("shim({ty})")), + ty::ShimKind::AsyncDropGlueCtor(_, ty) => cx.write_str(&format!("shim(Some({ty}))")), + } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index be6505ea1ecb7..6852c952658e7 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -820,13 +820,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } ty::Foreign(def_id) => self.print_def_path(def_id, &[])?, ty::Alias( + _, ref data @ ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, .. }, ) => data.print(self)?, ty::Placeholder(placeholder) => placeholder.print(self)?, - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { // We use verbose printing in 'NO_QUERIES' mode, to // avoid needing to call `predicates_of`. This should // only affect certain debug messages (e.g. messages printed @@ -846,12 +847,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { DefKind::TyAlias | DefKind::AssocTy => { // NOTE: I know we should check for NO_QUERIES here, but it's alright. // `type_of` on a type alias or assoc type should never cause a cycle. - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: d }, .. }) = *self - .tcx() - .type_of(parent) - .instantiate_identity() - .skip_norm_wip() - .kind() + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: d }, .. }) = + *self + .tcx() + .type_of(parent) + .instantiate_identity() + .skip_norm_wip() + .kind() { if d == def_id { // If the type alias directly starts with the `impl` of the @@ -1367,7 +1369,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let fn_args = if self.tcx().features().return_type_notation() && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx().opt_rpitit_info(def_id) - && let ty::Alias(alias_ty) = + && let ty::Alias(_, alias_ty) = self.tcx().fn_sig(fn_def_id).skip_binder().output().skip_binder().kind() && let Some(projection_ty) = alias_ty.try_to_projection() && projection_ty.kind == def_id @@ -1433,6 +1435,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p.pretty_print_fn_sig( tys, false, + // FIXME(splat): support splatted arguments here? + None, proj.skip_binder().term.as_type().expect("Return type was a const"), )?; resugared = true; @@ -1536,10 +1540,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { &mut self, inputs: &[Ty<'tcx>], c_variadic: bool, + splatted: Option, output: Ty<'tcx>, ) -> Result<(), PrintError> { write!(self, "(")?; - self.comma_sep(inputs.iter().copied())?; + let splatted_arg_index = splatted.map(usize::from); + let mut input_iter = inputs.iter().copied(); + if let Some(index) = splatted_arg_index { + self.comma_sep((&mut input_iter).take(usize::from(index)))?; + write!(self, ", #[splat]")?; + self.comma_sep(input_iter)?; + } else { + self.comma_sep(input_iter)?; + } if c_variadic { if !inputs.is_empty() { write!(self, ", ")?; @@ -1566,14 +1579,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } match ct.kind() { - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { kind, args, .. }) => { + ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => { match kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } => { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } => { self.pretty_print_value_path(def_id, args)?; } - ty::UnevaluatedConstKind::Anon { def_id } => { + ty::AliasConstKind::Anon { def_id } => { if def_id.is_local() && let span = self.tcx().def_span(def_id) && let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) @@ -3148,7 +3161,7 @@ define_print! { } write!(p, "fn")?; - p.pretty_print_fn_sig(self.inputs(), self.c_variadic(), self.output())?; + p.pretty_print_fn_sig(self.inputs(), self.c_variadic(), self.splatted(), self.output())?; } ty::TraitRef<'tcx> { diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index e680e62f50931..c5b50163fbbb1 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -133,7 +133,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { | ty::FnPtr(_, _) | ty::Tuple(_) | ty::Dynamic(_, _) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Bound(_, _) | ty::Pat(_, _) | ty::Placeholder(_) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 19dc2705e7f5b..49bac6a130231 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -372,7 +372,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::CoroutineClosure(did, args) => { ty::CoroutineClosure(did, args.try_fold_with(folder)?) } - ty::Alias(data) => ty::Alias(data.try_fold_with(folder)?), + ty::Alias(is_rigid, data) => ty::Alias(is_rigid, data.try_fold_with(folder)?), ty::Pat(ty, pat) => ty::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?), ty::Bool @@ -411,7 +411,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::CoroutineWitness(did, args) => ty::CoroutineWitness(did, args.fold_with(folder)), ty::Closure(did, args) => ty::Closure(did, args.fold_with(folder)), ty::CoroutineClosure(did, args) => ty::CoroutineClosure(did, args.fold_with(folder)), - ty::Alias(data) => ty::Alias(data.fold_with(folder)), + ty::Alias(is_rigid, data) => ty::Alias(is_rigid, data.fold_with(folder)), ty::Pat(ty, pat) => ty::Pat(ty.fold_with(folder), pat.fold_with(folder)), ty::Bool @@ -459,7 +459,7 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { ty::CoroutineWitness(_did, args) => args.visit_with(visitor), ty::Closure(_did, args) => args.visit_with(visitor), ty::CoroutineClosure(_did, args) => args.visit_with(visitor), - ty::Alias(data) => data.visit_with(visitor), + ty::Alias(_, data) => data.visit_with(visitor), ty::Pat(ty, pat) => { try_visit!(ty.visit_with(visitor)); @@ -634,7 +634,9 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { folder: &mut F, ) -> Result { let kind = match self.kind() { - ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?), + ConstKind::Alias(is_rigid, alias_const) => { + ConstKind::Alias(is_rigid, alias_const.try_fold_with(folder)?) + } ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), @@ -649,7 +651,9 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { fn super_fold_with>>(self, folder: &mut F) -> Self { let kind = match self.kind() { - ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)), + ConstKind::Alias(is_rigid, alias_const) => { + ConstKind::Alias(is_rigid, alias_const.fold_with(folder)) + } ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), @@ -666,7 +670,7 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { match self.kind() { - ConstKind::Unevaluated(uv) => uv.visit_with(visitor), + ConstKind::Alias(_, alias_const) => alias_const.visit_with(visitor), ConstKind::Value(v) => v.visit_with(visitor), ConstKind::Expr(e) => e.visit_with(visitor), ConstKind::Error(e) => e.visit_with(visitor), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 93f0afda372f7..0042c87c3eda7 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -166,7 +166,7 @@ impl<'tcx> ty::CoroutineArgs> { if tcx.is_async_drop_in_place_coroutine(def_id) { layout.field_tys[*field].ty } else { - ty::EarlyBinder::bind(layout.field_tys[*field].ty) + ty::EarlyBinder::bind(tcx, layout.field_tys[*field].ty) .instantiate(tcx, self.args) .skip_norm_wip() } @@ -481,7 +481,11 @@ impl<'tcx> Ty<'tcx> { } #[inline] - pub fn new_alias(tcx: TyCtxt<'tcx>, alias_ty: ty::AliasTy<'tcx>) -> Ty<'tcx> { + pub fn new_alias( + tcx: TyCtxt<'tcx>, + is_rigid: ty::IsRigid, + alias_ty: ty::AliasTy<'tcx>, + ) -> Ty<'tcx> { if cfg!(debug_assertions) { match alias_ty.kind { ty::AliasTyKind::Projection { def_id } => { @@ -498,7 +502,7 @@ impl<'tcx> Ty<'tcx> { } } } - Ty::new(tcx, Alias(alias_ty)) + Ty::new(tcx, Alias(is_rigid, alias_ty)) } #[inline] @@ -537,8 +541,13 @@ impl<'tcx> Ty<'tcx> { #[inline] #[instrument(level = "debug", skip(tcx))] - pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { - Ty::new_alias(tcx, AliasTy::new_from_args(tcx, ty::Opaque { def_id }, args)) + pub fn new_opaque( + tcx: TyCtxt<'tcx>, + is_rigid: ty::IsRigid, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> Ty<'tcx> { + Ty::new_alias(tcx, is_rigid, AliasTy::new_from_args(tcx, ty::Opaque { def_id }, args)) } /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed` @@ -791,11 +800,13 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_projection_from_args( tcx: TyCtxt<'tcx>, + is_rigid: ty::IsRigid, item_def_id: DefId, args: ty::GenericArgsRef<'tcx>, ) -> Ty<'tcx> { Ty::new_alias( tcx, + is_rigid, AliasTy::new_from_args(tcx, ty::Projection { def_id: item_def_id }, args), ) } @@ -803,10 +814,15 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_projection( tcx: TyCtxt<'tcx>, + is_rigid: ty::IsRigid, item_def_id: DefId, args: impl IntoIterator>>, ) -> Ty<'tcx> { - Ty::new_alias(tcx, AliasTy::new(tcx, ty::Projection { def_id: item_def_id }, args)) + Ty::new_alias( + tcx, + is_rigid, + AliasTy::new(tcx, ty::Projection { def_id: item_def_id }, args), + ) } #[inline] @@ -982,8 +998,12 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_canonical_bound(tcx, var) } - fn new_alias(interner: TyCtxt<'tcx>, alias_ty: ty::AliasTy<'tcx>) -> Self { - Ty::new_alias(interner, alias_ty) + fn new_alias( + interner: TyCtxt<'tcx>, + is_rigid: ty::IsRigid, + alias_ty: ty::AliasTy<'tcx>, + ) -> Self { + Ty::new_alias(interner, is_rigid, alias_ty) } fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { @@ -1622,7 +1642,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_opaque(self) -> bool { - matches!(self.kind(), Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. })) + matches!(self.kind(), Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. })) } #[inline] @@ -1704,7 +1724,12 @@ impl<'tcx> Ty<'tcx> { let assoc_items = tcx.associated_item_def_ids( tcx.require_lang_item(hir::LangItem::DiscriminantKind, DUMMY_SP), ); - Ty::new_projection_from_args(tcx, assoc_items[0], tcx.mk_args(&[self.into()])) + Ty::new_projection_from_args( + tcx, + ty::IsRigid::No, + assoc_items[0], + tcx.mk_args(&[self.into()]), + ) } ty::Pat(ty, _) => ty.discriminant_ty(tcx), @@ -1837,7 +1862,7 @@ impl<'tcx> Ty<'tcx> { Ok(metadata_ty) => metadata_ty, Err(tail_ty) => { let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, DUMMY_SP); - Ty::new_projection(tcx, metadata_def_id, [tail_ty]) + Ty::new_projection(tcx, ty::IsRigid::No, metadata_def_id, [tail_ty]) } } } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 1287047581196..77820beef0228 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -36,6 +36,9 @@ pub struct TypeckResults<'tcx> { /// method calls, including those of overloaded operators. type_dependent_defs: ItemLocalMap>, + /// Resolved definitions for splatted function calls. + splatted_defs: ItemLocalMap>, + /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`) /// or patterns (`S { field }`). The index is often useful by itself, but to learn more /// about the field you also need definition of the variant to which the field @@ -229,6 +232,7 @@ impl<'tcx> TypeckResults<'tcx> { TypeckResults { hir_owner, type_dependent_defs: Default::default(), + splatted_defs: Default::default(), field_indices: Default::default(), user_provided_types: Default::default(), user_provided_sigs: Default::default(), @@ -287,6 +291,21 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } } + pub fn splatted_defs(&self) -> LocalTableInContext<'_, Result> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.splatted_defs } + } + + pub fn splatted_def(&self, id: HirId) -> Option { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.splatted_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) + } + + pub fn splatted_defs_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Result> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.splatted_defs } + } + pub fn field_indices(&self) -> LocalTableInContext<'_, FieldIdx> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.field_indices } } @@ -407,6 +426,10 @@ impl<'tcx> TypeckResults<'tcx> { matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) } + pub fn is_splatted_call(&self, expr: &hir::Expr<'_>) -> bool { + matches!(self.splatted_defs().get(expr.hir_id), Some(Ok(SplattedDef { .. }))) + } + /// Returns the computed binding mode for a `PatKind::Binding` pattern /// (after match ergonomics adjustments). pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> BindingMode { @@ -569,6 +592,18 @@ impl<'tcx> TypeckResults<'tcx> { } } +/// A resolved splatted function call. +#[derive(Debug, Copy, Clone, PartialEq, Eq, StableHash, TyEncodable, TyDecodable)] +pub struct SplattedDef { + /// The function DefId, if available (FnPtrs don't have DefIds) + pub def_id: Option, + /// The index of the first argument in the callee's splatted tuple, and the index of the + /// splatted tuple argument in the caller. + pub arg_index: u16, + /// The number of arguments in the splatted tuple. + pub arg_count: u16, +} + /// Validate that the given HirId (respectively its `local_id` part) can be /// safely used as a key in the maps of a TypeckResults. For that to be /// the case, the HirId must have the same `owner` as all the other IDs in diff --git a/compiler/rustc_middle/src/ty/typetree.rs b/compiler/rustc_middle/src/ty/typetree.rs new file mode 100644 index 0000000000000..9e941bdb849ec --- /dev/null +++ b/compiler/rustc_middle/src/ty/typetree.rs @@ -0,0 +1,208 @@ +use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; +use tracing::trace; + +use crate::ty::context::TyCtxt; +use crate::ty::{self, Ty}; + +/// Generate TypeTree information for autodiff. +/// This function creates TypeTree metadata that describes the memory layout +/// of function parameters and return types for Enzyme autodiff. +pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { + // Check if TypeTrees are disabled via NoTT flag + if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) { + return FncTree { args: vec![], ret: TypeTree::new() }; + } + + // Check if this is actually a function type + if !fn_ty.is_fn() { + return FncTree { args: vec![], ret: TypeTree::new() }; + } + + // Get the function signature + let fn_sig = fn_ty.fn_sig(tcx); + let sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + + // Create TypeTrees for each input parameter + let mut args = vec![]; + for ty in sig.inputs().iter() { + let type_tree = typetree_from_ty(tcx, *ty); + args.push(type_tree); + } + + // Create TypeTree for return type + let ret = typetree_from_ty(tcx, sig.output()); + + FncTree { args, ret } +} + +/// Generate a TypeTree for a specific type. +/// Mainly a convenience wrapper around the actual implementation. +pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { + let mut visited = Vec::new(); + typetree_from_ty_impl_inner(tcx, ty, 0, &mut visited, false) +} + +/// Maximum recursion depth for TypeTree generation to prevent stack overflow +/// from pathological deeply nested types. Combined with cycle detection. +const MAX_TYPETREE_DEPTH: usize = 6; + +/// Internal implementation with context about whether this is for a reference target. +fn typetree_from_ty_impl_inner<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec>, + is_reference_target: bool, +) -> TypeTree { + if depth >= MAX_TYPETREE_DEPTH { + trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty); + return TypeTree::new(); + } + + if visited.contains(&ty) { + return TypeTree::new(); + } + visited.push(ty); + + if ty.is_scalar() { + let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { + (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) + } else if ty.is_floating_point() { + match ty { + x if x == tcx.types.f16 => (Kind::Half, 2), + x if x == tcx.types.f32 => (Kind::Float, 4), + x if x == tcx.types.f64 => (Kind::Double, 8), + x if x == tcx.types.f128 => (Kind::F128, 16), + _ => (Kind::Integer, 0), + } + } else { + (Kind::Integer, 0) + }; + + // Use offset 0 for scalars that are direct targets of references (like &f64) + // Use offset -1 for scalars used directly (like function return types) + let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 }; + return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]); + } + + if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { + let Some(inner_ty) = ty.builtin_deref(true) else { + return TypeTree::new(); + }; + + let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true); + return TypeTree(vec![Type { + offset: -1, + size: tcx.data_layout.pointer_size().bytes_usize(), + kind: Kind::Pointer, + child, + }]); + } + + if ty.is_array() { + if let ty::Array(element_ty, len_const) = ty.kind() { + let len = len_const.try_to_target_usize(tcx).unwrap_or(0); + if len == 0 { + return TypeTree::new(); + } + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); + let mut types = Vec::new(); + for elem_type in &element_tree.0 { + types.push(Type { + offset: -1, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + + return TypeTree(types); + } + } + + if ty.is_slice() { + if let ty::Slice(element_ty) = ty.kind() { + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); + return element_tree; + } + } + + if let ty::Tuple(tuple_types) = ty.kind() { + if tuple_types.is_empty() { + return TypeTree::new(); + } + + let mut types = Vec::new(); + let mut current_offset = 0; + + for tuple_ty in tuple_types.iter() { + let element_tree = + typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false); + + let element_layout = tcx + .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty)) + .ok() + .map(|layout| layout.size.bytes_usize()) + .unwrap_or(0); + + for elem_type in &element_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + current_offset as isize + } else { + current_offset as isize + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + + current_offset += element_layout; + } + + return TypeTree(types); + } + + if let ty::Adt(adt_def, args) = ty.kind() { + if adt_def.is_struct() { + let struct_layout = + tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)); + if let Ok(layout) = struct_layout { + let mut types = Vec::new(); + + for (field_idx, field_def) in adt_def.all_fields().enumerate() { + let field_ty = field_def.ty(tcx, args); + let field_tree = typetree_from_ty_impl_inner( + tcx, + field_ty.skip_norm_wip(), + depth + 1, + visited, + false, + ); + + let field_offset = layout.fields.offset(field_idx).bytes_usize(); + + for elem_type in &field_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + field_offset as isize + } else { + field_offset as isize + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + } + + return TypeTree(types); + } + } + } + + TypeTree::new() +} diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ee6aaf9cb6e17..2c91555e3835f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -597,7 +597,38 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns `true` if `def_id` refers to a definition that does not have its own /// type-checking context, i.e. closure, coroutine or inline const. pub fn is_typeck_child(self, def_id: DefId) -> bool { - self.def_kind(def_id).is_typeck_child() + match self.def_kind(def_id) { + DefKind::InlineConst => !self.is_type_system_inline_const(def_id), + DefKind::Closure | DefKind::SyntheticCoroutineBody => true, + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const { .. } + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::AssocConst { .. } + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl { .. } => false, + } } /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). @@ -894,12 +925,14 @@ impl<'tcx> TyCtxt<'tcx> { /// [free]: ty::Free /// [expand_free_alias_tys]: Self::expand_free_alias_tys pub fn peel_off_free_alias_tys(self, mut ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) = ty.kind() else { return ty }; + let ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) = ty.kind() else { + return ty; + }; let limit = self.recursion_limit(); let mut depth = 0; - while let &ty::Alias(ty::AliasTy { kind: ty::Free { def_id }, args, .. }) = ty.kind() { + while let &ty::Alias(_, ty::AliasTy { kind: ty::Free { def_id }, args, .. }) = ty.kind() { if !limit.value_within_limit(depth) { let guar = self.dcx().delayed_bug("overflow expanding free alias type"); return Ty::new_error(self, guar); @@ -993,7 +1026,7 @@ impl<'tcx> TypeFolder> for OpaqueTypeExpander<'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *t.kind() { + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *t.kind() { self.expand_opaque_ty(def_id, args).unwrap_or(t) } else if t.has_opaque_types() { t.super_fold_with(self) @@ -1037,7 +1070,7 @@ impl<'tcx> TypeFolder> for FreeAliasTypeExpander<'tcx> { if !ty.has_type_flags(ty::TypeFlags::HAS_TY_FREE_ALIAS) { return ty; } - let &ty::Alias(ty::AliasTy { kind: ty::Free { def_id }, args, .. }) = ty.kind() else { + let &ty::Alias(_, ty::AliasTy { kind: ty::Free { def_id }, args, .. }) = ty.kind() else { return ty.super_fold_with(self); }; if !self.tcx.recursion_limit().value_within_limit(self.depth) { diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index af270988abc9c..620e6da6a8f88 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -181,15 +181,15 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector<'tcx> { match t.kind() { // If we are only looking for "constrained" regions, we have to ignore the // inputs to a projection as they may not appear in the normalized form. - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, - .. - }) => { - return; - } - // All free alias types should've been expanded beforehand. - ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) => { - bug!("unexpected free alias type") + ty::Alias(_, alias_ty) => { + match alias_ty.kind { + ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. } => return, + + // All free alias types should've been expanded beforehand. + ty::Free { .. } => { + bug!("unexpected free alias type") + } + } } _ => {} } @@ -200,10 +200,10 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector<'tcx> { fn visit_const(&mut self, c: ty::Const<'tcx>) { // if we are only looking for "constrained" region, we have to - // ignore the inputs of an unevaluated const, as they may not appear + // ignore the inputs of an alias const, as they may not appear // in the normalized form if self.just_constrained { - if let ty::ConstKind::Unevaluated(..) = c.kind() { + if let ty::ConstKind::Alias(..) = c.kind() { return; } } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index fa22e07612cfe..d90c2a4e759af 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_build/src/builder/block.rs b/compiler/rustc_mir_build/src/builder/block.rs index cfd9c4ec5d41a..47d231603d33b 100644 --- a/compiler/rustc_mir_build/src/builder/block.rs +++ b/compiler/rustc_mir_build/src/builder/block.rs @@ -335,7 +335,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if destination_ty.is_unit() || matches!( destination_ty.kind(), - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) ) { // We only want to assign an implicit `()` as the return value of the block if the 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 76360ee6c5b46..caf4f8d295c8a 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -72,12 +72,12 @@ pub(crate) fn as_constant_inner<'tcx>( ExprKind::NamedConst { def_id, args, ref user_ty } => { let user_ty = user_ty.as_ref().and_then(push_cuta); if tcx.is_type_const(def_id) { - let uneval = ty::UnevaluatedConst::new( + let uneval = ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, def_id), + ty::AliasConstKind::new_from_def_id(tcx, def_id), args, ); - let ct = ty::Const::new_unevaluated(tcx, uneval); + let ct = ty::Const::new_alias(tcx, ty::IsRigid::No, uneval); let const_ = Const::Ty(ty, ct); return ConstOperand { span, user_ty, const_ }; diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 8352ec92e88b0..f70416b2cf867 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -9,7 +9,7 @@ use std::borrow::Borrow; use std::sync::Arc; use std::{debug_assert_matches, mem}; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -551,9 +551,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the drop order for the first sub-branch, we lower sub-branches in reverse (#142163). let target_block = self.cfg.start_new_block(); for (pos, sub_branch) in branch.sub_branches.into_iter().rev().with_position() { - debug_assert!(pos != Position::Only); + debug_assert!(!pos.is_exactly_one()); let schedule_drops = - if pos == Position::Last { ScheduleDrops::Yes } else { ScheduleDrops::No }; + if pos.is_last() { ScheduleDrops::Yes } else { ScheduleDrops::No }; let binding_end = self.bind_and_guard_matched_candidate( sub_branch, fake_borrow_temps, diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 854a378bb993d..378e2e618fb93 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -547,6 +547,7 @@ fn construct_fn<'tcx>( body.spread_arg = if abi == ExternAbi::RustCall { // RustCall pseudo-ABI untuples the last argument. + // FIXME(splat): splat can untuple any argument, set spread_arg here Some(Local::new(arguments.len())) } else { None diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 8052ee26df841..ac9f6e384cf04 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -147,7 +147,9 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { // ``` // we should think what is the expected behavior here. // (we should probably just accept this by revealing opaques?) - if caller_sig.inputs_and_output != callee_sig.inputs_and_output { + if caller_sig.inputs_and_output != callee_sig.inputs_and_output + && !matches!(callee_sig.abi(), ExternAbi::RustTail) + { let caller_ty = self.tcx.type_of(self.caller_def_id).skip_binder(); self.report_signature_mismatch( @@ -189,6 +191,12 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { if callee_sig.c_variadic() { self.report_c_variadic_callee(expr.span); } + + for &arg_ty in callee_sig.inputs() { + if !arg_ty.is_sized(self.tcx, self.typing_env) { + self.report_unsized_argument(expr.span, arg_ty); + } + } } /// Returns true if the caller function needs a location argument @@ -417,6 +425,17 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { self.found_errors = Err(err); } + + fn report_unsized_argument(&mut self, sp: Span, arg_ty: Ty<'tcx>) { + let err = self + .tcx + .dcx() + .struct_span_err(sp, format!("unsized arguments cannot be used in a tail call")) + .with_note(format!("unsized argument of type `{arg_ty}`")) + .emit(); + + self.found_errors = Err(err); + } } impl<'a, 'tcx> Visitor<'a, 'tcx> for TailCallCkVisitor<'a, 'tcx> { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b71793c157621..c286c0819aea2 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -18,8 +18,8 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ - self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, TyCtxt, - UpvarArgs, + self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, SplattedDef, Ty, + TyCtxt, UpvarArgs, }; use rustc_middle::{bug, span_bug}; use rustc_span::{DesugaringKind, Span}; @@ -369,19 +369,26 @@ impl<'tcx> ThirBuildCx<'tcx> { let kind = match expr.kind { // Here comes the interesting stuff: hir::ExprKind::MethodCall(segment, receiver, args, fn_span) => { - // Rewrite a.b(c) into UFCS form like Trait::b(a, c) - let expr = self.method_callee(expr, segment.ident.span, None); - info!("Using method span: {:?}", expr.span); - let args = std::iter::once(receiver) - .chain(args.iter()) - .map(|expr| self.mirror_expr(expr)) - .collect(); - ExprKind::Call { - ty: expr.ty, - fun: self.thir.exprs.push(expr), - args, - from_hir_call: true, - fn_span, + if self.typeck_results.is_splatted_call(expr) { + // The callee has a splatted tuple argument. + // rewrite `receiver.f(a, u, v)` into `receiver.f(a, #[splat] (u, v))` + self.convert_splatted_callee(expr, fn_span, args, Some(receiver)) + } else { + // Rewrite a.b(c) into UFCS form like Trait::b(a, c) + let expr = self.method_callee(expr, segment.ident.span, None); + info!("Using method span: {:?}", expr.span); + + let args = std::iter::once(receiver) + .chain(args.iter()) + .map(|expr| self.mirror_expr(expr)) + .collect(); + ExprKind::Call { + ty: expr.ty, + fun: self.thir.exprs.push(expr), + args, + from_hir_call: true, + fn_span, + } } } @@ -412,6 +419,10 @@ impl<'tcx> ThirBuildCx<'tcx> { from_hir_call: true, fn_span: expr.span, } + } else if self.typeck_results.is_splatted_call(expr) { + // The callee has a splatted tuple argument. + // rewrite `f(a, u, v)` into `f(a, #[splat] (u, v))` + self.convert_splatted_callee(expr, fun.span, args, None) } else { // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind @@ -1205,6 +1216,116 @@ impl<'tcx> ThirBuildCx<'tcx> { } } + fn splatted_callee( + &mut self, + expr: &hir::Expr<'_>, + span: Span, + ) -> (Expr<'tcx>, u16 /* arg_index */, u16 /* arg_count */) { + let SplattedDef { def_id, arg_index, arg_count } = + self.typeck_results.splatted_def(expr.hir_id).unwrap_or_else(|| { + span_bug!(expr.span, "no splatted def for function or method callee") + }); + let def_id = def_id.unwrap_or_else(|| { + span_bug!(expr.span, "no splatted def for function or method callee") + }); + let def_kind = self.tcx.def_kind(def_id); + let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); + debug!( + "splatted_callee: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", + user_ty, def_kind, def_id, arg_index, arg_count + ); + + ( + Expr { + temp_scope_id: expr.hir_id.local_id, + ty: Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)), + span, + kind: ExprKind::ZstLiteral { user_ty }, + }, + arg_index, + arg_count, + ) + } + + /// The callee has a splatted tuple argument. + /// Rewrite a splatted call `receiver.f(a, u, v)` into `receiver.f(a, #[splat] (u, v))`. + /// The receiver is optional. + fn convert_splatted_callee( + &mut self, + expr: &hir::Expr<'_>, + fn_span: Span, + args: &'tcx [hir::Expr<'tcx>], + receiver: Option<&'tcx hir::Expr<'tcx>>, + ) -> ExprKind<'tcx> { + let tcx = self.tcx; + + // The callee has a splatted tuple argument. + let (func, tupled_arg_index, tupled_args_count) = self.splatted_callee(expr, fn_span); + let tupled_arg_index = usize::from(tupled_arg_index); + let tupled_args_count = usize::from(tupled_args_count); + + // Splatting an empty tuple is permitted: `a.f() -> Trait::f(a, #[splat] ())`. + // In that case, the tupled arg index is one past the end of the args. + if tupled_arg_index + tupled_args_count > args.len() { + span_bug!( + expr.span, + "splatted arg index out of bounds of function args: {:?} + {:?} > {:?} for function call: receiver {:?}, args {:?}", + tupled_arg_index, + tupled_args_count, + args.len(), + receiver, + args, + ); + } + + info!("Using splatted function span: {:?}", func.span); + + // Split into non-tupled and tupled arguments + let initial_non_tupled_args = + args.iter().take(tupled_arg_index).map(|e| self.mirror_expr(e)).collect_vec(); + let tupled_args = if tupled_arg_index == args.len() || tupled_args_count == 0 { + // Splatting an empty tuple, in the ABI this gets ignored + Default::default() + } else { + &args[tupled_arg_index..(tupled_arg_index + tupled_args_count)] + }; + let final_non_tupled_args = args + .iter() + .skip(tupled_arg_index + tupled_args_count) + .map(|e| self.mirror_expr(e)) + .collect_vec(); + + let tupled_arg_tys = tupled_args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e)); + + let temp_scope_id = + if receiver.is_some() { func.temp_scope_id } else { expr.hir_id.local_id }; + let tupled_args = Expr { + ty: Ty::new_tup_from_iter(tcx, tupled_arg_tys), + temp_scope_id, + span: expr.span, + kind: ExprKind::Tuple { fields: self.mirror_exprs(tupled_args) }, + }; + + let tupled_args = self.thir.exprs.push(tupled_args); + + let mut args = + if let Some(receiver) = receiver { vec![self.mirror_expr(receiver)] } else { vec![] }; + args.extend(initial_non_tupled_args); + args.push(tupled_args); + args.extend(final_non_tupled_args); + + // We need the tupled arguments in HIR/MIR for type checking, but codegen can + // de-tuple them for performance + let fn_span = if receiver.is_some() { func.span } else { expr.span }; + ExprKind::Call { + ty: func.ty, + fun: self.thir.exprs.push(func), + args: args.into_boxed_slice(), + from_hir_call: true, + fn_span, + } + } + fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId { let arm = Arm { pattern: self.pattern_from_hir(&arm.pat), diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 1199955dc7c63..7c714fd7281d5 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -49,7 +49,7 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let mut convert = ConstToPat::new(self, id, span, c); match c.kind() { - ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty), + ty::ConstKind::Alias(_, alias_const) => convert.alias_to_pat(alias_const, ty), ty::ConstKind::Value(value) => convert.valtree_to_pat(value), _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c), } @@ -77,17 +77,17 @@ impl<'tcx> ConstToPat<'tcx> { /// We errored. Signal that in the pattern, so that follow up errors can be silenced. fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { - if let ty::ConstKind::Unevaluated(uv) = self.c.kind() { - if let ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } = uv.kind + if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() { + if let ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } = alias_const.kind && let Some(def_id) = def_id.as_local() { // Include the container item in the output. err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); } - if let ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } = uv.kind + if let ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } = alias_const.kind { err.span_label(self.tcx.def_span(def_id), msg!("constant defined here")); } @@ -95,11 +95,7 @@ impl<'tcx> ConstToPat<'tcx> { Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None }) } - fn unevaluated_to_pat( - &mut self, - uv: ty::UnevaluatedConst<'tcx>, - ty: Ty<'tcx>, - ) -> Box> { + fn alias_to_pat(&mut self, alias_const: ty::AliasConst<'tcx>, ty: Ty<'tcx>) -> Box> { // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). @@ -109,13 +105,13 @@ impl<'tcx> ConstToPat<'tcx> { // instead of having this logic here let typing_env = self.tcx.erase_and_anonymize_regions(self.typing_env).with_codegen_normalized(self.tcx); - let uv = self.tcx.erase_and_anonymize_regions(uv); + let alias_const = self.tcx.erase_and_anonymize_regions(alias_const); // FIXME(gca): This will become insufficient once associated constants can be // implemented as `type` consts (project-const-generics#76). At that point it'll // become necessary to just use type system normalization for all const patterns // but that's not yet possible. - let mut thir_pat = if uv.kind.is_type_const(self.tcx) { + let mut thir_pat = if alias_const.kind.is_type_const(self.tcx) { let Ok(normalize) = self .tcx .try_normalize_erasing_regions(self.typing_env, Unnormalized::new_wip(self.c)) @@ -132,67 +128,68 @@ impl<'tcx> ConstToPat<'tcx> { } else { // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) { - Ok(Ok(c)) => c, - Err(ErrorHandled::Reported(_, _)) => { - // Let's tell the use where this failing const occurs. - let mut err = - self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); - // We've emitted an error on the original const, it would be redundant to complain - // on its use as well. - if let ty::ConstKind::Unevaluated(uv) = self.c.kind() - && let ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } - | ty::UnevaluatedConstKind::Free { .. } = uv.kind - { - err.downgrade_to_delayed_bug(); - } - return self.mk_err(err, ty); - } - Err(ErrorHandled::TooGeneric(_)) => { - let mut e = self - .tcx - .dcx() - .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); - for arg in uv.args { - if let ty::GenericArgKind::Type(ty) = arg.kind() - && let ty::Param(param_ty) = ty.kind() + let valtree = + match self.tcx.const_eval_resolve_for_typeck(typing_env, alias_const, self.span) { + Ok(Ok(c)) => c, + Err(ErrorHandled::Reported(_, _)) => { + // Let's tell the use where this failing const occurs. + let mut err = + self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); + // We've emitted an error on the original const, it would be redundant to complain + // on its use as well. + if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() + && let ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::Free { .. } = alias_const.kind { - let def_id = self.tcx.hir_enclosing_body_owner(self.id); - let generics = self.tcx.generics_of(def_id); - let param = generics.type_param(*param_ty, self.tcx); - let span = self.tcx.def_span(param.def_id); - e.span_label(span, "constant depends on this generic parameter"); - if let Some(ident) = self.tcx.def_ident_span(def_id) - && self.tcx.sess.source_map().is_multiline(ident.between(span)) + err.downgrade_to_delayed_bug(); + } + return self.mk_err(err, ty); + } + Err(ErrorHandled::TooGeneric(_)) => { + let mut err = self + .tcx + .dcx() + .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); + for arg in alias_const.args { + if let ty::GenericArgKind::Type(ty) = arg.kind() + && let ty::Param(param_ty) = ty.kind() { - // Display the `fn` name as well in the diagnostic, as the generic isn't - // in the same line and it could be confusing otherwise. - e.span_label(ident, ""); + let def_id = self.tcx.hir_enclosing_body_owner(self.id); + let generics = self.tcx.generics_of(def_id); + let param = generics.type_param(*param_ty, self.tcx); + let span = self.tcx.def_span(param.def_id); + err.span_label(span, "constant depends on this generic parameter"); + if let Some(ident) = self.tcx.def_ident_span(def_id) + && self.tcx.sess.source_map().is_multiline(ident.between(span)) + { + // Display the `fn` name as well in the diagnostic, as the generic isn't + // in the same line and it could be confusing otherwise. + err.span_label(ident, ""); + } } } + return self.mk_err(err, ty); } - return self.mk_err(e, ty); - } - Ok(Err(bad_ty)) => { - // The pattern cannot be turned into a valtree. - let e = match bad_ty.kind() { - ty::Adt(def, ..) => { - assert!(def.is_union()); - self.tcx.dcx().create_err(UnionPattern { span: self.span }) - } - ty::FnPtr(..) | ty::RawPtr(..) => { - self.tcx.dcx().create_err(PointerPattern { span: self.span }) - } - _ => self.tcx.dcx().create_err(InvalidPattern { - span: self.span, - non_sm_ty: bad_ty, - prefix: bad_ty.prefix_string(self.tcx).to_string(), - }), - }; - return self.mk_err(e, ty); - } - }; + Ok(Err(bad_ty)) => { + // The pattern cannot be turned into a valtree. + let e = match bad_ty.kind() { + ty::Adt(def, ..) => { + assert!(def.is_union()); + self.tcx.dcx().create_err(UnionPattern { span: self.span }) + } + ty::FnPtr(..) | ty::RawPtr(..) => { + self.tcx.dcx().create_err(PointerPattern { span: self.span }) + } + _ => self.tcx.dcx().create_err(InvalidPattern { + span: self.span, + non_sm_ty: bad_ty, + prefix: bad_ty.prefix_string(self.tcx).to_string(), + }), + }; + return self.mk_err(e, ty); + } + }; // Lower the valtree to a THIR pattern. self.valtree_to_pat(ty::Value { ty, valtree }) @@ -209,7 +206,7 @@ impl<'tcx> ConstToPat<'tcx> { // Mark the pattern to indicate that it is the result of lowering a named // constant. This is used for diagnostics. - thir_pat.extra.get_or_insert_default().expanded_const = uv.kind.opt_def_id(); + thir_pat.extra.get_or_insert_default().expanded_const = alias_const.kind.opt_def_id(); thir_pat } @@ -253,7 +250,7 @@ impl<'tcx> ConstToPat<'tcx> { // then error about that instead, because `TypeNotStructural` gives advice that is // relevant only when the problem is that `ty` does not derive `PartialEq`. // - // Note that this is a duplicate of a check in `unevaluated_to_pat()`, + // Note that this is a duplicate of a check in `alias_to_pat()`, // which we would run later if we weren’t emitting an error now. if possibly_inapplicable_derived_partial_eq && !has_impl { let mut err = diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 92cb9dd8de7ca..11b31cb3976c9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -657,11 +657,12 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let args = self.typeck_results.node_args(id); // FIXME(mgca): we will need to special case IACs here to have type system compatible // generic args, instead of how we represent them in body expressions. - let c = ty::Const::new_unevaluated( + let c = ty::Const::new_alias( self.tcx, - ty::UnevaluatedConst::new( + ty::IsRigid::No, + ty::AliasConst::new( self.tcx, - ty::UnevaluatedConstKind::new_from_def_id(self.tcx, def_id), + ty::AliasConstKind::new_from_def_id(self.tcx, def_id), args, ), ); diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index d7e0a819d7ea1..c9198da72c750 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -165,7 +165,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Never | ty::Tuple(_) | ty::UnsafeBinder(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Bound(_, _) | ty::Infer(_) @@ -205,7 +205,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Bound(_, _) | ty::Infer(_) diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 395127edd1e0a..f94b3f49ff6d1 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs index 216ada600ce34..73c1510b9a0e2 100644 --- a/compiler/rustc_mir_transform/src/check_call_recursion.rs +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -45,7 +45,7 @@ impl<'tcx> MirLint<'tcx> for CheckDropRecursion { if let DefKind::AssocFn = tcx.def_kind(def_id) && let Some(impl_id) = tcx.trait_impl_of_assoc(def_id.to_def_id()) && let trait_ref = tcx.impl_trait_ref(impl_id) - && tcx.is_lang_item(trait_ref.instantiate_identity().skip_norm_wip().def_id, LangItem::Drop) + && tcx.is_lang_item(trait_ref.def_id(), LangItem::Drop) // avoid erroneous `Drop` impls from causing ICEs below && let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip() && sig.inputs().skip_binder().len() == 1 diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 411f090e34921..a2148b1b8e9e3 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -223,8 +223,10 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( None, &mut PerParentDisambiguatorState::new(parent_def_id), ); - by_move_body.source = - mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id())); + by_move_body.source = mir::MirSource { + instance: InstanceKind::Item(body_def.def_id().to_def_id()), + promoted: None, + }; if let Some(dumper) = MirDumper::new(tcx, "built", &by_move_body) { dumper.set_disambiguator(&"after").dump_mir(&by_move_body); @@ -245,7 +247,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( body_def.explicit_predicates_of(tcx.explicit_predicates_of(coroutine_def_id)); // The type of the coroutine is the `by_move_coroutine_ty`. - body_def.type_of(ty::EarlyBinder::bind(by_move_coroutine_ty)); + body_def.type_of(ty::EarlyBinder::bind(tcx, by_move_coroutine_ty)); body_def.mir_built(tcx.arena.alloc(Steal::new(by_move_body))); diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 5be6887e937a1..41b973476af94 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -206,7 +206,7 @@ pub(super) fn create_coroutine_drop_shim<'tcx>( // Update the body's def to become the drop glue. let coroutine_instance = body.source.instance; let drop_glue = tcx.require_lang_item(LangItem::DropGlue, body.span); - let drop_instance = InstanceKind::DropGlue(drop_glue, Some(coroutine_ty)); + let drop_instance = InstanceKind::Shim(ShimKind::DropGlue(drop_glue, Some(coroutine_ty))); // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible // filename. diff --git a/compiler/rustc_mir_transform/src/coroutine/layout.rs b/compiler/rustc_mir_transform/src/coroutine/layout.rs index 7262d44dcab99..abf6894e7cde5 100644 --- a/compiler/rustc_mir_transform/src/coroutine/layout.rs +++ b/compiler/rustc_mir_transform/src/coroutine/layout.rs @@ -612,7 +612,7 @@ fn check_must_not_suspend_ty<'tcx>( } ty::Adt(def, _) => check_must_not_suspend_def(tcx, def.did(), hir_id, data), // FIXME: support adding the attribute to TAITs - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: def }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: def }, .. }) => { let mut has_emitted = false; for &(predicate, _) in tcx.explicit_item_bounds(def).skip_binder() { // We only look at the `DefId`, so it is safe to skip the binder here. diff --git a/compiler/rustc_mir_transform/src/coroutine/mod.rs b/compiler/rustc_mir_transform/src/coroutine/mod.rs index bb57ca1cd90a7..53283680c0966 100644 --- a/compiler/rustc_mir_transform/src/coroutine/mod.rs +++ b/compiler/rustc_mir_transform/src/coroutine/mod.rs @@ -71,7 +71,7 @@ use rustc_index::{Idx, IndexVec, indexvec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{ - self, CoroutineArgs, CoroutineArgsExt, GenericArgsRef, InstanceKind, Ty, TyCtxt, + self, CoroutineArgs, CoroutineArgsExt, GenericArgsRef, InstanceKind, ShimKind, Ty, TyCtxt, }; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::impls::always_storage_live_locals; diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 331c98fc198eb..cdd8d8fdc1802 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -52,7 +52,7 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> { fn instantiate_ty(&self, v: Ty<'tcx>) -> Ty<'tcx> { if let Some(instance) = self.instance { - instance.instantiate_mir(self.tcx, ty::EarlyBinder::bind(&v)) + instance.instantiate_mir(self.tcx, ty::EarlyBinder::bind(self.tcx, v)) } else { v } diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index ca1aeedf10180..d88d9231c2009 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -83,7 +83,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { // If the inner type matches the type bound by `Pointer` if inner_ty == bound_ty { // Do an instantiation using the parameters from the callsite - let instantiated_ty = EarlyBinder::bind(inner_ty) + let instantiated_ty = EarlyBinder::bind(self.tcx, inner_ty) .instantiate(self.tcx, args_ref) .skip_norm_wip(); if let Some((fn_id, fn_args)) = diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index b30d2bd135546..2db4502ecc6b6 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -742,7 +742,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { a1.size(&self.ecx) == a2.size(&self.ecx) && b1.size(&self.ecx) == b2.size(&self.ecx) // The alignment of the second component determines its offset, so that also needs to match. - && b1.align(&self.ecx) == b2.align(&self.ecx) + && b1.default_align(&self.ecx) == b2.default_align(&self.ecx) // None of the inputs may be a pointer. && !matches!(a1.primitive(), Primitive::Pointer(..)) && !matches!(b1.primitive(), Primitive::Pointer(..)) diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index d099fc3b9ddeb..cc216c39b8599 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -44,7 +44,7 @@ pub(crate) fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool !p.has_type_flags( // Only consider global clauses to simplify. TypeFlags::HAS_FREE_LOCAL_NAMES - // Clauses that refer to unevaluated constants as they cause cycles. + // Clauses that refer to alias constants as they cause cycles. | TypeFlags::HAS_CT_PROJECTION, ) }); diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index df71cd48a77fe..c36b687111c01 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -15,7 +15,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::{ - self, Instance, InstanceKind, Ty, TyCtxt, TypeFlags, TypeVisitableExt, Unnormalized, + self, Instance, InstanceKind, ShimKind, Ty, TyCtxt, TypeFlags, TypeVisitableExt, Unnormalized, }; use rustc_session::config::{DebugInfo, OptLevel}; use rustc_span::Spanned; @@ -420,9 +420,10 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { work_list.push(target); // If the place doesn't actually need dropping, treat it like a regular goto. - let ty = callsite - .callee - .instantiate_mir(tcx, ty::EarlyBinder::bind(&place.ty(callee_body, tcx).ty)); + let ty = callsite.callee.instantiate_mir( + tcx, + ty::EarlyBinder::bind(tcx, place.ty(callee_body, tcx).ty), + ); if ty.needs_drop(tcx, self.typing_env()) && let UnwindAction::Cleanup(unwind) = unwind { @@ -638,7 +639,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>( let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions( tcx, inliner.typing_env(), - ty::EarlyBinder::bind(callee_body.clone()), + ty::EarlyBinder::bind(tcx, callee_body.clone()), ) else { debug!("failed to normalize callee body"); return Err("implementation limitation -- could not normalize callee body"); @@ -739,18 +740,21 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( // the correct param-env for types being dropped. Stall resolving // the MIR for this instance until all of its const params are // substituted. - InstanceKind::DropGlue(_, Some(ty)) if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => { + InstanceKind::Shim(ShimKind::DropGlue(_, Some(ty))) + if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => + { debug!("still needs substitution"); return Err("implementation limitation -- HACK for dropping polymorphic type"); } - InstanceKind::AsyncDropGlue(_, ty) | InstanceKind::AsyncDropGlueCtorShim(_, ty) => { + InstanceKind::Shim(ShimKind::AsyncDropGlue(_, ty)) + | InstanceKind::Shim(ShimKind::AsyncDropGlueCtor(_, ty)) => { return if ty.still_further_specializable() { Err("still needs substitution") } else { Ok(()) }; } - InstanceKind::FutureDropPollShim(_, ty, ty2) => { + InstanceKind::Shim(ShimKind::FutureDropPoll(_, ty, ty2)) => { return if ty.still_further_specializable() || ty2.still_further_specializable() { Err("still needs substitution") } else { @@ -762,15 +766,15 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we // do not need to catch this here, we can wait until the inliner decides to continue // inlining a second time. - InstanceKind::VTableShim(_) - | InstanceKind::ReifyShim(..) - | InstanceKind::FnPtrShim(..) - | InstanceKind::ClosureOnceShim { .. } - | InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::DropGlue(..) - | InstanceKind::CloneShim(..) - | InstanceKind::ThreadLocalShim(..) - | InstanceKind::FnPtrAddrShim(..) => return Ok(()), + InstanceKind::Shim(ShimKind::VTable(_)) + | InstanceKind::Shim(ShimKind::Reify(..)) + | InstanceKind::Shim(ShimKind::FnPtr(..)) + | InstanceKind::Shim(ShimKind::ClosureOnce { .. }) + | InstanceKind::Shim(ShimKind::ConstructCoroutineInClosure { .. }) + | InstanceKind::Shim(ShimKind::DropGlue(..)) + | InstanceKind::Shim(ShimKind::Clone(..)) + | InstanceKind::Shim(ShimKind::ThreadLocal(..)) + | InstanceKind::Shim(ShimKind::FnPtrAddr(..)) => return Ok(()), } if inliner.tcx().is_constructor(callee_def_id) { @@ -1022,7 +1026,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( }); // Copy required constants from the callee_body into the caller_body. Although we are only - // pushing unevaluated consts to `required_consts`, here they may have been evaluated + // pushing constants that still need evaluation to `required_consts`, here they may have been evaluated // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. caller_body.required_consts.as_mut().unwrap().extend( callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()), @@ -1370,8 +1374,8 @@ fn try_instance_mir<'tcx>( tcx: TyCtxt<'tcx>, instance: InstanceKind<'tcx>, ) -> Result<&'tcx Body<'tcx>, &'static str> { - if let ty::InstanceKind::DropGlue(_, Some(ty)) | ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) = - instance + if let ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, Some(ty))) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(_, ty)) = instance && let ty::Adt(def, args) = ty.kind() { let fields = def.all_fields(); diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index 9d031b6548021..b1a9258d48bb3 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -4,7 +4,7 @@ use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::limit::Limit; use rustc_middle::mir::TerminatorKind; -use rustc_middle::ty::{self, GenericArgsRef, InstanceKind, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, GenericArgsRef, InstanceKind, ShimKind, TyCtxt, TypeVisitableExt}; use rustc_span::sym; use tracing::{instrument, trace}; @@ -26,24 +26,24 @@ fn should_recurse<'tcx>(tcx: TyCtxt<'tcx>, callee: ty::Instance<'tcx>) -> bool { // These have MIR and if that MIR is inlined, instantiated and then inlining is run // again, a function item can end up getting inlined. Thus we'll be able to cause // a cycle that way - InstanceKind::VTableShim(_) - | InstanceKind::ReifyShim(..) - | InstanceKind::FnPtrShim(..) - | InstanceKind::ClosureOnceShim { .. } - | InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::ThreadLocalShim { .. } - | InstanceKind::CloneShim(..) => {} + InstanceKind::Shim(ShimKind::VTable(_)) + | InstanceKind::Shim(ShimKind::Reify(..)) + | InstanceKind::Shim(ShimKind::FnPtr(..)) + | InstanceKind::Shim(ShimKind::ClosureOnce { .. }) + | InstanceKind::Shim(ShimKind::ConstructCoroutineInClosure { .. }) + | InstanceKind::Shim(ShimKind::ThreadLocal { .. }) + | InstanceKind::Shim(ShimKind::Clone(..)) => {} // This shim does not call any other functions, thus there can be no recursion. - InstanceKind::FnPtrAddrShim(..) => return false, + InstanceKind::Shim(ShimKind::FnPtrAddr(..)) => return false, // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this // needs some more analysis. - InstanceKind::DropGlue(..) - | InstanceKind::FutureDropPollShim(..) - | InstanceKind::AsyncDropGlue(..) - | InstanceKind::AsyncDropGlueCtorShim(..) => { + InstanceKind::Shim(ShimKind::DropGlue(..)) + | InstanceKind::Shim(ShimKind::FutureDropPoll(..)) + | InstanceKind::Shim(ShimKind::AsyncDropGlue(..)) + | InstanceKind::Shim(ShimKind::AsyncDropGlueCtor(..)) => { if callee.has_param() { return false; } @@ -76,7 +76,7 @@ fn process<'tcx>( let Ok(args) = caller.try_instantiate_mir_and_normalize_erasing_regions( tcx, typing_env, - ty::EarlyBinder::bind(args), + ty::EarlyBinder::bind(tcx, args), ) else { trace!(?caller, ?typing_env, ?args, "cannot normalize, skipping"); continue; diff --git a/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs b/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs index 81e98585995fd..b359077753fd6 100644 --- a/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs +++ b/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs @@ -93,8 +93,8 @@ fn find_unreachable_code_from<'tcx>( bb: BasicBlock, body: &Body<'tcx>, ) -> Option<(SourceInfo, &'static str)> { - let bb = &body.basic_blocks[bb]; - for stmt in &bb.statements { + let bbdata = &body.basic_blocks[bb]; + for stmt in &bbdata.statements { match &stmt.kind { // Ignore the implicit `()` return place assignment for unit functions/blocks StatementKind::Assign((_, Rvalue::Use(Operand::Constant(const_), _))) @@ -108,7 +108,10 @@ fn find_unreachable_code_from<'tcx>( StatementKind::Assign((place, _)) if place.as_local() == Some(RETURN_PLACE) => { continue; } - StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => { + // Ignore statements inserted by MIR building that do not correspond to user code. + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::BackwardIncompatibleDropHint { .. } => { continue; } StatementKind::FakeRead(..) => return Some((stmt.source_info, "definition")), @@ -116,10 +119,18 @@ fn find_unreachable_code_from<'tcx>( } } - let term = bb.terminator(); + let term = bbdata.terminator(); match term.kind { - // No user code in this bb, and our goto target may be reachable via other paths - TerminatorKind::Goto { .. } | TerminatorKind::Return => None, + // The user does not care for `goto` and compiler-generated drops. If the target block is + // only reachable through those terminators, continue searching there. + TerminatorKind::Goto { target } | TerminatorKind::Drop { target, .. } => { + if &body.basic_blocks.predecessors()[target][..] == &[bb] { + find_unreachable_code_from(target, body) + } else { + None + } + } + TerminatorKind::Return => None, _ => Some((term.source_info, "expression")), } } diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index d665a3603b5b5..fe09594d7c5f3 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -187,7 +187,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< return; } if body.span.edition().at_least_rust_2024() - || tcx.lints_that_dont_need_to_run(()).contains(&lint::LintId::of(TAIL_EXPR_DROP_ORDER)) + || tcx.skippable_lints(()).contains(&lint::LintId::of(TAIL_EXPR_DROP_ORDER)) { return; } diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 795b0b9cac82e..af1bad9f1baa1 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -235,6 +235,11 @@ fn maybe_drop_guard<'tcx>( ) -> bool { if ever_dropped.contains(index) { let ty = checked_places.places[index].ty(&body.local_decls, tcx).ty; + // FIXME(#155345): Liveness uses `TypingMode::PostAnalysis` + // even though it's run on `mir_promoted` which is still + // in an earlier `TypingMode`. This is odd and we have to + // manually mark aliases as non-rigid here. + let ty = ty::set_aliases_to_non_rigid(tcx, ty).skip_norm_wip(); matches!( ty.kind(), ty::Closure(..) @@ -244,7 +249,7 @@ fn maybe_drop_guard<'tcx>( | ty::Dynamic(..) | ty::Array(..) | ty::Slice(..) - | ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) + | ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) ) && ty.needs_drop(tcx, typing_env) } else { false diff --git a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs index 776840a6fe8b2..135cd5324e65d 100644 --- a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs +++ b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs @@ -4,7 +4,7 @@ use rustc_middle::mir::visit::*; use rustc_middle::mir::*; -use rustc_middle::ty::{self, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, Ty, TyCtxt}; pub(super) struct PostAnalysisNormalize; @@ -63,10 +63,10 @@ impl<'tcx> MutVisitor<'tcx> for PostAnalysisNormalizeVisitor<'tcx> { // We have to use `try_normalize_erasing_regions` here, since it's // possible that we visit impossible-to-satisfy where clauses here, // see #91745 - if let Ok(c) = self - .tcx - .try_normalize_erasing_regions(self.typing_env, Unnormalized::new_wip(constant.const_)) - { + if let Ok(c) = self.tcx.try_normalize_erasing_regions( + self.typing_env, + ty::set_aliases_to_non_rigid(self.tcx, constant.const_), + ) { constant.const_ = c; } self.super_const_operand(constant, location); @@ -77,10 +77,21 @@ impl<'tcx> MutVisitor<'tcx> for PostAnalysisNormalizeVisitor<'tcx> { // We have to use `try_normalize_erasing_regions` here, since it's // possible that we visit impossible-to-satisfy where clauses here, // see #91745 - if let Ok(t) = - self.tcx.try_normalize_erasing_regions(self.typing_env, Unnormalized::new_wip(*ty)) - { + if let Ok(t) = self.tcx.try_normalize_erasing_regions( + self.typing_env, + ty::set_aliases_to_non_rigid(self.tcx, *ty), + ) { *ty = t; } } + + #[inline] + fn visit_args(&mut self, args: &mut ty::GenericArgsRef<'tcx>, _: Location) { + if let Ok(a) = self.tcx.try_normalize_erasing_regions( + self.typing_env, + ty::set_aliases_to_non_rigid(self.tcx, *args), + ) { + *args = a; + } + } } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 6d9b8feea05f4..830129700a929 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -31,16 +31,15 @@ pub(super) fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } -fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<'tcx> { - debug!("make_shim({:?})", instance); +fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, shim: ty::ShimKind<'tcx>) -> Body<'tcx> { + debug!("make_shim({:?})", shim); - let mut result = match instance { - ty::InstanceKind::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceKind::VTableShim(def_id) => { + let mut result = match shim { + ty::ShimKind::VTable(def_id) => { let adjustment = Adjustment::Deref { source: DerefSource::MutPtr }; - build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id)) + build_call_shim(tcx, shim, Some(adjustment), CallKind::Direct(def_id)) } - ty::InstanceKind::FnPtrShim(def_id, ty) => { + ty::ShimKind::FnPtr(def_id, ty) => { let trait_ = tcx.parent(def_id); // Supports `Fn` or `async Fn` traits. let adjustment = match tcx @@ -53,17 +52,17 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< None => bug!("fn pointer {:?} is not an fn", ty), }; - build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect(ty)) + build_call_shim(tcx, shim, Some(adjustment), CallKind::Indirect(ty)) } // We are generating a call back to our def-id, which the // codegen backend knows to turn to an actual call, be it // a virtual call, or a direct call to a function for which // indirect calls must be codegen'd differently than direct ones // (such as `#[track_caller]`). - ty::InstanceKind::ReifyShim(def_id, _) => { - build_call_shim(tcx, instance, None, CallKind::Direct(def_id)) + ty::ShimKind::Reify(def_id, _) => { + build_call_shim(tcx, shim, None, CallKind::Direct(def_id)) } - ty::InstanceKind::ClosureOnceShim { call_once: _, closure: _, track_caller: _ } => { + ty::ShimKind::ClosureOnce { call_once: _, closure: _, track_caller: _ } => { let fn_mut = tcx.require_lang_item(LangItem::FnMut, DUMMY_SP); let call_mut = tcx .associated_items(fn_mut) @@ -72,15 +71,14 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< .unwrap() .def_id; - build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut)) + build_call_shim(tcx, shim, Some(Adjustment::RefMut), CallKind::Direct(call_mut)) } - ty::InstanceKind::ConstructCoroutineInClosureShim { - coroutine_closure_def_id, - receiver_by_ref, - } => build_construct_coroutine_by_move_shim(tcx, coroutine_closure_def_id, receiver_by_ref), + ty::ShimKind::ConstructCoroutineInClosure { coroutine_closure_def_id, receiver_by_ref } => { + build_construct_coroutine_by_move_shim(tcx, coroutine_closure_def_id, receiver_by_ref) + } - ty::InstanceKind::DropGlue(def_id, ty) => { + ty::ShimKind::DropGlue(def_id, ty) => { // FIXME(#91576): Drop shims for coroutines aren't subject to the MIR passes at the end // of this function. Is this intentional? if let Some(&ty::Coroutine(coroutine_def_id, args)) = ty.map(Ty::kind) { @@ -109,8 +107,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< }; let mut body = - EarlyBinder::bind(body.clone()).instantiate(tcx, args).skip_norm_wip(); - debug!("make_shim({:?}) = {:?}", instance, body); + EarlyBinder::bind(tcx, body.clone()).instantiate(tcx, args).skip_norm_wip(); + debug!("make_shim({:?}) = {:?}", shim, body); pm::run_passes( tcx, @@ -129,10 +127,10 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< build_drop_shim(tcx, def_id, ty, ty::TypingEnv::post_analysis(tcx, def_id)) } - ty::InstanceKind::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance), - ty::InstanceKind::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), - ty::InstanceKind::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), - ty::InstanceKind::FutureDropPollShim(def_id, proxy_ty, impl_ty) => { + ty::ShimKind::ThreadLocal(..) => build_thread_local_shim(tcx, shim), + ty::ShimKind::Clone(def_id, ty) => build_clone_shim(tcx, def_id, ty), + ty::ShimKind::FnPtrAddr(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), + ty::ShimKind::FutureDropPoll(def_id, proxy_ty, impl_ty) => { let mut body = async_destructor_ctor::build_future_drop_poll_shim(tcx, def_id, proxy_ty, impl_ty); @@ -148,10 +146,10 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< pm::Optimizations::Allowed, ); run_optimization_passes(tcx, &mut body); - debug!("make_shim({:?}) = {:?}", instance, body); + debug!("make_shim({:?}) = {:?}", shim, body); return body; } - ty::InstanceKind::AsyncDropGlue(def_id, ty) => { + ty::ShimKind::AsyncDropGlue(def_id, ty) => { let mut body = async_destructor_ctor::build_async_drop_shim(tcx, def_id, ty); // Main pass required here is StateTransform to convert sync drop ladder @@ -170,23 +168,17 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< Some(MirPhase::Runtime(RuntimePhase::PostCleanup)), ); run_optimization_passes(tcx, &mut body); - debug!("make_shim({:?}) = {:?}", instance, body); + debug!("make_shim({:?}) = {:?}", shim, body); return body; } - ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) => { + ty::ShimKind::AsyncDropGlueCtor(def_id, ty) => { let body = async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty); - debug!("make_shim({:?}) = {:?}", instance, body); + debug!("make_shim({:?}) = {:?}", shim, body); return body; } - ty::InstanceKind::Virtual(..) => { - bug!("InstanceKind::Virtual ({:?}) is for direct calls only", instance) - } - ty::InstanceKind::Intrinsic(_) => { - bug!("creating shims from intrinsics ({:?}) is unsupported", instance) - } }; - debug!("make_shim({:?}) = untransformed {:?}", instance, result); + debug!("make_shim({:?}) = untransformed {:?}", shim, result); deref_finder(tcx, &mut result, false); @@ -211,7 +203,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< Some(MirPhase::Runtime(RuntimePhase::Optimized)), ); - debug!("make_shim({:?}) = {:?}", instance, result); + debug!("make_shim({:?}) = {:?}", shim, result); result } @@ -300,7 +292,7 @@ pub fn build_drop_shim<'tcx>( } block(&mut blocks, TerminatorKind::Return); - let source = MirSource::from_instance(ty::InstanceKind::DropGlue(def_id, ty)); + let source = MirSource::from_shim(ty::ShimKind::DropGlue(def_id, ty)); let mut body = new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); @@ -480,11 +472,8 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } } -fn build_thread_local_shim<'tcx>( - tcx: TyCtxt<'tcx>, - instance: ty::InstanceKind<'tcx>, -) -> Body<'tcx> { - let def_id = instance.def_id(); +fn build_thread_local_shim<'tcx>(tcx: TyCtxt<'tcx>, shim: ty::ShimKind<'tcx>) -> Body<'tcx> { + let def_id = shim.def_id(); let span = tcx.def_span(def_id); let source_info = SourceInfo::outermost(span); @@ -502,7 +491,7 @@ fn build_thread_local_shim<'tcx>( )]); new_body( - MirSource::from_instance(instance), + MirSource::from_shim(shim), blocks, IndexVec::from_raw(vec![LocalDecl::new(tcx.thread_local_ptr_ty(def_id), span)]), 0, @@ -565,10 +554,8 @@ impl<'tcx> CloneShimBuilder<'tcx> { } fn into_mir(self) -> Body<'tcx> { - let source = MirSource::from_instance(ty::InstanceKind::CloneShim( - self.def_id, - self.sig.inputs_and_output[0], - )); + let source = + MirSource::from_shim(ty::ShimKind::Clone(self.def_id, self.sig.inputs_and_output[0])); new_body(source, self.blocks, self.local_decls, self.sig.inputs().len(), self.span) } @@ -776,14 +763,14 @@ impl<'tcx> CloneShimBuilder<'tcx> { #[instrument(level = "debug", skip(tcx), ret)] fn build_call_shim<'tcx>( tcx: TyCtxt<'tcx>, - instance: ty::InstanceKind<'tcx>, + shim: ty::ShimKind<'tcx>, rcvr_adjustment: Option, call_kind: CallKind<'tcx>, ) -> Body<'tcx> { // `FnPtrShim` contains the fn pointer type that a call shim is being built for - this is used // to instantiate into the signature of the shim. It is not necessary for users of this // MIR body to perform further instantiations (see `InstanceKind::has_polymorphic_mir_body`). - let (sig_args, untuple_args) = if let ty::InstanceKind::FnPtrShim(_, ty) = instance { + let (sig_args, untuple_args) = if let ty::ShimKind::FnPtr(_, ty) = shim { let sig = tcx.instantiate_bound_regions_with_erased(ty.fn_sig(tcx)); let untuple_args = sig.inputs(); @@ -796,12 +783,12 @@ fn build_call_shim<'tcx>( (None, None) }; - let def_id = instance.def_id(); + let def_id = shim.def_id(); let sig = tcx.fn_sig(def_id); let sig = sig.map_bound(|sig| tcx.instantiate_bound_regions_with_erased(sig)); - assert_eq!(sig_args.is_some(), !instance.has_polymorphic_mir_body()); + assert_eq!(sig_args.is_some(), !shim.has_polymorphic_mir_body()); let mut sig = if let Some(sig_args) = sig_args { sig.instantiate(tcx, &sig_args).skip_norm_wip() } else { @@ -830,14 +817,14 @@ fn build_call_shim<'tcx>( DerefSource::MutRef => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, fnty), DerefSource::MutPtr => Ty::new_mut_ptr(tcx, fnty), }, - Adjustment::RefMut => bug!("`RefMut` is never used with indirect calls: {instance:?}"), + Adjustment::RefMut => bug!("`RefMut` is never used with indirect calls: {shim:?}"), }; sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } // FIXME: Avoid having to adjust the signature both here and in // `fn_sig_for_fn_abi`. - if let ty::InstanceKind::VTableShim(..) = instance { + if let ty::ShimKind::VTable(..) = shim { // Modify fn(self, ...) to fn(self: *mut Self, ...) let mut inputs_and_output = sig.inputs_and_output.to_vec(); let self_arg = &mut inputs_and_output[0]; @@ -995,7 +982,7 @@ fn build_call_shim<'tcx>( } let mut body = - new_body(MirSource::from_instance(instance), blocks, local_decls, sig.inputs().len(), span); + new_body(MirSource::from_shim(shim), blocks, local_decls, sig.inputs().len(), span); if let ExternAbi::RustCall = sig.abi() { body.spread_arg = Some(Local::new(sig.inputs().len())); @@ -1119,7 +1106,7 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }), false, ); - let source = MirSource::from_instance(ty::InstanceKind::FnPtrAddrShim(def_id, self_ty)); + let source = MirSource::from_shim(ty::ShimKind::FnPtrAddr(def_id, self_ty)); new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span) } @@ -1218,7 +1205,7 @@ fn build_construct_coroutine_by_move_shim<'tcx>( false, ); - let source = MirSource::from_instance(ty::InstanceKind::ConstructCoroutineInClosureShim { + let source = MirSource::from_shim(ty::ShimKind::ConstructCoroutineInClosure { coroutine_closure_def_id, receiver_by_ref, }); 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 da88247800372..1347fd21db057 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -23,7 +23,8 @@ pub(super) fn build_async_destructor_ctor_shim<'tcx>( debug_assert_eq!(Some(def_id), tcx.lang_items().async_drop_in_place_fn()); let generic_body = tcx.optimized_mir(def_id); let args = tcx.mk_args(&[ty.into()]); - let mut body = EarlyBinder::bind(generic_body.clone()).instantiate(tcx, args).skip_norm_wip(); + let mut body = + EarlyBinder::bind(tcx, generic_body.clone()).instantiate(tcx, args).skip_norm_wip(); // Minimal shim passes except MentionedItems, // it causes error "mentioned_items for DefId(...async_drop_in_place...) have already been set @@ -106,7 +107,7 @@ pub(super) fn build_async_drop_shim<'tcx>( ); block(&mut blocks, TerminatorKind::Return); - let source = MirSource::from_instance(ty::InstanceKind::AsyncDropGlue(def_id, ty)); + let source = MirSource::from_shim(ty::ShimKind::AsyncDropGlue(def_id, ty)); let mut body = new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); @@ -175,17 +176,17 @@ pub(super) fn build_future_drop_poll_shim<'tcx>( proxy_ty: Ty<'tcx>, impl_ty: Ty<'tcx>, ) -> Body<'tcx> { - let instance = ty::InstanceKind::FutureDropPollShim(def_id, proxy_ty, impl_ty); + let shim = ty::ShimKind::FutureDropPoll(def_id, proxy_ty, impl_ty); let ty::Coroutine(coroutine_def_id, _) = impl_ty.kind() else { - bug!("build_future_drop_poll_shim not for coroutine impl type: ({:?})", instance); + bug!("build_future_drop_poll_shim not for coroutine impl type: ({:?})", shim); }; let span = tcx.def_span(def_id); if tcx.is_async_drop_in_place_coroutine(*coroutine_def_id) { - build_adrop_for_adrop_shim(tcx, proxy_ty, impl_ty, span, instance) + build_adrop_for_adrop_shim(tcx, proxy_ty, impl_ty, span, shim) } else { - build_adrop_for_coroutine_shim(tcx, proxy_ty, impl_ty, span, instance) + build_adrop_for_coroutine_shim(tcx, proxy_ty, impl_ty, span, shim) } } @@ -198,16 +199,16 @@ fn build_adrop_for_coroutine_shim<'tcx>( proxy_ty: Ty<'tcx>, impl_ty: Ty<'tcx>, span: Span, - instance: ty::InstanceKind<'tcx>, + shim: ty::ShimKind<'tcx>, ) -> Body<'tcx> { let ty::Coroutine(coroutine_def_id, impl_args) = impl_ty.kind() else { - bug!("build_adrop_for_coroutine_shim not for coroutine impl type: ({:?})", instance); + bug!("build_adrop_for_coroutine_shim not for coroutine impl type: ({:?})", shim); }; let source_info = SourceInfo::outermost(span); let body = tcx.optimized_mir(*coroutine_def_id).future_drop_poll().unwrap(); let mut body: Body<'tcx> = - EarlyBinder::bind(body.clone()).instantiate(tcx, impl_args).skip_norm_wip(); - body.source.instance = instance; + EarlyBinder::bind(tcx, body.clone()).instantiate(tcx, impl_args).skip_norm_wip(); + body.source.instance = ty::InstanceKind::Shim(shim); body.phase = MirPhase::Runtime(RuntimePhase::Initial); body.var_debug_info.clear(); @@ -291,7 +292,7 @@ fn build_adrop_for_adrop_shim<'tcx>( proxy_ty: Ty<'tcx>, impl_ty: Ty<'tcx>, span: Span, - instance: ty::InstanceKind<'tcx>, + shim: ty::ShimKind<'tcx>, ) -> Body<'tcx> { let source_info = SourceInfo::outermost(span); let proxy_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, proxy_ty); @@ -413,7 +414,7 @@ fn build_adrop_for_adrop_shim<'tcx>( false, )); - let source = MirSource::from_instance(instance); + let source = MirSource::from_shim(shim); let mut body = new_body(source, blocks, locals, sig.inputs().len(), span); body.phase = MirPhase::Runtime(RuntimePhase::Initial); return body; diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs index 25d15c12a7b3b..4b66c757729d3 100644 --- a/compiler/rustc_mir_transform/src/trivial_const.rs +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -52,11 +52,10 @@ where F: FnOnce() -> B, B: Deref>, { - if !matches!( - tcx.def_kind(def), - DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst - ) { - return None; + match tcx.def_kind(def) { + DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst => (), + DefKind::InlineConst if tcx.is_type_system_inline_const(def) => (), + _ => return None, } // If there are impossible predicates then MIR passes will replace the body with diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 3dca3f09db65d..bbbae98cd49d0 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -679,7 +679,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; let kind = match parent_ty.ty.kind() { - &ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { self.tcx.type_of(def_id).instantiate(self.tcx, args).skip_norm_wip().kind() } kind => kind, @@ -783,7 +783,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { return; }; - ty::EarlyBinder::bind(f_ty.ty) + ty::EarlyBinder::bind(self.tcx, f_ty.ty) .instantiate(self.tcx, args) .skip_norm_wip() } else { @@ -1551,7 +1551,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { pty.kind(), ty::Adt(..) | ty::Coroutine(..) - | ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) + | ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) ) { self.fail( location, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 0421edc543151..ceb044c102f69 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -226,8 +226,8 @@ use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, - TypeVisitable, TypeVisitableExt, TypeVisitor, Unnormalized, VtblEntry, + self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, ShimKind, Ty, TyCtxt, + TypeFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, Unnormalized, VtblEntry, }; use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; @@ -447,7 +447,7 @@ fn collect_items_rec<'tcx>( used_items.push(respan( starting_item.span, MonoItem::Fn(Instance { - def: InstanceKind::ThreadLocalShim(def_id), + def: InstanceKind::Shim(ShimKind::ThreadLocal(def_id)), args: GenericArgs::empty(), }), )); @@ -633,7 +633,7 @@ fn check_normalization_error<'tcx>( match self.instance.try_instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(t), + ty::EarlyBinder::bind(self.tcx, t), ) { Ok(_) => ControlFlow::Continue(()), Err(_) => ControlFlow::Break(()), @@ -697,7 +697,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(value), + ty::EarlyBinder::bind(self.tcx, value), ) } @@ -1013,10 +1013,10 @@ fn visit_instance_use<'tcx>( bug!("{:?} being reified", instance); } } - ty::InstanceKind::ThreadLocalShim(..) => { + ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(..)) => { bug!("{:?} being reified", instance); } - ty::InstanceKind::DropGlue(_, None) => { + ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None)) => { // Don't need to emit noop drop glue if we are calling directly. // // Note that we also optimize away the call to visit_instance_use in vtable construction @@ -1025,18 +1025,18 @@ fn visit_instance_use<'tcx>( output.push(create_fn_mono_item(tcx, instance, source)); } } - ty::InstanceKind::DropGlue(_, Some(_)) - | ty::InstanceKind::FutureDropPollShim(..) - | ty::InstanceKind::AsyncDropGlue(_, _) - | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) - | ty::InstanceKind::VTableShim(..) - | ty::InstanceKind::ReifyShim(..) - | ty::InstanceKind::ClosureOnceShim { .. } - | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } - | ty::InstanceKind::Item(..) - | ty::InstanceKind::FnPtrShim(..) - | ty::InstanceKind::CloneShim(..) - | ty::InstanceKind::FnPtrAddrShim(..) => { + ty::InstanceKind::Item(..) + | ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, Some(_))) + | ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(..)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(_, _)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(_, _)) + | ty::InstanceKind::Shim(ty::ShimKind::VTable(..)) + | ty::InstanceKind::Shim(ty::ShimKind::Reify(..)) + | ty::InstanceKind::Shim(ty::ShimKind::ClosureOnce { .. }) + | ty::InstanceKind::Shim(ty::ShimKind::ConstructCoroutineInClosure { .. }) + | ty::InstanceKind::Shim(ty::ShimKind::FnPtr(..)) + | ty::InstanceKind::Shim(ty::ShimKind::Clone(..)) + | ty::InstanceKind::Shim(ty::ShimKind::FnPtrAddr(..)) => { output.push(create_fn_mono_item(tcx, instance, source)); } } diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 8b79eaae28753..0e30f1f3828e6 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -242,7 +242,7 @@ fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &m let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions( tcx, ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(callee_ty), + ty::EarlyBinder::bind(tcx, callee_ty), ); check_call_site_abi(tcx, callee_ty, body.source.instance, || { let loc = Location { diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index f0fe4c9056187..5fa2f49584f2c 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -60,7 +60,7 @@ impl<'tcx> MoveCheckVisitor<'tcx> { self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(value), + ty::EarlyBinder::bind(self.tcx, value), ) } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index aee7153419883..bf4a2bdd15107 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -115,7 +115,7 @@ use rustc_middle::mono::{ MonoItemPartitions, Visibility, }; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; -use rustc_middle::ty::{self, InstanceKind, TyCtxt}; +use rustc_middle::ty::{self, InstanceKind, ShimKind, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::CodegenUnits; use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath}; @@ -630,20 +630,20 @@ fn characteristic_def_id_of_mono_item<'tcx>( MonoItem::Fn(instance) => { let def_id = match instance.def { ty::InstanceKind::Item(def) => def, - ty::InstanceKind::VTableShim(..) - | ty::InstanceKind::ReifyShim(..) - | ty::InstanceKind::FnPtrShim(..) - | ty::InstanceKind::ClosureOnceShim { .. } - | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } - | ty::InstanceKind::Intrinsic(..) - | ty::InstanceKind::DropGlue(..) + ty::InstanceKind::Intrinsic(..) | ty::InstanceKind::Virtual(..) - | ty::InstanceKind::CloneShim(..) - | ty::InstanceKind::ThreadLocalShim(..) - | ty::InstanceKind::FnPtrAddrShim(..) - | ty::InstanceKind::FutureDropPollShim(..) - | ty::InstanceKind::AsyncDropGlue(..) - | ty::InstanceKind::AsyncDropGlueCtorShim(..) => return None, + | ty::InstanceKind::Shim(ty::ShimKind::VTable(..)) + | ty::InstanceKind::Shim(ty::ShimKind::Reify(..)) + | ty::InstanceKind::Shim(ty::ShimKind::FnPtr(..)) + | ty::InstanceKind::Shim(ty::ShimKind::ClosureOnce { .. }) + | ty::InstanceKind::Shim(ty::ShimKind::ConstructCoroutineInClosure { .. }) + | ty::InstanceKind::Shim(ty::ShimKind::DropGlue(..)) + | ty::InstanceKind::Shim(ty::ShimKind::Clone(..)) + | ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(..)) + | ty::InstanceKind::Shim(ty::ShimKind::FnPtrAddr(..)) + | ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(..)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(..)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(..)) => return None, }; // If this is a method, we want to put it into the same module as @@ -794,27 +794,27 @@ fn mono_item_visibility<'tcx>( let def_id = match instance.def { InstanceKind::Item(def_id) - | InstanceKind::DropGlue(def_id, Some(_)) - | InstanceKind::FutureDropPollShim(def_id, _, _) - | InstanceKind::AsyncDropGlue(def_id, _) - | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id, + | InstanceKind::Shim(ShimKind::DropGlue(def_id, Some(_))) + | InstanceKind::Shim(ShimKind::FutureDropPoll(def_id, _, _)) + | InstanceKind::Shim(ShimKind::AsyncDropGlue(def_id, _)) + | InstanceKind::Shim(ShimKind::AsyncDropGlueCtor(def_id, _)) => def_id, // We match the visibility of statics here - InstanceKind::ThreadLocalShim(def_id) => { + InstanceKind::Shim(ShimKind::ThreadLocal(def_id)) => { return static_visibility(tcx, can_be_internalized, def_id); } // These are all compiler glue and such, never exported, always hidden. - InstanceKind::VTableShim(..) - | InstanceKind::ReifyShim(..) - | InstanceKind::FnPtrShim(..) + InstanceKind::Shim(ShimKind::VTable(..)) + | InstanceKind::Shim(ShimKind::Reify(..)) + | InstanceKind::Shim(ShimKind::FnPtr(..)) | InstanceKind::Virtual(..) | InstanceKind::Intrinsic(..) - | InstanceKind::ClosureOnceShim { .. } - | InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::DropGlue(..) - | InstanceKind::CloneShim(..) - | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden, + | InstanceKind::Shim(ShimKind::ClosureOnce { .. }) + | InstanceKind::Shim(ShimKind::ConstructCoroutineInClosure { .. }) + | InstanceKind::Shim(ShimKind::DropGlue(..)) + | InstanceKind::Shim(ShimKind::Clone(..)) + | InstanceKind::Shim(ShimKind::FnPtrAddr(..)) => return Visibility::Hidden, }; // Both the `start_fn` lang item and `main` itself should not be exported, @@ -1331,8 +1331,8 @@ pub(crate) fn provide(providers: &mut Providers) { // "Normal" functions size estimate: the number of // statements, plus one for the terminator. InstanceKind::Item(..) - | InstanceKind::DropGlue(..) - | InstanceKind::AsyncDropGlueCtorShim(..) => { + | InstanceKind::Shim(ShimKind::DropGlue(..)) + | InstanceKind::Shim(ShimKind::AsyncDropGlueCtor(..)) => { let mir = tcx.instance_mir(instance.def); mir.basic_blocks .iter() diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index 6fcb18177a142..bf24662e1e48a 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs @@ -30,12 +30,12 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In let before_feature_tys = tcx.instantiate_and_normalize_erasing_regions( closure_instance.args, typing_env, - ty::EarlyBinder::bind(before_feature_tys), + ty::EarlyBinder::bind(tcx, before_feature_tys), ); let after_feature_tys = tcx.instantiate_and_normalize_erasing_regions( closure_instance.args, typing_env, - ty::EarlyBinder::bind(after_feature_tys), + ty::EarlyBinder::bind(tcx, after_feature_tys), ); let new_size = tcx diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 824c78094a69b..5aa09e1ba7932 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -391,7 +391,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Bound(_, _) | ty::Error(_) => { return ensure_sufficient_stack(|| t.super_fold_with(self)); @@ -565,7 +565,7 @@ impl, I: Interner> TypeFolder for Canonicaliz }, // FIXME: See comment above -- we could fold the region separately or something. ty::ConstKind::Bound(_, _) - | ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Alias(_, _) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => return c.super_fold_with(self), diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 6c6ead109e20e..80b4d3be172ef 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -333,7 +333,7 @@ where { let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) }; let state = inspect::State { var_values, data }; - let state = eager_resolve_vars(delegate, state); + let state = eager_resolve_vars(&**delegate, state); Canonicalizer::canonicalize_response(delegate, max_input_universe, state) } diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index 589d055192029..6c346715a81c2 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -362,7 +362,7 @@ where // normalize to that, so we have to treat it as an uncovered ty param. // * Otherwise it may normalize to any non-type-generic type // be it local or non-local. - ty::Alias(ty::AliasTy { kind, .. }) => { + ty::Alias(_, ty::AliasTy { kind, .. }) => { if ty.has_type_flags( ty::TypeFlags::HAS_TY_PLACEHOLDER | ty::TypeFlags::HAS_TY_BOUND diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 8fd7d6d0471c7..2ebb0fb851a4c 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -37,7 +37,7 @@ pub trait SolverDelegate: Deref + Sized { fn evaluate_const( &self, param_env: ::ParamEnv, - uv: ty::UnevaluatedConst, + alias_const: ty::AliasConst, ) -> Option<::Const>; // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 3bf2f64cf8887..761b141202d98 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -1,6 +1,7 @@ +use std::fmt::Debug; + use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::inherent::*; -use rustc_type_ir::solve::{Goal, NoSolution}; use rustc_type_ir::{ self as ty, AliasTerm, Binder, FallibleTypeFolder, InferConst, InferCtxtLike, InferTy, Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, @@ -21,7 +22,6 @@ where { infcx: &'a Infcx, universes: Vec>, - stalled_goals: Vec>, normalize: F, } @@ -31,6 +31,12 @@ enum HasEscapingBoundVars { No, } +#[derive(PartialEq, Eq)] +pub enum NormalizationWasAmbiguous { + Yes, + No, +} + /// Finds the max universe present in infer vars. struct MaxUniverse<'a, Infcx, I> where @@ -97,64 +103,53 @@ where } } -impl<'a, Infcx, I, F> NormalizationFolder<'a, Infcx, I, F> +impl<'a, Infcx, I, F, E> NormalizationFolder<'a, Infcx, I, F> where Infcx: InferCtxtLike, I: Interner, - F: FnMut(AliasTerm) -> Result<(I::Term, Option>), NoSolution>, + F: FnMut(AliasTerm) -> Result<(I::Term, NormalizationWasAmbiguous), E>, { - pub fn new( - infcx: &'a Infcx, - universes: Vec>, - stalled_goals: Vec>, - normalize: F, - ) -> Self { - Self { infcx, universes, stalled_goals, normalize } - } - - pub fn stalled_goals(self) -> Vec> { - self.stalled_goals + pub fn new(infcx: &'a Infcx, universes: Vec>, normalize: F) -> Self { + Self { infcx, universes, normalize } } fn normalize_alias_term( &mut self, alias_term: AliasTerm, has_escaping: HasEscapingBoundVars, - ) -> Result, NoSolution> { - let current_universe = self.infcx.universe(); - self.infcx.create_next_universe(); - - let (normalized, ambig_goal) = (self.normalize)(alias_term)?; + ) -> Result, E> { + let (normalized, normalization_was_ambiguous) = (self.normalize)(alias_term)?; // Return ambiguous higher ranked alias as is, if // - it contains escaping vars, and - // - the normalized term contains infer vars newly created - // in the normalization above. - // The problem is that they may be resolved to types - // referencing the temporary placeholders. + // - the normalized term contains infer vars which may mention + // temporary placeholders after we've already mapped them back + // to bound vars. // // We can normalize the ambiguous alias again after the binder is instantiated. - if ambig_goal.is_some() && has_escaping == HasEscapingBoundVars::Yes { + if normalization_was_ambiguous == NormalizationWasAmbiguous::Yes + && has_escaping == HasEscapingBoundVars::Yes + { let mut visitor = MaxUniverse::new(self.infcx); normalized.visit_with(&mut visitor); let max_universe = visitor.max_universe(); - if current_universe.cannot_name(max_universe) { + if max_universe.can_name(self.universes.first().unwrap().unwrap()) { return Ok(None); } } - self.stalled_goals.extend(ambig_goal); Ok(Some(normalized)) } } -impl<'a, Infcx, I, F> FallibleTypeFolder for NormalizationFolder<'a, Infcx, I, F> +impl<'a, Infcx, I, F, E> FallibleTypeFolder for NormalizationFolder<'a, Infcx, I, F> where Infcx: InferCtxtLike, I: Interner, - F: FnMut(AliasTerm) -> Result<(I::Term, Option>), NoSolution>, + F: FnMut(AliasTerm) -> Result<(I::Term, NormalizationWasAmbiguous), E>, + E: Debug, { - type Error = NoSolution; + type Error = E; fn cx(&self) -> I { self.infcx.cx() @@ -173,16 +168,23 @@ where #[instrument(level = "trace", skip(self), ret)] fn try_fold_ty(&mut self, ty: I::Ty) -> Result { let infcx = self.infcx; - if !ty.has_aliases() { + let original = ty; + + if !self.cx().renormalize_rigid_aliases() && !ty.has_non_rigid_aliases() { return Ok(ty); } // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. let ty = ty.try_super_fold_with(self)?; - let ty::Alias(alias_ty) = ty.kind() else { return Ok(ty) }; + let ty::Alias(orig_is_rigid, alias_ty) = ty.kind() else { return Ok(ty) }; + // We support ambiguous aliases inside rigid alias. So we still recognize + // the rigidness of the outer alias. + if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { + return Ok(ty); + } - if ty.has_escaping_bound_vars() { + let normalized = if ty.has_escaping_bound_vars() { let (alias_ty, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_ty); let Some(result) = ensure_sufficient_stack(|| { @@ -192,58 +194,86 @@ where return Ok(ty); }; - Ok(PlaceholderReplacer::replace_placeholders( + PlaceholderReplacer::replace_placeholders( infcx, mapped_regions, mapped_types, mapped_consts, &self.universes, result.expect_ty(), - )) + ) } else { - Ok(ensure_sufficient_stack(|| { + ensure_sufficient_stack(|| { self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No) })? .map(|term| term.expect_ty()) - .unwrap_or(ty)) + .unwrap_or(ty) + }; + + if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { + // find out missing typing env change. + let original = crate::resolve::eager_resolve_vars(infcx, original); + let normalized = crate::resolve::eager_resolve_vars(infcx, normalized); + assert_eq!(original, normalized, "rigid alias is further normalized"); } + Ok(normalized) } #[instrument(level = "trace", skip(self), ret)] fn try_fold_const(&mut self, ct: I::Const) -> Result { let infcx = self.infcx; - if !ct.has_aliases() { + let original = ct; + + if !self.cx().renormalize_rigid_aliases() && !ct.has_non_rigid_aliases() { return Ok(ct); } // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. let ct = ct.try_super_fold_with(self)?; - let ty::ConstKind::Unevaluated(uv) = ct.kind() else { return Ok(ct) }; + let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return Ok(ct) }; + // We support ambiguous aliases inside rigid alias. So we still recognize + // the rigidness of the outer alias. + if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { + return Ok(ct); + } - if ct.has_escaping_bound_vars() { - let (uv, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv); + let normalized = if ct.has_escaping_bound_vars() { + let (alias_const, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_const); let Some(result) = ensure_sufficient_stack(|| { - self.normalize_alias_term(uv.into(), HasEscapingBoundVars::Yes) + self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::Yes) })? else { return Ok(ct); }; - Ok(PlaceholderReplacer::replace_placeholders( + PlaceholderReplacer::replace_placeholders( infcx, mapped_regions, mapped_types, mapped_consts, &self.universes, result.expect_const(), - )) + ) } else { - Ok(ensure_sufficient_stack(|| { - self.normalize_alias_term(uv.into(), HasEscapingBoundVars::No) + ensure_sufficient_stack(|| { + self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::No) })? .map(|term| term.expect_const()) - .unwrap_or(ct)) + .unwrap_or(ct) + }; + + if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { + // find out missing typing env change. + let original = crate::resolve::eager_resolve_vars(infcx, original); + let normalized = crate::resolve::eager_resolve_vars(infcx, normalized); + assert_eq!(original, normalized, "rigid alias is further normalized"); } + + Ok(normalized) + } + + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result { + if p.allow_normalization() { p.try_super_fold_with(self) } else { Ok(p) } } } diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index c3c57eccd6eff..542cb476c2f38 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -5,15 +5,13 @@ use rustc_type_ir::{ TypeVisitableExt, }; -use crate::delegate::SolverDelegate; - /////////////////////////////////////////////////////////////////////////// // EAGER RESOLUTION /// Resolves ty, region, and const vars to their inferred values or their root vars. -struct EagerResolver<'a, D, I = ::Interner> +struct EagerResolver<'a, D, I = ::Interner> where - D: SolverDelegate, + D: InferCtxtLike, I: Interner, { delegate: &'a D, @@ -22,25 +20,25 @@ where cache: DelayedMap, } -pub fn eager_resolve_vars>( - delegate: &D, +pub fn eager_resolve_vars>( + infcx: &Infcx, value: T, ) -> T { if value.has_infer() { - let mut folder = EagerResolver::new(delegate); + let mut folder = EagerResolver::new(infcx); value.fold_with(&mut folder) } else { value } } -impl<'a, D: SolverDelegate> EagerResolver<'a, D> { - fn new(delegate: &'a D) -> Self { +impl<'a, Infcx: InferCtxtLike> EagerResolver<'a, Infcx> { + fn new(delegate: &'a Infcx) -> Self { EagerResolver { delegate, cache: Default::default() } } } -impl, I: Interner> TypeFolder for EagerResolver<'_, D> { +impl, I: Interner> TypeFolder for EagerResolver<'_, Infcx> { fn cx(&self) -> I { self.delegate.cx() } diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index a946689eda4ec..9d94a2b5ac520 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -49,11 +49,11 @@ where // Structurally normalize the lhs. let lhs = if let Some(alias) = lhs.to_alias_term() { - let term = self.next_term_infer_of_kind(lhs); + let term = self.next_term_infer_of_alias_kind(alias); self.add_goal( GoalSource::TypeRelating, goal.with(cx, ty::ProjectionPredicate { projection_term: alias, term }), - ); + )?; term } else { lhs @@ -61,11 +61,11 @@ where // Structurally normalize the rhs. let rhs = if let Some(alias) = rhs.to_alias_term() { - let term = self.next_term_infer_of_kind(rhs); + let term = self.next_term_infer_of_alias_kind(alias); self.add_goal( GoalSource::TypeRelating, goal.with(cx, ty::ProjectionPredicate { projection_term: alias, term }), - ); + )?; term } else { rhs @@ -87,30 +87,7 @@ where ty::AliasRelationDirection::Equate => ty::Invariant, ty::AliasRelationDirection::Subtype => ty::Covariant, }; - match (lhs.to_alias_term(), rhs.to_alias_term()) { - (None, None) => { - self.relate(param_env, lhs, variance, rhs)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - - (Some(alias), None) => { - self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - (None, Some(alias)) => { - self.relate_rigid_alias_non_alias( - param_env, - alias, - variance.xform(ty::Contravariant), - lhs, - )?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - - (Some(alias_lhs), Some(alias_rhs)) => { - self.relate(param_env, alias_lhs, variance, alias_rhs)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - } + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } 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 cc75197b4cf2d..db1a8228c43cb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -68,7 +68,7 @@ where ) -> Result, NoSolutionOrRerunNonErased> { Self::probe_and_match_goal_against_assumption(ecx, parent_source, goal, assumption, |ecx| { for (nested_source, goal) in requirements { - ecx.add_goal(nested_source, goal); + ecx.add_goal(nested_source, goal)?; } ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -113,7 +113,7 @@ where bounds, ) { Ok(requirements) => { - ecx.add_goals(GoalSource::ImplWhereBound, requirements); + ecx.add_goals(GoalSource::ImplWhereBound, requirements)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } Err(_) => { @@ -539,28 +539,24 @@ where candidates: &mut Vec>, ) -> Result<(), RerunNonErased> { let cx = self.cx(); - cx.for_each_relevant_impl( - goal.predicate.trait_def_id(cx), - goal.predicate.self_ty(), - |impl_def_id| -> Result<_, _> { - // For every `default impl`, there's always a non-default `impl` - // that will *also* apply. There's no reason to register a candidate - // for this impl, since it is *not* proof that the trait goal holds. - if cx.impl_is_default(impl_def_id) { - return Ok(()); - } - match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { - ecx.evaluate_added_goals_and_make_canonical_response(certainty) - }) - .map_err_to_rerun()? - { - Ok(candidate) => candidates.push(candidate), - Err(NoSolution) => {} - } + cx.for_each_relevant_impl(goal.predicate.trait_ref(cx), |impl_def_id| -> Result<_, _> { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if cx.impl_is_default(impl_def_id) { + return Ok(()); + } + match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + }) + .map_err_to_rerun()? + { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => {} + } - Ok(()) - }, - ) + Ok(()) + }) } #[instrument(level = "trace", skip_all)] @@ -789,13 +785,21 @@ where return Ok(()); } - ty::Alias(alias_ty @ AliasTy { kind: ty::Projection { def_id }, .. }) => { - (alias_ty, def_id.into()) - } - ty::Alias(alias_ty @ AliasTy { kind: ty::Opaque { def_id }, .. }) => { + ty::Alias( + ty::IsRigid::Yes, + alias_ty @ AliasTy { kind: ty::Projection { def_id }, .. }, + ) => (alias_ty, def_id.into()), + + ty::Alias(ty::IsRigid::Yes, alias_ty @ AliasTy { kind: ty::Opaque { def_id }, .. }) => { (alias_ty, def_id.into()) } - ty::Alias(AliasTy { kind: ty::Inherent { .. } | ty::Free { .. }, .. }) => { + + ty::Alias(ty::IsRigid::No, _) => unreachable!("non-rigid self type: {self_ty:?}"), + + ty::Alias( + ty::IsRigid::Yes, + AliasTy { kind: ty::Inherent { .. } | ty::Free { .. }, .. }, + ) => { self.cx().delay_bug(format!("could not normalize {self_ty:?}, it is not WF")); return Ok(()); } @@ -971,7 +975,7 @@ where elaborate::elaborate(cx, [predicate]) .skip(1) .map(|predicate| goal.with(cx, predicate)), - ); + )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } }) @@ -1088,9 +1092,10 @@ where self.cx } fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { - if let ty::Alias(alias_ty) = ty.kind() + if let ty::Alias(is_rigid, alias_ty) = ty.kind() && let Some(opaque_ty) = alias_ty.try_to_opaque() { + debug_assert_eq!(is_rigid, ty::IsRigid::No); if opaque_ty == self.opaque_ty { return self.self_ty; } 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 2464d607f20ad..5bc56eae7755d 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 @@ -49,11 +49,14 @@ where ty::Dynamic(..) | ty::Param(..) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, - .. - }) + | ty::Alias( + ty::IsRigid::Yes, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, .. + }, + ) | ty::Placeholder(..) + | ty::Alias(ty::IsRigid::No, _) | ty::Bound(..) | ty::Infer(_) => { panic!("unexpected type `{ty:?}`") @@ -102,7 +105,7 @@ where .collect(), )), - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + ty::Alias(ty::IsRigid::Yes, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. @@ -227,15 +230,10 @@ where | ty::Foreign(..) | ty::Ref(_, _, Mutability::Mut) | ty::Adt(_, _) - | ty::Alias(_) + | ty::Alias(ty::IsRigid::Yes, _) | ty::Param(_) | ty::Placeholder(..) => Err(NoSolution), - ty::Bound(..) - | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - panic!("unexpected type `{ty:?}`") - } - // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())), @@ -272,6 +270,12 @@ where .instantiate(ecx.cx(), args) .skip_norm_wip() .map_bound(|bound| bound.types.to_vec())), + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Alias(ty::IsRigid::No, _) + | ty::Bound(..) => { + panic!("unexpected type `{ty:?}`") + } } } @@ -401,14 +405,15 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable Err(NoSolution), - ty::Bound(..) - | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Alias(ty::IsRigid::No, _) + | ty::Bound(..) => { panic!("unexpected type `{self_ty:?}`") } } @@ -545,7 +550,8 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable Err(NoSolution), - ty::Bound(..) - | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Alias(ty::IsRigid::No, _) + | ty::Bound(..) => { panic!("unexpected type `{self_ty:?}`") } } @@ -601,7 +608,8 @@ fn fn_item_to_async_callable( ]; let future_output_def_id = cx.require_projection_lang_item(SolverProjectionLangItem::FutureOutput); - let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); + let future_output_ty = + Ty::new_projection(cx, ty::IsRigid::No, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), @@ -650,6 +658,7 @@ fn coroutine_closure_to_ambiguous_coroutine( cx.require_projection_lang_item(SolverProjectionLangItem::AsyncFnKindUpvars); let tupled_upvars_ty = Ty::new_projection( cx, + ty::IsRigid::No, upvars_projection_def_id, [ I::GenericArg::from(args.kind_ty()), @@ -739,15 +748,16 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable( | ty::Never | ty::Tuple(_) | ty::Pat(_, _) - | ty::Alias(_) + | ty::Alias(ty::IsRigid::Yes, _) | ty::Param(_) | ty::Placeholder(..) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Error(_) | ty::UnsafeBinder(_) => return Err(NoSolution), - ty::Bound(..) - | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Alias(ty::IsRigid::No, _) + | ty::Bound(..) => { panic!("unexpected type `{self_ty:?}`") } } @@ -1035,7 +1045,7 @@ where } fn try_fold_ty(&mut self, ty: I::Ty) -> Result { - if let ty::Alias(alias_ty @ ty::AliasTy { kind: ty::Projection { .. }, .. }) = ty.kind() + if let ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Projection { .. }, .. }) = ty.kind() && let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())? { Ok(term.expect_ty()) 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 11c8202864a3a..198a8045b05d9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -124,7 +124,7 @@ where ) }, ), - ); + )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }, )); @@ -176,7 +176,7 @@ where .iter_instantiated(cx, impl_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)); - ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds)?; // For this impl to be `const`, we need to check its `[const]` bounds too. let const_conditions = cx @@ -190,7 +190,7 @@ where .skip_norm_wip(), ) }); - ecx.add_goals(GoalSource::ImplWhereBound, const_conditions); + ecx.add_goals(GoalSource::ImplWhereBound, const_conditions)?; then(ecx, certainty) }) @@ -242,8 +242,8 @@ where // `GoalSource::ImplWhereClause` here would be incorrect, as we also // impl them, which means we're "stepping out of the impl constructor" // again. To handle this, we treat these cycles as ambiguous for now. - ecx.add_goals(GoalSource::Misc, where_clause_bounds); - ecx.add_goals(GoalSource::Misc, const_conditions); + ecx.add_goals(GoalSource::Misc, where_clause_bounds)?; + ecx.add_goals(GoalSource::Misc, const_conditions)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -278,8 +278,8 @@ where ), ) }), - ); - }); + ) + })?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -432,7 +432,7 @@ where .to_host_effect_clause(cx, goal.predicate.constness), ) }), - ); + )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } 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 a66adc72f1787..5e1d202f8f994 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 @@ -3,12 +3,12 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::StableHash; -use rustc_type_ir::data_structures::{HashMap, HashSet}; +use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::region_constraint::RegionConstraint; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; -use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind}; +use rustc_type_ir::search_graph::{CandidateHeadUsages, IncreaseDepthForNested, PathKind}; use rustc_type_ir::solve::{ AccessedOpaques, ExternalRegionConstraints, FetchEligibleAssocItemResponse, MaybeInfo, NoSolutionOrRerunNonErased, OpaqueTypesJank, QueryResultOrRerunNonErased, RerunCondition, @@ -16,8 +16,8 @@ use rustc_type_ir::solve::{ }; use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, - OpaqueTypeKey, PredicateKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + OpaqueTypeKey, PredicateKind, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, TypingMode, }; use tracing::{Level, debug, instrument, trace, warn}; @@ -28,6 +28,7 @@ use crate::canonical::{ }; use crate::coherence; use crate::delegate::SolverDelegate; +use crate::normalize::{NormalizationFolder, NormalizationWasAmbiguous}; use crate::placeholder::BoundVarReplacer; use crate::resolve::eager_resolve_vars; use crate::solve::search_graph::SearchGraph; @@ -461,7 +462,7 @@ where Ok(i) => Ok(i), Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), Err(NoSolutionOrRerunNonErased::RerunNonErased(_)) => { - // check th t the opaque_accesses state mirrors the result we got. + // Check that the opaque_accesses state mirrors the result we got. assert!(opaque_accesses.should_bail().is_err()); Err(NoSolution) } @@ -483,7 +484,7 @@ where stalled_on: Option>, ) -> Result, NoSolutionOrRerunNonErased> { let (normalization_nested_goals, goal_evaluation) = - self.evaluate_goal_raw(source, goal, stalled_on)?; + self.evaluate_goal_raw(source, goal, stalled_on, IncreaseDepthForNested::Yes)?; assert!(normalization_nested_goals.is_empty()); Ok(goal_evaluation) } @@ -575,6 +576,7 @@ where source: GoalSource, goal: Goal, stalled_on: Option>, + increase_depth_for_nested: IncreaseDepthForNested, ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolutionOrRerunNonErased> { if let RerunStalled::WontMakeProgress(stalled_certainty) = self.rerunning_stalled_goal_may_make_progress(stalled_on.as_ref()) @@ -590,7 +592,7 @@ where )); } - self.evaluate_goal_cold(source, goal) + self.evaluate_goal_cold(source, goal, increase_depth_for_nested) } #[cold] @@ -599,12 +601,13 @@ where &mut self, source: GoalSource, goal: Goal, + increase_depth_for_nested: IncreaseDepthForNested, ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolutionOrRerunNonErased> { // We only care about one entry per `OpaqueTypeKey` here, // so we only canonicalize the lookup table and ignore // duplicate entries. let opaque_types = self.delegate.clone_opaque_types_lookup_table(); - let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); + let (goal, opaque_types) = eager_resolve_vars(&**self.delegate, (goal, opaque_types)); let typing_mode = self.typing_mode(); let step_kind = self.step_kind_for_source(source); @@ -662,6 +665,7 @@ where self.cx(), canonical_goal, step_kind, + increase_depth_for_nested, &mut inspect::ProofTreeBuilder::new_noop(), ); @@ -701,6 +705,7 @@ where self.cx(), canonical_goal, step_kind, + increase_depth_for_nested, &mut inspect::ProofTreeBuilder::new_noop(), ); assert!( @@ -1049,15 +1054,20 @@ where self.delegate.cx() } - pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { - goal.predicate = self.replace_alias_with_infer(goal.predicate, source, goal.param_env); - self.add_goal_raw(source, goal); - } - #[instrument(level = "debug", skip(self))] - fn add_goal_raw(&mut self, source: GoalSource, goal: Goal) { + pub(super) fn add_goal( + &mut self, + source: GoalSource, + mut goal: Goal, + ) -> Result<(), NoSolutionOrRerunNonErased> { + goal.predicate = self.normalize( + GoalSource::NormalizeGoal(self.step_kind_for_source(source)), + goal.param_env, + ty::Unnormalized::new_wip(goal.predicate), + )?; self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); self.nested_goals.push((source, goal, None)); + Ok(()) } #[instrument(level = "trace", skip(self, goals))] @@ -1065,19 +1075,11 @@ where &mut self, source: GoalSource, goals: impl IntoIterator>, - ) { + ) -> Result<(), NoSolutionOrRerunNonErased> { for goal in goals { - self.add_goal(source, goal); + self.add_goal(source, goal)?; } - } - - pub(super) fn replace_alias_with_infer>( - &mut self, - value: T, - source: GoalSource, - param_env: I::ParamEnv, - ) -> T { - value.fold_with(&mut ReplaceAliasWithInfer::new(self, source, param_env)) + Ok(()) } pub(super) fn next_region_var(&mut self) -> I::Region { @@ -1100,10 +1102,19 @@ where /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`. /// If `kind` is an integer inference variable this will still return a ty infer var. - pub(super) fn next_term_infer_of_kind(&mut self, term: I::Term) -> I::Term { - match term.kind() { - ty::TermKind::Ty(_) => self.next_ty_infer().into(), - ty::TermKind::Const(_) => self.next_const_infer().into(), + pub(super) fn next_term_infer_of_alias_kind( + &mut self, + alias_term: ty::AliasTerm, + ) -> I::Term { + match alias_term.kind { + ty::AliasTermKind::ProjectionTy { .. } + | ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::OpaqueTy { .. } + | ty::AliasTermKind::FreeTy { .. } => self.next_ty_infer().into(), + ty::AliasTermKind::FreeConst { .. } + | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::AnonConst { .. } + | ty::AliasTermKind::ProjectionConst { .. } => self.next_const_infer().into(), } } @@ -1240,88 +1251,17 @@ where param_env: I::ParamEnv, lhs: T, rhs: T, - ) -> Result<(), NoSolution> { + ) -> Result<(), NoSolutionOrRerunNonErased> { self.relate(param_env, lhs, ty::Variance::Invariant, rhs) } - /// This should be used when relating a rigid alias with another type. - /// - /// Normally we emit a nested `AliasRelate` when equating an inference - /// variable and an alias. This causes us to instead constrain the inference - /// variable to the alias without emitting a nested alias relate goals. - #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn relate_rigid_alias_non_alias( - &mut self, - param_env: I::ParamEnv, - alias: ty::AliasTerm, - variance: ty::Variance, - term: I::Term, - ) -> Result<(), NoSolution> { - // NOTE: this check is purely an optimization, the structural eq would - // always fail if the term is not an inference variable. - if term.is_infer() { - let cx = self.cx(); - // We need to relate `alias` to `term` treating only the outermost - // constructor as rigid, relating any contained generic arguments as - // normal. We do this by first structurally equating the `term` - // with the alias constructor instantiated with unconstrained infer vars, - // and then relate this with the whole `alias`. - // - // Alternatively we could modify `Equate` for this case by adding another - // variant to `StructurallyRelateAliases`. - let def_id = match alias.kind { - ty::AliasTermKind::ProjectionTy { def_id } => def_id.into(), - ty::AliasTermKind::InherentTy { def_id } => def_id.into(), - ty::AliasTermKind::OpaqueTy { def_id } => def_id.into(), - ty::AliasTermKind::FreeTy { def_id } => def_id.into(), - ty::AliasTermKind::AnonConst { def_id } => def_id.into(), - ty::AliasTermKind::ProjectionConst { def_id } => def_id.into(), - ty::AliasTermKind::FreeConst { def_id } => def_id.into(), - ty::AliasTermKind::InherentConst { def_id } => def_id.into(), - }; - let identity_args = self.fresh_args_for_item(def_id); - let rigid_ctor = alias.with_args(cx, identity_args); - let ctor_term = rigid_ctor.to_term(cx); - let obligations = self.delegate.eq_structurally_relating_aliases( - param_env, - term, - ctor_term, - self.origin_span, - )?; - debug_assert!(obligations.is_empty()); - self.relate(param_env, alias, variance, rigid_ctor) - } else { - Err(NoSolution) - } - } - - /// This should only be used when we're either instantiating a previously - /// unconstrained "return value" or when we're sure that all aliases in - /// the types are rigid. - #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq_structurally_relating_aliases>( - &mut self, - param_env: I::ParamEnv, - lhs: T, - rhs: T, - ) -> Result<(), NoSolution> { - let result = self.delegate.eq_structurally_relating_aliases( - param_env, - lhs, - rhs, - self.origin_span, - )?; - assert_eq!(result, vec![]); - Ok(()) - } - #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn sub>( &mut self, param_env: I::ParamEnv, sub: T, sup: T, - ) -> Result<(), NoSolution> { + ) -> Result<(), NoSolutionOrRerunNonErased> { self.relate(param_env, sub, ty::Variance::Covariant, sup) } @@ -1332,7 +1272,7 @@ where lhs: T, variance: ty::Variance, rhs: T, - ) -> Result<(), NoSolution> { + ) -> Result<(), NoSolutionOrRerunNonErased> { let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; for &goal in goals.iter() { let source = match goal.predicate.kind().skip_binder() { @@ -1343,7 +1283,7 @@ where ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc, p => unreachable!("unexpected nested goal in `relate`: {p:?}"), }; - self.add_goal(source, goal); + self.add_goal(source, goal)?; } Ok(()) } @@ -1484,7 +1424,7 @@ where opaque_args: I::GenericArgs, param_env: I::ParamEnv, hidden_ty: I::Ty, - ) { + ) -> Result<(), NoSolutionOrRerunNonErased> { let mut goals = Vec::new(); self.delegate.add_item_bounds_for_hidden_type( opaque_def_id, @@ -1493,7 +1433,8 @@ where hidden_ty, &mut goals, ); - self.add_goals(GoalSource::AliasWellFormed, goals); + self.add_goals(GoalSource::AliasWellFormed, goals)?; + Ok(()) } // Try to evaluate a const, or return `None` if the const is too generic. @@ -1502,13 +1443,13 @@ where pub(super) fn evaluate_const( &mut self, param_env: I::ParamEnv, - uv: ty::UnevaluatedConst, + alias_const: ty::AliasConst, ) -> Result, RerunNonErased> { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)?; + match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {} } - Ok(self.delegate.evaluate_const(param_env, uv)) + Ok(self.delegate.evaluate_const(param_env, alias_const)) } pub(super) fn evaluate_const_and_instantiate_projection_term( @@ -1516,9 +1457,9 @@ where param_env: I::ParamEnv, projection_term: ty::AliasTerm, expected_term: I::Term, - uv: ty::UnevaluatedConst, + alias_const: ty::AliasConst, ) -> QueryResultOrRerunNonErased { - match self.evaluate_const(param_env, uv)? { + match self.evaluate_const(param_env, alias_const)? { Some(evaluated) => { self.eq(param_env, expected_term, evaluated.into())?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -1531,19 +1472,18 @@ where // Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics and // HasInfers or something, make evaluate_const return that, and make this branch be // based on that, rather than checking `has_non_region_infer`. - if self.resolve_vars_if_possible(uv).has_non_region_infer() { + if self.resolve_vars_if_possible(alias_const).has_non_region_infer() { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { - // We do not instantiate to the `uv` passed in, but rather - // `goal.predicate.alias`. The `uv` passed in might correspond to the `impl` + // We do not instantiate to the `alias_const` passed in, but rather + // `goal.predicate.alias`. The `alias_const` passed in might correspond to the `impl` // form of a constant (with generic arguments corresponding to the impl block), // however, we want to structurally instantiate to the original, non-rebased, // trait `Self` form of the constant (with generic arguments being the trait // `Self` type). - self.relate_rigid_alias_non_alias( + self.eq( param_env, - projection_term, - ty::Invariant, + projection_term.to_term(self.cx(), ty::IsRigid::Yes), expected_term, )?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -1579,7 +1519,7 @@ where symbol: I::Symbol, ) -> Result { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature)?; + match self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature)? {} } Ok(may_use_unstable_feature(&**self.delegate, param_env, symbol)) @@ -1695,7 +1635,7 @@ where let external_constraints = self.compute_external_query_constraints(certainty, normalization_nested_goals); let (var_values, mut external_constraints) = - eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); + eager_resolve_vars(&**self.delegate, (self.var_values, external_constraints)); // Remove any trivial or duplicated region constraints once we've resolved regions let mut unique = HashSet::default(); @@ -1781,110 +1721,39 @@ where ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } } -} - -// FIXME: This should be eventually removed in favor of proper normalization. -// cc #156742 -/// Eagerly replace aliases with inference variables, emitting `AliasRelate` -/// goals, used when adding goals to the `EvalCtxt`. We compute the -/// `AliasRelate` goals before evaluating the actual goal to get all the -/// constraints we can. -/// -/// This is a performance optimization to more eagerly detect cycles during trait -/// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs. -/// -/// The emitted goals get evaluated in the context of the parent goal; by -/// replacing aliases in nested goals we essentially pull the normalization out of -/// the nested goal. We want to treat the goal as if the normalization still happens -/// inside of the nested goal by inheriting the `step_kind` of the nested goal and -/// storing it in the `GoalSource` of the emitted `AliasRelate` goals. -/// This is necessary for tests/ui/sized/coinductive-1.rs to compile. -struct ReplaceAliasWithInfer<'me, 'a, D, I> -where - D: SolverDelegate, - I: Interner, -{ - ecx: &'me mut EvalCtxt<'a, D>, - param_env: I::ParamEnv, - normalization_goal_source: GoalSource, - cache: HashMap, -} -impl<'me, 'a, D, I> ReplaceAliasWithInfer<'me, 'a, D, I> -where - D: SolverDelegate, - I: Interner, -{ - fn new( - ecx: &'me mut EvalCtxt<'a, D>, - for_goal_source: GoalSource, + pub(super) fn normalize>( + &mut self, + source: GoalSource, param_env: I::ParamEnv, - ) -> Self { - let step_kind = ecx.step_kind_for_source(for_goal_source); - ReplaceAliasWithInfer { - ecx, - param_env, - normalization_goal_source: GoalSource::NormalizeGoal(step_kind), - cache: Default::default(), - } - } -} + value: ty::Unnormalized, + ) -> Result { + let value = self.delegate.resolve_vars_if_possible(value.skip_normalization()); -impl TypeFolder for ReplaceAliasWithInfer<'_, '_, D, I> -where - D: SolverDelegate, - I: Interner, -{ - fn cx(&self) -> I { - self.ecx.cx() - } - - fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { - match ty.kind() { - ty::Alias(alias) if !ty.has_escaping_bound_vars() => { - let infer_ty = self.ecx.next_ty_infer(); - let projection = ty::ProjectionPredicate { - projection_term: alias.into(), - term: infer_ty.into(), - }; - self.ecx.add_goal_raw( - self.normalization_goal_source, - Goal::new(self.cx(), self.param_env, projection), - ); - infer_ty - } - _ => { - if !ty.has_aliases() { - ty - } else if let Some(&entry) = self.cache.get(&ty) { - return entry; - } else { - let res = ty.super_fold_with(self); - assert!(self.cache.insert(ty, res).is_none()); - res - } - } + if !self.cx().renormalize_rigid_aliases() && !value.has_non_rigid_aliases() { + return Ok(value); } - } - fn fold_const(&mut self, ct: I::Const) -> I::Const { - match ct.kind() { - ty::ConstKind::Unevaluated(uv) if !ct.has_escaping_bound_vars() => { - let infer_ct = self.ecx.next_const_infer(); - let projection = - ty::ProjectionPredicate { projection_term: uv.into(), term: infer_ct.into() }; - self.ecx.add_goal_raw( - self.normalization_goal_source, - Goal::new(self.cx(), self.param_env, projection), - ); - infer_ct - } - _ => ct.super_fold_with(self), - } - } + // To drop the mutable borrow of self early. + let infcx = self.delegate.deref(); + let mut folder = NormalizationFolder::new(infcx, vec![], |alias_term| { + let infer_term = self.next_term_infer_of_alias_kind(alias_term); + let pred = ty::ProjectionPredicate { projection_term: alias_term, term: infer_term }; + let goal = Goal::new(self.cx(), param_env, pred); + self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); + let GoalEvaluation { goal, certainty, has_changed: _, stalled_on } = + self.evaluate_goal(source, goal, None)?; + let normalization_was_ambiguous = match certainty { + Certainty::Yes => NormalizationWasAmbiguous::No, + Certainty::Maybe(_) => { + self.nested_goals.push((source, goal, stalled_on)); + NormalizationWasAmbiguous::Yes + } + }; - fn fold_predicate(&mut self, predicate: I::Predicate) -> I::Predicate { - if predicate.allow_normalization() { predicate.super_fold_with(self) } else { predicate } + Ok((self.resolve_vars_if_possible(infer_term), normalization_was_ambiguous)) + }); + value.try_fold_with(&mut folder) } } @@ -1919,7 +1788,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree, origin_span: I::Span, ) -> (Result, NoSolution>, inspect::GoalEvaluation) { let opaque_types = delegate.clone_opaque_types_lookup_table(); - let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); + let (goal, opaque_types) = eager_resolve_vars(&**delegate, (goal, opaque_types)); let typing_mode = delegate.typing_mode_raw().assert_not_erased(); let (orig_values, canonical_goal) = diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index 1c5d6e0b14c65..d9d18bdeea7e7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -98,10 +98,8 @@ where outer.opaque_accesses.update(nested.opaque_accesses)?; - let r = match r.map_err_to_rerun()? { - Ok(i) => Ok(i), - Err(NoSolution) => Err(NoSolution), - }; + // Unwrap is unreachable, we would have returned on the line above. + let r = r.map_err_to_rerun().unwrap(); if !nested.inspect.is_noop() { let probe_kind = probe_kind(&r); diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 658f98d356235..8b838f3567c84 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -24,7 +24,7 @@ mod trait_goals; use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Interner, TyVid, TypingMode}; +use rustc_type_ir::{self as ty, Interner, TyVid}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -171,7 +171,7 @@ where ) -> QueryResultOrRerunNonErased { match self.well_formed_goals(goal.param_env, goal.predicate) { Some(goals) => { - self.add_goals(GoalSource::Misc, goals); + self.add_goals(GoalSource::Misc, goals)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), @@ -196,7 +196,18 @@ where Goal { param_env, predicate: ct }: Goal, ) -> QueryResultOrRerunNonErased { match ct.kind() { - ty::ConstKind::Unevaluated(uv) => { + ty::ConstKind::Alias(ty::IsRigid::Yes, _) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Value(_) + | ty::ConstKind::Error(_) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + ty::ConstKind::Infer(_) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + + ty::ConstKind::Alias(ty::IsRigid::No, alias_const) => { // We never return `NoSolution` here as `evaluate_const` emits an // error itself when failing to evaluate, so emitting an additional fulfillment // error in that case is unnecessary noise. This may change in the future once @@ -205,18 +216,13 @@ where // FIXME(generic_const_exprs): Implement handling for generic // const expressions here. - if let Some(_normalized) = self.evaluate_const(param_env, uv)? { + if let Some(_normalized) = self.evaluate_const(param_env, alias_const)? { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } } - ty::ConstKind::Infer(_) => { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } - ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => { - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } + // We can freely ICE here as: // - `Param` gets replaced with a placeholder during canonicalization // - `Bound` cannot exist as we don't have a binder around the self Type @@ -246,7 +252,12 @@ where .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into); } - ty::ConstKind::Unevaluated(uv) => uv.type_of(self.cx()).skip_norm_wip(), + ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => { + alias_const.type_of(self.cx()).skip_norm_wip() + } + ty::ConstKind::Alias(ty::IsRigid::No, _) => unimplemented!( + "non-rigid unevaluated constant for compute_const_arg_has_type_goal: {ct:?}" + ), ty::ConstKind::Expr(_) => unimplemented!( "`feature(generic_const_exprs)` is not supported in the new trait solver" ), @@ -366,8 +377,12 @@ where param_env: I::ParamEnv, term: I::Term, ) -> Result { + if !self.cx().renormalize_rigid_aliases() && !term.is_non_rigid_alias() { + return Ok(term); + } + if let Some(alias) = term.to_alias_term() { - let normalized_term = self.next_term_infer_of_kind(term); + let normalized_term = self.next_term_infer_of_alias_kind(alias); let projection_goal = Goal::new( self.cx(), param_env, @@ -375,31 +390,13 @@ where ); // We normalize the self type to be able to relate it with // types from candidates. - self.add_goal(GoalSource::TypeRelating, projection_goal); + self.add_goal(GoalSource::TypeRelating, projection_goal)?; self.try_evaluate_added_goals()?; Ok(self.resolve_vars_if_possible(normalized_term)) } else { Ok(term) } } - - fn opaque_type_is_rigid(&self, def_id: I::OpaqueTyId) -> bool { - match self - .typing_mode() - // Caller should handle erased mode - .assert_not_erased() - { - // Opaques are never rigid outside of analysis mode. - TypingMode::Coherence | TypingMode::PostAnalysis | TypingMode::Codegen => false, - // During analysis, opaques are rigid unless they may be defined by - // the current body. - TypingMode::Typeck { defining_opaque_types_and_generators: non_rigid_opaques } - | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: non_rigid_opaques } - | TypingMode::PostBorrowck { defined_opaque_types: non_rigid_opaques } => { - !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id.into())) - } - } - } } /// The result of evaluating a goal. diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index e49e6147b5251..3427044915f88 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -81,15 +81,10 @@ where None }, |ecx| { - ecx.probe(|&result| ProbeKind::RigidAlias { result }) - .enter(|this| { - this.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - .map_err(Into::into) + ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { + this.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) }, ) } @@ -101,7 +96,7 @@ where param_env: I::ParamEnv, alias: ty::AliasTerm, term: I::Term, - ) { + ) -> Result<(), NoSolutionOrRerunNonErased> { if let Some(ct) = term.as_const() { let cx = self.cx(); let expected_ty = alias.expect_ct().type_of(cx).skip_norm_wip(); @@ -111,8 +106,9 @@ where param_env, predicate: ty::ClauseKind::ConstArgHasType(ct, expected_ty).upcast(cx), }, - ); + )?; } + Ok(()) } /// When normalizing an associated item, constrain the expected term to `term`. @@ -141,10 +137,15 @@ where /// fires a `span_bug!`. Registering the obligation here ensures the type /// mismatch is reported during normalization itself, tainting the MIR /// before validation runs. - fn instantiate_normalizes_to_term(&mut self, goal: Goal>, term: I::Term) { - self.push_const_arg_has_type_goal(goal.param_env, goal.predicate.alias, term); + fn instantiate_normalizes_to_term( + &mut self, + goal: Goal>, + term: I::Term, + ) -> Result<(), NoSolutionOrRerunNonErased> { + self.push_const_arg_has_type_goal(goal.param_env, goal.predicate.alias, term)?; self.eq(goal.param_env, goal.predicate.term, term) .expect("expected goal term to be fully unconstrained"); + Ok(()) } /// Unlike `instantiate_normalizes_to_term` this instantiates the expected term @@ -154,8 +155,13 @@ where goal: Goal>, term: ty::AliasTerm, ) { - self.relate_rigid_alias_non_alias(goal.param_env, term, ty::Invariant, goal.predicate.term) - .expect("expected goal term to be fully unconstrained"); + self.relate( + goal.param_env, + term.to_term(self.cx(), ty::IsRigid::Yes), + ty::Invariant, + goal.predicate.term, + ) + .expect("expected goal term to be fully unconstrained"); } } @@ -214,7 +220,7 @@ where let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); ecx.eq(goal.param_env, goal.predicate.alias, assumption_projection_pred.projection_term)?; - ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term); + ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term)?; // Add GAT where clauses from the trait's definition // FIXME: We don't need these, since these are the type's own WF obligations. @@ -224,7 +230,7 @@ where .iter_instantiated(cx, goal.predicate.alias.args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; then(ecx) } @@ -290,7 +296,7 @@ where .iter_instantiated(cx, impl_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)); - ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds)?; // Bail if the nested goals don't hold here. This is to avoid unnecessarily // computing the `type_of` query for associated types that never apply, as @@ -307,7 +313,7 @@ where .iter_instantiated(cx, goal.predicate.alias.args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; let error_response = |ecx: &mut EvalCtxt<'_, D>, guar| { let error_term = match goal.predicate.alias.kind { @@ -315,7 +321,7 @@ where ty::AliasTermKind::ProjectionConst { .. } => Const::new_error(cx, guar).into(), kind => panic!("expected projection, found {kind:?}"), }; - ecx.instantiate_normalizes_to_term(goal, error_term); + ecx.instantiate_normalizes_to_term(goal, error_term)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }; @@ -339,12 +345,10 @@ where ecx.add_goal( GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous), + )?; + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::Yes, ); - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - .map_err(Into::into); } // Outside of coherence, we treat the associated item as rigid instead. ty::TypingMode::Typeck { .. } @@ -356,11 +360,9 @@ where goal, goal.predicate.alias, ); - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - .map_err(Into::into); + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::Yes, + ); } }; } @@ -389,11 +391,11 @@ where // would be relevant if any of the nested goals refer to the `term`. // This is not the case here and we only prefer adding an ambiguous // nested goal for consistency. - ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); - return then(ecx, Certainty::Yes).map_err(Into::into); + ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous))?; + return then(ecx, Certainty::Yes); } else { ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - return then(ecx, Certainty::Yes).map_err(Into::into); + return then(ecx, Certainty::Yes); } } else { return error_response(ecx, cx.delay_bug("missing item")); @@ -429,23 +431,23 @@ where // Finally we construct the actual value of the associated type. let term = match goal.predicate.alias.kind { - ty::AliasTermKind::ProjectionTy { .. } => cx - .type_of(target_item_def_id.into()) - .instantiate(cx, target_args) - .skip_norm_wip() - .into(), + ty::AliasTermKind::ProjectionTy { .. } => { + let t = cx.type_of(target_item_def_id.into()).instantiate(cx, target_args); + let t = ecx.normalize(GoalSource::Misc, goal.param_env, t)?; + t.into() + } ty::AliasTermKind::ProjectionConst { .. } if cx.is_type_const(target_item_def_id.into()) => { - cx.const_of_item(target_item_def_id.into()) - .instantiate(cx, target_args) - .skip_norm_wip() - .into() + let c = + cx.const_of_item(target_item_def_id.into()).instantiate(cx, target_args); + let c = ecx.normalize(GoalSource::Misc, goal.param_env, c)?; + c.into() } ty::AliasTermKind::ProjectionConst { .. } => { - let uv = ty::UnevaluatedConst::new( + let alias_const = ty::AliasConst::new( cx, - ty::UnevaluatedConstKind::Projection { + ty::AliasConstKind::Projection { def_id: target_item_def_id.into().try_into().unwrap(), }, target_args, @@ -454,14 +456,14 @@ where goal.param_env, goal.predicate.alias, goal.predicate.term, - uv, + alias_const, ); } kind => panic!("expected projection, found {kind:?}"), }; - ecx.instantiate_normalizes_to_term(goal, term); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) + ecx.instantiate_normalizes_to_term(goal, term)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -480,7 +482,7 @@ where }; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.instantiate_normalizes_to_term(goal, error_term); + ecx.instantiate_normalizes_to_term(goal, error_term)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -561,7 +563,6 @@ where pred, [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))], ) - .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( @@ -690,7 +691,7 @@ where ); ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.instantiate_normalizes_to_term(goal, upvars_ty.into()); + ecx.instantiate_normalizes_to_term(goal, upvars_ty.into())?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -743,27 +744,24 @@ where .skip_norm_wip() } - ty::Alias(_) | ty::Param(_) | ty::Placeholder(..) => { + ty::Alias(ty::IsRigid::Yes, _) | ty::Param(_) | ty::Placeholder(..) => { // This is the "fallback impl" for type parameters, unnormalizable projections // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't // exist. Instead, `Pointee` should be a supertrait of `Sized`. - let alias_bound_result = - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let alias_bound_result = ecx + .probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| { let sized_predicate = ty::TraitRef::new( cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [I::GenericArg::from(goal.predicate.self_ty())], ); - ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate)); - ecx.instantiate_normalizes_to_term(goal, Ty::new_unit(cx).into()); + ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate))?; + ecx.instantiate_normalizes_to_term(goal, Ty::new_unit(cx).into())?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - - let alias_bound_result = match alias_bound_result.map_err_to_rerun()? { - Ok(i) => Ok(i), - Err(NoSolution) => Err(NoSolution), - }; + }) + .map_err_to_rerun()?; // In case the dummy alias-bound candidate does not apply, we instead treat this projection // as rigid. @@ -782,6 +780,7 @@ where None => Ty::new_unit(cx), Some(tail_ty) => Ty::new_projection( cx, + ty::IsRigid::No, metadata_def_id, [tail_ty.instantiate(cx, args).skip_norm_wip()], ), @@ -790,7 +789,9 @@ where ty::Tuple(elements) => match elements.last() { None => Ty::new_unit(cx), - Some(tail_ty) => Ty::new_projection(cx, metadata_def_id, [tail_ty]), + Some(tail_ty) => { + Ty::new_projection(cx, ty::IsRigid::No, metadata_def_id, [tail_ty]) + } }, ty::UnsafeBinder(_) => { @@ -799,6 +800,7 @@ where } ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Alias(ty::IsRigid::No, _) | ty::Bound(..) => panic!( "unexpected self ty `{:?}` when normalizing `::Metadata`", goal.predicate.self_ty() @@ -806,7 +808,7 @@ where }; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.instantiate_normalizes_to_term(goal, metadata_ty.into()); + ecx.instantiate_normalizes_to_term(goal, metadata_ty.into())?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -833,7 +835,13 @@ where CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.alias.kind, [self_ty]), + projection_term: ty::AliasTerm::new( + ecx.cx(), + cx.alias_term_kind_from_def_id( + goal.predicate.alias.expect_projection_def_id().into(), + ), + [self_ty], + ), term, } .upcast(cx), @@ -865,7 +873,13 @@ where CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.alias.kind, [self_ty]), + projection_term: ty::AliasTerm::new( + ecx.cx(), + cx.alias_term_kind_from_def_id( + goal.predicate.alias.expect_projection_def_id().into(), + ), + [self_ty], + ), term, } .upcast(cx), @@ -873,7 +887,6 @@ where // but that's already proven by the generator being WF. [], ) - .map_err(Into::into) } fn consider_builtin_fused_iterator_candidate( @@ -914,7 +927,7 @@ where ); let yield_ty = args.as_coroutine().yield_ty(); ecx.eq(goal.param_env, wrapped_expected_ty, yield_ty)?; - ecx.instantiate_normalizes_to_term(goal, expected_ty.into()); + ecx.instantiate_normalizes_to_term(goal, expected_ty.into())?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -1011,7 +1024,7 @@ where // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid // alias. In case there's a where-bound further constraining this alias it is preferred over // this impl candidate anyways. It's still a bit scuffed. - ty::Alias(_) | ty::Param(_) | ty::Placeholder(..) => { + ty::Alias(ty::IsRigid::Yes, _) | ty::Param(_) | ty::Placeholder(..) => { return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -1019,6 +1032,7 @@ where } ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Alias(ty::IsRigid::No, _) | ty::Bound(..) => panic!( "unexpected self ty `{:?}` when normalizing `::Discriminant`", goal.predicate.self_ty() @@ -1026,7 +1040,7 @@ where }; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.instantiate_normalizes_to_term(goal, discriminant_ty.into()); + ecx.instantiate_normalizes_to_term(goal, discriminant_ty.into())?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -1071,7 +1085,7 @@ where _ => panic!("unexpected associated type {:?} in `Field`", goal.predicate), }; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.instantiate_normalizes_to_term(goal, ty.into()); + ecx.instantiate_normalizes_to_term(goal, ty.into())?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -1089,7 +1103,7 @@ where impl_args: I::GenericArgs, impl_trait_ref: rustc_type_ir::TraitRef, target_container_def_id: I::DefId, - ) -> Result { + ) -> Result { let cx = self.cx(); Ok(if target_container_def_id == impl_trait_ref.def_id.into() { // Default value from the trait definition. No need to rebase. @@ -1114,7 +1128,7 @@ where .iter_instantiated(cx, target_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id.into(), target_args) }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs index f1baa608798fa..fc0c34e63e187 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs @@ -15,12 +15,12 @@ where &mut self, goal: Goal>, ) -> QueryResultOrRerunNonErased { - let uv = goal.predicate.projection_term.expect_ct(); + let alias_const = goal.predicate.projection_term.expect_ct(); self.evaluate_const_and_instantiate_projection_term( goal.param_env, goal.predicate.projection_term, goal.predicate.term, - uv, + alias_const, ) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs index 2264d4e299ee9..f96b794a64e31 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs @@ -29,17 +29,20 @@ where .iter_instantiated(cx, free_alias.args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; let actual = match free_alias.kind { ty::AliasTermKind::FreeTy { def_id } => { - cx.type_of(def_id.into()).instantiate(cx, free_alias.args).skip_norm_wip().into() + let free = cx.type_of(def_id.into()).instantiate(cx, free_alias.args); + let free = self.normalize(GoalSource::Misc, goal.param_env, free)?; + free.into() + } + ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id.into()) => { + let free = cx.const_of_item(def_id.into()).instantiate(cx, free_alias.args); + let free = self.normalize(GoalSource::Misc, goal.param_env, free)?; + + free.into() } - ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id.into()) => cx - .const_of_item(def_id.into()) - .instantiate(cx, free_alias.args) - .skip_norm_wip() - .into(), ty::AliasTermKind::FreeConst { .. } => { return self.evaluate_const_and_instantiate_projection_term( goal.param_env, @@ -51,7 +54,7 @@ where kind => panic!("expected free alias, found {kind:?}"), }; - self.push_const_arg_has_type_goal(goal.param_env, goal.predicate.projection_term, actual); + self.push_const_arg_has_type_goal(goal.param_env, goal.predicate.projection_term, actual)?; self.eq(goal.param_env, goal.predicate.term, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs index b1ad822ceea9c..f2cd91574d641 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs @@ -51,17 +51,19 @@ where .iter_instantiated(cx, inherent_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; let normalized: I::Term = match inherent.kind { ty::AliasTermKind::InherentTy { def_id } => { - cx.type_of(def_id.into()).instantiate(cx, inherent_args).skip_norm_wip().into() + let inherent = cx.type_of(def_id.into()).instantiate(cx, inherent_args); + let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; + inherent.into() + } + ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id.into()) => { + let inherent = cx.const_of_item(def_id.into()).instantiate(cx, inherent_args); + let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; + inherent.into() } - ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id.into()) => cx - .const_of_item(def_id.into()) - .instantiate(cx, inherent_args) - .skip_norm_wip() - .into(), ty::AliasTermKind::InherentConst { .. } => { // FIXME(gca): This is dead code at the moment. It should eventually call // self.evaluate_const like projected consts do in consider_impl_candidate in @@ -78,7 +80,7 @@ where goal.param_env, goal.predicate.projection_term, normalized, - ); + )?; self.eq(goal.param_env, goal.predicate.term, normalized)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index 81a035e8a9980..21cfa0561dd8b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -3,6 +3,7 @@ mod free_alias; mod inherent; mod opaque_types; +use rustc_type_ir::search_graph::IncreaseDepthForNested; use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, ProjectionPredicate}; use tracing::{instrument, trace}; @@ -44,7 +45,7 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { let ty::ProjectionPredicate { projection_term: alias, term } = goal.predicate; - let unconstrained_term = self.next_term_infer_of_kind(term); + let unconstrained_term = self.next_term_infer_of_alias_kind(alias); let normalizes_to = goal.with(self.cx(), ty::NormalizesTo { alias, term: unconstrained_term }); @@ -67,7 +68,19 @@ where let ( NestedNormalizationGoals(nested_goals), GoalEvaluation { goal: _, certainty, stalled_on: _, has_changed: _ }, - ) = self.evaluate_goal_raw(GoalSource::TypeRelating, normalizes_to, None)?; + ) = self.evaluate_goal_raw( + GoalSource::TypeRelating, + normalizes_to, + None, + // We don't increase depth for nested goals for this `NormalizesTo` goal, as + // evaluating `NormalizesTo` is an extra step only exists in the new solver + // that behaves like a function call rather than an independent nested goal + // evaluation, so increasing the depth may end up regressions which hit the + // recursion limits for crates compiled well with the old solver. Furthermore, + // those nested goals from `NormalizesTo` will be evaluated again as the + // caller's nested goals with increased depths anyway. + IncreaseDepthForNested::No, + )?; trace!(?nested_goals); @@ -77,34 +90,11 @@ where // since equating the normalized terms will lead to additional constraints. self.inspect.make_canonical_response(Certainty::AMBIGUOUS); - // FIXME: We shouldn't be doing this in the long term in favor of eager - // normalization. - // Normalize alias types in rhs. This is done in `EvalCtxt::add_goal` for nested - // goals, but we might be evaluating the root goal. - let term = self.replace_alias_with_infer(term, GoalSource::TypeRelating, goal.param_env); - - // Apply the constraints. - self.try_evaluate_added_goals()?; - - // Finally, equate the goal's RHS with the unconstrained var. - // - // SUBTLE: - // We structurally relate aliases here. This is necessary - // as we otherwise emit a nested `AliasRelate` goal in case the - // returned term is a rigid alias, resulting in overflow. - // - // It is correct as both `goal.predicate.term` and `unconstrained_rhs` - // start out as an unconstrained inference variable so any aliases get - // fully normalized when instantiating it. - // - // FIXME: Strictly speaking this may be incomplete if the normalized-to - // type contains an ambiguous alias referencing bound regions. We should - // consider changing this to only use "shallow structural equality". - self.eq_structurally_relating_aliases(goal.param_env, term, unconstrained_term)?; + self.eq(goal.param_env, term, unconstrained_term)?; // Add the nested goals from normalization to our own nested goals. for (s, g) in nested_goals { - self.add_goal(s, g); + self.add_goal(s, g)?; } self.evaluate_added_goals_and_make_canonical_response(certainty) diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs index 5d3b3117ee7fe..7524b2021c015 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs @@ -32,12 +32,12 @@ where opaque_ty.args, goal.param_env, expected, - ); + )?; // Trying to normalize an opaque type during coherence is always ambiguous. // We add a nested ambiguous goal here instead of using `Certainty::AMBIGUOUS`. // This allows us to return the nested goals to the parent `AliasRelate` goal. // This can then allow nested goals to fail after we've constrained the `term`. - self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous)); + self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous))?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) } @@ -48,10 +48,9 @@ where .filter(|&def_id| defining_opaque_types.contains(&def_id.into())) else { // If we're not in the defining scope, treat the alias as rigid. - self.relate_rigid_alias_non_alias( + self.eq( goal.param_env, - opaque_ty, - ty::Invariant, + opaque_ty.to_term(cx, ty::IsRigid::Yes), goal.predicate.term, )?; return self @@ -93,12 +92,15 @@ where TypingMode::PostTypeckUntilBorrowck { .. } => { let actual = cx .type_of_opaque_hir_typeck(def_id) - .instantiate(cx, opaque_ty.args) - .skip_norm_wip(); - let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() { - ty::ReErased => self.next_region_var(), - _ => re, + .instantiate(cx, opaque_ty.args); + let actual = actual.map(|v| { + fold_regions(cx, v, |re, _dbi| match re.kind() { + ty::ReErased => self.next_region_var(), + _ => re, + }) }); + let actual = + self.normalize(GoalSource::Misc, goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; } TypingMode::Coherence @@ -113,7 +115,7 @@ where normalized_args, goal.param_env, expected, - ); + )?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) } @@ -122,10 +124,9 @@ where .as_local() .filter(|&def_id| defined_opaque_types.contains(&def_id.into())) else { - self.relate_rigid_alias_non_alias( + self.eq( goal.param_env, - opaque_ty, - ty::Invariant, + opaque_ty.to_term(cx, ty::IsRigid::Yes), goal.predicate.term, )?; return self @@ -133,23 +134,25 @@ where .map_err(Into::into); }; - let actual = - cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args).skip_norm_wip(); + let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args); // FIXME: Actually use a proper binder here instead of relying on `ReErased`. // // This is also probably unsound or sth :shrug: - let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() { - ty::ReErased => self.next_region_var(), - _ => re, + let actual = actual.map(|v| { + fold_regions(cx, v, |re, _dbi| match re.kind() { + ty::ReErased => self.next_region_var(), + _ => re, + }) }); + let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) } TypingMode::PostAnalysis | TypingMode::Codegen => { // FIXME: Add an assertion that opaque type storage is empty. - let actual = - cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args).skip_norm_wip(); + let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args); + let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) @@ -172,10 +175,9 @@ where } // Always treat the opaque type as rigid. - self.relate_rigid_alias_non_alias( + self.eq( goal.param_env, - opaque_ty, - ty::Invariant, + opaque_ty.to_term(cx, ty::IsRigid::Yes), goal.predicate.term, )?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 549d5d903c045..778826ba60aee 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{ - AccessedOpaques, CanonicalInput, Certainty, NoSolution, NoSolutionOrRerunNonErased, QueryResult, + AccessedOpaques, CanonicalInput, Certainty, NoSolution, QueryResult, RerunResultExt, }; use rustc_type_ir::{Interner, MayBeErased, TypingMode}; @@ -141,17 +141,8 @@ where ) -> (QueryResult, AccessedOpaques) { ensure_sufficient_stack(|| { EvalCtxt::enter_canonical(cx, search_graph, input, inspect, |ecx, goal| { - let result = ecx.compute_goal(goal); - - // if we're in `RerunNonErased`, don't even bother with inspect, - // and immediately return - let result = match result { - Ok(i) => Ok(i), - Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), - Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => { - return Err(e.into()); - } - }; + // if we're in `RerunNonErased`, don't even bother with inspect, and immediately return + let result = ecx.compute_goal(goal).map_err_to_rerun()?; ecx.inspect.query_result(result); result.map_err(Into::into) 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 a8c5b95415621..09d96e3b3a143 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -106,7 +106,7 @@ where .iter_instantiated(cx, impl_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)); - ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds)?; // We currently elaborate all supertrait outlives obligations from impls. // This can be removed when we actually do coinduction correctly, and prove @@ -117,9 +117,9 @@ where .iter_instantiated(cx, impl_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; - then(ecx, maximal_certainty).map_err(Into::into) + then(ecx, maximal_certainty) }) } @@ -235,15 +235,15 @@ where // when merging candidates anyways. // // See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs. - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = + if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = goal.predicate.self_ty().kind() { + debug_assert!(is_rigid == ty::IsRigid::Yes); if ecx.opaque_accesses.might_rerun() { ecx.opaque_accesses.rerun_always(RerunReason::AutoTraitLeakage)?; return Err(NoSolution.into()); } - debug_assert!(ecx.opaque_type_is_rigid(def_id)); for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() { if item_bound .as_trait_clause() @@ -287,7 +287,7 @@ where // `GoalSource::ImplWhereClause` here would be incorrect, as we also // impl them, which means we're "stepping out of the impl constructor" // again. To handle this, we treat these cycles as ambiguous for now. - ecx.add_goals(GoalSource::Misc, nested_obligations); + ecx.add_goals(GoalSource::Misc, nested_obligations)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -399,7 +399,6 @@ where pred, [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))], ) - .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( @@ -450,7 +449,6 @@ where .chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred))) .map(|goal| (GoalSource::ImplWhereBound, goal)), ) - .map_err(Into::into) } fn consider_builtin_async_fn_kind_helper_candidate( @@ -696,7 +694,7 @@ where goal.predicate.trait_ref.args.type_at(1), assume, )?; - ecx.evaluate_added_goals_and_make_canonical_response(certainty).map_err(Into::into) + ecx.evaluate_added_goals_and_make_canonical_response(certainty) }, ) } @@ -737,13 +735,13 @@ where tys.iter().map(|elem_ty| { goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])) }), - ); + )?; } ty::Array(elem_ty, _) => { ecx.add_goal( GoalSource::ImplWhereBound, goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])), - ); + )?; } // All other types implement `BikeshedGuaranteedNoDrop` only if @@ -784,7 +782,7 @@ where [ty], ), ), - ); + )?; } ty::Bound(..) @@ -891,14 +889,14 @@ where param_env: goal.param_env, predicate: TraitRef::new(ecx.cx(), sized_trait, [base]).upcast(ecx.cx()), }, - ); + )?; ecx.add_goal( GoalSource::ImplWhereBound, Goal { param_env: goal.param_env, predicate: TraitRef::new(ecx.cx(), sized_trait, [ty]).upcast(ecx.cx()), }, - ); + )?; // FIXME(field_projections): This function does some questionable incomplete stuff by // returning `Err(NoSolution)` on ambiguity. ecx.try_evaluate_added_goals()? == Certainty::Yes @@ -1018,7 +1016,7 @@ where ecx.add_goals( GoalSource::ImplWhereBound, b_data.iter().map(|pred| goal.with(cx, pred.with_self_ty(cx, a_ty))), - ); + )?; // The type must be `Sized` to be unsized. ecx.add_goal( @@ -1031,10 +1029,10 @@ where [a_ty], ), ), - ); + )?; // The type must outlive the lifetime of the `dyn` we're unsizing into. - ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesPredicate(a_ty, b_region))); + ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesPredicate(a_ty, b_region)))?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -1085,7 +1083,6 @@ where ecx.try_evaluate_added_goals() }, ) - .map_err(Into::into) }) .is_ok() }; @@ -1124,11 +1121,9 @@ where return Err(NoSolution.into()); }; if matching_projections.next().is_some() { - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ) - .map_err(Into::into); + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); } ecx.enter_forall_with_assumptions( target_projection, @@ -1154,9 +1149,9 @@ where ecx.add_goal( GoalSource::ImplWhereBound, Goal::new(ecx.cx(), param_env, ty::OutlivesPredicate(a_region, b_region)), - ); + )?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -1235,7 +1230,7 @@ where [a_tail_ty, b_tail_ty], ), ), - ); + )?; self.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } @@ -1251,13 +1246,9 @@ where let self_ty = goal.predicate.self_ty(); let check_impls = || { let mut disqualifying_impl = None; - self.cx().for_each_relevant_impl( - goal.predicate.def_id(), - goal.predicate.self_ty(), - |impl_def_id| { - disqualifying_impl = Some(impl_def_id); - }, - ); + self.cx().for_each_relevant_impl(goal.predicate.trait_ref, |impl_def_id| { + disqualifying_impl = Some(impl_def_id); + }); if let Some(def_id) = disqualifying_impl { trace!(?def_id, ?goal, "disqualified auto-trait implementation"); // No need to actually consider the candidate here, @@ -1287,14 +1278,15 @@ where ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Free { .. } | ty::Inherent { .. }, - .. - }) + | ty::Alias( + ty::IsRigid::Yes, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Free { .. } | ty::Inherent { .. }, + .. + }, + ) | ty::Placeholder(..) => Some(Err(NoSolution.into())), - ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"), - // Coroutines have one special built-in candidate, `Unpin`, which // takes precedence over the structural auto trait candidate being // assembled. @@ -1317,7 +1309,7 @@ where // okay to consider auto traits because that'll reveal its hidden type. For // non-opaque aliases, we will not assemble any candidates since there's no way // to further look into its type. - ty::Alias(..) => None, + ty::Alias(ty::IsRigid::Yes, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => None, // For rigid types, any possible implementation that could apply to // the type (even if after unification and processing nested goals @@ -1347,6 +1339,10 @@ where | ty::Adt(_, _) | ty::UnsafeBinder(_) => check_impls(), ty::Error(_) => None, + + ty::Infer(_) | ty::Alias(ty::IsRigid::No, _) | ty::Bound(_, _) => { + panic!("unexpected type `{self_ty:?}`") + } } } @@ -1375,7 +1371,7 @@ where .collect::>() }, ); - ecx.add_goals(GoalSource::ImplWhereBound, goals); + ecx.add_goals(GoalSource::ImplWhereBound, goals)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index 22ec84231af49..768a9546cccd4 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -rustc-literal-escaper = "0.0.7" +rustc-literal-escaper = "0.0.8" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index fbceb185c190c..7040fc60e4f1a 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -602,7 +602,9 @@ impl<'a> Parser<'a> { // Look for usages of '=>' where '>=' was probably intended if self.token == token::FatArrow && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le)) - && !expected.iter().any(|tok| matches!(tok, TokenType::FatArrow | TokenType::Comma)) + && !expected + .iter() + .any(|tok| matches!(tok, TokenType::FatArrow | TokenType::CloseBrace)) { err.span_suggestion( self.token.span, diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml index 10b41a39c3bf2..e6922a5f2c919 100644 --- a/compiler/rustc_parse_format/Cargo.toml +++ b/compiler/rustc_parse_format/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc-literal-escaper = "0.0.7" +rustc-literal-escaper = "0.0.8" rustc_lexer = { path = "../rustc_lexer" } # tidy-alphabetical-end diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 079fdd923ac0d..7ae6005e9bc98 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -24,8 +24,9 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParamKind, HirId, - Item, ItemKind, MethodKind, Node, ParamName, Target, TraitItem, find_attr, + self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParam, + GenericParamKind, HirId, Item, ItemKind, MethodKind, Node, ParamName, Target, TraitItem, + find_attr, }; use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; @@ -203,9 +204,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Deprecated { span: attr_span, .. } => { self.check_deprecated(hir_id, *attr_span, target) } - AttributeKind::TargetFeature { attr_span, .. } => { - self.check_target_feature(hir_id, *attr_span, target, attrs) - } AttributeKind::RustcDumpObjectLifetimeDefaults => { self.check_dump_object_lifetime_defaults(hir_id); } @@ -219,7 +217,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::NonExhaustive(attr_span) => { self.check_non_exhaustive(*attr_span, span, target, item) } - &AttributeKind::FfiPure(attr_span) => self.check_ffi_pure(attr_span, attrs), AttributeKind::MayDangle(attr_span) => self.check_may_dangle(hir_id, *attr_span), AttributeKind::Link(_, attr_span) => self.check_link(hir_id, *attr_span, target), AttributeKind::MacroExport { span, .. } => { @@ -271,6 +268,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::ExportStable => (), AttributeKind::Feature(..) => (), AttributeKind::FfiConst => (), + AttributeKind::FfiPure(..) => (), AttributeKind::Fundamental => (), AttributeKind::Ignore { .. } => (), AttributeKind::InstructionSet(..) => (), @@ -405,6 +403,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::ShouldPanic { .. } => (), AttributeKind::Splat(..) => (), AttributeKind::Stability { .. } => (), + AttributeKind::TargetFeature { .. } => {} AttributeKind::TestRunner(..) => (), AttributeKind::ThreadLocal => (), AttributeKind::TypeLengthLimit { .. } => (), @@ -798,37 +797,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if the `#[target_feature]` attribute on `item` is valid. - fn check_target_feature( - &self, - hir_id: HirId, - attr_span: Span, - target: Target, - attrs: &[Attribute], - ) { - match target { - Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) - | Target::Fn => { - // `#[target_feature]` is not allowed in lang items. - if let Some(lang_item) = find_attr!(attrs, Lang(lang ) => lang) - // Calling functions with `#[target_feature]` is - // not unsafe on WASM, see #84988 - && !self.tcx.sess.target.is_like_wasm - && !self.tcx.sess.opts.actually_rustdoc - { - let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); - - self.dcx().emit_err(diagnostics::LangItemWithTargetFeature { - attr_span, - name: lang_item.name(), - sig_span: sig.span, - }); - } - } - _ => {} - } - } - fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) { if let Some(location) = match target { Target::AssocTy => { @@ -1112,21 +1080,20 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) { - if find_attr!(attrs, FfiConst) { - // `#[ffi_const]` functions cannot be `#[ffi_pure]` - self.dcx().emit_err(diagnostics::BothFfiConstAndPure { attr_span }); - } - } - /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl. fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) { - if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id) - && matches!( - param.kind, - hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } - ) - && matches!(param.source, hir::GenericParamSource::Generics) + let hir::Node::GenericParam( + param @ GenericParam { + kind: hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. }, + .. + }, + ) = self.tcx.hir_node(hir_id) + else { + self.dcx().delayed_bug("Checked in attr parser"); + return; + }; + + if matches!(param.source, hir::GenericParamSource::Generics) && let parent_hir_id = self.tcx.parent_hir_id(hir_id) && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id) && let hir::ItemKind::Impl(impl_) = item.kind diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index 2f40ecb67342f..a1b204152387c 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -314,7 +314,7 @@ impl<'tcx, 'a> TypeVisitor> for ExportableItemsChecker<'tcx, 'a> { | ty::CoroutineWitness(_, _) | ty::Never | ty::UnsafeBinder(_) - | ty::Alias(ty::AliasTy { kind: ty::AliasTyKind::Opaque { .. }, .. }) => { + | ty::Alias(_, ty::AliasTy { kind: ty::AliasTyKind::Opaque { .. }, .. }) => { return ControlFlow::Break(ty); } diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index f6c4b0cf77d12..92562cc462b87 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -5,7 +5,6 @@ use rustc_errors::codes::*; use rustc_errors::{ Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, MultiSpan, msg, }; -use rustc_hir::Target; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{MainDefinition, Ty}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; @@ -153,13 +152,6 @@ pub(crate) struct DocMaskedNotExternCrateSelf { pub item_span: Span, } -#[derive(Diagnostic)] -#[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] -pub(crate) struct BothFfiConstAndPure { - #[primary_span] - pub attr_span: Span, -} - #[derive(Diagnostic)] #[diag("`#[optimize(none)]` cannot be used with `#[inline]` attributes")] pub(crate) struct BothOptimizeNoneAndInline { @@ -326,14 +318,6 @@ pub(crate) struct DeprecatedAnnotationHasNoEffect { pub span: Span, } -#[derive(Diagnostic)] -#[diag("unknown external lang item: `{$lang_item}`", code = E0264)] -pub(crate) struct UnknownExternLangItem { - #[primary_span] - pub span: Span, - pub lang_item: Symbol, -} - #[derive(Diagnostic)] #[diag("`#[panic_handler]` function required, but not found")] pub(crate) struct MissingPanicHandler; @@ -378,37 +362,6 @@ pub(crate) struct LangItemWithTrackCaller { pub sig_span: Span, } -#[derive(Diagnostic)] -#[diag( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[target_feature]`" -)] -pub(crate) struct LangItemWithTargetFeature { - #[primary_span] - pub attr_span: Span, - pub name: Symbol, - #[label( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[target_feature]`" - )] - pub sig_span: Span, -} - -#[derive(Diagnostic)] -#[diag("`{$name}` lang item must be applied to a {$expected_target}", code = E0718)] -pub(crate) struct LangItemOnIncorrectTarget { - #[primary_span] - #[label("attribute should be applied to a {$expected_target}, not a {$actual_target}")] - pub span: Span, - pub name: Symbol, - pub expected_target: Target, - pub actual_target: Target, -} - #[derive(Diagnostic)] #[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 53fb6888ddcd8..a5a835d4a78c3 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -18,9 +18,7 @@ use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_session::cstore::ExternCrate; use rustc_span::{Span, Symbol, sym}; -use crate::diagnostics::{ - DuplicateLangItem, IncorrectCrateType, IncorrectTarget, LangItemOnIncorrectTarget, -}; +use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget}; use crate::weak_lang_items; pub(crate) enum Duplicate { @@ -29,6 +27,11 @@ pub(crate) enum Duplicate { CrateDepends, } +enum CollectWeak { + Allowed, + Ignore, +} + struct LanguageItemCollector<'ast, 'tcx> { items: LanguageItems, tcx: TyCtxt<'tcx>, @@ -60,28 +63,30 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { attrs: &'ast [ast::Attribute], item_span: Span, generics: Option<&'ast ast::Generics>, + collect_weak: CollectWeak, ) { if let Some((name, attr_span)) = extract_ast(attrs) { match LangItem::from_name(name) { - // Known lang item with attribute on correct target. - Some(lang_item) if actual_target == lang_item.target() => { - self.collect_item_extended( - lang_item, - def_id, - item_span, - attr_span, - generics, - actual_target, - ); - } - // Known lang item with attribute on incorrect target. + // Known lang item Some(lang_item) => { - self.tcx.dcx().emit_err(LangItemOnIncorrectTarget { - span: attr_span, - name, - expected_target: lang_item.target(), - actual_target, - }); + if actual_target != lang_item.target() { + self.tcx + .dcx() + .delayed_bug("lang item target is checked in attribute parser"); + return; + } + // Weak lang items are handled separately + // Weak only lang items are always handled here + if !lang_item.is_weak() || matches!(collect_weak, CollectWeak::Allowed) { + self.collect_item_extended( + lang_item, + def_id, + item_span, + attr_span, + generics, + actual_target, + ); + } } // Unknown lang item. _ => { @@ -300,6 +305,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &i.attrs, i.span, i.opt_generics(), + CollectWeak::Allowed, ); let parent_item = self.parent_item.replace(i); @@ -307,6 +313,17 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { self.parent_item = parent_item; } + fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { + self.check_for_lang( + Target::Fn, + self.resolver.owners[&i.id].def_id, + &i.attrs, + i.span, + None, + CollectWeak::Ignore, + ); + } + fn visit_variant(&mut self, variant: &'ast ast::Variant) { self.check_for_lang( Target::Variant, @@ -314,6 +331,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &variant.attrs, variant.span, None, + CollectWeak::Allowed, ); } @@ -347,7 +365,14 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { } }; - self.check_for_lang(target, self.resolver.owners[&i.id].def_id, &i.attrs, i.span, generics); + self.check_for_lang( + target, + self.resolver.owners[&i.id].def_id, + &i.attrs, + i.span, + generics, + CollectWeak::Allowed, + ); visit::walk_assoc_item(self, i, ctxt); } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index b42b27ec74184..50730b852455c 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -9,9 +9,7 @@ use rustc_middle::middle::lang_items::required; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; -use crate::diagnostics::{ - MissingLangItem, MissingPanicHandler, PanicUnwindWithoutStd, UnknownExternLangItem, -}; +use crate::diagnostics::{MissingLangItem, MissingPanicHandler, PanicUnwindWithoutStd}; use crate::lang_items::extract_ast; /// Checks the crate for usage of weak lang items, returning a vector of all the @@ -28,27 +26,23 @@ pub(crate) fn check_crate( items.missing.push(LangItem::EhPersonality); } - visit::Visitor::visit_crate(&mut WeakLangItemVisitor { tcx, items }, krate); + visit::Visitor::visit_crate(&mut WeakLangItemVisitor { items }, krate); verify(tcx, items); } -struct WeakLangItemVisitor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, +struct WeakLangItemVisitor<'a> { items: &'a mut lang_items::LanguageItems, } -impl<'ast> visit::Visitor<'ast> for WeakLangItemVisitor<'_, '_> { +impl<'ast> visit::Visitor<'ast> for WeakLangItemVisitor<'_> { fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { - if let Some((lang_item, _)) = extract_ast(&i.attrs) { - if let Some(item) = LangItem::from_name(lang_item) - && item.is_weak() - { - if self.items.get(item).is_none() { - self.items.missing.push(item); - } - } else { - self.tcx.dcx().emit_err(UnknownExternLangItem { span: i.span, lang_item }); + if let Some((lang_item, _)) = extract_ast(&i.attrs) + && let Some(item) = LangItem::from_name(lang_item) + && item.is_weak() + { + if self.items.get(item).is_none() { + self.items.missing.push(item); } } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 88ec44beec753..14fd5f8e9a2dc 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -126,7 +126,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { #[inline] pub fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> RevealedTy<'tcx> { fn reveal_inner<'tcx>(cx: &RustcPatCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> RevealedTy<'tcx> { - let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *ty.kind() + debug_assert!(!cx.tcx.next_trait_solver_globally()); + let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *ty.kind() else { bug!() }; @@ -138,7 +139,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } RevealedTy(ty) } - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) = ty.kind() { + if let ty::Alias(ty::IsRigid::No, ty::AliasTy { kind: ty::Opaque { .. }, .. }) = ty.kind() { reveal_inner(self, ty) } else { RevealedTy(ty) @@ -426,7 +427,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | ty::CoroutineClosure(..) | ty::Coroutine(_, _) | ty::UnsafeBinder(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Error(_) => ConstructorSet::Unlistable, ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1cd1101db090d..76af497c1d063 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -215,6 +215,7 @@ where } } ty::Alias( + _, data @ ty::AliasTy { kind: kind @ (ty::Inherent { def_id } @@ -274,7 +275,7 @@ where try_visit!(self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref)); } } - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) => { // Skip repeated `Opaque`s to avoid infinite recursion. if self.visited_tys.insert(ty) { // The intent is to treat `impl Trait1 + Trait2` identically to @@ -947,7 +948,8 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { // definition of the field let ident = Ident::new(sym::dummy, use_ctxt); - let (_, def_id) = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id); + let (_, def_id) = + self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id.owner.def_id); !field.vis.is_accessible_from(def_id, self.tcx) } diff --git a/compiler/rustc_proc_macro/Cargo.toml b/compiler/rustc_proc_macro/Cargo.toml index 54c765075113e..e31bb4efe363d 100644 --- a/compiler/rustc_proc_macro/Cargo.toml +++ b/compiler/rustc_proc_macro/Cargo.toml @@ -16,7 +16,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -rustc-literal-escaper = "0.0.7" +rustc-literal-escaper = "0.0.8" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 81b32f4da10f6..181498443073d 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -312,6 +312,7 @@ impl RustcInternal for FnSig { tables: &mut Tables<'_, BridgeTys>, tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { + // FIXME(splat): When `#[splat]` is complete (or stable), add splatted to the public FnSig let fn_sig_kind = rustc_ty::FnSigKind::default() .set_abi(self.abi.internal(tables, tcx)) .set_safety(self.safety.internal(tables, tcx)) diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index f9ee9f4ee779e..2f280ef9fe4c0 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -487,7 +487,7 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( fields.iter().map(|ty| ty.stable(tables, cx)).collect(), )), - ty::Alias(alias_ty) => { + ty::Alias(_, alias_ty) => { TyKind::Alias(alias_ty.kind.stable(tables, cx), alias_ty.stable(tables, cx)) } ty::Param(param_ty) => TyKind::Param(param_ty.stable(tables, cx)), @@ -552,16 +552,14 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { } } ty::ConstKind::Param(param) => crate::ty::TyConstKind::Param(param.stable(tables, cx)), - ty::ConstKind::Unevaluated(uv) => { - let Some(def_id) = uv.kind.opt_def_id() else { - // FIXME: implement (both AliasTy and UnevaluatedConst will be needing this soon) - panic!( - "non-defid unevaluated constants are not supported by rustc_public at the moment" - ) + ty::ConstKind::Alias(_, alias_const) => { + let Some(def_id) = alias_const.kind.opt_def_id() else { + // FIXME: implement (both AliasTy and AliasConst will be needing this soon) + panic!("non-defid alias consts are not supported by rustc_public at the moment") }; crate::ty::TyConstKind::Unevaluated( tables.const_def(def_id), - uv.args.stable(tables, cx), + alias_const.args.stable(tables, cx), ) } ty::ConstKind::Error(_) => unreachable!(), @@ -994,18 +992,7 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { ty::InstanceKind::Virtual(_def_id, idx) => { crate::mir::mono::InstanceKind::Virtual { idx } } - ty::InstanceKind::VTableShim(..) - | ty::InstanceKind::ReifyShim(..) - | ty::InstanceKind::FnPtrAddrShim(..) - | ty::InstanceKind::ClosureOnceShim { .. } - | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } - | ty::InstanceKind::ThreadLocalShim(..) - | ty::InstanceKind::DropGlue(..) - | ty::InstanceKind::CloneShim(..) - | ty::InstanceKind::FnPtrShim(..) - | ty::InstanceKind::FutureDropPollShim(..) - | ty::InstanceKind::AsyncDropGlue(..) - | ty::InstanceKind::AsyncDropGlueCtorShim(..) => crate::mir::mono::InstanceKind::Shim, + ty::InstanceKind::Shim(..) => crate::mir::mono::InstanceKind::Shim, }; crate::mir::mono::Instance { def, kind } } diff --git a/compiler/rustc_public_bridge/src/builder.rs b/compiler/rustc_public_bridge/src/builder.rs index ea7f37d72d0dd..525582fc835f3 100644 --- a/compiler/rustc_public_bridge/src/builder.rs +++ b/compiler/rustc_public_bridge/src/builder.rs @@ -38,7 +38,7 @@ impl<'tcx> BodyBuilder<'tcx> { let mut mono_body = self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(body), + ty::EarlyBinder::bind(self.tcx, body), ); self.visit_body(&mut mono_body); mono_body diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index c309df59e9eca..87a8661edeb65 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -645,7 +645,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { /// Check if this is an empty DropGlue shim. pub fn is_empty_drop_shim(&self, instance: ty::Instance<'tcx>) -> bool { - matches!(instance.def, ty::InstanceKind::DropGlue(_, None)) + matches!(instance.def, ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None))) } /// Convert a non-generic crate item into an instance. diff --git a/compiler/rustc_query_impl/src/handle_cycle_error.rs b/compiler/rustc_query_impl/src/handle_cycle_error.rs index 79e7788cafe81..7e7ed3ab3b525 100644 --- a/compiler/rustc_query_impl/src/handle_cycle_error.rs +++ b/compiler/rustc_query_impl/src/handle_cycle_error.rs @@ -43,9 +43,10 @@ pub(crate) fn fn_sig<'tcx>( unreachable!() }; - ty::EarlyBinder::bind(ty::Binder::dummy( - tcx.mk_fn_sig_safe_rust_abi(std::iter::repeat_n(err, arity), err), - )) + ty::EarlyBinder::bind( + tcx, + ty::Binder::dummy(tcx.mk_fn_sig_safe_rust_abi(std::iter::repeat_n(err, arity), err)), + ) } pub(crate) fn check_representability<'tcx>( @@ -104,7 +105,7 @@ pub(crate) fn variances_of<'tcx>( err: Diag<'_>, ) -> &'tcx [ty::Variance] { let _guar = err.delay_as_bug(); - let n = tcx.generics_of(def_id).own_params.len(); + let n = tcx.generics_of(def_id).count(); tcx.arena.alloc_from_iter(iter::repeat_n(ty::Bivariant, n)) } diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index cb52c21c87ae9..a1d61d0d9f2f0 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start indexmap = "2.4.0" -itertools = "0.12" +itertools = "0.15" pulldown-cmark = { version = "0.11", features = [ "html", ], default-features = false } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 911c6142e4b22..bb9bf8976f672 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -548,7 +548,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { root_id, vis, vis_span: item.vis.span, - on_unknown_attr: OnUnknownData::from_attrs(self.r.tcx, &item.attrs), + on_unknown_attr: OnUnknownData::from_attrs(self.r, &item.attrs), }); self.r.indeterminate_imports.push(import); @@ -863,7 +863,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { || ast::attr::contains_name(&item.attrs, sym::no_implicit_prelude), ); self.parent_scope.module = module.to_module(); - if let Some(directive) = OnUnknownData::from_attrs(self.r.tcx, &item.attrs) { + if let Some(directive) = OnUnknownData::from_attrs(self.r, &item.attrs) { self.r.on_unknown_data.insert(local_def_id, directive); } } @@ -1040,7 +1040,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { module_path: Vec::new(), vis, vis_span: item.vis.span, - on_unknown_attr: OnUnknownData::from_attrs(self.r.tcx, &item.attrs), + on_unknown_attr: OnUnknownData::from_attrs(self.r, &item.attrs), }); if used { self.r.import_use_map.insert(import, Used::Other); @@ -1172,7 +1172,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { module_path: Vec::new(), vis: Visibility::Restricted(CRATE_DEF_ID), vis_span: item.vis.span, - on_unknown_attr: OnUnknownData::from_attrs(this.r.tcx, &item.attrs), + on_unknown_attr: OnUnknownData::from_attrs(this.r, &item.attrs), }) }; @@ -1353,7 +1353,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { module_path: Vec::new(), vis, vis_span: item.vis.span, - on_unknown_attr: OnUnknownData::from_attrs(self.r.tcx, &item.attrs), + on_unknown_attr: OnUnknownData::from_attrs(self.r, &item.attrs), }); self.r.import_use_map.insert(import, Used::Other); let import_decl = self.r.new_import_decl(decl, import); diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 32e4bb9262fa8..4c5c591ad94e3 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -10,9 +10,9 @@ // // Checking for unused imports is split into three steps: // -// - `UnusedImportCheckVisitor` walks the AST to find all the unused imports -// inside of `UseTree`s, recording their `NodeId`s and grouping them by -// the parent `use` item +// - `UnusedImportCheckVisitor` visits the `use` items collected during late +// resolution to find all the unused imports inside of `UseTree`s, recording +// their `NodeId`s and grouping them by the parent `use` item // // - `calc_unused_spans` then walks over all the `use` items marked in the // previous step to collect the spans associated with the `NodeId`s and to @@ -410,7 +410,7 @@ fn calc_unused_spans( } impl Resolver<'_, '_> { - pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { + pub(crate) fn check_unused(&mut self, use_items: Vec<&ast::Item>) { let tcx = self.tcx; let mut maybe_unused_extern_crates = FxHashMap::default(); @@ -465,7 +465,10 @@ impl Resolver<'_, '_> { base_id: ast::DUMMY_NODE_ID, item_span: DUMMY_SP, }; - visit::walk_crate(&mut visitor, krate); + // `use_items` is in crate DFS order, so diagnostics and side effects are unchanged. + for item in use_items { + visitor.visit_item(item); + } visitor.report_unused_extern_crate_items(maybe_unused_extern_crates); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index cdc2df5bf2945..de3f8c380fc44 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -17,8 +17,7 @@ use tracing::{debug, instrument}; use crate::macros::MacroRulesScopeRef; use crate::{ - ConstArgContext, ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, - with_owner_tables, + ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, with_owner_tables, }; pub(crate) fn collect_definitions<'ra>( @@ -115,12 +114,6 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { self.invocation_parent.impl_trait_context = orig_itc; } - fn with_const_arg(&mut self, ctxt: ConstArgContext, f: F) { - let orig = mem::replace(&mut self.invocation_parent.const_arg_context, ctxt); - f(self); - self.invocation_parent.const_arg_context = orig; - } - fn collect_field(&mut self, field: &'a FieldDef, index: Option) { let index = |this: &Self| { index.unwrap_or_else(|| { @@ -186,7 +179,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { // Does that prevents errors from happening? maybe let mut parser = AttributeParser::new( &self.r.tcx.sess, - self.r.tcx.features(), + self.r.features, self.r.tcx().registered_tools(()), ShouldEmit::Nothing, ); @@ -430,10 +423,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_anon_const(&mut self, constant: &'a AnonConst) { + // Note that `visit_anon_const` is skipped for AnonConst nodes wrapped in an + // ExprKind::ConstBlock - these are handled in visit_expr, and are DefKind::InlineConst. + // `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so // to avoid affecting stable we have to feature gate the not creating // anon consts - if !self.r.tcx.features().min_generic_const_args() { + if !self.r.features.min_generic_const_args() { let parent = self .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) .def_id(); @@ -441,16 +437,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } match constant.mgca_disambiguation { - MgcaDisambiguation::Direct => self.with_const_arg(ConstArgContext::Direct, |this| { - visit::walk_anon_const(this, constant); - }), + MgcaDisambiguation::Direct => visit::walk_anon_const(self, constant), MgcaDisambiguation::AnonConst => { - self.with_const_arg(ConstArgContext::NonDirect, |this| { - let parent = this - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - this.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - }) + let parent = self + .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) + .def_id(); + self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } }; } @@ -459,60 +451,28 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_expr(&mut self, expr: &'a Expr) { debug!(?self.invocation_parent); - let parent_def = match &expr.kind { + match &expr.kind { ExprKind::MacCall(..) => { self.visit_macro_invoc(expr.id); self.visit_invoc(expr.id); - return; } ExprKind::Closure(..) | ExprKind::Gen(..) => { - self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id() + let def = self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id(); + self.with_parent(def, |this| visit::walk_expr(this, expr)); } ExprKind::ConstBlock(constant) => { - // Under `min_generic_const_args` a `const { }` block sometimes - // corresponds to an anon const rather than an inline const. - let def_kind = match self.invocation_parent.const_arg_context { - ConstArgContext::Direct => DefKind::AnonConst, - ConstArgContext::NonDirect => DefKind::InlineConst, - }; - - return self.with_const_arg(ConstArgContext::NonDirect, |this| { - for attr in &expr.attrs { - visit::walk_attribute(this, attr); - } - - let def = - this.create_def(constant.id, None, def_kind, constant.value.span).def_id(); - this.with_parent(def, |this| visit::walk_anon_const(this, constant)); - }); - } - - // Avoid overwriting `const_arg_context` as we may want to treat const blocks - // as being anon consts if we are inside a const argument. - ExprKind::Struct(_) | ExprKind::Call(..) | ExprKind::Tup(..) | ExprKind::Array(..) => { - return visit::walk_expr(self, expr); - } - // FIXME(mgca): we may want to handle block labels in some manner - ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() => match stmt.kind { - // FIXME(mgca): this probably means that mac calls that expand - // to semi'd const blocks are handled differently to just writing - // out a semi'd const block. - StmtKind::Expr(..) | StmtKind::MacCall(..) => return visit::walk_expr(self, expr), - - // Fallback to normal behaviour - StmtKind::Let(..) | StmtKind::Item(..) | StmtKind::Semi(..) | StmtKind::Empty => { - self.invocation_parent.parent_def + for attr in &expr.attrs { + visit::walk_attribute(self, attr); } - }, - _ => self.invocation_parent.parent_def, - }; - - self.with_const_arg(ConstArgContext::NonDirect, |this| { - // Note in some cases the `parent_def` here may be the existing parent - // and this is actually a no-op `with_parent` call. - this.with_parent(parent_def, |this| visit::walk_expr(this, expr)) - }) + let def = self + .create_def(constant.id, None, DefKind::InlineConst, constant.value.span) + .def_id(); + // use specifically walk_anon_const, not walk_expr, to skip self.visit_anon_const + self.with_parent(def, |this| visit::walk_anon_const(this, constant)); + } + _ => visit::walk_expr(self, expr), + } } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_resolve/src/error_helper.rs b/compiler/rustc_resolve/src/error_helper.rs index 81c1b6fa8d675..c8abc3556f8fe 100644 --- a/compiler/rustc_resolve/src/error_helper.rs +++ b/compiler/rustc_resolve/src/error_helper.rs @@ -109,7 +109,7 @@ impl TypoSuggestion { } /// A free importable items suggested in case of resolution failure. -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct ImportSuggestion { pub did: Option, pub descr: &'static str, @@ -1628,7 +1628,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| { // Avoid non-importable candidates. if name_binding.is_assoc_item() - && !this.tcx.features().import_trait_associated_functions() + && !this.features.import_trait_associated_functions() { return; } @@ -1809,10 +1809,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }) => { if span.allows_unstable(feature) { true - } else if self.tcx.features().enabled(feature) { + } else if self.features.enabled(feature) { true } else if let Some(implied_by) = implied_by - && self.tcx.features().enabled(implied_by) + && self.features.enabled(implied_by) { true } else { @@ -4144,13 +4144,17 @@ pub(crate) struct OnUnknownData { } impl OnUnknownData { - pub(crate) fn from_attrs<'tcx>( - tcx: TyCtxt<'tcx>, + pub(crate) fn from_attrs( + r: &Resolver<'_, '_>, attrs: &[ast::Attribute], ) -> Option { - if tcx.features().diagnostic_on_unknown() + if r.features.diagnostic_on_unknown() && let Some(Attribute::Parsed(AttributeKind::OnUnknown { directive, .. })) = - AttributeParser::parse_limited(tcx.sess, attrs, &[sym::diagnostic, sym::on_unknown]) + AttributeParser::parse_limited( + r.tcx.sess, + attrs, + &[sym::diagnostic, sym::on_unknown], + ) { Some(Self { directive: directive? }) } else { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 2e69405811db4..d5babe2e94a90 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -16,16 +16,16 @@ use tracing::{debug, instrument}; use crate::diagnostics::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; use crate::hygiene::Macros20NormalizedSyntaxContext; -use crate::imports::{Import, NameResolution}; +use crate::imports::{Import, NameResolution, cycle_detection}; use crate::late::{ ConstantHasGenerics, DiagMetadata, NoConstantGenericsReason, PathSource, Rib, RibKind, }; use crate::macros::{MacroRulesScope, sub_namespace_match}; use crate::{ AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingKey, CmResolver, Decl, DeclKind, - Determinacy, Finalize, IdentKey, ImportKind, ImportSummary, LateDecl, LocalModule, Module, - ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PrivacyError, Res, ResolutionError, - Resolver, Scope, ScopeSet, Segment, Stage, Symbol, Used, diagnostics, + Determinacy, ExternModule, Finalize, IdentKey, ImportKind, ImportSummary, LateDecl, + LocalModule, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PrivacyError, + Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, Stage, Symbol, Used, diagnostics, }; #[derive(Copy, Clone)] @@ -608,21 +608,36 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize.map(|f| Finalize { used: Used::Scope, ..f }), ) }; - let decl = self.reborrow().resolve_ident_in_module_non_globs_unadjusted( - module, - ident, - orig_ident_span, - ns, - adjusted_parent_scope, - if matches!(scope_set, ScopeSet::Module(..)) { - Shadowing::Unrestricted - } else { - Shadowing::Restricted - }, - adjusted_finalize, - ignore_decl, - ignore_import, - ); + let shadowing = if matches!(scope_set, ScopeSet::Module(..)) { + Shadowing::Unrestricted + } else { + Shadowing::Restricted + }; + let decl = if module.is_local() { + self.reborrow().resolve_ident_in_local_module_non_globs_unadjusted( + module.expect_local(), + ident, + orig_ident_span, + ns, + adjusted_parent_scope, + shadowing, + adjusted_finalize, + ignore_decl, + ignore_import, + ) + } else { + self.reborrow().resolve_ident_in_extern_module_non_globs_unadjusted( + module.expect_extern(), + ident, + orig_ident_span, + ns, + adjusted_parent_scope, + shadowing, + adjusted_finalize, + ignore_decl, + ) + }; + match decl { Ok(decl) => { if let Some(lint_id) = derive_fallback_lint_id { @@ -746,7 +761,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::BuiltinTypes => match self.builtin_type_decls.get(&ident.name) { Some(decl) => { if matches!(ident.name, sym::f16) - && !self.tcx.features().f16() + && !self.features.f16() && !orig_ident_span.allows_unstable(sym::f16) && finalize.is_some() { @@ -759,7 +774,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .emit(); } if matches!(ident.name, sym::f128) - && !self.tcx.features().f128() + && !self.features.f128() && !orig_ident_span.allows_unstable(sym::f128) && finalize.is_some() { @@ -1078,10 +1093,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - /// Attempts to resolve `ident` in namespace `ns` of non-glob bindings in `module`. - fn resolve_ident_in_module_non_globs_unadjusted<'r>( + /// Attempts to resolve `ident` in namespace `ns` of non-glob bindings in an external `module`. + fn resolve_ident_in_extern_module_non_globs_unadjusted<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, - module: Module<'ra>, + module: ExternModule<'ra>, ident: IdentKey, orig_ident_span: Span, ns: Namespace, @@ -1091,16 +1106,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // This binding should be ignored during in-module resolution, so that we don't get // "self-confirming" import resolutions during import validation and checking. ignore_decl: Option>, - ignore_import: Option>, ) -> Result, ControlFlow> { let key = BindingKey::new(ident, ns); - // `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding - // doesn't need to be mutable. It will fail when there is a cycle of imports, and without - // the exclusive access infinite recursion will crash the compiler with stack overflow. - let resolution = &*self - .resolution_or_default(module, key, orig_ident_span) - .try_borrow_mut_unchecked() - .map_err(|_| ControlFlow::Continue(Determined))?; + let resolution = + &*self.resolution(module.to_module(), key).ok_or(ControlFlow::Continue(Determined))?; let binding = resolution.non_glob_decl.filter(|b| Some(*b) != ignore_decl); @@ -1120,22 +1129,64 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let accessible = self.is_accessible_from(binding.vis(), parent_scope.module); return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) }; } + Err(ControlFlow::Continue(Determined)) + } - // In extern modules everything is determined from the start. - if !module.is_local() { - return Err(ControlFlow::Continue(Determined)); - }; + /// Attempts to resolve `ident` in namespace `ns` of non-glob bindings in a local `module`. + fn resolve_ident_in_local_module_non_globs_unadjusted<'r>( + mut self: CmResolver<'r, 'ra, 'tcx>, + module: LocalModule<'ra>, + ident: IdentKey, + orig_ident_span: Span, + ns: Namespace, + parent_scope: &ParentScope<'ra>, + shadowing: Shadowing, + finalize: Option, + // This binding should be ignored during in-module resolution, so that we don't get + // "self-confirming" import resolutions during import validation and checking. + ignore_decl: Option>, + ignore_import: Option>, + ) -> Result, ControlFlow> { + let key = BindingKey::new(ident, ns); + let resolution = self.resolution(module.to_module(), key); - // Check if one of single imports can still define the name, block if it can. - if self.reborrow().single_import_can_define_name( - &resolution, - None, - ns, - ignore_import, - ignore_decl, - parent_scope, - ) { - return Err(ControlFlow::Break(Undetermined)); + let binding = + resolution.as_ref().and_then(|r| r.non_glob_decl).filter(|b| Some(*b) != ignore_decl); + + if let Some(finalize) = finalize { + return self.get_mut().finalize_module_binding( + ident, + orig_ident_span, + binding, + parent_scope, + finalize, + shadowing, + ); + } + + // Items and single imports are not shadowable, if we have one, then it's determined. + if let Some(binding) = binding { + let accessible = self.is_accessible_from(binding.vis(), parent_scope.module); + return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) }; + } + + if let Some(resolution) = resolution { + // We need to detect resolution cycles to avoid infinite recursion. The guard ensures + // the resolution is removed when this resolve call ends. + let _cycle_guard = cycle_detection::enter_cycle_detector(module, key) + .map_err(|_| ControlFlow::Continue(Determined))?; + + // Check if one of single imports can still define the name, block if it can. + if self.reborrow().single_import_can_define_name( + &resolution, + None, + ns, + ignore_import, + ignore_decl, + parent_scope, + ) { + return Err(ControlFlow::Break(Undetermined)); + } } // Check if one of unexpanded macros can still define the name. @@ -1161,15 +1212,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, ) -> Result, ControlFlow> { let key = BindingKey::new(ident, ns); - // `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding - // doesn't need to be mutable. It will fail when there is a cycle of imports, and without - // the exclusive access infinite recursion will crash the compiler with stack overflow. - let resolution = &*self - .resolution_or_default(module.to_module(), key, orig_ident_span) - .try_borrow_mut_unchecked() - .map_err(|_| ControlFlow::Continue(Determined))?; + let resolution = self.resolution(module.to_module(), key); - let binding = resolution.glob_decl.filter(|b| Some(*b) != ignore_decl); + let binding = + resolution.as_ref().and_then(|r| r.glob_decl).filter(|b| Some(*b) != ignore_decl); if let Some(finalize) = finalize { return self.get_mut().finalize_module_binding( @@ -1182,17 +1228,24 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ); } + // We need to detect resolution cycles to avoid infinite recursion. The guard ensures + // the resolution is removed when this resolve call ends. + let _cycle_guard = cycle_detection::enter_cycle_detector(module, key) + .map_err(|_| ControlFlow::Continue(Determined))?; + // Check if one of single imports can still define the name, // if it can then our result is not determined and can be invalidated. - if self.reborrow().single_import_can_define_name( - &resolution, - binding, - ns, - ignore_import, - ignore_decl, - parent_scope, - ) { - return Err(ControlFlow::Break(Undetermined)); + if let Some(resolution) = resolution { + if self.reborrow().single_import_can_define_name( + &resolution, + binding, + ns, + ignore_import, + ignore_decl, + parent_scope, + ) { + return Err(ControlFlow::Break(Undetermined)); + } } // So we have a resolution that's from a glob import. This resolution is determined @@ -1535,7 +1588,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } RibKind::ConstParamTy => { - if !self.tcx.features().generic_const_parameter_types() { + if !self.features.generic_const_parameter_types() { if let Some(span) = finalize { self.report_error( span, @@ -1564,7 +1617,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } NoConstantGenericsReason::NonTrivialConstArg => { ResolutionError::ParamInNonTrivialAnonConst { - is_gca: self.tcx.features().generic_const_args(), + is_gca: self.features.generic_const_args(), name: rib_ident.name, param_kind: ParamKindInNonTrivialAnonConst::Type, } @@ -1629,7 +1682,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | RibKind::ForwardGenericParamBan(_) => continue, RibKind::ConstParamTy => { - if !self.tcx.features().generic_const_parameter_types() { + if !self.features.generic_const_parameter_types() { if let Some(span) = finalize { self.report_error( span, @@ -1656,7 +1709,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } NoConstantGenericsReason::NonTrivialConstArg => { ResolutionError::ParamInNonTrivialAnonConst { - is_gca: self.tcx.features().generic_const_args(), + is_gca: self.features.generic_const_args(), name: rib_ident.name, param_kind: ParamKindInNonTrivialAnonConst::Const { name: rib_ident.name, @@ -2019,7 +2072,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module, || { let import_inherent_item_error_flag = - self.tcx.features().import_trait_associated_functions() + self.features.import_trait_associated_functions() && matches!( res, Res::Def( diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index d823da9756c81..c4d668edefa32 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -32,7 +32,7 @@ use crate::diagnostics::{ ConsiderMarkingAsPubCrate, }; use crate::error_helper::{OnUnknownData, Suggestion}; -use crate::ref_mut::CmCell; +use crate::ref_mut::{CmCell, CmRefCell}; use crate::{ AmbiguityError, BindingKey, CmResolver, Decl, DeclData, DeclKind, Determinacy, Finalize, IdentKey, ImportSuggestion, ImportSummary, LocalModule, ModuleOrUniformRoot, ParentScope, @@ -69,7 +69,6 @@ impl<'ra> PendingDecl<'ra> { } /// Contains data for specific kinds of imports. -#[derive(Clone)] pub(crate) enum ImportKind<'ra> { Single { /// `source` in `use prefix::source as target`. @@ -157,7 +156,7 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { } /// One import. -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct ImportData<'ra> { pub kind: ImportKind<'ra>, @@ -280,7 +279,7 @@ impl<'ra> ImportData<'ra> { } /// Records information about the resolution of a name in a namespace of a module. -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) struct NameResolution<'ra> { /// Single imports that may define the name in the namespace. /// Imports are arena-allocated, so it's ok to use pointers as keys. @@ -292,6 +291,8 @@ pub(crate) struct NameResolution<'ra> { pub orig_ident_span: Span, } +pub(crate) type NameResolutionRef<'ra> = Interned<'ra, CmRefCell>>; + impl<'ra> NameResolution<'ra> { pub(crate) fn new(orig_ident_span: Span) -> Self { NameResolution { single_imports: FxIndexSet::default(), orig_ident_span, .. } @@ -320,9 +321,62 @@ impl<'ra> NameResolution<'ra> { } } +// module to keep the TLS private and only accessible through the function `enter_cycle_detector`. +pub(crate) mod cycle_detection { + use std::ptr; + + use crate::{BindingKey, CacheRefCell, LocalModule}; + + thread_local!( + /// During import resolution, recursive imports can form cycles. + /// This set stores the active resolution stack for the current thread. + /// By keeping track of the module and `BindingKey` pair that identifies + /// the specific resolution. + /// + /// The pointer is the interned address of a `Interned<'ra, ModuleData>` allocated + /// in the `Resolver Arenas` (lifetime `'ra`), it is thus stable and allows casting + /// to a `*const ()` for comparison. This is done because we can't use lifetimes + /// other than `'static` in thread local storage. + static ACTIVE_RESOLUTIONS: CacheRefCell> = Default::default(); + ); + + pub(crate) struct ActiveResolutionGuard { + key: (*const (), BindingKey), + } + + impl Drop for ActiveResolutionGuard { + fn drop(&mut self) { + ACTIVE_RESOLUTIONS.with_borrow_mut(|ar| { + // Only this guard is allowed to remove this key. + assert!( + Some(self.key) == ar.pop(), + "This guard should be the only one removing this key" + ); + }); + } + } + + /// Returns `Err(())` if a cycle is detected, otherwise this returns a + /// guard that will remove the resolution when dropped. + pub(crate) fn enter_cycle_detector<'ra>( + module: LocalModule<'ra>, + binding_key: BindingKey, + ) -> Result { + let module_key = ptr::from_ref(module.0.0).cast(); + let key = (module_key, binding_key); + ACTIVE_RESOLUTIONS.with_borrow_mut(|ar| { + if ar.contains(&key) { + return Err(()); + } + ar.push(key); + Ok(ActiveResolutionGuard { key }) + }) + } +} + /// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved /// import errors within the same use tree into a single diagnostic. -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct UnresolvedImportError { pub(crate) span: Span, pub(crate) label: Option, @@ -341,7 +395,7 @@ fn pub_use_of_private_extern_crate_hack( import: ImportSummary, decl: Decl<'_>, ) -> Option { - match (import.is_single, decl.kind) { + match (import.is_single, &decl.kind) { (true, DeclKind::Import { import: decl_import, .. }) if let ImportKind::ExternCrate { def_id, .. } = decl_import.kind && import.vis.is_public() => @@ -640,6 +694,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (binding, t) = { let resolution = &mut *self .resolution_or_default(module.to_module(), key, orig_ident_span) + .0 .borrow_mut(self); let old_decl = resolution.determined_decl(); let old_vis = old_decl.map(|d| d.vis()); @@ -779,7 +834,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match import_decls[ns] { PendingDecl::Ready(Some(import_decl)) => { if import_decl.is_assoc_item() - && !this.tcx.features().import_trait_associated_functions() + && !this.features.import_trait_associated_functions() { feature_err( this.tcx.sess, @@ -822,8 +877,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { continue; }; - if module.is_trait() && !self.tcx.features().import_trait_associated_functions() - { + if module.is_trait() && !self.features.import_trait_associated_functions() { feature_err( self.tcx.sess, sym::import_trait_associated_functions, @@ -1487,7 +1541,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // If importing of trait asscoiated items is enabled, an also find an // `Enum`, then note that inherent associated items cannot be imported. - let note = if self.tcx.features().import_trait_associated_functions() + let note = if self.features.import_trait_associated_functions() && let PathResult::Module(ModuleOrUniformRoot::Module(m)) = path_res && let Some(Res::Def(DefKind::Enum, _)) = m.res() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bf53b891a08f1..c94f2f3ffb85d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1664,7 +1664,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }; // We'll ban these with a `ConstParamTy` rib, so just clear these ribs for better // diagnostics, so we don't mention anything about const param tys having generics at all. - if !self.r.tcx.features().generic_const_parameter_types() { + if !self.r.features.generic_const_parameter_types() { forward_ty_ban_rib_const_param_ty.bindings.clear(); forward_const_ban_rib_const_param_ty.bindings.clear(); } @@ -1701,7 +1701,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { this.ribs[TypeNS].push(forward_ty_ban_rib_const_param_ty); this.ribs[ValueNS].push(forward_const_ban_rib_const_param_ty); - if this.r.tcx.features().generic_const_parameter_types() { + if this.r.features.generic_const_parameter_types() { this.visit_ty(ty) } else { this.ribs[TypeNS].push(Rib::new(RibKind::ConstParamTy)); @@ -1812,8 +1812,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } LifetimeRibKind::ImplTrait => { - if self.r.tcx.features().anonymous_lifetime_in_impl_trait() - { + if self.r.features.anonymous_lifetime_in_impl_trait() { None } else { Some(LifetimeUseSet::Many) @@ -2991,7 +2990,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.with_generic_param_rib( &generics.params, RibKind::Item( - if self.r.tcx.features().generic_const_items() { + if self.r.features.generic_const_items() { HasGenericParams::Yes(generics.span) } else { HasGenericParams::No @@ -3008,7 +3007,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { LifetimeRibKind::Elided(LifetimeRes::Static), |this| { if rhs_kind.is_type_const() - && !this.r.tcx.features().generic_const_parameter_types() + && !this.r.features.generic_const_parameter_types() { this.with_rib(TypeNS, RibKind::ConstParamTy, |this| { this.with_rib(ValueNS, RibKind::ConstParamTy, |this| { @@ -3255,7 +3254,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { RibKind::Normal => { // FIXME(non_lifetime_binders): Stop special-casing // const params to error out here. - if self.r.tcx.features().non_lifetime_binders() + if self.r.features.non_lifetime_binders() && matches!(param.kind, GenericParamKind::Type { .. }) { Res::Def(def_kind, def_id.to_def_id()) @@ -3409,7 +3408,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { |this| { this.visit_generics(generics); if rhs_kind.is_type_const() - && !this.r.tcx.features().generic_const_parameter_types() + && !this.r.features.generic_const_parameter_types() { this.with_rib(TypeNS, RibKind::ConstParamTy, |this| { this.with_rib(ValueNS, RibKind::ConstParamTy, |this| { @@ -5038,10 +5037,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let tcx = self.r.tcx(); let gate_err_sym_msg = match prim { - PrimTy::Float(FloatTy::F16) if !tcx.features().f16() => { + PrimTy::Float(FloatTy::F16) if !self.r.features.f16() => { Some((sym::f16, "the type `f16` is unstable")) } - PrimTy::Float(FloatTy::F128) if !tcx.features().f128() => { + PrimTy::Float(FloatTy::F128) if !self.r.features.f128() => { Some((sym::f128, "the type `f128` is unstable")) } _ => None, @@ -5196,8 +5195,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { AnonConstKind::FieldDefaultValue => ConstantHasGenerics::Yes, AnonConstKind::InlineConst => ConstantHasGenerics::Yes, AnonConstKind::ConstArg(_) => { - if self.r.tcx.features().generic_const_exprs() - || self.r.tcx.features().min_generic_const_args() + if self.r.features.generic_const_exprs() + || self.r.features.min_generic_const_args() || is_trivial_const_arg { ConstantHasGenerics::Yes @@ -5624,12 +5623,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of -/// lifetime generic parameters and function parameters. -struct ItemInfoCollector<'a, 'ra, 'tcx> { +/// lifetime generic parameters and function parameters. Also collects all `use` and +/// `extern crate` items so that `check_unused` doesn't need to walk the crate again. +struct ItemInfoCollector<'a, 'ast, 'ra, 'tcx> { r: &'a mut Resolver<'ra, 'tcx>, + /// All `use` and `extern crate` items, in the order in which they are visited. + use_items: Vec<&'ast Item>, } -impl ItemInfoCollector<'_, '_, '_> { +impl ItemInfoCollector<'_, '_, '_, '_> { fn collect_fn_info(&mut self, decl: &FnDecl, id: NodeId) { self.r .delegation_fn_sigs @@ -5663,7 +5665,7 @@ fn required_generic_args_suggestion(generics: &ast::Generics) -> Option if required.is_empty() { None } else { Some(format!("<{}>", required.join(", "))) } } -impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { +impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, 'ast, '_, '_> { fn visit_item(&mut self, item: &'ast Item) { match &item.kind { ItemKind::TyAlias(TyAlias { generics, .. }) @@ -5696,11 +5698,13 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { } } + ItemKind::Use(..) | ItemKind::ExternCrate(..) => { + self.use_items.push(item); + } + ItemKind::Mod(..) | ItemKind::Static(..) | ItemKind::ConstBlock(..) - | ItemKind::Use(..) - | ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(..) | ItemKind::MacCall(..) @@ -5731,9 +5735,12 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { } impl<'ra, 'tcx> Resolver<'ra, 'tcx> { - pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { + /// Returns the `use` and `extern crate` items of the crate, for use by `check_unused`. + pub(crate) fn late_resolve_crate<'ast>(&mut self, krate: &'ast Crate) -> Vec<&'ast Item> { with_owner(self, CRATE_NODE_ID, |this| { - visit::walk_crate(&mut ItemInfoCollector { r: this }, krate); + let mut info_collector = ItemInfoCollector { r: this, use_items: Vec::new() }; + visit::walk_crate(&mut info_collector, krate); + let use_items = info_collector.use_items; let mut late_resolution_visitor = LateResolutionVisitor::new(this); late_resolution_visitor .resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); @@ -5746,6 +5753,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { crate::diagnostics::UnusedLabel, ); } + use_items }) } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 95fdf8124a225..8c5c7f5b2d76d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -133,7 +133,7 @@ pub(super) struct MissingLifetime { /// Description of the lifetimes appearing in a function parameter. /// This is used to provide a literal explanation to the elision failure. -#[derive(Clone, Debug)] +#[derive(Debug)] pub(super) struct ElisionFnParameter { /// The index of the argument in the original definition. pub index: usize, @@ -1775,8 +1775,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the // benefits of including them here outweighs the small number of false positives. Some(Res::Def(DefKind::Struct | DefKind::Enum, _)) - if self.r.tcx.features().adt_const_params() - || self.r.tcx.features().min_adt_const_params() => + if self.r.features.adt_const_params() || self.r.features.min_adt_const_params() => { Applicability::MaybeIncorrect } @@ -3977,7 +3976,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }) .emit(), NoConstantGenericsReason::NonTrivialConstArg => { - assert!(!self.r.tcx.features().generic_const_exprs()); + assert!(!self.r.features.generic_const_exprs()); self.r .dcx() .create_err(diagnostics::ParamInNonTrivialAnonConst { @@ -3985,8 +3984,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { name: lifetime_ref.ident.name, param_kind: diagnostics::ParamKindInNonTrivialAnonConst::Lifetime, help: self.r.tcx.sess.is_nightly_build(), - is_gca: self.r.tcx.features().generic_const_args(), - help_gca: self.r.tcx.features().generic_const_args(), + is_gca: self.r.features.generic_const_args(), + help_gca: self.r.features.generic_const_args(), }) .emit() } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 061471ccc97e0..3b93f2d1fd130 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -50,7 +50,7 @@ use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard}; use rustc_data_structures::unord::{UnordItems, UnordMap, UnordSet}; use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; -use rustc_feature::BUILTIN_ATTRIBUTES; +use rustc_feature::{BUILTIN_ATTRIBUTES, Features}; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{ @@ -77,6 +77,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; use crate::error_helper::OnUnknownData; +use crate::imports::NameResolutionRef; use crate::ref_mut::{CmCell, CmRefCell}; mod build_reduced_graph; @@ -186,7 +187,6 @@ struct InvocationParent { parent_def: LocalDefId, impl_trait_context: ImplTraitContext, in_attr: bool, - const_arg_context: ConstArgContext, owner: NodeId, } @@ -195,7 +195,6 @@ impl InvocationParent { parent_def: CRATE_DEF_ID, impl_trait_context: ImplTraitContext::Existential, in_attr: false, - const_arg_context: ConstArgContext::NonDirect, owner: CRATE_NODE_ID, }; } @@ -207,13 +206,6 @@ enum ImplTraitContext { InBinding, } -#[derive(Copy, Clone, Debug)] -enum ConstArgContext { - Direct, - /// Either inside of an `AnonConst` or not inside a const argument at all. - NonDirect, -} - /// Used for tracking import use types which will be used for redundant import checking. /// /// ### Used::Scope Example @@ -639,7 +631,7 @@ impl BindingKey { } } -type Resolutions<'ra> = CmRefCell>>>; +type Resolutions<'ra> = CmRefCell>>; /// One node in the tree of modules. /// @@ -992,7 +984,7 @@ impl<'ra> fmt::Debug for LocalModule<'ra> { } /// Data associated with any name declaration. -#[derive(Clone, Debug)] +#[derive(Debug)] struct DeclData<'ra> { kind: DeclKind<'ra>, ambiguity: CmCell, bool /*warning*/)>>, @@ -1026,7 +1018,7 @@ impl std::hash::Hash for DeclData<'_> { } /// Name declaration kind. -#[derive(Clone, Copy, Debug)] +#[derive(Debug)] enum DeclKind<'ra> { /// The name declaration is a definition (possibly without a `DefId`), /// can be provided by source code or built into the language. @@ -1550,7 +1542,8 @@ pub struct Resolver<'ra, 'tcx> { impl_trait_names: FxHashMap = default::fx_hash_map(), /// Stores `#[diagnostic::on_unknown]` attributes placed on module declarations. - on_unknown_data: FxHashMap, + on_unknown_data: FxHashMap = default::fx_hash_map(), + features: &'tcx Features, } /// This provides memory for the rest of the crate. The `'ra` lifetime that is @@ -1596,11 +1589,10 @@ impl<'ra> ResolverArenas<'ra> { fn alloc_import(&'ra self, import: ImportData<'ra>) -> Import<'ra> { Interned::new_unchecked(self.imports.alloc(import)) } - fn alloc_name_resolution( - &'ra self, - orig_ident_span: Span, - ) -> &'ra CmRefCell> { - self.name_resolutions.alloc(CmRefCell::new(NameResolution::new(orig_ident_span))) + fn alloc_name_resolution(&'ra self, orig_ident_span: Span) -> NameResolutionRef<'ra> { + Interned::new_unchecked( + self.name_resolutions.alloc(CmRefCell::new(NameResolution::new(orig_ident_span))), + ) } fn alloc_macro_rules_scope(&'ra self, scope: MacroRulesScope<'ra>) -> MacroRulesScopeRef<'ra> { self.dropless.alloc(CacheCell::new(scope)) @@ -1808,11 +1800,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let registered_tools = tcx.registered_tools(()); let edition = tcx.sess.edition(); - let mut on_unknown_data = default::fx_hash_map(); - if let Some(directive) = OnUnknownData::from_attrs(tcx, attrs) { - on_unknown_data.insert(CRATE_DEF_ID, directive); - } - let mut resolver = Resolver { tcx, @@ -1880,10 +1867,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { current_crate_outer_attr_insert_span, disambiguators: Default::default(), delegation_infos: Default::default(), - on_unknown_data, + features: tcx.features(), .. }; + if let Some(directive) = OnUnknownData::from_attrs(&resolver, attrs) { + resolver.on_unknown_data.insert(CRATE_DEF_ID, directive); + } + let root_parent_scope = ParentScope::module(graph_root, resolver.arenas); resolver.invocation_parent_scopes.insert(LocalExpnId::ROOT, root_parent_scope); resolver.feed_visibility(crate_feed, Visibility::Public); @@ -2082,17 +2073,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.tcx .sess .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate)); - self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); + let use_items = + self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); self.tcx.sess.time("resolve_main", || self.resolve_main()); - self.tcx.sess.time("resolve_check_unused", || self.check_unused(krate)); + self.tcx.sess.time("resolve_check_unused", || self.check_unused(use_items)); self.tcx.sess.time("resolve_report_errors", || self.report_errors(krate)); self.tcx .sess .time("resolve_postprocess", || self.cstore_mut().postprocess(self.tcx, krate)); }); - // Make sure we don't mutate the cstore from here on. - self.tcx.untracked().cstore.freeze(); + // Don't mutate the cstore or stable crate id map from here on. + self.tcx.untracked().freeze_cstore(); } fn traits_in_scope( @@ -2212,7 +2204,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module: Module<'ra>, key: BindingKey, ) -> Option>> { - self.resolutions(module).borrow().get(&key).map(|resolution| resolution.borrow()) + self.resolutions(module).borrow().get(&key).map(|resolution| resolution.0.borrow()) } fn resolution_or_default( @@ -2220,8 +2212,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module: Module<'ra>, key: BindingKey, orig_ident_span: Span, - ) -> &'ra CmRefCell> { - self.resolutions(module) + ) -> NameResolutionRef<'ra> { + *self + .resolutions(module) .borrow_mut_unchecked() .entry(key) .or_insert_with(|| self.arenas.alloc_name_resolution(orig_ident_span)) @@ -2832,8 +2825,6 @@ type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; // parallel name resolution. use std::cell::{Cell as CacheCell, RefCell as CacheRefCell}; -// FIXME: `*_unchecked` methods in the module below should be eliminated in the process -// of migration to parallel name resolution. mod ref_mut { use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut}; use std::fmt; @@ -2941,6 +2932,8 @@ mod ref_mut { } #[track_caller] + // FIXME: this should be eliminated in the process of migration + // to parallel name resolution. pub(crate) fn borrow_mut_unchecked(&self) -> RefMut<'_, T> { self.0.borrow_mut() } @@ -2953,10 +2946,6 @@ mod ref_mut { self.0.borrow_mut() } - pub(crate) fn try_borrow_mut_unchecked(&self) -> Result, BorrowMutError> { - self.0.try_borrow_mut() - } - #[track_caller] pub(crate) fn try_borrow_mut<'ra, 'tcx>( &self, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index fade23f9cf308..595c2fdf011dd 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -699,7 +699,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // We are trying to avoid reporting this error if other related errors were reported. - if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes() { + if res != Res::Err && inner_attr && !self.features.custom_inner_attributes() { let is_macro = match res { Res::Def(..) => true, Res::NonMacroAttr(..) => false, @@ -727,8 +727,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic && !DIAGNOSTIC_ATTRIBUTES.iter().any(|(attr, feature)| { - attribute.ident.name == *attr - && feature.is_none_or(|f| self.tcx.features().enabled(f)) + attribute.ident.name == *attr && feature.is_none_or(|f| self.features.enabled(f)) }) { let name = attribute.ident.name; @@ -750,7 +749,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let candidates = DIAGNOSTIC_ATTRIBUTES .iter() .filter_map(|(attr, feature)| { - feature.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*attr) + feature.is_none_or(|f| self.features.enabled(f)).then_some(*attr) }) .collect::>(); @@ -1112,7 +1111,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let feature = stability.feature; let is_allowed = - |feature| self.tcx.features().enabled(feature) || span.allows_unstable(feature); + |feature| self.features.enabled(feature) || span.allows_unstable(feature); let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); if !is_allowed(feature) && !allowed_by_implication { stability::report_unstable( @@ -1242,7 +1241,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> SyntaxExtension { let mut ext = compile_declarative_macro( self.tcx.sess, - self.tcx.features(), + self.features, macro_def, ident, attrs, diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml index 9069d2c233db7..8eff14d0cfcfa 100644 --- a/compiler/rustc_sanitizers/Cargo.toml +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -14,5 +14,5 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } tracing = "0.1" -twox-hash = "1.6.3" +twox-hash = { version = "1.6.3", default-features = false } # tidy-alphabetical-end diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 0810e327b63e3..6b3554331b420 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -252,9 +252,12 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc ); let term = tcx.normalize_erasing_regions( ty::TypingEnv::fully_monomorphized(), - Unnormalized::new_wip(projection_term.to_term(tcx)), + Unnormalized::new_wip(projection_term.to_term(tcx, ty::IsRigid::No)), + ); + debug!( + "Projection {:?} -> {term}", + projection_term.to_term(tcx, ty::IsRigid::No) ); - debug!("Projection {:?} -> {term}", projection_term.to_term(tcx),); ty::ExistentialPredicate::Projection( ty::ExistentialProjection::erase_self_ty( tcx, @@ -310,7 +313,7 @@ pub(crate) fn transform_instance<'tcx>( // FIXME: account for async-drop-glue if (matches!(instance.def, ty::InstanceKind::Virtual(..)) && tcx.is_lang_item(instance.def_id(), LangItem::DropGlue)) - || matches!(instance.def, ty::InstanceKind::DropGlue(..)) + || matches!(instance.def, ty::InstanceKind::Shim(ty::ShimKind::DropGlue(..))) { // Adjust the type ids of DropGlues // @@ -364,7 +367,7 @@ pub(crate) fn transform_instance<'tcx>( tcx.types.unit }; instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1)); - } else if let ty::InstanceKind::VTableShim(def_id) = instance.def + } else if let ty::InstanceKind::Shim(ty::ShimKind::VTable(def_id)) = instance.def && let Some(trait_id) = tcx.trait_of_assoc(def_id) { // Adjust the type ids of VTableShims to the type id expected in the call sites for the @@ -461,7 +464,7 @@ pub(crate) fn transform_instance<'tcx>( fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option { match instance.def { - ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => { + ty::InstanceKind::Item(def_id) | ty::InstanceKind::Shim(ty::ShimKind::FnPtr(def_id, _)) => { tcx.opt_associated_item(def_id).map(|item| item.def_id) } _ => None, @@ -506,7 +509,7 @@ fn implemented_method<'tcx>( trait_method = assoc; method_id = trait_method_def_id; trait_id = tcx.parent(method_id); - trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args)); + trait_ref = ty::EarlyBinder::bind(tcx, TraitRef::from_assoc(tcx, trait_id, instance.args)); trait_id } else { return None; diff --git a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs index 3243e23fcf981..23aa088c68ffa 100644 --- a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs +++ b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs @@ -6,7 +6,7 @@ use std::hash::Hasher; -use rustc_middle::ty::{Instance, InstanceKind, ReifyReason, Ty, TyCtxt}; +use rustc_middle::ty::{Instance, InstanceKind, ReifyReason, ShimKind, Ty, TyCtxt}; use rustc_target::callconv::FnAbi; use twox_hash::XxHash64; @@ -46,7 +46,7 @@ pub fn typeid_for_instance<'tcx>( // // This was implemented for KCFI support in #123106 and #123052 (which introduced the // ReifyReason). The tracking issue for KCFI support for Rust is #123479. - if matches!(instance.def, InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr))) { + if matches!(instance.def, InstanceKind::Shim(ShimKind::Reify(_, Some(ReifyReason::FnPtr)))) { options.insert(TypeIdOptions::USE_CONCRETE_SELF); } // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 089a9322e6a3c..4492cdf2d2f8a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -255,6 +255,17 @@ pub enum AnnotateMoves { Enabled(Option), } +/// The different settings that the `-Z Instrument-mcount` flag can have. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub enum InstrumentMcount { + /// `-Z instrument-mcount=no` + Disabled, + /// `-Z instrument-mcount=yes` + Mcount, + /// `-Z instrument-mcount=fentry` + Fentry, +} + /// Settings for `-Z instrument-xray` flag. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] pub struct InstrumentXRay { @@ -3085,11 +3096,11 @@ pub(crate) mod dep_tracking { use super::{ AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CodegenRetagOptions, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, - FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, - LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, - OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks, - SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, - WasiExecModel, + FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentMcount, InstrumentXRay, + LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, + OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, + ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, + SymbolManglingVersion, WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3151,6 +3162,7 @@ pub(crate) mod dep_tracking { TlsModel, InstrumentCoverage, CoverageOptions, + InstrumentMcount, InstrumentXRay, CrateType, MergeFunctions, @@ -3330,23 +3342,29 @@ impl DumpMonoStatsFormat { /// `-Z patchable-function-entry` representation - how many nops to put before and after function /// entry. -#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] +#[derive(Clone, PartialEq, Hash, Debug, Default)] pub struct PatchableFunctionEntry { /// Nops before the entry prefix: u8, /// Nops after the entry entry: u8, + /// An optional section name to record the entry location + section: Option, } impl PatchableFunctionEntry { - pub fn from_total_and_prefix_nops( + pub fn from_parts( total_nops: u8, prefix_nops: u8, + section: Option, ) -> Option { if total_nops < prefix_nops { None + // Section name cannot contain null characters. + } else if section.as_ref().map(|x| x.contains('\0') || x.is_empty()).unwrap_or(false) { + None } else { - Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops }) + Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops, section }) } } pub fn prefix(&self) -> u8 { @@ -3355,6 +3373,9 @@ impl PatchableFunctionEntry { pub fn entry(&self) -> u8 { self.entry } + pub fn section(&self) -> Option<&str> { + self.section.as_ref().map(|x| x.as_str()) + } } /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 39fe9c80923ec..94237132bd393 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -224,3 +224,13 @@ pub struct Untracked { /// The interned [StableCrateId]s. pub stable_crate_ids: FreezeLock, } + +impl Untracked { + /// Freezes the cstore and, with it, the `StableCrateId` map, making reads of + /// both lock-free. The cstore is frozen first so any in-flight crate loading + /// (which writes the map) finishes before the map is frozen. + pub fn freeze_cstore(&self) { + self.cstore.freeze(); + self.stable_crate_ids.freeze(); + } +} diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 41b35f5700eca..31627887b662b 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -311,6 +311,12 @@ pub(crate) struct CannotMixAndMatchSanitizers { )] pub(crate) struct CannotEnableCrtStaticLinux; +#[derive(Diagnostic)] +#[diag( + "pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static`" +)] +pub(crate) struct CannotEnableCrtStaticPointerAuth; + #[derive(Diagnostic)] #[diag("`-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`")] pub(crate) struct SanitizerCfiRequiresLto; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 897aa91f7dbda..4e5b38d34b824 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -784,7 +784,7 @@ mod desc { pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; - pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; + pub(crate) const parse_patchable_function_entry: &str = "a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops"; pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `kernel-hwaddress`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`, or 'realtime'"; @@ -804,6 +804,8 @@ mod desc { pub(crate) const parse_coverage_options: &str = "`block` | `branch` | `condition`"; pub(crate) const parse_codegen_retag_options: &str = "either no value or a comma-separated list of settings: `no-precise-im`, `no-precise-pin`"; + pub(crate) const parse_instrument_mcount: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), or `fentry` on supported targets."; pub(crate) const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub(crate) const parse_unpretty: &str = "`string` or `string=string`"; pub(crate) const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; @@ -1204,20 +1206,24 @@ pub mod parse { ) -> bool { let mut total_nops = 0; let mut prefix_nops = 0; + let mut section = None; if !parse_number(&mut total_nops, v) { - let parts = v.and_then(|v| v.split_once(',')).unzip(); - if !parse_number(&mut total_nops, parts.0) { + let parts: Vec<_> = v.unwrap_or("").split(',').collect(); + if parts.len() < 2 || parts.len() > 3 { return false; } - if !parse_number(&mut prefix_nops, parts.1) { + + if !parse_number(&mut total_nops, Some(parts[0])) { + return false; + } + if !parse_number(&mut prefix_nops, Some(parts[1])) { return false; } + section = parts.get(2).map(|x| x.to_string()); } - if let Some(pfe) = - PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops) - { + if let Some(pfe) = PatchableFunctionEntry::from_parts(total_nops, prefix_nops, section) { *slot = pfe; return true; } @@ -1583,6 +1589,19 @@ pub mod parse { true } + pub(crate) fn parse_instrument_mcount(slot: &mut InstrumentMcount, v: Option<&str>) -> bool { + let mut use_mcount = false; + if parse_bool(&mut use_mcount, v) { + *slot = if use_mcount { InstrumentMcount::Mcount } else { InstrumentMcount::Disabled }; + true + } else if let Some("fentry") = v { + *slot = InstrumentMcount::Fentry; + true + } else { + false + } + } + pub(crate) fn parse_instrument_xray( slot: &mut Option, v: Option<&str>, @@ -2453,7 +2472,7 @@ options! { "a default MIR inlining threshold (default: 50)"), input_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR (default: no)"), - instrument_mcount: bool = (false, parse_bool, [TRACKED], + instrument_mcount: InstrumentMcount = (InstrumentMcount::Disabled, parse_instrument_mcount, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), instrument_xray: Option = (None, parse_instrument_xray, [TRACKED], "insert function instrument code for XRay-based tracing (default: no) @@ -2635,6 +2654,7 @@ options! { "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), + ptrauth_elf_got: bool = (false, parse_bool, [TRACKED], "enable signing of ELF GOT entries"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], @@ -2654,6 +2674,8 @@ options! { remark_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], "directory into which to write optimization remarks (if not specified, they will be \ written to standard error output)"), + renormalize_rigid_aliases: bool = (false, parse_bool, [TRACKED], + "do not skip rigid aliases in normalization for internal debugging"), retpoline: bool = (false, parse_bool, [TRACKED] { TARGET_MODIFIER: Retpoline }, "enables retpoline-indirect-branches and retpoline-indirect-calls target features (default: no)"), retpoline_external_thunk: bool = (false, parse_bool, [TRACKED] { TARGET_MODIFIER: RetpolineExternalThunk }, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 15a87771056c4..986dababf770a 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -29,16 +29,16 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{RealFileName, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ - Arch, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, - SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, - TargetTuple, TlsModel, apple, + Arch, CfgAbi, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, + SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, + Target, TargetTuple, TlsModel, apple, }; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use crate::config::{ self, Cfg, CheckCfg, CoverageLevel, CoverageOptions, CrateType, DebugInfo, ErrorOutputType, - FunctionReturn, Input, InstrumentCoverage, OptLevel, OutFileName, OutputType, + FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, OptLevel, OutFileName, OutputType, SwitchWithOptPath, }; use crate::filesearch::FileSearch; @@ -1229,6 +1229,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::CannotEnableCrtStaticLinux); } + // FIXME(jchlanda) Pauthtest does not support static linking. It must be dynamically linked, + // with a dynamic linker acting as the ELF interpreter that can resolve pauth relocations and + // enforce pointer authentication constraints. + if sess.crt_static(None) && sess.target.cfg_abi == CfgAbi::Pauthtest { + sess.dcx().emit_err(errors::CannotEnableCrtStaticPointerAuth); + } + // LLVM CFI requires LTO. if sess.is_sanitizer_cfi_enabled() && !(sess.lto() == config::Lto::Fat || sess.opts.cg.linker_plugin_lto.enabled()) @@ -1340,6 +1347,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } + if sess.opts.unstable_opts.instrument_mcount == InstrumentMcount::Fentry + && !sess.target.options.supports_fentry + { + sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "fentry".to_string() }); + } + if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray { sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 827e33dcd6632..71eae246ebaff 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -515,6 +515,7 @@ symbols! { await_macro, backchain, backend_repr, + bcmp_fn, begin_panic, bench, bevy_ecs, @@ -1141,6 +1142,7 @@ symbols! { irrefutable_let_patterns, is, is_auto, + is_splatted, is_val_statically_known, isa_attribute, isize, @@ -1168,6 +1170,7 @@ symbols! { lang_items, large_assignments, last, + lasx, lateout, lazy_normalization_consts, lazy_type_alias, @@ -1225,6 +1228,7 @@ symbols! { loop_hints, loop_match, lr, + lsx, lt, m68k, m68k_target_feature, @@ -1280,7 +1284,11 @@ symbols! { mem_variant_count, mem_zeroed, member_constraints, + memcmp_fn, + memcpy_fn, + memmove_fn, memory, + memset_fn, memtag, message, meta, @@ -1868,6 +1876,7 @@ symbols! { saturating_sub, sdylib, search_unbox, + section, select_unpredictable, self_in_typedefs, self_struct_ctor, @@ -1996,6 +2005,7 @@ symbols! { speed, spirv, splat, + splatted_index, spotlight, sqrtf16, sqrtf32, @@ -2036,6 +2046,7 @@ symbols! { strict_provenance_lints, string_deref_patterns, stringify, + strlen_fn, struct_field_attributes, struct_inherit, struct_variant, @@ -2365,6 +2376,7 @@ symbols! { xloop, xmm_reg, xop_target_feature, + xreg, xtensa, xtensa_target_feature, yeet_desugar_details, diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index ffb00632ea4f4..9f9c30abe2c1b 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -118,7 +118,7 @@ impl<'tcx> AbiStableHash<'tcx> for Ty<'tcx> { | ty::CoroutineWitness(_, _) | ty::Never | ty::Tuple(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Bound(_, _) | ty::Placeholder(_) diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index c13300a735c3c..5e021eb5667d4 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -62,13 +62,13 @@ pub(super) fn mangle<'tcx>( let mut p = LegacySymbolMangler { tcx, path: SymbolPath::new(), keep_within_component: false }; p.print_def_path( def_id, - if let ty::InstanceKind::DropGlue(_, _) - | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) - | ty::InstanceKind::FutureDropPollShim(_, _, _) = instance.def + if let ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, _)) + | ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(_, _)) + | ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(_, _, _)) = instance.def { // Add the name of the dropped type to the symbol name &*instance.args - } else if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def { + } else if let ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(_, ty)) = instance.def { let ty::Coroutine(_, cor_args) = ty.kind() else { bug!(); }; @@ -81,13 +81,13 @@ pub(super) fn mangle<'tcx>( .unwrap(); match instance.def { - ty::InstanceKind::ThreadLocalShim(..) => { + ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(..)) => { p.write_str("{{tls-shim}}").unwrap(); } - ty::InstanceKind::VTableShim(..) => { + ty::InstanceKind::Shim(ty::ShimKind::VTable(..)) => { p.write_str("{{vtable-shim}}").unwrap(); } - ty::InstanceKind::ReifyShim(_, reason) => { + ty::InstanceKind::Shim(ty::ShimKind::Reify(_, reason)) => { p.write_str("{{reify-shim").unwrap(); match reason { Some(ReifyReason::FnPtr) => p.write_str("-fnptr").unwrap(), @@ -98,14 +98,17 @@ pub(super) fn mangle<'tcx>( } // FIXME(async_closures): This shouldn't be needed when we fix // `Instance::ty`/`Instance::def_id`. - ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref, .. } => { + ty::InstanceKind::Shim(ty::ShimKind::ConstructCoroutineInClosure { + receiver_by_ref, + .. + }) => { p.write_str(if receiver_by_ref { "{{by-move-shim}}" } else { "{{by-ref-shim}}" }) .unwrap(); } _ => {} } - if let ty::InstanceKind::FutureDropPollShim(..) = instance.def { + if let ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(..)) = instance.def { let _ = p.write_str("{{drop-shim}}"); } @@ -243,11 +246,12 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { match *ty.kind() { // Print all nominal types as paths (unlike `pretty_print_type`). ty::FnDef(def_id, args) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { def_id } | ty::Opaque { def_id }, - args, - .. - }) + | ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { def_id } | ty::Opaque { def_id }, args, .. + }, + ) | ty::Closure(def_id, args) | ty::CoroutineClosure(def_id, args) | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args), @@ -269,7 +273,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { Ok(()) } - ty::Alias(ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { panic!("unexpected inherent projection") } diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index c3b02eb46ba1e..d61437212a266 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -92,7 +92,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mono::{InstantiationMode, MonoItem}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_middle::ty::{self, Instance, InstanceKind, TyCtxt}; use rustc_session::config::SymbolManglingVersion; use tracing::debug; @@ -149,29 +149,22 @@ pub fn typeid_for_trait_ref<'tcx>( v0::mangle_typeid_for_trait_ref(tcx, trait_ref) } -/// Computes the symbol name for the given instance. This function will call -/// `compute_instantiating_crate` if it needs to factor the instantiating crate -/// into the symbol name. -fn compute_symbol_name<'tcx>( +pub fn symbol_name_from_attrs<'tcx>( tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - compute_instantiating_crate: impl FnOnce() -> CrateNum, -) -> String { - let def_id = instance.def_id(); - let args = instance.args; - - debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + instance_kind: InstanceKind<'tcx>, +) -> Option { + let def_id = instance_kind.def_id(); if let Some(def_id) = def_id.as_local() { if tcx.proc_macro_decls_static(()) == Some(def_id) { let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); - return rustc_session::generate_proc_macro_decls_symbol(stable_crate_id); + return Some(rustc_session::generate_proc_macro_decls_symbol(stable_crate_id)); } } // FIXME(eddyb) Precompute a custom symbol name based on attributes. let attrs = if tcx.def_kind(def_id).has_codegen_attrs() { - &tcx.codegen_instance_attrs(instance.def) + &tcx.codegen_instance_attrs(instance_kind) } else { CodegenFnAttrs::EMPTY }; @@ -197,7 +190,7 @@ fn compute_symbol_name<'tcx>( // legacy symbol mangling scheme. let name = if let Some(name) = attrs.symbol_name { name } else { tcx.item_name(def_id) }; - return v0::mangle_internal_symbol(tcx, name.as_str()); + return Some(v0::mangle_internal_symbol(tcx, name.as_str())); } let wasm_import_module_exception_force_mangling = { @@ -225,15 +218,35 @@ fn compute_symbol_name<'tcx>( if !wasm_import_module_exception_force_mangling { if let Some(name) = attrs.symbol_name { // Use provided name - return name.to_string(); + return Some(name.to_string()); } if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { // Don't mangle - return tcx.item_name(def_id).to_string(); + return Some(tcx.item_name(def_id).to_string()); } } + None +} + +/// Computes the symbol name for the given instance. This function will call +/// `compute_instantiating_crate` if it needs to factor the instantiating crate +/// into the symbol name. +fn compute_symbol_name<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + compute_instantiating_crate: impl FnOnce() -> CrateNum, +) -> String { + let def_id = instance.def_id(); + let args = instance.args; + + debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + + if let Some(symbol) = symbol_name_from_attrs(tcx, instance.def) { + return symbol; + } + // If we're dealing with an instance of a function that's inlined from // another crate but we're marking it as globally shared to our // compilation (aka we're not making an internal copy in each of our diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index c6624a7820559..b26545fa2b4b0 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -49,25 +49,31 @@ pub(super) fn mangle<'tcx>( // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance. let shim_kind = match instance.def { - ty::InstanceKind::ThreadLocalShim(_) => Some("tls"), - ty::InstanceKind::VTableShim(_) => Some("vtable"), - ty::InstanceKind::ReifyShim(_, None) => Some("reify"), - ty::InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"), - ty::InstanceKind::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"), + ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(_)) => Some("tls"), + ty::InstanceKind::Shim(ty::ShimKind::VTable(_)) => Some("vtable"), + ty::InstanceKind::Shim(ty::ShimKind::Reify(_, None)) => Some("reify"), + ty::InstanceKind::Shim(ty::ShimKind::Reify(_, Some(ReifyReason::FnPtr))) => { + Some("reify_fnptr") + } + ty::InstanceKind::Shim(ty::ShimKind::Reify(_, Some(ReifyReason::Vtable))) => { + Some("reify_vtable") + } // FIXME(async_closures): This shouldn't be needed when we fix // `Instance::ty`/`Instance::def_id`. - ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: true, .. } => { - Some("by_move") - } - ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: false, .. } => { - Some("by_ref") - } - ty::InstanceKind::FutureDropPollShim(_, _, _) => Some("drop"), + ty::InstanceKind::Shim(ty::ShimKind::ConstructCoroutineInClosure { + receiver_by_ref: true, + .. + }) => Some("by_move"), + ty::InstanceKind::Shim(ty::ShimKind::ConstructCoroutineInClosure { + receiver_by_ref: false, + .. + }) => Some("by_ref"), + ty::InstanceKind::Shim(ty::ShimKind::FutureDropPoll(_, _, _)) => Some("drop"), _ => None, }; - if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def { + if let ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(_, ty)) = instance.def { let ty::Coroutine(_, cor_args) = ty.kind() else { bug!(); }; @@ -548,7 +554,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // We may still encounter projections here due to the printing // logic sometimes passing identity-substituted impl headers. - ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) => { self.print_def_path(def_id, args)?; } @@ -690,13 +696,13 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { return Ok(()); } - // We may still encounter unevaluated consts due to the printing + // We may still encounter alias consts due to the printing // logic sometimes passing identity-substituted impl headers. - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { kind, args, .. }) => match kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { + ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => match kind { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { return self.print_def_path(def_id, args); } }, diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index 51e7ee8daa439..4aa69dac2d7db 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -8,12 +8,19 @@ def_reg_class! { LoongArch LoongArchInlineAsmRegClass { reg, freg, + vreg, + xreg, } } impl LoongArchInlineAsmRegClass { pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { - &[] + match self { + Self::freg => &['w', 'u'], + Self::vreg => &['u'], + Self::xreg => &['w'], + _ => &[], + } } pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { @@ -35,6 +42,7 @@ impl LoongArchInlineAsmRegClass { pub fn supported_types( self, arch: InlineAsmArch, + allow_experimental_reg: bool, ) -> &'static [(InlineAsmType, Option)] { match (self, arch) { (Self::reg, InlineAsmArch::LoongArch64) => { @@ -42,6 +50,27 @@ impl LoongArchInlineAsmRegClass { } (Self::reg, InlineAsmArch::LoongArch32) => types! { _: I8, I16, I32, F16, F32; }, (Self::freg, _) => types! { f: F16, F32; d: F64; }, + (Self::vreg, _) => { + if allow_experimental_reg { + types! { + lsx: F16, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + } + } else { + &[] + } + } + (Self::xreg, _) => { + if allow_experimental_reg { + types! { + lasx: F16, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4); + } + } else { + &[] + } + } _ => unreachable!("unsupported register class"), } } @@ -108,6 +137,70 @@ def_regs! { f29: freg = ["$f29","$fs5"], f30: freg = ["$f30","$fs6"], f31: freg = ["$f31","$fs7"], + vr0: vreg = ["$vr0"], + vr1: vreg = ["$vr1"], + vr2: vreg = ["$vr2"], + vr3: vreg = ["$vr3"], + vr4: vreg = ["$vr4"], + vr5: vreg = ["$vr5"], + vr6: vreg = ["$vr6"], + vr7: vreg = ["$vr7"], + vr8: vreg = ["$vr8"], + vr9: vreg = ["$vr9"], + vr10: vreg = ["$vr10"], + vr11: vreg = ["$vr11"], + vr12: vreg = ["$vr12"], + vr13: vreg = ["$vr13"], + vr14: vreg = ["$vr14"], + vr15: vreg = ["$vr15"], + vr16: vreg = ["$vr16"], + vr17: vreg = ["$vr17"], + vr18: vreg = ["$vr18"], + vr19: vreg = ["$vr19"], + vr20: vreg = ["$vr20"], + vr21: vreg = ["$vr21"], + vr22: vreg = ["$vr22"], + vr23: vreg = ["$vr23"], + vr24: vreg = ["$vr24"], + vr25: vreg = ["$vr25"], + vr26: vreg = ["$vr26"], + vr27: vreg = ["$vr27"], + vr28: vreg = ["$vr28"], + vr29: vreg = ["$vr29"], + vr30: vreg = ["$vr30"], + vr31: vreg = ["$vr31"], + xr0: xreg = ["$xr0"], + xr1: xreg = ["$xr1"], + xr2: xreg = ["$xr2"], + xr3: xreg = ["$xr3"], + xr4: xreg = ["$xr4"], + xr5: xreg = ["$xr5"], + xr6: xreg = ["$xr6"], + xr7: xreg = ["$xr7"], + xr8: xreg = ["$xr8"], + xr9: xreg = ["$xr9"], + xr10: xreg = ["$xr10"], + xr11: xreg = ["$xr11"], + xr12: xreg = ["$xr12"], + xr13: xreg = ["$xr13"], + xr14: xreg = ["$xr14"], + xr15: xreg = ["$xr15"], + xr16: xreg = ["$xr16"], + xr17: xreg = ["$xr17"], + xr18: xreg = ["$xr18"], + xr19: xreg = ["$xr19"], + xr20: xreg = ["$xr20"], + xr21: xreg = ["$xr21"], + xr22: xreg = ["$xr22"], + xr23: xreg = ["$xr23"], + xr24: xreg = ["$xr24"], + xr25: xreg = ["$xr25"], + xr26: xreg = ["$xr26"], + xr27: xreg = ["$xr27"], + xr28: xreg = ["$xr28"], + xr29: xreg = ["$xr29"], + xr30: xreg = ["$xr30"], + xr31: xreg = ["$xr31"], #error = ["$r0","$zero"] => "constant zero cannot be used as an operand for inline asm", #error = ["$r2","$tp"] => @@ -132,4 +225,60 @@ impl LoongArchInlineAsmReg { ) -> fmt::Result { out.write_str(self.name()) } + + pub fn overlapping_regs(self, mut cb: impl FnMut(LoongArchInlineAsmReg)) { + macro_rules! reg_conflicts { + ( + $( + $f:ident : $v:ident : $x:ident + ),*; + ) => { + match self { + $( + Self::$f | Self::$v | Self::$x => { + cb(Self::$f); + cb(Self::$v); + cb(Self::$x); + } + )* + r => cb(r), + } + }; + } + + reg_conflicts! { + f0 : vr0 : xr0, + f1 : vr1 : xr1, + f2 : vr2 : xr2, + f3 : vr3 : xr3, + f4 : vr4 : xr4, + f5 : vr5 : xr5, + f6 : vr6 : xr6, + f7 : vr7 : xr7, + f8 : vr8 : xr8, + f9 : vr9 : xr9, + f10 : vr10 : xr10, + f11 : vr11 : xr11, + f12 : vr12 : xr12, + f13 : vr13 : xr13, + f14 : vr14 : xr14, + f15 : vr15 : xr15, + f16 : vr16 : xr16, + f17 : vr17 : xr17, + f18 : vr18 : xr18, + f19 : vr19 : xr19, + f20 : vr20 : xr20, + f21 : vr21 : xr21, + f22 : vr22 : xr22, + f23 : vr23 : xr23, + f24 : vr24 : xr24, + f25 : vr25 : xr25, + f26 : vr26 : xr26, + f27 : vr27 : xr27, + f28 : vr28 : xr28, + f29 : vr29 : xr29, + f30 : vr30 : xr30, + f31 : vr31 : xr31; + } + } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 1129c2eddf2d3..8d99035fd0db4 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -474,7 +474,7 @@ impl InlineAsmReg { Self::RiscV(_) => cb(self), Self::PowerPC(r) => r.overlapping_regs(|r| cb(Self::PowerPC(r))), Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), - Self::LoongArch(_) => cb(self), + Self::LoongArch(r) => r.overlapping_regs(|r| cb(Self::LoongArch(r))), Self::Mips(_) => cb(self), Self::S390x(r) => r.overlapping_regs(|r| cb(Self::S390x(r))), Self::Sparc(_) => cb(self), @@ -655,7 +655,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.supported_types(arch).into(), Self::PowerPC(r) => r.supported_types(arch).into(), Self::Hexagon(r) => r.supported_types(arch).into(), - Self::LoongArch(r) => r.supported_types(arch).into(), + Self::LoongArch(r) => r.supported_types(arch, allow_experimental_reg).into(), Self::Mips(r) => r.supported_types(arch).into(), Self::S390x(r) => r.supported_types(arch).into(), Self::Sparc(r) => r.supported_types(arch).into(), diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 7e1eafa10097f..4bc88cc4f9705 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -399,7 +399,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { BackendRepr::Scalar(scalar) => PassMode::Direct(scalar_attrs(scalar, Size::ZERO)), BackendRepr::ScalarPair(a, b) => PassMode::Pair( scalar_attrs(a, Size::ZERO), - scalar_attrs(b, a.size(cx).align_to(b.align(cx).abi)), + scalar_attrs(b, a.size(cx).align_to(b.default_align(cx).abi)), ), BackendRepr::SimdVector { .. } => PassMode::Direct(ArgAttributes::new()), BackendRepr::Memory { .. } => Self::indirect_pass_mode(&layout), @@ -827,6 +827,9 @@ impl<'a, Ty> FnAbi<'a, Ty> { ArgAttribute::default() }; arg.cast_to_with_attrs(Reg { kind: RegKind::Integer, size }, attr.into()); + } else if self.conv == CanonAbi::RustTail { + assert!(arg.layout.is_sized(), "extern \"tail\" arguments must be sized"); + arg.pass_by_stack_offset(None); } } diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index 9e7ff620fea43..13f364f3d8258 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -26,8 +26,8 @@ pub(crate) mod managarm_mlibc; pub(crate) mod motor; pub(crate) mod msvc; pub(crate) mod netbsd; -pub(crate) mod nto_qnx; pub(crate) mod openbsd; +pub(crate) mod qnx_sdp; pub(crate) mod redox; pub(crate) mod solaris; pub(crate) mod solid; diff --git a/compiler/rustc_target/src/spec/base/nto_qnx.rs b/compiler/rustc_target/src/spec/base/qnx_sdp.rs similarity index 100% rename from compiler/rustc_target/src/spec/base/nto_qnx.rs rename to compiler/rustc_target/src/spec/base/qnx_sdp.rs diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 7156c3b08b918..922b1fb3dff8d 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -223,6 +223,7 @@ impl Target { forward!(supports_stack_protector); forward!(small_data_threshold_support); forward!(entry_name); + forward!(supports_fentry); forward!(supports_xray); // we're going to run `update_from_cli`, but that won't change the target's AbiMap @@ -407,6 +408,7 @@ impl ToJson for Target { target_option_val!(small_data_threshold_support); target_option_val!(entry_name); target_option_val!(entry_abi); + target_option_val!(supports_fentry); target_option_val!(supports_xray); // Serializing `-Clink-self-contained` needs a dynamic key to support the @@ -626,6 +628,7 @@ struct TargetSpecJson { supports_stack_protector: Option, small_data_threshold_support: Option, entry_name: Option>, + supports_fentry: Option, supports_xray: Option, entry_abi: Option, } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 87c40fa588c03..78f4690443525 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1488,6 +1488,7 @@ supported_targets! { ("armv7-unknown-linux-musleabihf", armv7_unknown_linux_musleabihf), ("aarch64-unknown-linux-gnu", aarch64_unknown_linux_gnu), ("aarch64-unknown-linux-musl", aarch64_unknown_linux_musl), + ("aarch64-unknown-linux-pauthtest", aarch64_unknown_linux_pauthtest), ("aarch64_be-unknown-linux-musl", aarch64_be_unknown_linux_musl), ("x86_64-unknown-linux-musl", x86_64_unknown_linux_musl), ("i686-unknown-linux-musl", i686_unknown_linux_musl), @@ -1790,10 +1791,10 @@ supported_targets! { ("aarch64-unknown-nto-qnx700", aarch64_unknown_nto_qnx700), ("aarch64-unknown-nto-qnx710", aarch64_unknown_nto_qnx710), ("aarch64-unknown-nto-qnx710_iosock", aarch64_unknown_nto_qnx710_iosock), - ("aarch64-unknown-nto-qnx800", aarch64_unknown_nto_qnx800), + ("aarch64-unknown-qnx", aarch64_unknown_qnx), ("x86_64-pc-nto-qnx710", x86_64_pc_nto_qnx710), ("x86_64-pc-nto-qnx710_iosock", x86_64_pc_nto_qnx710_iosock), - ("x86_64-pc-nto-qnx800", x86_64_pc_nto_qnx800), + ("x86_64-pc-qnx", x86_64_pc_qnx), ("i686-pc-nto-qnx700", i686_pc_nto_qnx700), ("aarch64-unknown-linux-ohos", aarch64_unknown_linux_ohos), @@ -1824,6 +1825,12 @@ supported_targets! { ("x86_64-unknown-linux-gnuasan", x86_64_unknown_linux_gnuasan), ("x86_64-unknown-linux-gnumsan", x86_64_unknown_linux_gnumsan), ("x86_64-unknown-linux-gnutsan", x86_64_unknown_linux_gnutsan), + + ("aarch64-oe-linux-gnu", aarch64_oe_linux_gnu), + ("armv7-oe-linux-gnueabihf", armv7_oe_linux_gnueabihf), + ("i686-oe-linux-gnu", i686_oe_linux_gnu), + ("riscv64-oe-linux-gnu", riscv64_oe_linux_gnu), + ("x86_64-oe-linux-gnu", x86_64_oe_linux_gnu), } /// Cow-Vec-Str: Cow<'static, [Cow<'static, str>]> @@ -1994,6 +2001,7 @@ crate::target_spec_enum! { OpenBsd = "openbsd", Psp = "psp", Psx = "psx", + Qnx = "qnx", Qurt = "qurt", Redox = "redox", Rtems = "rtems", @@ -2034,7 +2042,6 @@ crate::target_spec_enum! { Nto70 = "nto70", Nto71 = "nto71", Nto71IoSock = "nto71_iosock", - Nto80 = "nto80", Ohos = "ohos", Relibc = "relibc", Sgx = "sgx", @@ -2072,6 +2079,7 @@ crate::target_spec_enum! { Ilp32e = "ilp32e", Llvm = "llvm", MacAbi = "macabi", + Pauthtest = "pauthtest", Sim = "sim", SoftFloat = "softfloat", Spe = "spe", @@ -2112,6 +2120,8 @@ crate::target_spec_enum! { // PowerPC ElfV1 = "elfv1", ElfV2 = "elfv2", + // Pointer authentication: Pauthtest + Pauthtest = "pauthtest", Unspecified = "", } @@ -2688,6 +2698,9 @@ pub struct TargetOptions { /// Default value is `CanonAbi::C` pub entry_abi: CanonAbi, + /// Whether the target supports fentry instrumentation. + pub supports_fentry: bool, + /// Whether the target supports XRay instrumentation. pub supports_xray: bool, @@ -2929,6 +2942,7 @@ impl Default for TargetOptions { supports_stack_protector: true, entry_name: "main".into(), entry_abi: CanonAbi::C, + supports_fentry: false, supports_xray: false, default_address_space: rustc_abi::AddressSpace::ZERO, small_data_threshold_support: SmallDataThresholdSupport::DefaultForArch, @@ -3394,9 +3408,10 @@ impl Target { ) } Arch::AArch64 => { - check!( - self.llvm_abiname == LlvmAbi::Unspecified, - "`llvm_abiname` is unused on aarch64" + check_matches!( + self.llvm_abiname, + LlvmAbi::Unspecified | LlvmAbi::Pauthtest, + "invalid llvm ABI for aarch64" ); check!(self.llvm_floatabi.is_none(), "`llvm_floatabi` is unused on aarch64"); // FIXME: Ensure that target_abi = "ilp32" correlates with actually using that ABI. @@ -3409,6 +3424,7 @@ impl Target { CfgAbi::Ilp32 | CfgAbi::Llvm | CfgAbi::MacAbi + | CfgAbi::Pauthtest | CfgAbi::Sim | CfgAbi::Uwp | CfgAbi::Unspecified diff --git a/compiler/rustc_target/src/spec/targets/aarch64_oe_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_oe_linux_gnu.rs new file mode 100644 index 0000000000000..4486da9a471ac --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_oe_linux_gnu.rs @@ -0,0 +1,17 @@ +use crate::spec::{Target, TargetMetadata}; + +pub(crate) fn target() -> Target { + let mut base = super::aarch64_unknown_linux_gnu::target(); + + base.metadata = TargetMetadata { + description: Some("64-bit Linux (kernel 3.2+, glibc 2.17+) for yocto".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }; + + base.llvm_target = "aarch64-oe-linux-gnu".into(); + base.options.linker = Some("aarch64-oe-linux-gcc".into()); + + base +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs new file mode 100644 index 0000000000000..739694ea1509d --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs @@ -0,0 +1,38 @@ +use crate::spec::{ + Arch, CfgAbi, Env, FramePointer, LinkSelfContainedDefault, LlvmAbi, StackProbeType, Target, + TargetMetadata, TargetOptions, base, +}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "aarch64-unknown-linux-pauthtest".into(), + metadata: TargetMetadata { + description: Some("ARM64 Linux with pauth enabled musl".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: Arch::AArch64, + + options: TargetOptions { + env: Env::Musl, + cfg_abi: CfgAbi::Pauthtest, + llvm_abiname: LlvmAbi::Pauthtest, + // `pauthtest` requires v8.3a, which includes lse, no need for outline-atomics + features: "+v8.3a,+pauth".into(), + max_atomic_width: Some(128), + stack_probes: StackProbeType::Inline, + crt_static_default: false, + crt_static_allows_dylibs: false, + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, + link_self_contained: LinkSelfContainedDefault::False, + mcount: "\u{1}_mcount".into(), + ..base::linux::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs index e1f29f832f0b5..db8905dba6cc3 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs @@ -1,11 +1,12 @@ -use crate::spec::base::nto_qnx; +use crate::spec::base::qnx_sdp; use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { - let mut target = nto_qnx::aarch64(); - target.metadata.description = Some("ARM64 QNX Neutrino 7.0 RTOS".into()); + let mut target = qnx_sdp::aarch64(); + target.metadata.description = Some("ARM64 QNX SDP 7.0".into()); target.options.pre_link_args = - nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); + qnx_sdp::pre_link_args(qnx_sdp::ApiVariant::Default, qnx_sdp::Arch::Aarch64); + // for QNX SDP 7.x, we keep target_os = "nto" for backwards compatibility, and use target_env to specify which version target.options.env = Env::Nto70; target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs index 1baa56630d3a9..3a76a0e34d699 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs @@ -1,12 +1,12 @@ -use crate::spec::base::nto_qnx; +use crate::spec::base::qnx_sdp; use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { - let mut target = nto_qnx::aarch64(); - target.metadata.description = - Some("ARM64 QNX Neutrino 7.1 RTOS with io-pkt network stack".into()); + let mut target = qnx_sdp::aarch64(); + target.metadata.description = Some("ARM64 QNX SDP 7.1 with io-pkt network stack".into()); target.options.pre_link_args = - nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); + qnx_sdp::pre_link_args(qnx_sdp::ApiVariant::Default, qnx_sdp::Arch::Aarch64); + // for QNX SDP 7.x, we keep target_os = "nto" for backwards compatibility, and use target_env to specify which version target.options.env = Env::Nto71; target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs index 80ae93247a3f0..e612105201ff0 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs @@ -1,12 +1,12 @@ -use crate::spec::base::nto_qnx; +use crate::spec::base::qnx_sdp; use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { - let mut target = nto_qnx::aarch64(); - target.metadata.description = - Some("ARM64 QNX Neutrino 7.1 RTOS with io-sock network stack".into()); + let mut target = qnx_sdp::aarch64(); + target.metadata.description = Some("ARM64 QNX SDP 7.1 with io-sock network stack".into()); target.options.pre_link_args = - nto_qnx::pre_link_args(nto_qnx::ApiVariant::IoSock, nto_qnx::Arch::Aarch64); + qnx_sdp::pre_link_args(qnx_sdp::ApiVariant::IoSock, qnx_sdp::Arch::Aarch64); + // for QNX SDP 7.x, we keep target_os = "nto" for backwards compatibility, and use target_env to specify which version target.options.env = Env::Nto71IoSock; target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs deleted file mode 100644 index 5d265087c4a21..0000000000000 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::spec::base::nto_qnx; -use crate::spec::{Env, Target}; - -pub(crate) fn target() -> Target { - let mut target = nto_qnx::aarch64(); - target.metadata.description = Some("ARM64 QNX Neutrino 8.0 RTOS".into()); - target.options.pre_link_args = - nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); - target.options.env = Env::Nto80; - target -} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_qnx.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_qnx.rs new file mode 100644 index 0000000000000..cbd2f4224185d --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_qnx.rs @@ -0,0 +1,12 @@ +use crate::spec::base::qnx_sdp; +use crate::spec::{Os, Target}; + +pub(crate) fn target() -> Target { + let mut target = qnx_sdp::aarch64(); + target.metadata.description = Some("ARM64 QNX SDP 8.0+".into()); + target.options.pre_link_args = + qnx_sdp::pre_link_args(qnx_sdp::ApiVariant::Default, qnx_sdp::Arch::Aarch64); + // for QNX SDP 8.0, we have target_os = "qnx" and no target_env + target.options.os = Os::Qnx; + target +} diff --git a/compiler/rustc_target/src/spec/targets/armv7_oe_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_oe_linux_gnueabihf.rs new file mode 100644 index 0000000000000..bf0cdb5cdc33f --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7_oe_linux_gnueabihf.rs @@ -0,0 +1,17 @@ +use crate::spec::{Target, TargetMetadata}; + +pub(crate) fn target() -> Target { + let mut base = super::armv7_unknown_linux_gnueabihf::target(); + + base.metadata = TargetMetadata { + description: Some("Armv7-A Linux, hardfloat (kernel 3.2, glibc 2.17) for yocto".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }; + + base.llvm_target = "armv7-oe-linux-gnueabihf".into(); + base.options.linker = Some("arm-oe-linux-gnueabi-gcc".into()); + + base +} diff --git a/compiler/rustc_target/src/spec/targets/i686_oe_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_oe_linux_gnu.rs new file mode 100644 index 0000000000000..0e8165d25cee7 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/i686_oe_linux_gnu.rs @@ -0,0 +1,21 @@ +use crate::spec::{Target, TargetMetadata}; + +pub(crate) fn target() -> Target { + let mut base = super::i686_unknown_linux_gnu::target(); + + base.metadata = TargetMetadata { + description: Some("32-bit Linux (kernel 3.2, glibc 2.17+) for yocto".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }; + + base.llvm_target = "i686-oe-linux-gnu".into(); + + base.options.linker = Some("i686-oe-linux-gcc".into()); + + base.options.cpu = "core2".into(); + base.options.features = "+sse3".into(); + + base +} diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/i686_pc_nto_qnx700.rs index 7184a54961e20..c8def7e5bc423 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_nto_qnx700.rs @@ -1,9 +1,9 @@ -use crate::spec::base::nto_qnx; +use crate::spec::base::qnx_sdp; use crate::spec::{Arch, Env, RustcAbi, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { - let mut meta = nto_qnx::meta(); - meta.description = Some("32-bit x86 QNX Neutrino 7.0 RTOS".into()); + let mut meta = qnx_sdp::meta(); + meta.description = Some("32-bit x86 QNX SDP 7.0".into()); meta.std = Some(false); Target { llvm_target: "i586-pc-unknown".into(), @@ -17,14 +17,15 @@ pub(crate) fn target() -> Target { rustc_abi: Some(RustcAbi::X86Sse2), cpu: "pentium4".into(), max_atomic_width: Some(64), - pre_link_args: nto_qnx::pre_link_args( - nto_qnx::ApiVariant::Default, - nto_qnx::Arch::I586, + pre_link_args: qnx_sdp::pre_link_args( + qnx_sdp::ApiVariant::Default, + qnx_sdp::Arch::I586, ), + // for QNX SDP 7.x, we keep target_os = "nto" for backwards compatibility, and use target_env to specify which version env: Env::Nto70, vendor: "pc".into(), stack_probes: StackProbeType::Inline, - ..base::nto_qnx::opts() + ..base::qnx_sdp::opts() }, } } diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs index 932f6034433e5..7bc48b1998fac 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { base.supported_sanitizers = SanitizerSet::ADDRESS; base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); base.stack_probes = StackProbeType::Inline; + base.supports_fentry = true; Target { llvm_target: "i686-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs index 5c85a3e13820c..212c2b8db56da 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32", "-Wl,-melf_i386"]); base.stack_probes = StackProbeType::Inline; + base.supports_fentry = true; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/targets/riscv64_oe_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv64_oe_linux_gnu.rs new file mode 100644 index 0000000000000..8d8d579444536 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv64_oe_linux_gnu.rs @@ -0,0 +1,17 @@ +use crate::spec::{Target, TargetMetadata}; + +pub(crate) fn target() -> Target { + let mut base = super::riscv64gc_unknown_linux_gnu::target(); + + base.metadata = TargetMetadata { + description: Some("RISC-V Linux (kernel 4.20, glibc 2.29) for yocto".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }; + + base.llvm_target = "riscv64-oe-linux-gnu".into(); + base.options.linker = Some("riscv64-oe-linux-gcc".into()); + + base +} diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs index 8859e0d650a2b..e6b58bff22848 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_fentry = true; Target { llvm_target: "s390x-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 21e705ebbec5d..5ff9f025ba1d9 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -13,6 +13,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_fentry = true; Target { llvm_target: "s390x-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_none_softfloat.rs index 8c411091ff50a..d90b8df9f37dc 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_none_softfloat.rs @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { rustc_abi: Some(RustcAbi::Softfloat), stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::KERNELADDRESS, + supports_fentry: true, ..Default::default() }; diff --git a/compiler/rustc_target/src/spec/targets/x86_64_oe_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_oe_linux_gnu.rs new file mode 100644 index 0000000000000..5877d5092e103 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_oe_linux_gnu.rs @@ -0,0 +1,17 @@ +use crate::spec::{Target, TargetMetadata}; + +pub(crate) fn target() -> Target { + let mut base = super::x86_64_unknown_linux_gnu::target(); + + base.metadata = TargetMetadata { + description: Some("64-bit Linux (kernel 3.2+, glibc 2.17+) for yocto".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }; + + base.llvm_target = "x86_64-oe-linux-gnu".into(); + base.options.linker = Some("x86_64-oe-linux-gcc".into()); + + base +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs index 9160015485097..6d814725d16ed 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs @@ -1,12 +1,12 @@ -use crate::spec::base::nto_qnx; +use crate::spec::base::qnx_sdp; use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { - let mut target = nto_qnx::x86_64(); - target.metadata.description = - Some("x86 64-bit QNX Neutrino 7.1 RTOS with io-pkt network stack".into()); + let mut target = qnx_sdp::x86_64(); + target.metadata.description = Some("x86 64-bit QNX SDP 7.1 with io-pkt network stack".into()); target.options.pre_link_args = - nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::X86_64); + qnx_sdp::pre_link_args(qnx_sdp::ApiVariant::Default, qnx_sdp::Arch::X86_64); + // for QNX SDP 7.x, we keep target_os = "nto" for backwards compatibility, and use target_env to specify which version target.options.env = Env::Nto71; target } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs index 1e97ae6b7a08e..33789b61a18b1 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs @@ -1,12 +1,12 @@ -use crate::spec::base::nto_qnx; +use crate::spec::base::qnx_sdp; use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { - let mut target = nto_qnx::x86_64(); - target.metadata.description = - Some("x86 64-bit QNX Neutrino 7.1 RTOS with io-sock network stack".into()); + let mut target = qnx_sdp::x86_64(); + target.metadata.description = Some("x86 64-bit QNX SDP 7.1 with io-sock network stack".into()); target.options.pre_link_args = - nto_qnx::pre_link_args(nto_qnx::ApiVariant::IoSock, nto_qnx::Arch::X86_64); + qnx_sdp::pre_link_args(qnx_sdp::ApiVariant::IoSock, qnx_sdp::Arch::X86_64); + // for QNX SDP 7.x, we keep target_os = "nto" for backwards compatibility, and use target_env to specify which version target.options.env = Env::Nto71IoSock; target } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs deleted file mode 100644 index bd98df621db16..0000000000000 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::spec::base::nto_qnx; -use crate::spec::{Env, Target}; - -pub(crate) fn target() -> Target { - let mut target = nto_qnx::x86_64(); - target.metadata.description = Some("x86 64-bit QNX Neutrino 8.0 RTOS".into()); - target.options.pre_link_args = - nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::X86_64); - target.options.env = Env::Nto80; - target -} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_qnx.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_qnx.rs new file mode 100644 index 0000000000000..59eff3dbeb5d4 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_qnx.rs @@ -0,0 +1,12 @@ +use crate::spec::base::qnx_sdp; +use crate::spec::{Os, Target}; + +pub(crate) fn target() -> Target { + let mut target = qnx_sdp::x86_64(); + target.metadata.description = Some("x86 64-bit QNX SDP 8.0+".into()); + target.options.pre_link_args = + qnx_sdp::pre_link_args(qnx_sdp::ApiVariant::Default, qnx_sdp::Arch::X86_64); + // for QNX SDP 8.0, we have target_os = "qnx" and no target_env + target.options.os = Os::Qnx; + target +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index defa9f146d798..0d87a7b760c61 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -19,6 +19,7 @@ pub(crate) fn target() -> Target { | SanitizerSet::SAFESTACK | SanitizerSet::THREAD | SanitizerSet::REALTIME; + base.supports_fentry = true; base.supports_xray = true; Target { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs index ee883532ad4ac..a664f22479913 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs @@ -15,6 +15,7 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_fentry = true; base.supports_xray = true; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs index 768b1a1ba1128..c0d65818d325b 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.linker_flavor = LinkerFlavor::Gnu(Cc::No, Lld::Yes); base.linker = Some("rust-lld".into()); base.panic_strategy = PanicStrategy::Abort; + base.supports_fentry = true; Target { llvm_target: "x86_64-unknown-linux-none".into(), diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 3ce76d78e6c8a..0cd9128762cbc 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_trait_selection/src/diagnostics.rs b/compiler/rustc_trait_selection/src/diagnostics.rs index 662480587fdd2..ba89b0987659e 100644 --- a/compiler/rustc_trait_selection/src/diagnostics.rs +++ b/compiler/rustc_trait_selection/src/diagnostics.rs @@ -21,11 +21,11 @@ use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlig pub(crate) mod note_and_explain; #[derive(Diagnostic)] -#[diag("unable to construct a constant value for the unevaluated constant {$unevaluated}")] +#[diag("unable to construct a constant value for the alias const {$alias_const}")] pub(crate) struct UnableToConstructConstantValue<'a> { #[primary_span] pub span: Span, - pub unevaluated: ty::UnevaluatedConst<'a>, + pub alias_const: ty::AliasConst<'a>, } pub(crate) struct NegativePositiveConflict<'tcx> { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 600bbe84691ef..9822e8cdef8b2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -246,10 +246,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, ) { let (alias, &def_id, concrete) = match (expected.kind(), found.kind()) { - (ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }), _) => { + (ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }), _) => { (proj, def_id, found) } - (_, ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. })) => { + (_, ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. })) => { (proj, def_id, expected) } _ => return, @@ -314,7 +314,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "the associated type `{}` is defined as `{}` in the implementation, \ but the where-bound `{}` shadows this definition\n\ see issue #152409 for more information", - self.ty_to_string(tcx.mk_ty_from_kind(ty::Alias(*alias))), + self.ty_to_string(alias.to_ty(tcx, ty::IsRigid::No)), self.ty_to_string(concrete), self.ty_to_string(alias.self_ty()) )); @@ -823,9 +823,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // ^^^^^ let len1 = sig1.inputs().len(); let len2 = sig2.inputs().len(); + let splatted_arg_index1 = sig1.splatted().map(usize::from); + let splatted_arg_index2 = sig2.splatted().map(usize::from); if len1 == len2 { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { self.push_comma(&mut values.0, &mut values.1, i); + if Some(i) == splatted_arg_index1 { + values.0.push("#[splat]", splatted_arg_index1 != splatted_arg_index2); + values.0.push_normal(" "); + } + if Some(i) == splatted_arg_index2 { + values.1.push("#[splat]", splatted_arg_index1 != splatted_arg_index2); + values.1.push_normal(" "); + } let (x1, x2) = self.cmp(*l, *r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); @@ -1671,7 +1681,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && values.expected.sort_string(self.tcx) != values.found.sort_string(self.tcx); let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) { - (true, ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. })) => { + (true, ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. })) => { let sm = self.tcx.sess.source_map(); let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo()); DiagStyledString::normal(format!( @@ -1681,9 +1691,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { pos.col.to_usize() + 1, )) } - (true, &ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, .. })) - if self.tcx.is_impl_trait_in_trait(def_id) => - { + ( + true, + &ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, .. }), + ) if self.tcx.is_impl_trait_in_trait(def_id) => { let sm = self.tcx.sess.source_map(); let pos = sm.lookup_char_pos(self.tcx.def_span(def_id).lo()); DiagStyledString::normal(format!( @@ -2519,7 +2530,7 @@ impl TyCategory { pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> { match *ty.kind() { ty::Closure(def_id, _) => Some((Self::Closure, def_id)), - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) => { let kind = if tcx.ty_is_opaque_future(ty) { Self::OpaqueFuture } else { Self::Opaque }; Some((kind, def_id)) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 2aea9e5bcf369..624aef89911f7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -1060,7 +1060,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { GenericArgKind::Type(ty) => { if matches!( ty.kind(), - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) @@ -1079,9 +1079,9 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { } } GenericArgKind::Const(ct) => { - if matches!(ct.kind(), ty::ConstKind::Unevaluated(..)) { + if matches!(ct.kind(), ty::ConstKind::Alias(..)) { // You can't write the generic arguments for - // unevaluated constants. + // alias constants. walker.skip_current_subtree(); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index dfa0d4a3bfd48..2d48b41bcb361 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -162,7 +162,12 @@ pub fn suggest_new_region_bound( TyKind::OpaqueDef(opaque) => { // Get the identity type for this RPIT let did = opaque.def_id.to_def_id(); - let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did)); + let ty = Ty::new_opaque( + tcx, + ty::IsRigid::No, + did, + ty::GenericArgs::identity_for_item(tcx, did), + ); if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg { GenericBound::Outlives(Lifetime { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 7a6a6358ceb33..f93d28c29cdfa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -46,8 +46,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } ( - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), ) => { // Issue #63167 diag.note("distinct uses of `impl Trait` result in different opaque types"); @@ -89,24 +89,28 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } ( - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, - .. - }), - ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. }, - .. - }), + ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. }, .. + }, + ), + ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. }, .. + }, + ), ) => { diag.note("an associated type was expected, but a different one was found"); } // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. ( ty::Param(p), - ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }), + ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }), ) | ( - ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }), + ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }), ty::Param(p), ) if !tcx.is_impl_trait_in_trait(def_id) && let Some(generics) = body_generics => @@ -229,10 +233,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } ( ty::Param(p), - ty::Dynamic(..) | ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), + ty::Dynamic(..) | ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), ) | ( - ty::Dynamic(..) | ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), + ty::Dynamic(..) | ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), ty::Param(p), ) => { if let Some(generics) = body_generics { @@ -308,6 +312,7 @@ impl Trait for X { } ( ty::Alias( + _, proj_ty @ ty::AliasTy { kind: ty::Projection { def_id } | ty::Inherent { def_id }, .. @@ -328,6 +333,7 @@ impl Trait for X { ( _, ty::Alias( + _, proj_ty @ ty::AliasTy { kind: ty::Projection { def_id } | ty::Inherent { def_id }, .. @@ -368,9 +374,10 @@ impl Trait for X { } ( ty::Dynamic(t, _), - ty::Alias(ty::AliasTy { - kind: ty::Opaque { def_id: opaque_def_id }, .. - }), + ty::Alias( + _, + ty::AliasTy { kind: ty::Opaque { def_id: opaque_def_id }, .. }, + ), ) if let Some(def_id) = t.principal_def_id() && tcx .explicit_item_self_bounds(opaque_def_id) @@ -429,8 +436,8 @@ impl Trait for X { )); } } - (_, ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. })) - | (ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }), _) => { + (_, ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. })) + | (ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }), _) => { if let Some(body_owner_def_id) = body_owner_def_id && def_id.is_local() && matches!( @@ -830,7 +837,7 @@ fn foo(&self) -> Self::T { String::new() } }; let assoc = tcx.associated_item(def_id); - if let ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = *proj_ty.self_ty().kind() { let opaque_local_def_id = def_id.as_local(); @@ -881,9 +888,10 @@ fn foo(&self) -> Self::T { String::new() } .filter_map(|item| { let method = tcx.fn_sig(item.def_id).instantiate_identity().skip_norm_wip(); match *method.output().skip_binder().kind() { - ty::Alias(ty::AliasTy { - kind: ty::Projection { def_id: item_def_id }, .. - }) if item_def_id == proj_ty_item_def_id => Some(( + ty::Alias( + _, + ty::AliasTy { kind: ty::Projection { def_id: item_def_id }, .. }, + ) if item_def_id == proj_ty_item_def_id => Some(( tcx.def_span(item.def_id), format!("consider calling `{}`", tcx.def_path_str(item.def_id)), )), diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index f6ef59918af0c..7e706a9838306 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -1278,7 +1278,12 @@ pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>( let tcx = infcx.tcx; let mut err = infcx.dcx().create_err(diagnostics::OpaqueCapturesLifetime { span, - opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args), + opaque_ty: Ty::new_opaque( + tcx, + ty::IsRigid::No, + opaque_ty_key.def_id.to_def_id(), + opaque_ty_key.args, + ), opaque_ty_span: tcx.def_span(opaque_ty_key.def_id), }); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index c20132ce93e8d..aa10b4ee6d39c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -769,20 +769,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { StatementAsExpression::CorrectType } ( - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: last_def_id }, .. }), - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: exp_def_id }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: last_def_id }, .. }), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: exp_def_id }, .. }), ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType, ( - ty::Alias(ty::AliasTy { - kind: ty::Opaque { def_id: last_def_id }, - args: last_bounds, - .. - }), - ty::Alias(ty::AliasTy { - kind: ty::Opaque { def_id: exp_def_id }, - args: exp_bounds, - .. - }), + ty::Alias( + _, + ty::AliasTy { + kind: ty::Opaque { def_id: last_def_id }, args: last_bounds, .. + }, + ), + ty::Alias( + _, + ty::AliasTy { + kind: ty::Opaque { def_id: exp_def_id }, args: exp_bounds, .. + }, + ), ) => { debug!( "both opaque, likely future {:?} {:?} {:?} {:?}", diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs index b8e8204572d82..60ae0cd644460 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -6,7 +6,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, lang_items}; use rustc_middle::ty::{ - AssocContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv, Unnormalized, + self, AssocContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv, Unnormalized, }; use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, sym}; use tracing::debug; @@ -104,7 +104,12 @@ pub fn call_kind<'tcx>( tcx.get_diagnostic_item(sym::deref_target).expect("deref method but no deref target"); let deref_target_ty = tcx.normalize_erasing_regions( typing_env, - Unnormalized::new(Ty::new_projection(tcx, deref_target_def_id, method_args)), + Unnormalized::new(Ty::new_projection( + tcx, + ty::IsRigid::No, + deref_target_def_id, + method_args, + )), ); let deref_target_span = if let Ok(Some(instance)) = Instance::try_resolve(tcx, typing_env, method_did, method_args) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 4cbaa991ed204..973a088a7f5d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1612,7 +1612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { infer::BoundRegionConversionTime::HigherRankedType, bound_predicate.rebind(data), ); - let unnormalized_term = data.projection_term.to_term(self.tcx); + let unnormalized_term = data.projection_term.to_term(self.tcx, ty::IsRigid::No); // FIXME(-Znext-solver): For diagnostic purposes, it would be nice // to deeply normalize this type. let normalized_term = ocx.normalize( @@ -1651,7 +1651,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let normalized_term = ocx.normalize( &ObligationCause::dummy(), obligation.param_env, - Unnormalized::new_wip(alias_term.to_term(self.tcx)), + Unnormalized::new_wip( + alias_term.to_term(self.tcx, ty::IsRigid::No), + ), ); if let Err(terr) = ocx.eq( @@ -1919,10 +1921,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::Closure(..) => Some(9), ty::Tuple(..) => Some(10), ty::Param(..) => Some(11), - ty::Alias(ty::AliasTy { kind: ty::Projection { .. }, .. }) => Some(12), - ty::Alias(ty::AliasTy { kind: ty::Inherent { .. }, .. }) => Some(13), - ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) => Some(14), - ty::Alias(ty::AliasTy { kind: ty::Free { .. }, .. }) => Some(15), + ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }) => Some(12), + ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }) => Some(13), + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => Some(14), + ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => Some(15), ty::Never => Some(16), ty::Adt(..) => Some(17), ty::Coroutine(..) => Some(18), @@ -2147,7 +2149,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let impl_trait_ref = ocx.normalize( &ObligationCause::dummy(), param_env, - ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args), + ty::EarlyBinder::bind(self.tcx, single.trait_ref) + .instantiate(self.tcx, impl_args), ); ocx.register_obligations( @@ -2805,12 +2808,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.infcx.tcx } + // FIXME: why don't we also instantiate const and region params with infer vars + // here? Because diagnostics isn't soundness critical and no one bothers to be + // pedantic yet. fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Param(_) = *ty.kind() { - let infcx = self.infcx; - *self.var_map.entry(ty).or_insert_with(|| infcx.next_ty_var(DUMMY_SP)) - } else { - ty.super_fold_with(self) + match ty.kind() { + ty::Param(_) => { + let infcx = self.infcx; + *self.var_map.entry(ty).or_insert_with(|| infcx.next_ty_var(DUMMY_SP)) + } + // FIXME(#155345): This should automatically + // handled by type folders instead of needing to do it + // manually here. + &ty::Alias(is_rigid, alias) + if is_rigid == ty::IsRigid::Yes + && ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) => + { + let alias = alias.fold_with(self); + Ty::new_alias(self.cx(), ty::IsRigid::No, alias) + } + _ => ty.super_fold_with(self), } } } @@ -3752,12 +3769,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() { - ty::ConstKind::Unevaluated(uv) => { + ty::ConstKind::Alias(_, alias_const) => { let mut err = self.dcx().struct_span_err(span, "unconstrained generic constant"); - let const_span = uv.kind.def_span(self.tcx); + let const_span = alias_const.kind.def_span(self.tcx); - let const_ty = uv.type_of(self.tcx).skip_norm_wip(); + let const_ty = alias_const.type_of(self.tcx).skip_norm_wip(); let cast = if const_ty != self.tcx.types.usize { " as usize" } else { "" }; let msg = "try adding a `where` bound"; if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(const_span) { @@ -3795,7 +3812,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Ok(err) } _ => { - bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); + bug!("const evaluatable failed for non-alias const `{ct:?}`"); } }, _ => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 9059ad4eed546..b0fda96078c54 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -334,7 +334,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return; } - let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id); let mut private_candidate: Option<(Ty<'tcx>, Ty<'tcx>, Span)> = None; for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) { @@ -346,8 +345,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { continue; } - let (adjusted_ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field_ident, base_def.did(), fn_body_hir_id); + let (adjusted_ident, def_scope) = self.tcx.adjust_ident_and_get_scope( + field_ident, + base_def.did(), + typeck_results.hir_owner.def_id, + ); let Some((_, field_def)) = base_def.non_enum_variant().fields.iter_enumerated().find(|(_, field)| { @@ -476,7 +478,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let self_ty = trait_pred.skip_binder().self_ty(); let (param_ty, projection) = match *self_ty.kind() { ty::Param(_) => (true, None), - ty::Alias(alias) => { + ty::Alias(_, alias) => { if let Some(projection) = alias.try_to_projection() { (false, Some(projection)) } else { @@ -1484,7 +1486,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()), )) } - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { self.tcx .item_self_bounds(def_id) .instantiate(self.tcx, args) @@ -2556,6 +2558,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.children.clear(); let mut span = obligation.cause.span; + let mut is_async_fn_return = false; if let DefKind::Closure = self.tcx.def_kind(obligation.cause.body_id) && let parent = self.tcx.local_parent(obligation.cause.body_id) && let DefKind::Fn | DefKind::AssocFn = self.tcx.def_kind(parent) @@ -2571,10 +2574,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // and // async fn foo() -> dyn Display Box span = fn_sig.decl.output.span(); + is_async_fn_return = true; err.span(span); } let body = self.tcx.hir_body_owned_by(obligation.cause.body_id); + if !is_async_fn_return + && let Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = + self.tcx.hir_node_by_def_id(obligation.cause.body_id) + && matches!(closure.fn_decl.output, hir::FnRetTy::DefaultReturn(_)) + { + return true; + } + let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); @@ -4051,7 +4063,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } } - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) => { // If the previous type is async fn, this is the future generated by the body of an async function. // Avoid printing it twice (it was already printed in the `ty::Coroutine` arm below). let is_future = tcx.ty_is_opaque_future(ty); @@ -4534,6 +4546,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let projection_ty = trait_pred.map_bound(|trait_pred| { Ty::new_projection( self.tcx, + ty::IsRigid::No, item_def_id, // Future::Output has no args [trait_pred.self_ty()], @@ -4878,7 +4891,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .skip_binder() .projection_term .expect_ty() - .to_ty(self.tcx), + .to_ty(self.tcx, ty::IsRigid::No), found, })]; } @@ -4961,6 +4974,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let fn_sig @ ty::FnSig { .. } = fn_ty.fn_sig(tcx).skip_binder() + // FIXME(splat): this might need to change if the Fn* traits start using/supporting splat && fn_sig.abi() == ExternAbi::Rust && !fn_sig.c_variadic() && fn_sig.safety() == hir::Safety::Safe @@ -4976,7 +4990,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Extract `::Target` assoc type and check that it is `T` && let Some(deref_target_did) = tcx.lang_items().deref_target() - && let projection = Ty::new_projection_from_args(tcx,deref_target_did, tcx.mk_args(&[ty::GenericArg::from(found_ty)])) + && let projection = Ty::new_projection_from_args(tcx,ty::IsRigid::No, deref_target_did, tcx.mk_args(&[ty::GenericArg::from(found_ty)])) && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(Unnormalized::new_wip(projection)) && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation)) && infcx.can_eq(param_env, deref_target, target_ty) @@ -5338,7 +5352,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let TypeError::Sorts(expected_found) = diff else { continue; }; - let &ty::Alias(ty::AliasTy { kind: kind @ ty::Projection { def_id }, .. }) = + let &ty::Alias(_, ty::AliasTy { kind: kind @ ty::Projection { def_id }, .. }) = expected_found.expected.kind() else { continue; @@ -5666,7 +5680,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Look for an RPITIT - let ty::Alias(alias_ty @ ty::AliasTy { kind: ty::Projection { def_id }, .. }) = + let ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Projection { def_id }, .. }) = trait_pred.self_ty().skip_binder().kind() else { return; diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 9606efe4a3d8b..10d5202905c72 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -225,7 +225,7 @@ pub fn report_item_does_not_constrain_error<'tcx>( err.note("consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]`"); err.span_note(opaque_type_span, "this opaque type is supposed to be constrained"); if let Some((key, span)) = non_defining_use { - let opaque_ty = Ty::new_opaque(tcx, key.def_id.into(), key.args); + let opaque_ty = Ty::new_opaque(tcx, ty::IsRigid::No, key.def_id.into(), key.args); err.span_note( span, format!("this use of `{opaque_ty}` does not have unique universal generic arguments"), diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 209404de5f7ff..e0877829b25f3 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -218,9 +218,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn evaluate_const( &self, param_env: ty::ParamEnv<'tcx>, - uv: ty::UnevaluatedConst<'tcx>, + alias_const: ty::AliasConst<'tcx>, ) -> Option> { - let ct = ty::Const::new_unevaluated(self.tcx, uv); + let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const); match crate::traits::try_evaluate_const(&self.0, ct, param_env) { Ok(ct) => Some(ct), diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index ec62b43ad69ed..098dcfb30a7b6 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -34,7 +34,9 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( } ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { let ct_ty = match ct.kind() { - ty::ConstKind::Unevaluated(uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(obligation.param_env) } @@ -300,7 +302,7 @@ impl<'tcx> BestObligation<'tcx> { ) -> ControlFlow> { assert!(!self.consider_ambiguities); let tcx = goal.infcx().tcx; - if let ty::Alias(alias) = *self_ty.kind() { + if let ty::Alias(_, alias) = *self_ty.kind() { let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span); let pred = ty::ProjectionPredicate { projection_term: alias.into(), term: infer_term.into() }; diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index e796becf42142..7bf41d343ecf4 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -151,7 +151,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { self.final_state, ); - return eager_resolve_vars(infcx, impl_args); + return eager_resolve_vars(&**infcx, impl_args); } inspect::ProbeStep::AddGoal(..) => {} inspect::ProbeStep::MakeCanonicalResponse { .. } @@ -331,7 +331,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { infcx, depth, orig_values, - goal: eager_resolve_vars(infcx, uncanonicalized_goal), + goal: eager_resolve_vars(&**infcx, uncanonicalized_goal), result, final_revision, source, diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index f709b8d8a5fb0..a1a7fdc6f6b82 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{ self, Binder, Flags, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, Unnormalized, }; -use rustc_next_trait_solver::normalize::NormalizationFolder; +use rustc_next_trait_solver::normalize::{NormalizationFolder, NormalizationWasAmbiguous}; use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use super::{FulfillmentCtxt, NextSolverError}; @@ -42,26 +42,34 @@ where let infcx = at.infcx; let value = value.skip_normalization(); let value = infcx.resolve_vars_if_possible(value); + + if !infcx.tcx.renormalize_rigid_aliases() && !value.has_non_rigid_aliases() { + return Normalized { value, obligations: Default::default() }; + } + let original_value = value.clone(); - let mut folder = - NormalizationFolder::new(infcx, universes.clone(), Default::default(), |alias_term| { - let delegate = <&SolverDelegate<'tcx>>::from(infcx); - let infer_term = - delegate.next_term_var_of_kind(alias_term.to_term(infcx.tcx), at.cause.span); - let predicate = - ty::ProjectionPredicate { projection_term: alias_term, term: infer_term }; - let goal = Goal::new(infcx.tcx, at.param_env, predicate); - let result = delegate.evaluate_root_goal(goal, at.cause.span, None)?; - let normalized = infcx.resolve_vars_if_possible(infer_term); - let stalled_goal = match result.certainty { - Certainty::Yes => None, - Certainty::Maybe { .. } => Some(infcx.resolve_vars_if_possible(result.goal)), - }; - Ok((normalized, stalled_goal)) - }); + let mut stalled_goals = vec![]; + let mut folder = NormalizationFolder::new(infcx, universes.clone(), |alias_term| { + let delegate = <&SolverDelegate<'tcx>>::from(infcx); + let infer_term = delegate.next_term_var_of_alias_kind(alias_term, at.cause.span); + let predicate = ty::ProjectionPredicate { projection_term: alias_term, term: infer_term }; + let goal = Goal::new(infcx.tcx, at.param_env, predicate); + let result = match delegate.evaluate_root_goal(goal, at.cause.span, None) { + Ok(result) => result, + Err(err) => return Err(err), + }; + let normalized = infcx.resolve_vars_if_possible(infer_term); + let normalization_was_ambiguous = match result.certainty { + Certainty::Yes => NormalizationWasAmbiguous::No, + Certainty::Maybe { .. } => { + stalled_goals.push(result.goal); + NormalizationWasAmbiguous::Yes + } + }; + Ok((normalized, normalization_was_ambiguous)) + }); if let Ok(value) = value.try_fold_with(&mut folder) { - let obligations = folder - .stalled_goals() + let obligations = stalled_goals .into_iter() .map(|goal| { Obligation::new(infcx.tcx, at.cause.clone(), goal.param_env, goal.predicate) @@ -84,8 +92,7 @@ struct ReplaceAliasWithInfer<'me, 'tcx> { impl<'me, 'tcx> ReplaceAliasWithInfer<'me, 'tcx> { fn term_to_infer(&mut self, alias_term: ty::AliasTerm<'tcx>) -> ty::Term<'tcx> { let infcx = self.at.infcx; - let infer_term = - infcx.next_term_var_of_kind(alias_term.to_term(infcx.tcx), self.at.cause.span); + let infer_term = infcx.next_term_var_of_alias_kind(alias_term, self.at.cause.span); let obligation = Obligation::new( infcx.tcx, self.at.cause.clone(), @@ -113,12 +120,15 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !ty.has_aliases() { + if !self.cx().renormalize_rigid_aliases() && !ty.has_non_rigid_aliases() { return ty; } let ty = ty.super_fold_with(self); - let ty::Alias(alias) = *ty.kind() else { return ty }; + let ty::Alias(orig_is_rigid, alias) = *ty.kind() else { return ty }; + if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { + return ty; + } if ty.has_escaping_bound_vars() { let (replaced, ..) = @@ -131,20 +141,26 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if !ct.has_aliases() { + if !self.cx().renormalize_rigid_aliases() && !ct.has_non_rigid_aliases() { return ct; } let ct = ct.super_fold_with(self); - let ty::ConstKind::Unevaluated(uv) = ct.kind() else { return ct }; + let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return ct }; + if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { + return ct; + } if ct.has_escaping_bound_vars() { - let (replaced, ..) = - BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, uv); + let (replaced, ..) = BoundVarReplacer::replace_bound_vars( + self.at.infcx, + &mut self.universes, + alias_const, + ); let _ = self.term_to_infer(replaced.into()); ct } else { - self.term_to_infer(uv.into()).expect_const() + self.term_to_infer(alias_const.into()).expect_const() } } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 5331773e58bed..a39f498e36fd8 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -552,7 +552,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { pub fn is_of_param(&self, ty: Ty<'tcx>) -> bool { match ty.kind() { ty::Param(_) => true, - ty::Alias(p @ ty::AliasTy { kind: ty::Projection { .. }, .. }) => { + ty::Alias(_, p @ ty::AliasTy { kind: ty::Projection { .. }, .. }) => { self.is_of_param(p.self_ty()) } _ => false, @@ -561,7 +561,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'tcx>) -> bool { if let Some(ty) = p.term().skip_binder().as_type() { - matches!(ty.kind(), ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { .. }, .. }) if proj == &p.skip_binder().projection_term.expect_ty()) + matches!(ty.kind(), ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { .. }, .. }) if proj == &p.skip_binder().projection_term.expect_ty()) } else { false } @@ -607,6 +607,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { // if possible. predicates.push_back(bound_predicate.rebind(p)); } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(p)) => { + let p = bound_predicate.rebind(p); + if self.is_param_no_infer(p.skip_binder().trait_ref.args) && is_new_pred { + self.add_user_pred(computed_preds, predicate); + } + } ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => { let p = bound_predicate.rebind(p); debug!( @@ -768,15 +774,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { let ct = super::try_evaluate_const(selcx.infcx, c, obligation.param_env); if let Err(EvaluateConstErr::InvalidConstParamTy(_)) = ct { - let span = unevaluated.kind.def_span(self.tcx); + let span = alias_const.kind.def_span(self.tcx); self.tcx .dcx() - .emit_err(UnableToConstructConstantValue { span, unevaluated }); + .emit_err(UnableToConstructConstantValue { span, alias_const }); } ct @@ -811,8 +817,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { | ty::PredicateKind::DynCompatible(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) - | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {} + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => {} ty::PredicateKind::Ambiguous => return false, // FIXME(generic_const_exprs): you can absolutely add this as a where clauses diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 8647e51370b6b..41fff770374fb 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -542,7 +542,7 @@ fn impl_intersection_has_negative_obligation( // So there are no infer variables left now, except regions which aren't resolved by `resolve_vars_if_possible`. assert!(!impl1_header_args.has_non_region_infer()); - let param_env = ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)) + let param_env = ty::EarlyBinder::bind(tcx, tcx.param_env(impl1_def_id)) .instantiate(tcx, impl1_header_args) .skip_norm_wip(); diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index cd9acde9d7b99..4b54f9c3a2c26 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -30,7 +30,7 @@ pub fn is_const_evaluatable<'tcx>( ) -> Result<(), NotConstEvaluatable> { let tcx = infcx.tcx; match tcx.expand_abstract_consts(unexpanded_ct).kind() { - ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (), + ty::ConstKind::Alias(_, _) | ty::ConstKind::Expr(_) => (), ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) @@ -44,10 +44,7 @@ pub fn is_const_evaluatable<'tcx>( let is_anon_ct = matches!( ct.kind(), - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { - kind: ty::UnevaluatedConstKind::Anon { .. }, - .. - }) + ty::ConstKind::Alias(_, ty::AliasConst { kind: ty::AliasConstKind::Anon { .. }, .. }) ); if !is_anon_ct { @@ -69,7 +66,7 @@ pub fn is_const_evaluatable<'tcx>( // here. tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); } - ty::ConstKind::Unevaluated(_) => { + ty::ConstKind::Alias(_, _) => { match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) { Err(EvaluateConstErr::HasGenericsOrInfers) => { Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( @@ -93,8 +90,8 @@ pub fn is_const_evaluatable<'tcx>( crate::traits::evaluate_const(infcx, unexpanded_ct, param_env); Ok(()) } else { - let uv = match unexpanded_ct.kind() { - ty::ConstKind::Unevaluated(uv) => uv, + let alias_const = match unexpanded_ct.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, ty::ConstKind::Expr(_) => { bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled") } @@ -117,7 +114,7 @@ pub fn is_const_evaluatable<'tcx>( tcx.dcx() .struct_span_fatal( // Slightly better span than just using `span` alone - if span == DUMMY_SP { uv.kind.def_span(tcx) } else { span }, + if span == DUMMY_SP { alias_const.kind.def_span(tcx) } else { span }, "failed to evaluate generic const expression", ) .with_note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") @@ -131,9 +128,9 @@ pub fn is_const_evaluatable<'tcx>( } Err(EvaluateConstErr::HasGenericsOrInfers) => { - let err = if uv.has_non_region_infer() { + let err = if alias_const.has_non_region_infer() { NotConstEvaluatable::MentionsInfer - } else if uv.has_non_region_param() { + } else if alias_const.has_non_region_param() { NotConstEvaluatable::MentionsParam } else { let guar = infcx.dcx().span_delayed_bug( diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index f20c97e443984..7b45dfe48a7fe 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -604,7 +604,7 @@ fn receiver_for_self_ty<'tcx>( if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) } }); - let result = EarlyBinder::bind(receiver_ty).instantiate(tcx, args).skip_norm_wip(); + let result = EarlyBinder::bind(tcx, receiver_ty).instantiate(tcx, args).skip_norm_wip(); debug!( "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}", receiver_ty, self_ty, method_def_id, result @@ -859,13 +859,13 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { ControlFlow::Continue(()) } } - ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, .. }) if self.tcx.is_impl_trait_in_trait(*def_id) => { // We'll deny these later in their own pass ControlFlow::Continue(()) } - ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { .. }, .. }) => { + ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { .. }, .. }) => { match self.allow_self_projections { AllowSelfProjections::Yes => { // Only walk contained types if the parent trait is not a supertrait. @@ -886,11 +886,10 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { let ct = self.tcx.expand_abstract_consts(ct); match ct.kind() { - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { - kind: ty::UnevaluatedConstKind::Projection { def_id }, - args, - .. - }) if self.tcx.features().min_generic_const_args() => { + ty::ConstKind::Alias( + _, + ty::AliasConst { kind: ty::AliasConstKind::Projection { def_id }, args, .. }, + ) if self.tcx.features().min_generic_const_args() => { match self.allow_self_projections { AllowSelfProjections::Yes => { let trait_def_id = self.tcx.parent(def_id); @@ -966,7 +965,7 @@ impl<'tcx> TypeVisitor> for IllegalRpititVisitor<'tcx> { type Result = ControlFlow; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if let ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }) = *ty.kind() + if let ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }) = *ty.kind() && Some(proj) != self.allowed && self.tcx.is_impl_trait_in_trait(def_id) { diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 2cbae17d69bcd..f6a7bbdc686ec 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -179,6 +179,7 @@ fn evaluate_host_effect_from_conditionally_const_item_bounds<'tcx>( let mut consider_ty = obligation.predicate.self_ty(); while let ty::Alias( + _, alias_ty @ ty::AliasTy { kind: kind @ (ty::Projection { def_id } | ty::Opaque { def_id }), .. @@ -274,6 +275,7 @@ fn evaluate_host_effect_from_item_bounds<'tcx>( let mut consider_ty = obligation.predicate.self_ty(); while let ty::Alias( + _, alias_ty @ ty::AliasTy { kind: kind @ (ty::Projection { def_id } | ty::Opaque { def_id }), .. @@ -376,7 +378,7 @@ fn evaluate_host_effect_for_copy_clone_goal<'tcx>( | ty::Foreign(..) | ty::Ref(_, _, ty::Mutability::Mut) | ty::Adt(_, _) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => Err(EvaluationFailure::NoSolution), diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a77254e8428cf..12b9940a83f44 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -559,7 +559,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { return ProcessResult::Changed(PendingPredicateObligations::new()); } ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } // FIXME(generic_const_exprs): we should construct an alias like // `>::Output` when this is an `Expr` representing // `lhs + rhs`. @@ -677,10 +679,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { match const_evaluatable::is_const_evaluatable( self.selcx.infcx, - uv, + alias_const, obligation.param_env, obligation.cause.span, ) { @@ -688,7 +690,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Err(NotConstEvaluatable::MentionsInfer) => { pending_obligation.stalled_on.clear(); pending_obligation.stalled_on.extend( - uv.walk().filter_map(TyOrConstInferVar::maybe_from_generic_arg), + alias_const + .walk() + .filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); ProcessResult::Unchanged } @@ -717,12 +721,12 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); match (c1.kind(), c2.kind()) { - (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) + (ty::ConstKind::Alias(_, a), ty::ConstKind::Alias(_, b)) if a.kind == b.kind && matches!( a.kind, - ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } + ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } ) => { if let Ok(new_obligations) = infcx @@ -741,8 +745,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { )); } } - (_, ty::ConstKind::Unevaluated(_)) - | (ty::ConstKind::Unevaluated(_), _) => (), + (_, ty::ConstKind::Alias(_, _)) | (ty::ConstKind::Alias(_, _), _) => (), (_, _) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) @@ -762,7 +765,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let stalled_on = &mut pending_obligation.stalled_on; let mut evaluate = |c: Const<'tcx>| { - if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { match super::try_evaluate_const( self.selcx.infcx, c, @@ -771,7 +774,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Ok(val) => Ok(val), e @ Err(EvaluateConstErr::HasGenericsOrInfers) => { stalled_on.extend( - unevaluated + alias_const .args .iter() .filter_map(TyOrConstInferVar::maybe_from_generic_arg), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 5ddfaf361940c..a98d06351f258 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -280,6 +280,9 @@ fn do_normalize_predicates<'tcx>( let infcx = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis()); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let predicates = ocx.normalize(&cause, elaborated_env, Unnormalized::new_wip(predicates)); + // FIXME: opaque types in param env might be in defining scope but we're + // using non body analysis for here. So the rigidness marker is wrong. + let predicates = ty::set_aliases_to_non_rigid(tcx, predicates).skip_norm_wip(); let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { @@ -366,8 +369,8 @@ pub fn normalize_param_env_or_error<'tcx>( // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. - if let ty::ConstKind::Unevaluated(uv) = c.kind() - && matches!(uv.kind, ty::UnevaluatedConstKind::Anon { .. }) + if let ty::ConstKind::Alias(_, alias_const) = c.kind() + && matches!(alias_const.kind, ty::AliasConstKind::Anon { .. }) { let infcx = self.0.infer_ctxt().build(TypingMode::non_body_analysis()); let c = evaluate_const(&infcx, c, ty::ParamEnv::empty()); @@ -398,10 +401,10 @@ pub fn normalize_param_env_or_error<'tcx>( // we cannot prove `T: Trait`. // // The same thing is true for const generics- attempting to prove - // `T: Trait` with the same thing as a where clauses + // `T: Trait` with the same thing as a where clauses // will fail. After normalization we may be attempting to prove `T: Trait<4>` with - // the unnormalized where clause `T: Trait`. In order - // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)` + // the unnormalized where clause `T: Trait`. In order + // for the obligation to hold `4` must be equal to `ConstKind::Alias(...)` // but as we do not have lazy norm implemented, equating the two consts fails outright. // // Ideally we would not normalize consts here at all but it is required for backwards @@ -544,7 +547,7 @@ pub fn deeply_normalize_param_env_ignoring_regions<'tcx>( #[derive(Debug)] pub enum EvaluateConstErr { /// The constant being evaluated was either a generic parameter or inference variable, *or*, - /// some unevaluated constant with either generic parameters or inference variables in its + /// some alias const with either generic parameters or inference variables in its /// generic arguments. HasGenericsOrInfers, /// The type this constant evaluated to is not valid for use in const generics. This should @@ -557,7 +560,7 @@ pub enum EvaluateConstErr { } // FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine -// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// FIXME(generic_const_exprs): Consider accepting a `ty::AliasConst` when we are not rolling our own // normalization scheme /// Evaluates a type system constant returning a `ConstKind::Error` in cases where CTFE failed and /// returning the passed in constant if it was not fully concrete (i.e. depended on generic parameters @@ -580,7 +583,7 @@ pub fn evaluate_const<'tcx>( } // FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine -// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// FIXME(generic_const_exprs): Consider accepting a `ty::AliasConst` when we are not rolling our own // normalization scheme /// Evaluates a type system constant making sure to not allow constants that depend on generic parameters /// or inference variables to succeed in evaluating. @@ -605,11 +608,9 @@ pub fn try_evaluate_const<'tcx>( | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), - ty::ConstKind::Unevaluated(uv) => { - let opt_anon_const_kind = match uv.kind { - ty::UnevaluatedConstKind::Anon { def_id } => { - Some((def_id, tcx.anon_const_kind(def_id))) - } + ty::ConstKind::Alias(_, alias_const) => { + let opt_anon_const_kind = match alias_const.kind { + ty::AliasConstKind::Anon { def_id } => Some((def_id, tcx.anon_const_kind(def_id))), _ => None, }; @@ -629,14 +630,14 @@ pub fn try_evaluate_const<'tcx>( // completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason // about if you have to consider gce whatsoever. Some((def_id, ty::AnonConstKind::GCE)) => { - if uv.has_non_region_infer() || uv.has_non_region_param() { + if alias_const.has_non_region_infer() || alias_const.has_non_region_param() { // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause // inference variables and generic parameters to show up in `ty::Const` even though the anon const // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. match tcx.thir_abstract_const(def_id) { Ok(Some(ct)) => { let ct = tcx.expand_abstract_consts( - ct.instantiate(tcx, uv.args).skip_norm_wip(), + ct.instantiate(tcx, alias_const.args).skip_norm_wip(), ); if let Err(e) = ct.error_reported() { return Err(EvaluateConstErr::EvaluationFailure(e)); @@ -645,8 +646,10 @@ pub fn try_evaluate_const<'tcx>( // the generic arguments provided for it, then we should *not* attempt to evaluate it. return Err(EvaluateConstErr::HasGenericsOrInfers); } else { - let args = - replace_param_and_infer_args_with_placeholder(tcx, uv.args); + let args = replace_param_and_infer_args_with_placeholder( + tcx, + alias_const.args, + ); let typing_env = infcx .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); @@ -663,11 +666,11 @@ pub fn try_evaluate_const<'tcx>( let typing_env = infcx .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); - (uv.args, typing_env) + (alias_const.args, typing_env) } } Some((def_id, ty::AnonConstKind::RepeatExprCount)) => { - if uv.has_non_region_infer() { + if alias_const.has_non_region_infer() { // Diagnostics will sometimes replace the identity args of anon consts in // array repeat expr counts with inference variables so we have to handle this // even though it is not something we should ever actually encounter. @@ -703,9 +706,9 @@ pub fn try_evaluate_const<'tcx>( // logic does not go through type system normalization. If it did this would // be a backwards compatibility problem as we do not enforce "syntactic" non- // usage of generic parameters like we do here. - if uv.args.has_non_region_param() - || uv.args.has_non_region_infer() - || uv.args.has_non_region_placeholders() + if alias_const.args.has_non_region_param() + || alias_const.args.has_non_region_infer() + || alias_const.args.has_non_region_placeholders() { return Err(EvaluateConstErr::HasGenericsOrInfers); } @@ -714,19 +717,21 @@ pub fn try_evaluate_const<'tcx>( // to prevent query cycle. let typing_env = ty::TypingEnv::fully_monomorphized(); - (uv.args, typing_env) + (alias_const.args, typing_env) } }; - let uv = ty::UnevaluatedConst::new(tcx, uv.kind, args); - let erased_uv = tcx.erase_and_anonymize_regions(uv); + let alias_const = ty::AliasConst::new(tcx, alias_const.kind, args); + let erased_alias_const = tcx.erase_and_anonymize_regions(alias_const); use rustc_middle::mir::interpret::ErrorHandled; // FIXME: `def_span` will point at the definition of this const; ideally, we'd point at // where it gets used as a const generic. - let span = uv.kind.def_span(tcx); - match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, span) { - Ok(Ok(val)) => Ok(ty::Const::new_value(tcx, val, uv.type_of(tcx).skip_norm_wip())), + let span = alias_const.kind.def_span(tcx); + match tcx.const_eval_resolve_for_typeck(typing_env, erased_alias_const, span) { + Ok(Ok(val)) => { + Ok(ty::Const::new_value(tcx, val, alias_const.type_of(tcx).skip_norm_wip())) + } Ok(Err(_)) => { let e = tcx.dcx().delayed_bug( "Type system constant with non valtree'able type evaluated but no error emitted", @@ -923,7 +928,9 @@ fn is_impossible_associated_item( tcx, ObligationCause::dummy_with_span(*span), param_env, - ty::EarlyBinder::bind(*pred).instantiate(tcx, impl_trait_ref.args).skip_norm_wip(), + ty::EarlyBinder::bind(tcx, *pred) + .instantiate(tcx, impl_trait_ref.args) + .skip_norm_wip(), ) }) }); diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 6dc378718d639..2a28fab776a12 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -229,7 +229,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { ) .ok() .flatten() - .unwrap_or_else(|| proj.to_term(infcx.tcx)); + .unwrap_or_else(|| proj.to_term(infcx.tcx, ty::IsRigid::No)); PlaceholderReplacer::replace_placeholders( infcx, @@ -391,11 +391,14 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + // We don't use the rigid marker in old solver. + debug_assert!(!ty.has_rigid_aliases()); + if !needs_normalization(self.selcx.infcx, &ty) { return ty; } - let ty::Alias(data) = *ty.kind() else { return ty.super_fold_with(self) }; + let ty::Alias(_, data) = *ty.kind() else { return ty.super_fold_with(self) }; // We try to be a little clever here as a performance optimization in // cases where there are nested projections under binders. @@ -459,18 +462,21 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx #[instrument(skip(self), level = "debug")] fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + // We don't use the rigid marker in old solver. + debug_assert!(!ct.has_rigid_aliases()); + let tcx = self.selcx.tcx(); if tcx.features().generic_const_exprs() // Normalize type_const items even with feature `generic_const_exprs`. - && !matches!(ct.kind(), ty::ConstKind::Unevaluated(uv) if uv.kind.is_type_const(tcx)) + && !matches!(ct.kind(), ty::ConstKind::Alias(_, alias_const) if alias_const.kind.is_type_const(tcx)) || !needs_normalization(self.selcx.infcx, &ct) { return ct; } - let uv = match ct.kind() { - ty::ConstKind::Unevaluated(uv) => uv, + let alias_const = match ct.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, _ => return ct.super_fold_with(self), }; @@ -478,20 +484,20 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx // unless a `min_generic_const_args` feature gate error has already // been emitted earlier in compilation. // - // That's because we can only end up with an Unevaluated ty::Const for a const item + // That's because we can only end up with an Alias ty::Const for a const item // if it was marked with `type const`. Using this attribute without the mgca // feature gate causes a parse error. - let ct = match uv.kind { - ty::UnevaluatedConstKind::Projection { .. } => { - self.normalize_trait_projection(uv.into()).expect_const() + let ct = match alias_const.kind { + ty::AliasConstKind::Projection { .. } => { + self.normalize_trait_projection(alias_const.into()).expect_const() } - ty::UnevaluatedConstKind::Inherent { .. } => { - self.normalize_inherent_projection(uv.into()).expect_const() + ty::AliasConstKind::Inherent { .. } => { + self.normalize_inherent_projection(alias_const.into()).expect_const() } - ty::UnevaluatedConstKind::Free { .. } => { - self.normalize_free_alias(uv.into()).expect_const() + ty::AliasConstKind::Free { .. } => { + self.normalize_free_alias(alias_const.into()).expect_const() } - ty::UnevaluatedConstKind::Anon { .. } => { + ty::AliasConstKind::Anon { .. } => { let ct = ct.super_fold_with(self); super::with_replaced_escaping_bound_vars( self.selcx.infcx, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e267d18515a40..e2257703f5775 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -738,7 +738,7 @@ fn project<'cx, 'tcx>( } ProjectionCandidateSet::None => { let tcx = selcx.tcx(); - let term = obligation.predicate.to_term(tcx); + let term = obligation.predicate.to_term(tcx, ty::IsRigid::No); Ok(Projected::NoProgress(term)) } // Error occurred while trying to processing impls. @@ -1583,7 +1583,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>( } else { // We know that `self_ty` has the same metadata as `tail`. This allows us // to prove predicates like `Wrapper::Metadata == Tail::Metadata`. - Ty::new_projection(tcx, metadata_def_id, [tail]) + Ty::new_projection(tcx, ty::IsRigid::No, metadata_def_id, [tail]) } }); (metadata_ty.into(), obligations) @@ -1678,6 +1678,7 @@ fn confirm_closure_candidate<'cx, 'tcx>( tcx.require_lang_item(LangItem::AsyncFnKindUpvars, obligation.cause.span); let tupled_upvars_ty = Ty::new_projection( tcx, + ty::IsRigid::No, upvars_projection_def_id, [ ty::GenericArg::from(kind_ty), @@ -1807,6 +1808,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( // N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`. let tupled_upvars_ty = Ty::new_projection( tcx, + ty::IsRigid::No, upvars_projection_def_id, [ ty::GenericArg::from(kind_ty), @@ -1855,7 +1857,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( sym::Output => { let future_output_def_id = tcx.require_lang_item(LangItem::FutureOutput, obligation.cause.span); - Ty::new_projection(tcx, future_output_def_id, [sig.output()]) + Ty::new_projection(tcx, ty::IsRigid::No, future_output_def_id, [sig.output()]) } name => bug!("no such associated type: {name}"), }; @@ -1889,7 +1891,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( sym::Output => { let future_output_def_id = tcx.require_lang_item(LangItem::FutureOutput, obligation.cause.span); - Ty::new_projection(tcx, future_output_def_id, [sig.output()]) + Ty::new_projection(tcx, ty::IsRigid::No, future_output_def_id, [sig.output()]) } name => bug!("no such associated type: {name}"), }; @@ -2058,7 +2060,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( // `Projected::NoProgress`. This will ensure that the projection is // checked for well-formedness, and it's either satisfied by a trivial // where clause in its env or it results in an error. - return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx))); + return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx, ty::IsRigid::No))); } else { return Ok(Projected::Progress(Progress { term: if obligation.predicate.kind.is_type() { diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 6a6ade3b23b36..a9ed6126ea752 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -391,17 +391,17 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( constraints.dtorck_types.extend( dtorck_types .iter() - .map(|t| EarlyBinder::bind(*t).instantiate(tcx, args).skip_norm_wip()), + .map(|t| EarlyBinder::bind(tcx, *t).instantiate(tcx, args).skip_norm_wip()), ); constraints.outlives.extend( outlives .iter() - .map(|t| EarlyBinder::bind(*t).instantiate(tcx, args).skip_norm_wip()), + .map(|t| EarlyBinder::bind(tcx, *t).instantiate(tcx, args).skip_norm_wip()), ); constraints.overflows.extend( overflows .iter() - .map(|t| EarlyBinder::bind(*t).instantiate(tcx, args).skip_norm_wip()), + .map(|t| EarlyBinder::bind(tcx, *t).instantiate(tcx, args).skip_norm_wip()), ); } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index d8d31fb7be196..e8371a3c0f257 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -203,7 +203,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { return Ok(*ty); } - let &ty::Alias(data) = ty.kind() else { + let &ty::Alias(_, data) = ty.kind() else { let res = ty.try_super_fold_with(self)?; self.cache.insert(ty, res); return Ok(res); @@ -272,21 +272,19 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { return Ok(constant); } - let uv = match constant.kind() { - ty::ConstKind::Unevaluated(uv) => uv, + let alias_const = match constant.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, _ => return constant.try_super_fold_with(self), }; - let constant = match uv.kind { - ty::UnevaluatedConstKind::Anon { .. } => { - crate::traits::with_replaced_escaping_bound_vars( - self.infcx, - &mut self.universes, - constant, - |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), - ) - } - _ => self.try_fold_free_or_assoc(uv.into())?.expect_const(), + let constant = match alias_const.kind { + ty::AliasConstKind::Anon { .. } => crate::traits::with_replaced_escaping_bound_vars( + self.infcx, + &mut self.universes, + constant, + |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), + ), + _ => self.try_fold_free_or_assoc(alias_const.into())?.expect_const(), }; debug!(?constant, ?self.param_env); constant.try_super_fold_with(self) @@ -373,11 +371,11 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { result.normalized_term }; // `tcx.normalize_canonicalized_projection` may normalize to a type that - // still has unevaluated consts, so keep normalizing here if that's the case. + // still has alias consts, so keep normalizing here if that's the case. // Similarly, `tcx.normalize_canonicalized_free_alias` will only unwrap one layer // of type/const and we need to continue folding it to reveal the TAIT behind it - // or further normalize nested unevaluated consts. - if res != term.to_term(tcx) + // or further normalize nested alias consts. + if res != term.to_term(tcx, ty::IsRigid::No) && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || matches!( term.kind, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 3b599db8ff1c2..b278c32cb61ae 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -194,7 +194,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // quickly check if the self-type is a projection at all. match obligation.predicate.skip_binder().trait_ref.self_ty().kind() { // Excluding IATs and type aliases here as they don't have meaningful item bounds. - ty::Alias(ty::AliasTy { kind: ty::Projection { .. } | ty::Opaque { .. }, .. }) => {} + ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. } | ty::Opaque { .. }, .. }) => {} ty::Infer(ty::TyVar(_)) => { span_bug!( obligation.cause.span, @@ -681,7 +681,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // These may potentially implement `FnPtr` ty::Placeholder(..) | ty::Dynamic(_, _) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Infer(_) | ty::Param(..) | ty::Bound(_, _) => {} @@ -784,10 +784,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } ty::Param(..) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, - .. - }) + | ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, + .. + }, + ) | ty::Placeholder(..) | ty::Bound(..) => { // In these cases, we don't know what the actual @@ -838,7 +841,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) => { if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate { .. })) { // We do not generate an auto impl candidate for `impl Trait`s which already // reference our auto trait. diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 6e55b901bfadf..73dc9bff6343d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -771,12 +771,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(EvaluatedToOkModuloRegions) } - ty::PredicateKind::DynCompatible(trait_def_id) => { - if self.tcx().is_dyn_compatible(trait_def_id) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } + ty::PredicateKind::DynCompatible(_) => { + bug!( + "Obligation {obligation:?} should have been handled by fulfillment already." + ) } ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { @@ -852,10 +850,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { match const_evaluatable::is_const_evaluatable( self.infcx, - uv, + alias_const, obligation.param_env, obligation.cause.span, ) { @@ -882,12 +880,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); match (c1.kind(), c2.kind()) { - (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) + (ty::ConstKind::Alias(_, a), ty::ConstKind::Alias(_, b)) if a.kind == b.kind && matches!( a.kind, - ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } + ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } ) => { if let Ok(InferOk { obligations, value: () }) = self @@ -907,8 +905,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } } - (_, ty::ConstKind::Unevaluated(_)) - | (ty::ConstKind::Unevaluated(_), _) => (), + (_, ty::ConstKind::Alias(_, _)) | (ty::ConstKind::Alias(_, _), _) => (), (_, _) => { if let Ok(InferOk { obligations, value: () }) = self .infcx @@ -927,7 +924,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(_) = c.kind() { + if let ty::ConstKind::Alias(_, _) = c.kind() { match crate::traits::try_evaluate_const( self.infcx, c, @@ -987,7 +984,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(uv) => uv.type_of(self.tcx()).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(self.tcx()).skip_norm_wip() + } // FIXME(generic_const_exprs): See comment in `fulfill.rs` ty::ConstKind::Expr(_) => return Ok(EvaluatedToOk), ty::ConstKind::Placeholder(_) => { @@ -1658,6 +1657,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { loop { let (alias_ty, def_id) = match *self_ty.kind() { ty::Alias( + _, alias_ty @ ty::AliasTy { kind: ty::Projection { def_id } | ty::Opaque { def_id }, .. @@ -2348,10 +2348,13 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::Placeholder(..) | ty::Dynamic(..) | ty::Param(..) - | ty::Alias(ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, - .. - }) + | ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, + .. + }, + ) | ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble constituent types of unexpected type: {:?}", t); @@ -2420,7 +2423,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { assumptions: vec![], }), - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { if self.infcx.can_define_opaque_ty(def_id) { unreachable!() } else { diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 6c1236bec8320..c73974d661365 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -41,11 +41,16 @@ impl<'tcx> At<'_, 'tcx> { if self.infcx.next_trait_solver() { let term = term.skip_normalization(); + + if !self.infcx.tcx.renormalize_rigid_aliases() && !term.is_non_rigid_alias() { + return Ok(term); + }; + let Some(alias) = term.to_alias_term() else { return Ok(term); }; - let new_infer = self.infcx.next_term_var_of_kind(term, self.cause.span); + let new_infer = self.infcx.next_term_var_of_alias_kind(alias, self.cause.span); // We simply emit an `Projection` goal here, since that will take care of // normalizing the LHS of the projection until it is a rigid projection diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 8eec1d85a0ea0..a47f933f5c25e 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -284,7 +284,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( }; let ty_to_impl_span = |ty: Ty<'_>| { - if let ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, .. }) = ty.kind() + if let ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, .. }) = ty.kind() && let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(def_id) && let Some(impl_item) = items.iter().find(|item| item.owner_id.to_def_id() == impl_item_id) @@ -801,15 +801,18 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // Simple cases that are WF if their type args are WF. } - ty::Alias(ty::AliasTy { - kind: ty::Projection { def_id } | ty::Opaque { def_id } | ty::Free { def_id }, - args, - .. - }) => { + ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { def_id } | ty::Opaque { def_id } | ty::Free { def_id }, + args, + .. + }, + ) => { let obligations = self.nominal_obligations(def_id, args); self.out.extend(obligations); } - ty::Alias(data @ ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { + ty::Alias(_, data @ ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { self.add_wf_preds_for_inherent_projection(data.into()); return; // Subtree handled by compute_inherent_projection. } @@ -1061,10 +1064,11 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { let tcx = self.tcx(); match c.kind() { - ty::ConstKind::Unevaluated(uv) => { + ty::ConstKind::Alias(_, alias_const) => { if !c.has_escaping_bound_vars() { // Skip type consts as mGCA doesn't support evaluatable clauses - if !uv.kind.is_type_const(tcx) && !tcx.features().generic_const_args() { + if !alias_const.kind.is_type_const(tcx) && !tcx.features().generic_const_args() + { let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( ty::ClauseKind::ConstEvaluatable(c), )); @@ -1078,15 +1082,15 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); } - match uv.kind { - ty::UnevaluatedConstKind::Inherent { .. } => { - self.add_wf_preds_for_inherent_projection(uv.into()); + match alias_const.kind { + ty::AliasConstKind::Inherent { .. } => { + self.add_wf_preds_for_inherent_projection(alias_const.into()); return; // Subtree is handled by above function } - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { - let obligations = self.nominal_obligations(def_id, uv.args); + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { + let obligations = self.nominal_obligations(def_id, alias_const.args); self.out.extend(obligations); } } @@ -1108,7 +1112,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::ConstKind::Expr(_) => { // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the // trait bound `typeof(N): Add` holds. This is currently unnecessary - // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Alias` // which means that the `DefId` would have been typeck'd elsewhere. However in // the future we may allow directly lowering to `ConstKind::Expr` in which case // we would not be proving bounds we should. diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index 5bfbda9d90e6b..c2f517d6019c6 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -38,10 +38,13 @@ pub(crate) fn coroutine_hidden_types<'tcx>( let assumptions = compute_assumptions(tcx, def_id, bound_tys); - ty::EarlyBinder::bind(ty::Binder::bind_with_vars( - ty::CoroutineWitnessTypes { types: bound_tys, assumptions }, - tcx.mk_bound_variable_kinds(&vars), - )) + ty::EarlyBinder::bind( + tcx, + ty::Binder::bind_with_vars( + ty::CoroutineWitnessTypes { types: bound_tys, assumptions }, + tcx.mk_bound_variable_kinds(&vars), + ), + ) } fn compute_assumptions<'tcx>( diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml index e61717e5e9ce2..5da9435e77e63 100644 --- a/compiler/rustc_transmute/Cargo.toml +++ b/compiler/rustc_transmute/Cargo.toml @@ -16,7 +16,7 @@ tracing = "0.1" [dev-dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index 682cf941561fc..45e71d8a38860 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index e782557d126bf..51db1b8efde2f 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -10,7 +10,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ FnAbiError, HasTyCtxt, HasTypingEnv, LayoutCx, LayoutOf, TyAndLayout, fn_can_unwind, }; -use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, InstanceKind, ShimKind, Ty, TyCtxt, Unnormalized}; use rustc_span::DUMMY_SP; use rustc_span::def_id::DefId; use rustc_target::callconv::{ @@ -38,7 +38,7 @@ fn fn_sig_for_fn_abi<'tcx>( instance: ty::Instance<'tcx>, typing_env: ty::TypingEnv<'tcx>, ) -> ty::FnSig<'tcx> { - if let InstanceKind::ThreadLocalShim(..) = instance.def { + if let InstanceKind::Shim(ShimKind::ThreadLocal(..)) = instance.def { return tcx.mk_fn_sig_safe_rust_abi([], tcx.thread_local_ptr_ty(instance.def_id())); } @@ -50,7 +50,7 @@ fn fn_sig_for_fn_abi<'tcx>( ); // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. - if let ty::InstanceKind::VTableShim(..) = instance.def { + if let ty::InstanceKind::Shim(ty::ShimKind::VTable(..)) = instance.def { let mut inputs_and_output = sig.inputs_and_output.to_vec(); inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); @@ -82,22 +82,23 @@ fn fn_sig_for_fn_abi<'tcx>( // a separate def-id for these bodies. let mut coroutine_kind = args.as_coroutine_closure().kind(); - let env_ty = - if let InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref, .. } = - instance.def - { - coroutine_kind = ty::ClosureKind::FnOnce; - - // Implementations of `FnMut` and `Fn` for coroutine-closures - // still take their receiver by ref. - if receiver_by_ref { - Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty) - } else { - coroutine_ty - } + let env_ty = if let InstanceKind::Shim(ShimKind::ConstructCoroutineInClosure { + receiver_by_ref, + .. + }) = instance.def + { + coroutine_kind = ty::ClosureKind::FnOnce; + + // Implementations of `FnMut` and `Fn` for coroutine-closures + // still take their receiver by ref. + if receiver_by_ref { + Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty) } else { - tcx.closure_env_ty(coroutine_ty, coroutine_kind, tcx.lifetimes.re_erased) - }; + coroutine_ty + } + } else { + tcx.closure_env_ty(coroutine_ty, coroutine_kind, tcx.lifetimes.re_erased) + }; let sig = tcx.instantiate_bound_regions_with_erased(sig); @@ -264,7 +265,8 @@ impl<'tcx> FnAbiDesc<'tcx> { ) -> Self { let ty::PseudoCanonicalInput { typing_env, value: (instance, extra_args) } = query; let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); - let is_tls_shim_call = matches!(instance.def, ty::InstanceKind::ThreadLocalShim(_)); + let is_tls_shim_call = + matches!(instance.def, ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(_))); Self { layout_cx: LayoutCx::new(tcx, typing_env), sig: tcx.normalize_erasing_regions( @@ -458,8 +460,10 @@ fn fn_abi_sanity_check<'tcx>( // omitted entirely in the calling convention. assert!(arg.is_ignore()); } - if let PassMode::Indirect { on_stack, .. } = arg.mode { - assert!(!on_stack, "rust abi shouldn't use on_stack"); + if let PassMode::Indirect { on_stack, .. } = arg.mode + && spec_abi != ExternAbi::RustTail + { + assert!(!on_stack, "rustic abi {spec_abi:?} shouldn't use on_stack"); } } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { assert_matches!( diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 85bc9b985a277..6f7a17c1e4a92 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -70,12 +70,9 @@ fn recurse_build<'tcx>( } &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty), &ExprKind::NamedConst { def_id, args, user_ty: _ } => { - let uneval = ty::UnevaluatedConst::new( - tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, def_id), - args, - ); - ty::Const::new_unevaluated(tcx, uneval) + let uneval = + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, def_id), args); + ty::Const::new_alias(tcx, ty::IsRigid::No, uneval) } ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), @@ -385,7 +382,7 @@ fn thir_abstract_const<'tcx>( let root_span = body.exprs[body_id].span; - Ok(Some(ty::EarlyBinder::bind(recurse_build(tcx, body, body_id, root_span)?))) + Ok(Some(ty::EarlyBinder::bind(tcx, recurse_build(tcx, body, body_id, root_span)?))) } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 659229a58d539..e83525c16b965 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -116,7 +116,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' tcx.impl_trait_ref(impl_def_id).instantiate_identity().skip_norm_wip().args, ); tcx.arena.alloc_from_iter( - ty::EarlyBinder::bind(tcx.assumed_wf_types_for_rpitit(rpitit_def_id)) + ty::EarlyBinder::bind_iter(tcx.assumed_wf_types_for_rpitit(rpitit_def_id)) .iter_instantiated_copied(tcx, args) .map(Unnormalized::skip_norm_wip) .chain(tcx.assumed_wf_types(impl_def_id).into_iter().copied()), diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 7386afbe53771..34a3a96f89c40 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -38,16 +38,16 @@ fn resolve_instance_raw<'tcx>( } else if tcx.is_lang_item(def_id, LangItem::DropGlue) { let ty = args.type_at(0); - if ty.needs_drop(tcx, typing_env) { + let shim = if ty.needs_drop(tcx, typing_env) { debug!(" => nontrivial drop glue"); match *ty.kind() { ty::Coroutine(coroutine_def_id, ..) => { // FIXME: sync drop of coroutine with async drop (generate both versions?) // Currently just ignored if tcx.optimized_mir(coroutine_def_id).coroutine_drop_async().is_some() { - ty::InstanceKind::DropGlue(def_id, None) + ty::ShimKind::DropGlue(def_id, None) } else { - ty::InstanceKind::DropGlue(def_id, Some(ty)) + ty::ShimKind::DropGlue(def_id, Some(ty)) } } ty::Closure(..) @@ -57,14 +57,15 @@ fn resolve_instance_raw<'tcx>( | ty::Dynamic(..) | ty::Array(..) | ty::Slice(..) - | ty::UnsafeBinder(..) => ty::InstanceKind::DropGlue(def_id, Some(ty)), + | ty::UnsafeBinder(..) => ty::ShimKind::DropGlue(def_id, Some(ty)), // Drop shims can only be built from ADTs. _ => return Ok(None), } } else { debug!(" => trivial drop glue"); - ty::InstanceKind::DropGlue(def_id, None) - } + ty::ShimKind::DropGlue(def_id, None) + }; + ty::InstanceKind::Shim(shim) } else if tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace) { let ty = args.type_at(0); @@ -82,14 +83,14 @@ fn resolve_instance_raw<'tcx>( _ => return Ok(None), } debug!(" => nontrivial async drop glue ctor"); - ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) + ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(def_id, ty)) } else { debug!(" => trivial async drop glue ctor"); - ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) + ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlueCtor(def_id, ty)) } } else if tcx.is_async_drop_in_place_coroutine(def_id) { let ty = args.type_at(0); - ty::InstanceKind::AsyncDropGlue(def_id, ty) + ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(def_id, ty)) } else { debug!(" => free item"); ty::InstanceKind::Item(def_id) @@ -279,7 +280,7 @@ fn resolve_associated_item<'tcx>( }; Some(Instance { - def: ty::InstanceKind::CloneShim(trait_item_id, self_ty), + def: ty::InstanceKind::Shim(ty::ShimKind::Clone(trait_item_id, self_ty)), args: rcvr_args, }) } else { @@ -296,7 +297,10 @@ fn resolve_associated_item<'tcx>( return Ok(None); } Some(Instance { - def: ty::InstanceKind::FnPtrAddrShim(trait_item_id, self_ty), + def: ty::InstanceKind::Shim(ty::ShimKind::FnPtrAddr( + trait_item_id, + self_ty, + )), args: rcvr_args, }) } else { @@ -326,7 +330,10 @@ fn resolve_associated_item<'tcx>( Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind)) } ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { - def: ty::InstanceKind::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), + def: ty::InstanceKind::Shim(ty::ShimKind::FnPtr( + trait_item_id, + rcvr_args.type_at(0), + )), args: rcvr_args, }), ty::CoroutineClosure(coroutine_closure_def_id, args) => { @@ -339,10 +346,12 @@ fn resolve_associated_item<'tcx>( Some(Instance::new_raw(coroutine_closure_def_id, args)) } else { Some(Instance { - def: ty::InstanceKind::ConstructCoroutineInClosureShim { - coroutine_closure_def_id, - receiver_by_ref: target_kind != ty::ClosureKind::FnOnce, - }, + def: ty::InstanceKind::Shim( + ty::ShimKind::ConstructCoroutineInClosure { + coroutine_closure_def_id, + receiver_by_ref: target_kind != ty::ClosureKind::FnOnce, + }, + ), args, }) } @@ -362,10 +371,12 @@ fn resolve_associated_item<'tcx>( // If we're computing `AsyncFnOnce` for a by-ref closure then // construct a new body that has the right return types. Some(Instance { - def: ty::InstanceKind::ConstructCoroutineInClosureShim { - coroutine_closure_def_id, - receiver_by_ref: false, - }, + def: ty::InstanceKind::Shim( + ty::ShimKind::ConstructCoroutineInClosure { + coroutine_closure_def_id, + receiver_by_ref: false, + }, + ), args, }) } else { @@ -376,7 +387,10 @@ fn resolve_associated_item<'tcx>( Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind)) } ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { - def: ty::InstanceKind::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), + def: ty::InstanceKind::Shim(ty::ShimKind::FnPtr( + trait_item_id, + rcvr_args.type_at(0), + )), args: rcvr_args, }), _ => bug!( diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index eb0cc98736adb..cdf280381247a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -39,38 +39,45 @@ fn layout_of<'tcx>( tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, ) -> Result, &'tcx LayoutError<'tcx>> { - let PseudoCanonicalInput { typing_env, value: ty } = query; - debug!(?ty); + let PseudoCanonicalInput { typing_env: original_typing_env, value: original_ty } = query; + debug!(?original_ty); // Optimization: We convert to TypingMode::PostAnalysis and convert opaque types in // the where bounds to their hidden types. This reduces overall uncached invocations // of `layout_of` and is thus a small performance improvement. - let typing_env = typing_env.with_post_analysis_normalized(tcx); - let unnormalized_ty = ty; + let typing_env = original_typing_env.with_post_analysis_normalized(tcx); + // Switching to `PostAnalysis` typing mode will reveal opaque types that's marked + // as rigid in the original typing env. + let unnormalized_ty = if typing_env != original_typing_env { + ty::set_aliases_to_non_rigid(tcx, original_ty) + } else { + ty::Unnormalized::new_wip(original_ty) + }; // FIXME: We might want to have two different versions of `layout_of`: // One that can be called after typecheck has completed and can use // `normalize_erasing_regions` here and another one that can be called // before typecheck has completed and uses `try_normalize_erasing_regions`. - let ty = match tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) { + let normalized_ty = match tcx.try_normalize_erasing_regions(typing_env, unnormalized_ty) { Ok(t) => t, Err(normalization_error) => { - return Err(tcx - .arena - .alloc(LayoutError::NormalizationFailure(ty, normalization_error))); + return Err(tcx.arena.alloc(LayoutError::NormalizationFailure( + unnormalized_ty.skip_normalization(), + normalization_error, + ))); } }; - if ty != unnormalized_ty { + if normalized_ty != original_ty { // Ensure this layout is also cached for the normalized type. - return tcx.layout_of(typing_env.as_query_input(ty)); + return tcx.layout_of(typing_env.as_query_input(normalized_ty)); } match typing_env.typing_mode() { ty::TypingMode::Codegen => { let with_postanalysis = ty::TypingEnv::new(typing_env.param_env, ty::TypingMode::PostAnalysis); - let res = tcx.layout_of(with_postanalysis.as_query_input(ty)); + let res = tcx.layout_of(with_postanalysis.as_query_input(normalized_ty)); match res { Err(LayoutError::TooGeneric(_)) => {} _ => return res, @@ -86,8 +93,8 @@ fn layout_of<'tcx>( let cx = LayoutCx::new(tcx, typing_env); - let layout = layout_of_uncached(&cx, ty)?; - let layout = TyAndLayout { ty, layout }; + let layout = layout_of_uncached(&cx, normalized_ty)?; + let layout = TyAndLayout { ty: normalized_ty, layout }; // If we are running with `-Zprint-type-sizes`, maybe record layouts // for dumping later. @@ -170,7 +177,7 @@ fn extract_const_value<'tcx>( } Err(error(cx, LayoutError::TooGeneric(ty))) } - ty::ConstKind::Unevaluated(_) => { + ty::ConstKind::Alias(_, _) => { let err = if ct.has_param() { LayoutError::TooGeneric(ty) } else { @@ -437,7 +444,8 @@ fn layout_of_uncached<'tcx>( } let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() { - let pointee_metadata = Ty::new_projection(tcx, metadata_def_id, [pointee]); + let pointee_metadata = + Ty::new_projection(tcx, ty::IsRigid::No, metadata_def_id, [pointee]); let metadata_ty = match tcx.try_normalize_erasing_regions( cx.typing_env, Unnormalized::new_wip(pointee_metadata), @@ -557,7 +565,7 @@ fn layout_of_uncached<'tcx>( .field_tys .iter() .map(|local| { - let field_ty = EarlyBinder::bind(local.ty); + let field_ty = EarlyBinder::bind(tcx, local.ty); let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty.instantiate(tcx, args).skip_norm_wip()); cx.spanned_layout_of(uninit_ty, local.source_info.span) diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 8c07a7ec3d2de..decf1ffb5570d 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -84,7 +84,7 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { // Verify the ABI-mandated alignment and size for scalars. - let align = layout.backend_repr.scalar_align(cx); + let align = layout.backend_repr.scalar_platform_align(cx); let size = layout.backend_repr.scalar_size(cx); if let Some(align) = align { assert_eq!( @@ -208,9 +208,9 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou }; // The fields should be at the right offset, and match the `scalar` layout. let size1 = scalar1.size(cx); - let align1 = scalar1.align(cx).abi; + let align1 = scalar1.default_align(cx).abi; let size2 = scalar2.size(cx); - let align2 = scalar2.align(cx).abi; + let align2 = scalar2.default_align(cx).abi; assert_eq!( offset1, Size::ZERO, @@ -251,7 +251,7 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou BackendRepr::SimdVector { element, count } => { let align = layout.align.abi; let size = layout.size; - let element_align = element.align(cx).abi; + let element_align = element.default_align(cx).abi; let element_size = element.size(cx); // Currently, vectors must always be aligned to at least their elements: assert!(align >= element_align); @@ -321,7 +321,7 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou } // The top-level ABI and the ABI of the variants should be coherent. let scalar_coherent = |s1: Scalar, s2: Scalar| { - s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx) + s1.size(cx) == s2.size(cx) && s1.default_align(cx) == s2.default_align(cx) }; let abi_coherent = match (layout.backend_repr, variant.backend_repr) { (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2), diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index e2529e7a77226..136fe09929eef 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -222,7 +222,7 @@ where for field_ty in &witness.field_tys { queue_type( self, - EarlyBinder::bind(field_ty.ty) + EarlyBinder::bind(tcx, field_ty.ty) .instantiate(tcx, args) .skip_norm_wip(), ); @@ -374,7 +374,9 @@ fn drop_tys_helper<'tcx>( match subty.kind() { ty::Adt(adt_id, args) => { for subty in tcx.adt_drop_tys(adt_id.did())? { - vec.push(EarlyBinder::bind(subty).instantiate(tcx, args).skip_norm_wip()); + vec.push( + EarlyBinder::bind(tcx, subty).instantiate(tcx, args).skip_norm_wip(), + ); } } _ => vec.push(subty), diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index d95624b9a24d2..bac5d47e98676 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -210,14 +210,14 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) { t.super_visit_with(self); match *t.kind() { - ty::Alias(alias_ty @ ty::AliasTy { kind: ty::Opaque { def_id }, .. }) + ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Opaque { def_id }, .. }) if def_id.is_local() => { self.visit_opaque_ty(alias_ty); } // Skips type aliases, as they are meant to be transparent. // FIXME(type_alias_impl_trait): can we require mentioning nested type aliases explicitly? - ty::Alias(ty::AliasTy { kind: ty::Free { def_id }, args, .. }) + ty::Alias(_, ty::AliasTy { kind: ty::Free { def_id }, args, .. }) if let Some(def_id) = def_id.as_local() => { if !self.seen.insert(def_id) { @@ -230,6 +230,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { .visit_with(self); } ty::Alias( + _, alias_ty @ ty::AliasTy { kind: ty::Projection { def_id: alias_def_id }, .. }, ) => { // This avoids having to do normalization of `Self::AssocTy` by only @@ -299,7 +300,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { .type_of(alias_def_id) .instantiate(self.tcx, alias_ty.args) .skip_norm_wip(); - let ty::Alias(alias_ty @ ty::AliasTy { kind: ty::Opaque { .. }, .. }) = + let ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Opaque { .. }, .. }) = *ty.kind() else { bug!("{ty:?}") @@ -316,6 +317,13 @@ fn opaque_types_defined_by<'tcx>( tcx: TyCtxt<'tcx>, item: LocalDefId, ) -> &'tcx ty::List { + // Closures and coroutines are type checked with their parent + // Note that we also support `SyntheticCoroutineBody` since we create + // a MIR body for the def kind, and some MIR passes (like promotion) + // may require doing analysis using its typing env. + if tcx.is_typeck_child(item.to_def_id()) { + return tcx.opaque_types_defined_by(tcx.local_parent(item)); + } let kind = tcx.def_kind(item); trace!(?kind); let mut collector = OpaqueTypeCollector::new(tcx, item); @@ -328,16 +336,13 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { + | DefKind::AnonConst + | DefKind::InlineConst => { + // Non-type-system inline consts should be caught by `if tcx.is_typeck_child` above + debug_assert!(kind != DefKind::InlineConst || tcx.is_type_system_inline_const(item)); + collector.collect_taits_declared_in_body(); } - // Closures and coroutines are type checked with their parent - // Note that we also support `SyntheticCoroutineBody` since we create - // a MIR body for the def kind, and some MIR passes (like promotion) - // may require doing analysis using its typing env. - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => { - collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item))); - } DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} DefKind::OpaqueTy | DefKind::Mod @@ -348,6 +353,8 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Trait | DefKind::ForeignTy | DefKind::TraitAlias + | DefKind::Closure + | DefKind::SyntheticCoroutineBody | DefKind::TyParam | DefKind::ConstParam | DefKind::Ctor(_, _) diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index ebec1888e6a95..f60a03fa71b26 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -21,6 +21,24 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( ) -> V::Result { let kind = tcx.def_kind(item); trace!(?kind); + let mut visit_alias = || { + if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { + // If the type of the item uses `_`, we're gonna error out anyway, but + // typeck (which type_of invokes below), will call back into opaque_types_defined_by + // causing a cycle. So we just bail out in this case. + if ty.is_suggestable_infer_ty() { + return V::Result::output(); + } + // Associated types in traits don't necessarily have a type that we can visit + try_visit!( + visitor.visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) + ); + } + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { + try_visit!(visitor.visit(span, pred.skip_norm_wip())); + } + V::Result::output() + }; match kind { // Walk over the signature of the function DefKind::AssocFn | DefKind::Fn => { @@ -47,24 +65,8 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { - if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { - // If the type of the item uses `_`, we're gonna error out anyway, but - // typeck (which type_of invokes below), will call back into opaque_types_defined_by - // causing a cycle. So we just bail out in this case. - if ty.is_suggestable_infer_ty() { - return V::Result::output(); - } - // Associated types in traits don't necessarily have a type that we can visit - try_visit!( - visitor - .visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) - ); - } - for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { - try_visit!(visitor.visit(span, pred.skip_norm_wip())); - } - } + | DefKind::AnonConst => return visit_alias(), + DefKind::InlineConst if tcx.is_type_system_inline_const(item) => return visit_alias(), DefKind::OpaqueTy => { for (pred, span) in tcx .explicit_item_bounds(item) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 504dedf8902fd..29bda55bbbaab 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -144,11 +144,14 @@ fn adt_sizedness_constraint<'tcx>( return None; } - Some(ty::EarlyBinder::bind(constraint_ty)) + Some(ty::EarlyBinder::bind(tcx, constraint_ty)) } /// See `ParamEnv` struct definition for details. fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { + if tcx.is_typeck_child(def_id) { + return tcx.param_env(tcx.typeck_root_def_id(def_id)); + } // Compute the bounds on Self and the type parameters. let ty::InstantiatedPredicates { predicates, .. } = tcx.predicates_of(def_id).instantiate_identity(tcx); @@ -223,7 +226,7 @@ impl<'tcx> TypeVisitor> for ImplTraitInTraitFinder<'_, 'tcx> { } fn visit_ty(&mut self, ty: Ty<'tcx>) { - if let ty::Alias(unshifted_alias_ty) = *ty.kind() + if let ty::Alias(_, unshifted_alias_ty) = *ty.kind() && let Some(unshifted_alias_ty) = unshifted_alias_ty.try_to_projection() && let Some( ty::ImplTraitInTraitData::Trait { fn_def_id, .. } @@ -387,7 +390,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) | ty::CoroutineWitness(_, _) | ty::Never | ty::Tuple(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Bound(_, _) | ty::Placeholder(_) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index dbd8beed2ed26..6f41a608763e3 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -369,11 +369,33 @@ generate!( impl !TypeVisitable for ty::EarlyBinder {} ); -impl EarlyBinder { - pub fn bind(value: T) -> EarlyBinder { +impl> EarlyBinder { + pub fn bind(cx: I, value: T) -> EarlyBinder { + // Instantiation will require normalization. + let value = ty::set_aliases_to_non_rigid(cx, value).skip_normalization(); + EarlyBinder { value, _tcx: PhantomData } + } +} + +impl> + Clone> EarlyBinder { + pub fn bind_iter(value: T) -> EarlyBinder { + #[cfg(debug_assertions)] + { + value.clone().into_iter().for_each(|v| assert!(!v.has_rigid_aliases())); + } + + EarlyBinder { value, _tcx: PhantomData } + } +} + +impl> EarlyBinder { + pub fn bind_no_rigid_aliases(value: T) -> EarlyBinder { + debug_assert!(!value.has_rigid_aliases()); EarlyBinder { value, _tcx: PhantomData } } +} +impl EarlyBinder { pub fn as_ref(&self) -> EarlyBinder { EarlyBinder { value: &self.value, _tcx: PhantomData } } @@ -426,6 +448,12 @@ impl EarlyBinder { } } +impl EarlyBinder> { + pub fn def_id(&self) -> I::TraitId { + self.value.def_id + } +} + impl EarlyBinder> { pub fn transpose(self) -> Option> { self.value.map(|value| EarlyBinder { value, _tcx: PhantomData }) @@ -523,6 +551,14 @@ pub struct IterInstantiatedCopied<'a, I: Interner, Iter: IntoIterator> { args: &'a [I::GenericArg], } +impl<'a, I: Interner, Iter: IntoIterator> Clone + for IterInstantiatedCopied<'a, I, Iter> +{ + fn clone(&self) -> IterInstantiatedCopied<'a, I, Iter> { + IterInstantiatedCopied { it: self.it.clone(), cx: self.cx, args: self.args } + } +} + impl Iterator for IterInstantiatedCopied<'_, I, Iter> where Iter::Item: Deref, @@ -567,6 +603,12 @@ pub struct IterIdentityCopied { _tcx: PhantomData I>, } +impl> Clone for IterIdentityCopied { + fn clone(&self) -> IterIdentityCopied { + IterIdentityCopied { it: self.it.clone(), _tcx: self._tcx } + } +} + impl Iterator for IterIdentityCopied where Iter::Item: Deref, diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 2955aff10eb6a..0645839ef0263 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -9,7 +9,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use crate::{self as ty, BoundVarIndexKind, Interner, UnevaluatedConst}; +use crate::{self as ty, AliasConst, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] @@ -34,7 +34,7 @@ pub enum ConstKind { /// An unnormalized const item such as an anon const or assoc const or free const item. /// Right now anything other than anon consts does not actually work properly but this /// should - Unevaluated(ty::UnevaluatedConst), + Alias(ty::IsRigid, ty::AliasConst), /// Used to hold computed value. Value(I::ValueConst), @@ -43,7 +43,7 @@ pub enum ConstKind { /// propagated to avoid useless error messages. Error(I::ErrorGuaranteed), - /// Unevaluated non-const-item, used by `feature(generic_const_exprs)` to represent + /// A non-const-item expression awaiting evaluation, used by `feature(generic_const_exprs)` to represent /// const arguments such as `N + 1` or `foo(N)` Expr(I::ExprConst), } @@ -59,7 +59,9 @@ impl fmt::Debug for ConstKind { Infer(var) => write!(f, "{var:?}"), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), - Unevaluated(uv) => write!(f, "{uv:?}"), + Alias(is_rigid, alias_const) => { + write!(f, "AliasConst({is_rigid:?}, {alias_const:?})") + } Value(val) => write!(f, "{val:?}"), Error(_) => write!(f, "{{const error}}"), Expr(expr) => write!(f, "{expr:?}"), @@ -67,46 +69,42 @@ impl fmt::Debug for ConstKind { } } -impl UnevaluatedConst { +impl AliasConst { #[inline] - pub fn new( - interner: I, - kind: UnevaluatedConstKind, - args: I::GenericArgs, - ) -> UnevaluatedConst { + pub fn new(interner: I, kind: AliasConstKind, args: I::GenericArgs) -> AliasConst { if cfg!(debug_assertions) { let def_id = match kind { - ty::UnevaluatedConstKind::Projection { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Inherent { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Free { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Anon { def_id } => def_id.into(), + ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::Free { def_id } => def_id.into(), + ty::AliasConstKind::Anon { def_id } => def_id.into(), }; interner.debug_assert_args_compatible(def_id, args); } - UnevaluatedConst { kind, args, _use_alias_new_instead: () } + AliasConst { kind, args, _use_alias_new_instead: () } } pub fn type_of(self, interner: I) -> ty::Unnormalized { let def_id = match self.kind { - ty::UnevaluatedConstKind::Projection { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Inherent { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Free { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Anon { def_id } => def_id.into(), + ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::Free { def_id } => def_id.into(), + ty::AliasConstKind::Anon { def_id } => def_id.into(), }; interner.type_of(def_id).instantiate(interner, self.args) } } -/// UnevaluatedConstKind is extremely similar to AliasTyKind, and likely should be reasoned about +/// AliasConstKind is extremely similar to AliasTyKind, and likely should be reasoned about /// and handled in very similar ways. The documentation for AliasTyKind/etc. may be helpful when -/// learning about UnevaluatedConstKind. +/// learning about AliasConstKind. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] -pub enum UnevaluatedConstKind { +pub enum AliasConstKind { /// A projection `::AssocConst` Projection { def_id: I::TraitAssocConstId }, /// An associated constant in an inherent `impl` @@ -114,38 +112,38 @@ pub enum UnevaluatedConstKind { /// A free constant, outside an impl block. Free { def_id: I::FreeConstAliasId }, /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`. - Anon { def_id: I::UnevaluatedConstId }, + Anon { def_id: I::AnonConstId }, } -impl UnevaluatedConstKind { +impl AliasConstKind { pub fn new_from_def_id(interner: I, def_id: I::DefId) -> Self { - interner.unevaluated_const_kind_from_def_id(def_id) + interner.alias_const_kind_from_def_id(def_id) } pub fn is_type_const(self, interner: I) -> bool { match self { - UnevaluatedConstKind::Projection { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Free { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Projection { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Inherent { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Free { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Anon { def_id } => interner.is_type_const(def_id.into()), } } pub fn def_span(self, interner: I) -> I::Span { match self { - UnevaluatedConstKind::Projection { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Free { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Projection { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Inherent { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Free { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Anon { def_id } => interner.def_span(def_id.into()), } } pub fn opt_def_id(self) -> Option { match self { - UnevaluatedConstKind::Projection { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Free { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => Some(def_id.into()), + AliasConstKind::Projection { def_id } => Some(def_id.into()), + AliasConstKind::Inherent { def_id } => Some(def_id.into()), + AliasConstKind::Free { def_id } => Some(def_id.into()), + AliasConstKind::Anon { def_id } => Some(def_id.into()), } } } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index be3661518d7d9..aae921249b4f4 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -276,7 +276,7 @@ fn elaborate_component_to_clause( // We might end up here if we have `Foo<::Assoc>: 'a`. // With this, we can deduce that `::Assoc: 'a`. Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - alias_ty.to_ty(cx), + alias_ty.to_ty(cx, ty::IsRigid::No), outlives_region, ))) } @@ -414,7 +414,10 @@ pub fn elaborate_outlives_assumptions( } Component::Alias(alias_ty) => { - collected.insert(ty::OutlivesPredicate(alias_ty.to_ty(cx).into(), r2)); + collected.insert(ty::OutlivesPredicate( + alias_ty.to_ty(cx, ty::IsRigid::No).into(), + r2, + )); } Component::UnresolvedInferenceVariable(_) | Component::EscapingAlias(_) => { diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index aa4785439813f..74abfad6e5abc 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -2,7 +2,7 @@ use derive_where::derive_where; use rustc_abi::ExternAbi; use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; -use crate::solve::NoSolution; +use crate::solve::{NoSolution, NoSolutionOrRerunNonErased}; use crate::{self as ty, Interner}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -41,6 +41,7 @@ pub enum TypeError { ArgumentSorts(ExpectedFound, usize), Traits(ExpectedFound), VariadicMismatch(ExpectedFound), + SplatMismatch(ExpectedFound>), /// Instantiating a type variable with the given type would have /// created a cycle (because it appears somewhere within that @@ -76,7 +77,7 @@ impl TypeError { match self { CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | ArraySize(_) | ArgumentSorts(..) | Sorts(_) - | VariadicMismatch(_) | TargetFeatureCast(_) => false, + | VariadicMismatch(_) | SplatMismatch(_) | TargetFeatureCast(_) => false, Mutability | ArgumentMutability(_) @@ -100,3 +101,9 @@ impl From> for NoSolution { NoSolution } } + +impl From> for NoSolutionOrRerunNonErased { + fn from(_: TypeError) -> NoSolutionOrRerunNonErased { + NoSolutionOrRerunNonErased::NoSolution(NoSolution) + } +} diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 26d98b5d8ad7d..a514cf9d9052f 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -256,12 +256,12 @@ impl { + ty::Param(_) | ty::Alias(ty::IsRigid::Yes, _) => { if INSTANTIATE_RHS_WITH_INFER { return true; } } - ty::Error(_) | ty::Alias(..) | ty::Bound(..) => return true, + ty::Error(_) | ty::Alias(ty::IsRigid::No, _) | ty::Bound(..) => return true, ty::Infer(var) => return self.var_and_ty_may_unify(var, lhs), // These types only unify with inference variables or their own @@ -338,12 +338,22 @@ impl self.var_and_ty_may_unify(var, rhs), + // Since we ensure that the rhs is not non-rigid alias, + // lhs rigid alias can only unify with it if it's a rigid alias of the same kind. + ty::Alias(ty::IsRigid::Yes, lhs_alias) => { + INSTANTIATE_LHS_WITH_INFER + || match rhs.kind() { + ty::Alias(ty::IsRigid::Yes, rhs_alias) => { + lhs_alias.kind == rhs_alias.kind + && self.args_may_unify_inner(lhs_alias.args, rhs_alias.args, depth) + } + _ => false, + } + } // As we're walking the whole type, it may encounter projections // inside of binders and what not, so we're just going to assume that - // projections can unify with other stuff. - // - // Looking forward to lazy normalization this is the safer strategy anyways. - ty::Alias(..) => true, + // non-rigid alias can unify with anything. + ty::Alias(ty::IsRigid::No, _) => true, ty::Int(_) | ty::Uint(_) @@ -468,7 +478,7 @@ impl { @@ -499,9 +509,7 @@ impl { - true - } + ty::ConstKind::Expr(_) | ty::ConstKind::Alias(_, _) | ty::ConstKind::Error(_) => true, ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) => true, } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 19c59df0604c3..da7d8b5acbc1e 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -79,10 +79,10 @@ bitflags::bitflags! { const HAS_TY_OPAQUE = 1 << 12; /// Does this have `Inherent`? const HAS_TY_INHERENT = 1 << 13; - /// Does this have `ConstKind::Unevaluated`? + /// Does this have `ConstKind::Alias`? const HAS_CT_PROJECTION = 1 << 14; - /// Does this have `Alias` or `ConstKind::Unevaluated`? + /// Does this have `Alias` or `ConstKind::Alias`? /// /// Rephrased, could this term be normalized further? const HAS_ALIAS = TypeFlags::HAS_TY_PROJECTION.bits() @@ -146,6 +146,18 @@ bitflags::bitflags! { /// Does this have a `Bound(BoundVarIndexKind::Canonical, _)`? const HAS_CANONICAL_BOUND = 1 << 26; + + /// Does this have any aliases with `IsRigid::Yes`? + /// + /// We have both rigid and non-rigid flags because both can be true for a single + /// subject. E.g. one arg is rigid while another is non-rigid for some ADTs. + const HAS_RIGID_ALIAS = 1 << 27; + + /// Does this have any aliases with `IsRigid::No`? + /// + /// We have a separate flag from `HAS_ALIAS` because `HAS_ALIAS` doesn't care + /// about rigidness while we rely on rigidness to skip renormalization. + const HAS_NON_RIGID_ALIAS = 1 << 28; } } @@ -295,14 +307,14 @@ impl FlagComputation { self.add_args(args.as_slice()); } - ty::Alias(alias) => { + ty::Alias(is_rigid, alias) => { + self.add_is_rigid(is_rigid); self.add_flags(match alias.kind { ty::Projection { .. } => TypeFlags::HAS_TY_PROJECTION, ty::Free { .. } => TypeFlags::HAS_TY_FREE_ALIAS, ty::Opaque { .. } => TypeFlags::HAS_TY_OPAQUE, ty::Inherent { .. } => TypeFlags::HAS_TY_INHERENT, }); - self.add_alias_ty(alias); } @@ -420,8 +432,8 @@ impl FlagComputation { self.add_term(term); } ty::PredicateKind::DynCompatible(_def_id) => {} - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { - self.add_const(uv); + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { + self.add_const(alias_const); } ty::PredicateKind::ConstEquate(expected, found) => { self.add_const(expected); @@ -465,8 +477,9 @@ impl FlagComputation { fn add_const_kind(&mut self, c: &ty::ConstKind) { match *c { - ty::ConstKind::Unevaluated(uv) => { - self.add_args(uv.args.as_slice()); + ty::ConstKind::Alias(is_rigid, alias_const) => { + self.add_is_rigid(is_rigid); + self.add_args(alias_const.args.as_slice()); self.add_flags(TypeFlags::HAS_CT_PROJECTION); } ty::ConstKind::Infer(infer) => match infer { @@ -529,6 +542,13 @@ impl FlagComputation { } } + fn add_is_rigid(&mut self, is_rigid: ty::IsRigid) { + match is_rigid { + ty::IsRigid::Yes => self.add_flags(TypeFlags::HAS_RIGID_ALIAS), + ty::IsRigid::No => self.add_flags(TypeFlags::HAS_NON_RIGID_ALIAS), + } + } + fn add_term(&mut self, term: I::Term) { match term.kind() { ty::TermKind::Ty(ty) => self.add_ty(ty), diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 0fe68b5256691..8b1401c0609f9 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -554,3 +554,66 @@ where if c.has_regions() { c.super_fold_with(self) } else { c } } } + +pub fn set_aliases_to_non_rigid(cx: I, value: T) -> ty::Unnormalized +where + T: TypeFoldable, +{ + if !value.has_rigid_aliases() { + return ty::Unnormalized::new(value); + } + + let mut folder = RigidnessFolder { cx }; + ty::Unnormalized::new(value.fold_with(&mut folder)) +} + +struct RigidnessFolder { + cx: I, +} + +impl TypeFolder for RigidnessFolder { + #[inline] + fn cx(&self) -> I { + self.cx + } + + fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { + if t.has_rigid_aliases() { t.super_fold_with(self) } else { t } + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + if !t.has_rigid_aliases() { + return t; + } + + match t.kind() { + ty::Alias(ty::IsRigid::Yes, alias_ty) => { + let alias_ty = alias_ty.fold_with(self); + I::Ty::new_alias(self.cx(), ty::IsRigid::No, alias_ty) + } + _ => t.super_fold_with(self), + } + } + + fn fold_const(&mut self, c: I::Const) -> I::Const { + if !c.has_rigid_aliases() { + return c; + } + + match c.kind() { + ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => { + let alias_const = alias_const.fold_with(self); + I::Const::new_alias(self.cx, ty::IsRigid::No, alias_const) + } + _ => c.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.has_rigid_aliases() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_rigid_aliases() { c.super_fold_with(self) } else { c } + } +} diff --git a/compiler/rustc_type_ir/src/generic_arg.rs b/compiler/rustc_type_ir/src/generic_arg.rs index cbd9522e7a3f9..c9c6a8fe05825 100644 --- a/compiler/rustc_type_ir/src/generic_arg.rs +++ b/compiler/rustc_type_ir/src/generic_arg.rs @@ -18,16 +18,3 @@ pub enum GenericArgKind { } impl Eq for GenericArgKind {} - -#[derive_where(Clone, Copy, PartialEq, Debug; I: Interner)] -#[derive(GenericTypeVisitable)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) -)] -pub enum TermKind { - Ty(I::Ty), - Const(I::Const), -} - -impl Eq for TermKind {} diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index bffb6aa8060ed..77e5fcac07b3f 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -53,26 +53,30 @@ pub trait Ty>: fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self; - fn new_alias(interner: I, alias_ty: ty::AliasTy) -> Self; + fn new_alias(interner: I, is_rigid: ty::IsRigid, alias_ty: ty::AliasTy) -> Self; fn new_projection_from_args( interner: I, + is_rigid: ty::IsRigid, def_id: I::TraitAssocTyId, args: I::GenericArgs, ) -> Self { Self::new_alias( interner, + is_rigid, ty::AliasTy::new_from_args(interner, ty::AliasTyKind::Projection { def_id }, args), ) } fn new_projection( interner: I, + is_rigid: ty::IsRigid, def_id: I::TraitAssocTyId, args: impl IntoIterator>, ) -> Self { Self::new_alias( interner, + is_rigid, ty::AliasTy::new(interner, ty::AliasTyKind::Projection { def_id }, args), ) } @@ -190,7 +194,7 @@ pub trait Ty>: | ty::CoroutineWitness(_, _) | ty::Never | ty::Tuple(_) - | ty::Alias(_) + | ty::Alias(_, _) | ty::Param(_) | ty::Bound(_, _) | ty::Placeholder(_) @@ -276,7 +280,7 @@ pub trait Const>: fn new_placeholder(interner: I, param: ty::PlaceholderConst) -> Self; - fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst) -> Self; + fn new_alias(interner: I, is_rigid: ty::IsRigid, alias_const: ty::AliasConst) -> Self; fn new_expr(interner: I, expr: I::ExprConst) -> Self; @@ -403,15 +407,28 @@ pub trait Term>: fn to_alias_term(self) -> Option> { match self.kind() { ty::TermKind::Ty(ty) => match ty.kind() { - ty::Alias(alias_ty) => Some(alias_ty.into()), + ty::Alias(_, alias_ty) => Some(alias_ty.into()), _ => None, }, ty::TermKind::Const(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(uv) => Some(uv.into()), + ty::ConstKind::Alias(_, alias_const) => Some(alias_const.into()), _ => None, }, } } + + fn is_non_rigid_alias(self) -> bool { + match self.kind() { + ty::TermKind::Ty(ty) => match ty.kind() { + ty::Alias(is_rigid, _) => is_rigid == ty::IsRigid::No, + _ => false, + }, + ty::TermKind::Const(ct) => match ct.kind() { + ty::ConstKind::Alias(is_rigid, _) => is_rigid == ty::IsRigid::No, + _ => false, + }, + } + } } #[rust_analyzer::prefer_underscore_import] diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 5a98ff74cea42..815890c989d93 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -16,7 +16,7 @@ use crate::solve::{ AccessedOpaques, CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect, }; use crate::visit::{Flags, TypeVisitable}; -use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; +use crate::{self as ty, CanonicalParamEnvCacheEntry, TraitRef, search_graph}; #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] pub trait Interner: @@ -56,13 +56,12 @@ pub trait Interner: type CoroutineId: SpecificDefId; type AdtId: SpecificDefId; type ImplId: SpecificDefId; - type UnevaluatedConstId: SpecificDefId; + type AnonConstId: SpecificDefId; type TraitAssocTyId: SpecificDefId + Into + TryFrom; type TraitAssocConstId: SpecificDefId + Into - + Into + TryFrom; type TraitAssocTermId: SpecificDefId; type OpaqueTyId: SpecificDefId; @@ -75,19 +74,13 @@ pub trait Interner: + Into + TypeFoldable; type FreeTyAliasId: SpecificDefId + Into; - type FreeConstAliasId: SpecificDefId - + Into - + Into; + type FreeConstAliasId: SpecificDefId + Into; type FreeTermAliasId: SpecificDefId; type ImplOrTraitAssocTyId: SpecificDefId + Into; - type ImplOrTraitAssocConstId: SpecificDefId - + Into - + Into; + type ImplOrTraitAssocConstId: SpecificDefId + Into; type ImplOrTraitAssocTermId: SpecificDefId; type InherentAssocTyId: SpecificDefId + Into; - type InherentAssocConstId: SpecificDefId - + Into - + Into; + type InherentAssocConstId: SpecificDefId + Into; type InherentAssocTermId: SpecificDefId; type Span: Span; @@ -244,10 +237,7 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; - fn unevaluated_const_kind_from_def_id( - self, - def_id: Self::DefId, - ) -> ty::UnevaluatedConstKind; + fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind; // FIXME: remove in favor of explicit construction fn alias_term_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasTermKind; @@ -292,6 +282,8 @@ pub trait Interner: fn assumptions_on_binders(self) -> bool; + fn renormalize_rigid_aliases(self) -> bool; + fn coroutine_hidden_types( self, def_id: Self::CoroutineId, @@ -406,8 +398,7 @@ pub trait Interner: fn for_each_relevant_impl( self, - trait_def_id: Self::TraitId, - self_ty: Self::Ty, + trait_ref: TraitRef, f: impl FnMut(Self::ImplId) -> R, ) -> R; fn for_each_blanket_impl( @@ -517,7 +508,7 @@ declare_lift_into! { TraitId, Ty, Tys, - UnevaluatedConstId, + AnonConstId, } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index c6ab804d81c9f..f3bacff256d19 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,12 +1,12 @@ use std::fmt; +#[cfg(feature = "nightly")] +use crate::{AliasConst, ClosureKind}; use crate::{ AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, Placeholder, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; -#[cfg(feature = "nightly")] -use crate::{ClosureKind, UnevaluatedConst}; pub trait IrPrint { fn print(t: &T, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; @@ -100,7 +100,7 @@ mod into_diag_arg_impls { } } - impl IntoDiagArg for UnevaluatedConst { + impl IntoDiagArg for AliasConst { fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { format!("{self:?}").into_diag_arg(path) } diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 51d8a8264ac97..0fdfe17b963fa 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -52,6 +52,7 @@ mod pattern; mod predicate; mod predicate_kind; mod region_kind; +mod term_kind; mod ty; mod ty_info; mod ty_kind; @@ -81,6 +82,7 @@ pub use predicate_kind::*; pub use region_kind::*; pub use rustc_ast_ir::{FloatTy, IntTy, Movability, Mutability, Pinnedness, UintTy}; use rustc_type_ir_macros::GenericTypeVisitable; +pub use term_kind::*; pub use ty::{Alias, *}; pub use ty_info::*; pub use ty_kind::*; diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index 857738d207b4f..ea92699958094 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -45,6 +45,7 @@ TrivialTypeTraversalImpls! { (), bool, usize, + u8, u16, u32, u64, diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index a346b83fac054..e8c32d0cc1f6f 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -148,7 +148,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // trait-ref. Therefore, if we see any higher-ranked regions, // we simply fallback to the most restrictive rule, which // requires that `Pi: 'a` for all `i`. - ty::Alias(alias_ty) => { + ty::Alias(_, alias_ty) => { if !alias_ty.has_escaping_bound_vars() { // best case: no escaping regions, so push the // projection and skip the subtree (thus generating no diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 5bbe1e1aea3c2..169c6fbe71607 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -9,10 +9,9 @@ use rustc_type_ir_macros::{ }; use crate::inherent::*; -use crate::ty::AliasTerm; use crate::upcast::{Upcast, UpcastFrom}; use crate::visit::TypeVisitableExt as _; -use crate::{self as ty, Alias, AliasTyKind, Interner, UnevaluatedConstKind}; +use crate::{self as ty, Alias, Interner}; /// `A: 'region` #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, A)] @@ -505,7 +504,7 @@ impl ExistentialProjection { debug_assert!(!self_ty.has_escaping_bound_vars()); ProjectionPredicate { - projection_term: AliasTerm::new( + projection_term: ty::AliasTerm::new( interner, interner.alias_term_kind_from_def_id(self.def_id.into()), [self_ty.into()].iter().chain(self.args.iter()), @@ -537,354 +536,6 @@ impl ty::Binder> { } } -#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic, GenericTypeVisitable)] -#[cfg_attr( - feature = "nightly", - derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) -)] -pub enum AliasTermKind { - /// A projection `::AssocType`. - /// - /// Can get normalized away if monomorphic enough. - /// - /// The `def_id` is the `DefId` of the `TraitItem` for the associated type. - /// - /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this - /// associated type, which is in `interner.associated_item(def_id).container`, - /// aka. `interner.parent(def_id)`. - ProjectionTy { def_id: I::TraitAssocTyId }, - - /// An associated type in an inherent `impl` - /// - /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. - InherentTy { def_id: I::InherentAssocTyId }, - - /// An opaque type (usually from `impl Trait` in type aliases or function return types) - /// - /// `def_id` is the `DefId` of the `OpaqueType` item. - /// - /// Can only be normalized away in `PostAnalysis` mode or its defining scope. - /// - /// During codegen, `interner.type_of(def_id)` can be used to get the type of the - /// underlying type if the type is an opaque. - OpaqueTy { def_id: I::OpaqueTyId }, - - /// A type alias that actually checks its trait bounds. - /// - /// Currently only used if the type alias references opaque types. - /// Can always be normalized away. - FreeTy { def_id: I::FreeTyAliasId }, - - /// An unevaluated anonymous constants. - AnonConst { def_id: I::UnevaluatedConstId }, - /// An unevaluated const coming from an associated const. - ProjectionConst { def_id: I::TraitAssocConstId }, - /// A top level const item not part of a trait or impl. - FreeConst { def_id: I::FreeConstAliasId }, - /// An associated const in an inherent `impl` - InherentConst { def_id: I::InherentAssocConstId }, -} - -impl AliasTermKind { - pub fn descr(self) -> &'static str { - match self { - AliasTermKind::ProjectionTy { .. } => "associated type", - AliasTermKind::ProjectionConst { .. } => "associated const", - AliasTermKind::InherentTy { .. } => "inherent associated type", - AliasTermKind::InherentConst { .. } => "inherent associated const", - AliasTermKind::OpaqueTy { .. } => "opaque type", - AliasTermKind::FreeTy { .. } => "type alias", - AliasTermKind::FreeConst { .. } => "unevaluated constant", - AliasTermKind::AnonConst { .. } => "unevaluated constant", - } - } - - pub fn is_type(self) -> bool { - match self { - AliasTermKind::ProjectionTy { .. } - | AliasTermKind::InherentTy { .. } - | AliasTermKind::OpaqueTy { .. } - | AliasTermKind::FreeTy { .. } => true, - - AliasTermKind::AnonConst { .. } - | AliasTermKind::ProjectionConst { .. } - | AliasTermKind::InherentConst { .. } - | AliasTermKind::FreeConst { .. } => false, - } - } - - pub fn is_trait_projection(self) -> bool { - match self { - AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. } => true, - AliasTermKind::InherentTy { .. } - | AliasTermKind::OpaqueTy { .. } - | AliasTermKind::FreeTy { .. } - | AliasTermKind::AnonConst { .. } - | AliasTermKind::FreeConst { .. } - | AliasTermKind::InherentConst { .. } => false, - } - } -} - -impl From> for AliasTermKind { - fn from(value: ty::AliasTyKind) -> Self { - match value { - ty::Projection { def_id } => AliasTermKind::ProjectionTy { def_id }, - ty::Opaque { def_id } => AliasTermKind::OpaqueTy { def_id }, - ty::Free { def_id } => AliasTermKind::FreeTy { def_id }, - ty::Inherent { def_id } => AliasTermKind::InherentTy { def_id }, - } - } -} - -impl From> for AliasTermKind { - fn from(value: ty::UnevaluatedConstKind) -> Self { - match value { - ty::UnevaluatedConstKind::Projection { def_id } => { - AliasTermKind::ProjectionConst { def_id } - } - ty::UnevaluatedConstKind::Inherent { def_id } => { - AliasTermKind::InherentConst { def_id } - } - ty::UnevaluatedConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, - ty::UnevaluatedConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, - } - } -} - -impl AliasTerm { - pub fn new_from_args( - interner: I, - kind: AliasTermKind, - args: I::GenericArgs, - ) -> AliasTerm { - if cfg!(debug_assertions) { - let def_id = match kind { - AliasTermKind::ProjectionTy { def_id } => def_id.into(), - AliasTermKind::InherentTy { def_id } => def_id.into(), - AliasTermKind::OpaqueTy { def_id } => def_id.into(), - AliasTermKind::FreeTy { def_id } => def_id.into(), - AliasTermKind::AnonConst { def_id } => def_id.into(), - AliasTermKind::ProjectionConst { def_id } => def_id.into(), - AliasTermKind::FreeConst { def_id } => def_id.into(), - AliasTermKind::InherentConst { def_id } => def_id.into(), - }; - interner.debug_assert_args_compatible(def_id, args); - } - AliasTerm { kind, args, _use_alias_new_instead: () } - } - - pub fn new( - interner: I, - kind: AliasTermKind, - args: impl IntoIterator>, - ) -> AliasTerm { - let args = interner.mk_args_from_iter(args.into_iter().map(Into::into)); - Self::new_from_args(interner, kind, args) - } - - pub fn new_from_def_id(interner: I, def_id: I::DefId, args: I::GenericArgs) -> AliasTerm { - let kind = interner.alias_term_kind_from_def_id(def_id); - Self::new_from_args(interner, kind, args) - } - - pub fn expect_ty(self) -> ty::AliasTy { - let kind = match self.kind { - AliasTermKind::ProjectionTy { def_id } => AliasTyKind::Projection { def_id }, - AliasTermKind::InherentTy { def_id } => AliasTyKind::Inherent { def_id }, - AliasTermKind::OpaqueTy { def_id } => AliasTyKind::Opaque { def_id }, - AliasTermKind::FreeTy { def_id } => AliasTyKind::Free { def_id }, - kind @ (AliasTermKind::InherentConst { .. } - | AliasTermKind::FreeConst { .. } - | AliasTermKind::AnonConst { .. } - | AliasTermKind::ProjectionConst { .. }) => { - panic!("Cannot turn `{}` into `AliasTy`", kind.descr()) - } - }; - ty::AliasTy { kind, args: self.args, _use_alias_new_instead: () } - } - - pub fn expect_ct(self) -> ty::UnevaluatedConst { - let kind = match self.kind { - AliasTermKind::InherentConst { def_id } => UnevaluatedConstKind::Inherent { def_id }, - AliasTermKind::FreeConst { def_id } => UnevaluatedConstKind::Free { def_id }, - AliasTermKind::AnonConst { def_id } => UnevaluatedConstKind::Anon { def_id }, - AliasTermKind::ProjectionConst { def_id } => { - UnevaluatedConstKind::Projection { def_id } - } - kind @ (AliasTermKind::ProjectionTy { .. } - | AliasTermKind::InherentTy { .. } - | AliasTermKind::OpaqueTy { .. } - | AliasTermKind::FreeTy { .. }) => { - panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr()) - } - }; - ty::UnevaluatedConst { kind, args: self.args, _use_alias_new_instead: () } - } - - pub fn to_term(self, interner: I) -> I::Term { - let alias_ty = |kind| { - Ty::new_alias(interner, ty::AliasTy::new_from_args(interner, kind, self.args)).into() - }; - let unevaluated_const = |kind| { - I::Const::new_unevaluated( - interner, - ty::UnevaluatedConst::new(interner, kind, self.args), - ) - .into() - }; - match self.kind { - AliasTermKind::FreeConst { def_id } => { - unevaluated_const(UnevaluatedConstKind::Free { def_id }) - } - AliasTermKind::InherentConst { def_id } => { - unevaluated_const(UnevaluatedConstKind::Inherent { def_id }) - } - AliasTermKind::AnonConst { def_id } => { - unevaluated_const(UnevaluatedConstKind::Anon { def_id }) - } - AliasTermKind::ProjectionConst { def_id } => { - unevaluated_const(UnevaluatedConstKind::Projection { def_id }) - } - AliasTermKind::ProjectionTy { def_id } => alias_ty(ty::Projection { def_id }), - AliasTermKind::InherentTy { def_id } => alias_ty(ty::Inherent { def_id }), - AliasTermKind::OpaqueTy { def_id } => alias_ty(ty::Opaque { def_id }), - AliasTermKind::FreeTy { def_id } => alias_ty(ty::Free { def_id }), - } - } - - pub fn with_args(self, interner: I, args: I::GenericArgs) -> Self { - Self::new_from_args(interner, self.kind, args) - } - - pub fn expect_projection_ty_def_id(self) -> I::TraitAssocTyId { - match self.kind { - AliasTermKind::ProjectionTy { def_id } => def_id, - kind => panic!("expected projection ty, found {kind:?}"), - } - } - - pub fn expect_opaque_ty_def_id(self) -> I::OpaqueTyId { - match self.kind { - AliasTermKind::OpaqueTy { def_id } => def_id, - kind => panic!("expected opaque ty, found {kind:?}"), - } - } -} - -/// The following methods work only with (trait) associated term projections. -// FIXME: Replace by an impl on Alias -impl AliasTerm { - pub fn self_ty(self) -> I::Ty { - self.args.type_at(0) - } - - pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { - AliasTerm::new( - interner, - self.kind, - [self_ty.into()].into_iter().chain(self.args.iter().skip(1)), - ) - } - - pub fn expect_projection_def_id(self) -> I::TraitAssocTermId { - match self.kind { - AliasTermKind::ProjectionTy { def_id } => def_id.into(), - AliasTermKind::ProjectionConst { def_id } => def_id.into(), - kind => panic!("expected projection alias, found {kind:?}"), - } - } - - pub fn trait_def_id(self, interner: I) -> I::TraitId { - interner.projection_parent(self.expect_projection_def_id()) - } - - /// Extracts the underlying trait reference and own args from this projection. - /// For example, if this is a projection of `::Item<'a>`, - /// then this function would return a `T: StreamingIterator` trait reference and - /// `['a]` as the own args. - pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef, I::GenericArgsSlice) { - interner.trait_ref_and_own_args_for_alias(self.expect_projection_def_id(), self.args) - } - - /// Extracts the underlying trait reference from this projection. - /// For example, if this is a projection of `::Item`, - /// then this function would return a `T: Iterator` trait reference. - /// - /// WARNING: This will drop the args for generic associated types - /// consider calling [Self::trait_ref_and_own_args] to get those - /// as well. - pub fn trait_ref(self, interner: I) -> TraitRef { - self.trait_ref_and_own_args(interner).0 - } - - /// Extract the own args from this projection. - /// For example, if this is a projection of `::Item<'a>`, - /// then this function would return the slice `['a]` as the own args. - pub fn own_args(self, interner: I) -> I::GenericArgsSlice { - self.trait_ref_and_own_args(interner).1 - } -} - -/// The following methods work only with inherent associated term projections. -// FIXME: Replace by an impl on Alias -impl AliasTerm { - pub fn expect_inherent_def_id(self) -> I::InherentAssocTermId { - match self.kind { - AliasTermKind::InherentTy { def_id } => def_id.into(), - AliasTermKind::InherentConst { def_id } => def_id.into(), - kind => panic!("expected inherent alias, found {kind:?}"), - } - } - - /// Transform the generic parameters to have the given `impl` args as the base and the GAT args on top of that. - /// - /// Does the following transformation: - /// - /// ```text - /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m] - /// - /// I_i impl args - /// P_j GAT args - /// ``` - pub fn rebase_inherent_args_onto_impl( - self, - impl_args: I::GenericArgs, - interner: I, - ) -> I::GenericArgs { - debug_assert!(matches!( - self.kind, - AliasTermKind::InherentTy { .. } | AliasTermKind::InherentConst { .. } - )); - interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1))) - } -} - -/// The following methods work only with free term aliases. -// FIXME: Replace by an impl on Alias -impl AliasTerm { - pub fn expect_free_def_id(self) -> I::FreeTermAliasId { - match self.kind { - AliasTermKind::FreeTy { def_id } => def_id.into(), - AliasTermKind::FreeConst { def_id } => def_id.into(), - kind => panic!("expected free alias, found {kind:?}"), - } - } -} - -impl From> for AliasTerm { - fn from(ty: ty::AliasTy) -> Self { - AliasTerm { args: ty.args, kind: AliasTermKind::from(ty.kind), _use_alias_new_instead: () } - } -} - -impl From> for AliasTerm { - fn from(ty: ty::UnevaluatedConst) -> Self { - AliasTerm { args: ty.args, kind: AliasTermKind::from(ty.kind), _use_alias_new_instead: () } - } -} - /// This kind of predicate has no *direct* correspondent in the /// syntax, but it roughly corresponds to the syntactic forms: /// @@ -904,7 +555,7 @@ impl From> for AliasTerm { derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) )] pub struct ProjectionPredicate { - pub projection_term: AliasTerm, + pub projection_term: ty::AliasTerm, pub term: I::Term, } @@ -966,7 +617,7 @@ impl fmt::Debug for ProjectionPredicate { feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) )] -pub struct NormalizesTo> { +pub struct NormalizesTo> { pub alias: Alias, pub term: I::Term, } diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 4b23bfd0f7765..626649027da28 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -52,9 +52,9 @@ use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; use crate::visit::TypeSuperVisitable; use crate::{ AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, ConstKind, DebruijnIndex, - FallibleTypeFolder, InferCtxtLike, InferTy, Interner, OutlivesPredicate, RegionKind, TyKind, - TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, TypingMode, UniverseIndex, Variance, - VisitorResult, + FallibleTypeFolder, InferCtxtLike, InferTy, Interner, IsRigid, OutlivesPredicate, RegionKind, + TyKind, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, TypingMode, UniverseIndex, + Variance, VisitorResult, set_aliases_to_non_rigid, }; #[derive_where(Clone, Debug; I: Interner)] @@ -840,7 +840,15 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< escaping_outlives, I::BoundVarKinds::from_vars(infcx.cx(), bound_vars), ); - candidates.push(RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives)); + let candidate = RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives); + if max_universe(infcx, candidate.clone()) < u { + candidates.push(candidate); + } else { + // `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave + // a placeholder type in `u`, so this type-outlives constraint cannot be + // handled by the region-outlives-only eager placeholder machinery. + candidates.push(Ambiguity); + } } let assumptions = match assumptions { @@ -885,12 +893,17 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< // while we did skip the binder, bound vars aren't in any universe so // this can't be an escaping bound var - candidates.extend( - regions_outliving(escaping_r, assumptions, infcx.cx()) - .filter(|r2| max_universe(infcx, *r2) < u) - .map(|r2| AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2)))) - .collect::>(), - ); + for r2 in regions_outliving(escaping_r, assumptions, infcx.cx()) + .filter(|r2| max_universe(infcx, *r2) < u) + { + let candidate = + AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2))); + if max_universe(infcx, candidate.clone()) < u { + candidates.push(candidate); + } else { + candidates.push(Ambiguity); + } + } } // I'm not convinced our handling here is *complete* so for now @@ -978,11 +991,14 @@ pub fn regions_outlived_by_placeholder( } /// The largest universe a variable or placeholder was from in `t` -pub fn max_universe, I: Interner, T: TypeVisitable>( +pub fn max_universe, I: Interner, T: TypeFoldable>( infcx: &Infcx, t: T, ) -> UniverseIndex { let mut visitor = MaxUniverse::new(infcx); + // `max_universe` is also used while rewriting constraints to lower universes, + // so do not rely on callers having already resolved non-region infer vars. + let t = infcx.resolve_vars_if_possible(t); t.visit_with(&mut visitor); visitor.max_universe() } @@ -1036,11 +1052,17 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> TypeVisitor fn visit_region(&mut self, r: I::Region) { match r.kind() { RegionKind::RePlaceholder(p) => self.max_universe = self.max_universe.max(p.universe), - RegionKind::ReVar(var) => { - let u = self.infcx.universe_of_lt(var).unwrap(); - debug!("var {var:?} in universe {u:?}"); - self.max_universe = self.max_universe.max(u); - } + RegionKind::ReVar(var) => match self.infcx.opportunistic_resolve_lt_var(var).kind() { + RegionKind::RePlaceholder(p) => { + self.max_universe = self.max_universe.max(p.universe) + } + RegionKind::ReVar(var) => { + let u = self.infcx.universe_of_lt(var).unwrap(); + debug!("var {var:?} in universe {u:?}"); + self.max_universe = self.max_universe.max(u); + } + _ => (), + }, _ => (), } } @@ -1106,7 +1128,12 @@ fn alias_outlives_candidates_from_assumptions region_constraints: vec![RegionConstraint::RegionOutlives(r2, r)], }; - if let Ok(_) = relation.relate(alias.to_ty(infcx.cx()), alias2) { + // FIXME(#155345): Both sides should be rigid in the future. + // Currently we can't guarantee that. + if let Ok(_) = relation.relate( + alias.to_ty(infcx.cx(), IsRigid::No), + set_aliases_to_non_rigid(infcx.cx(), alias2).skip_norm_wip(), + ) { candidates .push(RegionConstraint::And(relation.region_constraints.into_boxed_slice())); } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index bdf1efd04afc7..1922cbd954cf8 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -169,6 +169,10 @@ impl Relate for ty::FnSig { return Err(TypeError::AbiMismatch(ExpectedFound::new(a.abi(), b.abi()))); }; + if a.splatted() != b.splatted() { + return Err(TypeError::SplatMismatch(ExpectedFound::new(a.splatted(), b.splatted()))); + } + let a_inputs = a.inputs(); let b_inputs = b.inputs(); if a_inputs.len() != b_inputs.len() { @@ -222,22 +226,22 @@ impl Relate for ty::AliasTy { } else { relate_args_invariantly(relation, a.args, b.args)? }; - Ok(ty::AliasTy::new_from_args(cx, a.kind, args)) + Ok(ty::AliasTy::new_from_args(relation.cx(), a.kind, args)) } } } -impl Relate for ty::UnevaluatedConst { +impl Relate for ty::AliasConst { fn relate>( relation: &mut R, - a: ty::UnevaluatedConst, - b: ty::UnevaluatedConst, - ) -> RelateResult> { + a: ty::AliasConst, + b: ty::AliasConst, + ) -> RelateResult> { let cx = relation.cx(); if a.kind != b.kind { Err(TypeError::ConstMismatch(ExpectedFound::new( - Const::new_unevaluated(cx, a), - Const::new_unevaluated(cx, b), + Const::new_alias(cx, ty::IsRigid::yes_if_next_solver(cx), a), + Const::new_alias(cx, ty::IsRigid::yes_if_next_solver(cx), b), ))) } else { // FIXME(mgca): remove this @@ -245,7 +249,7 @@ impl Relate for ty::UnevaluatedConst { let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::UnevaluatedConst::new(cx, a.kind, args)) + Ok(ty::AliasConst::new(cx, a.kind, args)) } } } @@ -523,9 +527,12 @@ pub fn structurally_relate_tys>( } // Alias tend to mostly already be handled downstream due to normalization. - (ty::Alias(a), ty::Alias(b)) => { - let alias_ty = relation.relate(a, b)?; - Ok(Ty::new_alias(cx, alias_ty)) + (ty::Alias(is_rigid_a, alias_a), ty::Alias(is_rigid_b, alias_b)) => { + // Users shouldn't know about this so the mismatch should be caught + // during development rather than presented as type error. + debug_assert_eq!(is_rigid_a, is_rigid_b, "{a:?} != {b:?}"); + let alias_ty = relation.relate(alias_a, alias_b)?; + Ok(Ty::new_alias(cx, is_rigid_a, alias_ty)) } (ty::Pat(a_ty, a_pat), ty::Pat(b_ty, b_pat)) => { @@ -543,7 +550,7 @@ pub fn structurally_relate_tys>( } /// Relates `a` and `b` structurally, calling the relation for all nested values. -/// Any semantic equality, e.g. of unevaluated consts, and inference variables have +/// Any semantic equality, e.g. of alias consts, and inference variables have /// to be handled by the caller. /// /// FIXME: This is not totally structural, which probably should be fixed. @@ -611,8 +618,11 @@ pub fn structurally_relate_consts>( // While this is slightly incorrect, it shouldn't matter for `min_const_generics` // and is the better alternative to waiting until `generic_const_exprs` can // be stabilized. - (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) => { - return Ok(Const::new_unevaluated(cx, relation.relate(au, bu)?)); + (ty::ConstKind::Alias(is_rigid_a, au), ty::ConstKind::Alias(is_rigid_b, bu)) => { + // Users shouldn't know about this so the mismatch should be caught + // during development rather than presented as type error. + debug_assert_eq!(is_rigid_a, is_rigid_b, "{a:?} != {b:?}"); + return Ok(Const::new_alias(cx, is_rigid_a, relation.relate(au, bu)?)); } (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { let expr = relation.relate(ae, be)?; diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 37bbd6d920794..d5f9df2249df7 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -115,9 +115,29 @@ where panic!("We do not expect to encounter `Fresh` variables in the new solver") } - (other, ty::Alias(..)) | (ty::Alias(..), other) if infcx.next_trait_solver() => { - match relation.structurally_relate_aliases() { - StructurallyRelateAliases::Yes => match other { + (ty::Alias(is_rigid_a, _), ty::Alias(is_rigid_b, _)) if infcx.next_trait_solver() => { + match (is_rigid_a, is_rigid_b) { + (ty::IsRigid::Yes, ty::IsRigid::Yes) => structurally_relate_tys(relation, a, b), + _ => match relation.structurally_relate_aliases() { + StructurallyRelateAliases::Yes => structurally_relate_tys(relation, a, b), + StructurallyRelateAliases::No => { + relation.register_alias_relate_predicate(a, b); + Ok(a) + } + }, + } + } + + (other, ty::Alias(is_rigid, _)) | (ty::Alias(is_rigid, _), other) + if infcx.next_trait_solver() => + { + if let StructurallyRelateAliases::No = relation.structurally_relate_aliases() + && is_rigid == ty::IsRigid::No + { + relation.register_alias_relate_predicate(a, b); + Ok(a) + } else { + match other { ty::Infer(infer_ty) => match infer_ty { // Normally, we shouldn't be combining an infer ty with an alias here. But // when we evaluate a `Projection(assoc_ty, expected)` goal, we normalize @@ -139,10 +159,6 @@ where | ty::InferTy::FreshFloatTy(_) => unreachable!(), }, _ => structurally_relate_tys(relation, a, b), - }, - StructurallyRelateAliases::No => { - relation.register_alias_relate_predicate(a, b); - Ok(a) } } } @@ -150,8 +166,8 @@ where // All other cases of inference are errors (ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Sorts(ExpectedFound::new(a, b))), - (ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }), _) - | (_, ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. })) => { + (ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }), _) + | (_, ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. })) => { assert!(!infcx.next_trait_solver()); match infcx.typing_mode_raw().assert_not_erased() { // During coherence, opaque types should be treated as *possibly* @@ -223,7 +239,13 @@ where Ok(a) } - (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) + (ty::ConstKind::Alias(ty::IsRigid::Yes, _), ty::ConstKind::Alias(ty::IsRigid::Yes, _)) + if (infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver()) => + { + structurally_relate_consts(relation, a, b) + } + + (ty::ConstKind::Alias(..), _) | (_, ty::ConstKind::Alias(..)) if infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver() => { match relation.structurally_relate_aliases() { diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 3a962b3832aa3..a42ed8795e127 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -263,6 +263,12 @@ impl CandidateHeadUsages { } #[derive(Debug, Clone, Copy)] +pub enum IncreaseDepthForNested { + Yes, + No, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct AvailableDepth(usize); impl AvailableDepth { /// Returns the remaining depth allowed for nested goals. @@ -276,6 +282,13 @@ impl AvailableDepth { stack: &Stack, ) -> Option { if let Some(last) = stack.last() { + match last.increase_depth_for_nested { + IncreaseDepthForNested::Yes => {} + IncreaseDepthForNested::No => { + return Some(last.available_depth); + } + } + if last.available_depth.0 == 0 { return None; } @@ -566,7 +579,7 @@ impl EvaluationResult { encountered_overflow, // Unlike `encountered_overflow`, we share `heads`, `required_depth`, // and `nested_goals` between evaluations. - required_depth: final_entry.required_depth, + required_depth: final_entry.required_depth(), heads: final_entry.heads, nested_goals: final_entry.nested_goals, // We only care about the final result. @@ -597,7 +610,7 @@ pub struct SearchGraph, X: Cx = ::Cx> { /// don't need to track the nested goals used while computing a provisional /// cache entry. enum UpdateParentGoalCtxt<'a, X: Cx> { - Ordinary(&'a NestedGoals), + Ordinary { nested_goals: &'a NestedGoals, min_reachable_available_depth: AvailableDepth }, CycleOnStack(X::Input), ProvisionalCacheHit, } @@ -619,13 +632,11 @@ impl, X: Cx> SearchGraph { fn update_parent_goal( stack: &mut Stack, step_kind_from_parent: PathKind, - required_depth_for_nested: usize, heads: impl Iterator, encountered_overflow: bool, context: UpdateParentGoalCtxt<'_, X>, ) { if let Some((parent_index, parent)) = stack.last_mut_with_index() { - parent.required_depth = parent.required_depth.max(required_depth_for_nested + 1); parent.encountered_overflow |= encountered_overflow; for (head_index, head) in heads { @@ -650,7 +661,9 @@ impl, X: Cx> SearchGraph { } } let parent_depends_on_cycle = match context { - UpdateParentGoalCtxt::Ordinary(nested_goals) => { + UpdateParentGoalCtxt::Ordinary { nested_goals, min_reachable_available_depth } => { + parent.min_reached_available_depth = + parent.min_reached_available_depth.min(min_reachable_available_depth); parent.nested_goals.extend_from_child(step_kind_from_parent, nested_goals); !nested_goals.is_empty() } @@ -739,8 +752,9 @@ impl, X: Cx> SearchGraph { input, step_kind_from_parent, available_depth, + min_reached_available_depth: available_depth, provisional_result: None, - required_depth: 0, + increase_depth_for_nested: IncreaseDepthForNested::Yes, heads: Default::default(), encountered_overflow: false, usages: None, @@ -761,6 +775,7 @@ impl, X: Cx> SearchGraph { cx: X, input: X::Input, step_kind_from_parent: PathKind, + increase_depth_for_nested: IncreaseDepthForNested, inspect: &mut D::ProofTreeBuilder, ) -> X::Result { let Some(available_depth) = @@ -816,7 +831,8 @@ impl, X: Cx> SearchGraph { step_kind_from_parent, available_depth, provisional_result: None, - required_depth: 0, + min_reached_available_depth: available_depth, + increase_depth_for_nested, heads: Default::default(), encountered_overflow: false, usages: None, @@ -838,10 +854,14 @@ impl, X: Cx> SearchGraph { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - evaluation_result.required_depth, evaluation_result.heads.iter(), evaluation_result.encountered_overflow, - UpdateParentGoalCtxt::Ordinary(&evaluation_result.nested_goals), + UpdateParentGoalCtxt::Ordinary { + nested_goals: &evaluation_result.nested_goals, + min_reachable_available_depth: AvailableDepth( + available_depth.0 - evaluation_result.required_depth, + ), + }, ); let result = evaluation_result.result; @@ -1116,7 +1136,6 @@ impl, X: Cx> SearchGraph { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - 0, heads.iter(), encountered_overflow, UpdateParentGoalCtxt::ProvisionalCacheHit, @@ -1239,10 +1258,14 @@ impl, X: Cx> SearchGraph { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - required_depth, heads, encountered_overflow, - UpdateParentGoalCtxt::Ordinary(nested_goals), + UpdateParentGoalCtxt::Ordinary { + nested_goals, + min_reachable_available_depth: AvailableDepth( + available_depth.0 - required_depth, + ), + }, ); debug!(?required_depth, "global cache hit"); @@ -1271,7 +1294,6 @@ impl, X: Cx> SearchGraph { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - 0, iter::once((head_index, head)), false, UpdateParentGoalCtxt::CycleOnStack(input), @@ -1407,7 +1429,8 @@ impl, X: Cx> SearchGraph { provisional_result: Some(result), // We can keep these goals from previous iterations as they are only // ever read after finalizing this evaluation. - required_depth: stack_entry.required_depth, + min_reached_available_depth: stack_entry.min_reached_available_depth, + increase_depth_for_nested: stack_entry.increase_depth_for_nested, heads: stack_entry.heads, nested_goals: stack_entry.nested_goals, // We reset these two fields when rerunning this goal. We could diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs index 8348666be412d..776ab28f9fcdc 100644 --- a/compiler/rustc_type_ir/src/search_graph/stack.rs +++ b/compiler/rustc_type_ir/src/search_graph/stack.rs @@ -4,7 +4,8 @@ use derive_where::derive_where; use rustc_index::IndexVec; use crate::search_graph::{ - AvailableDepth, CandidateHeadUsages, Cx, CycleHeads, HeadUsages, NestedGoals, PathKind, + AvailableDepth, CandidateHeadUsages, Cx, CycleHeads, HeadUsages, IncreaseDepthForNested, + NestedGoals, PathKind, }; rustc_index::newtype_index! { @@ -28,8 +29,20 @@ pub(super) struct StackEntry { /// The available depth of a given goal, immutable. pub available_depth: AvailableDepth, - /// The maximum depth required while evaluating this goal. - pub required_depth: usize, + /// The minimum available depth encountered while evaluating this goal's nested goals. + /// If there's no nested goal, this is equal to the `available_depth`. + pub min_reached_available_depth: AvailableDepth, + + /// Whether evaluating nested goals of a given goal should increase the depth. + /// + /// Normally, it should be `Yes`, but among rustc's predicate goals, `normalizes-to` + /// goals are exceptions. They act like functions that used for normalizing associated + /// terms while evaluating projection goals and since their expected terms are always fully + /// unconstrained intentionally, they often return ambiguous nested goals to the caller's + /// context. As these nested goals are evaluated again in the caller's context, we don't + /// want to increase depths when they are evaluated as nested goals for `normalizes-to` + /// goals, otherwise we will encounter recursion limit overflows more often. + pub increase_depth_for_nested: IncreaseDepthForNested, /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. @@ -57,6 +70,12 @@ pub(super) struct StackEntry { pub nested_goals: NestedGoals, } +impl StackEntry { + pub(super) fn required_depth(&self) -> usize { + self.available_depth.0 - self.min_reached_available_depth.0 + } +} + /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*. diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs new file mode 100644 index 0000000000000..aed634d4f3a21 --- /dev/null +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -0,0 +1,358 @@ +use derive_where::derive_where; +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash_NoContext}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; + +use crate::inherent::*; +use crate::{self as ty, AliasTerm, Interner}; + +#[derive_where(Clone, Copy, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable)] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) +)] +pub enum TermKind { + Ty(I::Ty), + Const(I::Const), +} + +impl Eq for TermKind {} + +#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic, GenericTypeVisitable)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) +)] +pub enum AliasTermKind { + /// A projection `::AssocType`. + /// + /// Can get normalized away if monomorphic enough. + /// + /// The `def_id` is the `DefId` of the `TraitItem` for the associated type. + /// + /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this + /// associated type, which is in `interner.associated_item(def_id).container`, + /// aka. `interner.parent(def_id)`. + ProjectionTy { def_id: I::TraitAssocTyId }, + + /// An associated type in an inherent `impl` + /// + /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. + InherentTy { def_id: I::InherentAssocTyId }, + + /// An opaque type (usually from `impl Trait` in type aliases or function return types) + /// + /// `def_id` is the `DefId` of the `OpaqueType` item. + /// + /// Can only be normalized away in `PostAnalysis` mode or its defining scope. + /// + /// During codegen, `interner.type_of(def_id)` can be used to get the type of the + /// underlying type if the type is an opaque. + OpaqueTy { def_id: I::OpaqueTyId }, + + /// A type alias that actually checks its trait bounds. + /// + /// Currently only used if the type alias references opaque types. + /// Can always be normalized away. + FreeTy { def_id: I::FreeTyAliasId }, + + /// An anonymous constant. + AnonConst { def_id: I::AnonConstId }, + /// A const alias coming from an associated const. + ProjectionConst { def_id: I::TraitAssocConstId }, + /// A top level const item not part of a trait or impl. + FreeConst { def_id: I::FreeConstAliasId }, + /// An associated const in an inherent `impl` + InherentConst { def_id: I::InherentAssocConstId }, +} + +impl AliasTermKind { + pub fn descr(self) -> &'static str { + match self { + AliasTermKind::ProjectionTy { .. } => "associated type", + AliasTermKind::ProjectionConst { .. } => "associated const", + AliasTermKind::InherentTy { .. } => "inherent associated type", + AliasTermKind::InherentConst { .. } => "inherent associated const", + AliasTermKind::OpaqueTy { .. } => "opaque type", + AliasTermKind::FreeTy { .. } => "type alias", + AliasTermKind::FreeConst { .. } => "const alias", + AliasTermKind::AnonConst { .. } => "anonymous constant", + } + } + + pub fn is_type(self) -> bool { + match self { + AliasTermKind::ProjectionTy { .. } + | AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. } => true, + + AliasTermKind::AnonConst { .. } + | AliasTermKind::ProjectionConst { .. } + | AliasTermKind::InherentConst { .. } + | AliasTermKind::FreeConst { .. } => false, + } + } + + pub fn is_trait_projection(self) -> bool { + match self { + AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. } => true, + AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. } + | AliasTermKind::AnonConst { .. } + | AliasTermKind::FreeConst { .. } + | AliasTermKind::InherentConst { .. } => false, + } + } +} + +impl From> for AliasTermKind { + fn from(value: ty::AliasTyKind) -> Self { + match value { + ty::Projection { def_id } => AliasTermKind::ProjectionTy { def_id }, + ty::Opaque { def_id } => AliasTermKind::OpaqueTy { def_id }, + ty::Free { def_id } => AliasTermKind::FreeTy { def_id }, + ty::Inherent { def_id } => AliasTermKind::InherentTy { def_id }, + } + } +} + +impl From> for AliasTermKind { + fn from(value: ty::AliasConstKind) -> Self { + match value { + ty::AliasConstKind::Projection { def_id } => AliasTermKind::ProjectionConst { def_id }, + ty::AliasConstKind::Inherent { def_id } => AliasTermKind::InherentConst { def_id }, + ty::AliasConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, + ty::AliasConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, + } + } +} + +impl AliasTerm { + pub fn new_from_args( + interner: I, + kind: AliasTermKind, + args: I::GenericArgs, + ) -> AliasTerm { + if cfg!(debug_assertions) { + let def_id = match kind { + AliasTermKind::ProjectionTy { def_id } => def_id.into(), + AliasTermKind::InherentTy { def_id } => def_id.into(), + AliasTermKind::OpaqueTy { def_id } => def_id.into(), + AliasTermKind::FreeTy { def_id } => def_id.into(), + AliasTermKind::AnonConst { def_id } => def_id.into(), + AliasTermKind::ProjectionConst { def_id } => def_id.into(), + AliasTermKind::FreeConst { def_id } => def_id.into(), + AliasTermKind::InherentConst { def_id } => def_id.into(), + }; + interner.debug_assert_args_compatible(def_id, args); + } + AliasTerm { kind, args, _use_alias_new_instead: () } + } + + pub fn new( + interner: I, + kind: AliasTermKind, + args: impl IntoIterator>, + ) -> AliasTerm { + let args = interner.mk_args_from_iter(args.into_iter().map(Into::into)); + Self::new_from_args(interner, kind, args) + } + + pub fn new_from_def_id(interner: I, def_id: I::DefId, args: I::GenericArgs) -> AliasTerm { + let kind = interner.alias_term_kind_from_def_id(def_id); + Self::new_from_args(interner, kind, args) + } + + pub fn expect_ty(self) -> ty::AliasTy { + let kind = match self.kind { + AliasTermKind::ProjectionTy { def_id } => ty::AliasTyKind::Projection { def_id }, + AliasTermKind::InherentTy { def_id } => ty::AliasTyKind::Inherent { def_id }, + AliasTermKind::OpaqueTy { def_id } => ty::AliasTyKind::Opaque { def_id }, + AliasTermKind::FreeTy { def_id } => ty::AliasTyKind::Free { def_id }, + kind @ (AliasTermKind::InherentConst { .. } + | AliasTermKind::FreeConst { .. } + | AliasTermKind::AnonConst { .. } + | AliasTermKind::ProjectionConst { .. }) => { + panic!("Cannot turn `{}` into `AliasTy`", kind.descr()) + } + }; + ty::AliasTy { kind, args: self.args, _use_alias_new_instead: () } + } + + pub fn expect_ct(self) -> ty::AliasConst { + let kind = match self.kind { + AliasTermKind::InherentConst { def_id } => ty::AliasConstKind::Inherent { def_id }, + AliasTermKind::FreeConst { def_id } => ty::AliasConstKind::Free { def_id }, + AliasTermKind::AnonConst { def_id } => ty::AliasConstKind::Anon { def_id }, + AliasTermKind::ProjectionConst { def_id } => ty::AliasConstKind::Projection { def_id }, + kind @ (AliasTermKind::ProjectionTy { .. } + | AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. }) => { + panic!("Cannot turn `{}` into `AliasConst`", kind.descr()) + } + }; + ty::AliasConst { kind, args: self.args, _use_alias_new_instead: () } + } + + pub fn to_term(self, interner: I, is_rigid: ty::IsRigid) -> I::Term { + let alias_ty = |kind| { + Ty::new_alias(interner, is_rigid, ty::AliasTy::new_from_args(interner, kind, self.args)) + .into() + }; + let alias_const = |kind| { + I::Const::new_alias(interner, is_rigid, ty::AliasConst::new(interner, kind, self.args)) + .into() + }; + match self.kind { + AliasTermKind::FreeConst { def_id } => alias_const(ty::AliasConstKind::Free { def_id }), + AliasTermKind::InherentConst { def_id } => { + alias_const(ty::AliasConstKind::Inherent { def_id }) + } + AliasTermKind::AnonConst { def_id } => alias_const(ty::AliasConstKind::Anon { def_id }), + AliasTermKind::ProjectionConst { def_id } => { + alias_const(ty::AliasConstKind::Projection { def_id }) + } + AliasTermKind::ProjectionTy { def_id } => alias_ty(ty::Projection { def_id }), + AliasTermKind::InherentTy { def_id } => alias_ty(ty::Inherent { def_id }), + AliasTermKind::OpaqueTy { def_id } => alias_ty(ty::Opaque { def_id }), + AliasTermKind::FreeTy { def_id } => alias_ty(ty::Free { def_id }), + } + } + + pub fn with_args(self, interner: I, args: I::GenericArgs) -> Self { + Self::new_from_args(interner, self.kind, args) + } + + pub fn expect_projection_ty_def_id(self) -> I::TraitAssocTyId { + match self.kind { + AliasTermKind::ProjectionTy { def_id } => def_id, + kind => panic!("expected projection ty, found {kind:?}"), + } + } + + pub fn expect_opaque_ty_def_id(self) -> I::OpaqueTyId { + match self.kind { + AliasTermKind::OpaqueTy { def_id } => def_id, + kind => panic!("expected opaque ty, found {kind:?}"), + } + } +} + +/// The following methods work only with (trait) associated term projections. +// FIXME: Replace by an impl on Alias +impl AliasTerm { + pub fn self_ty(self) -> I::Ty { + self.args.type_at(0) + } + + pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { + AliasTerm::new( + interner, + self.kind, + [self_ty.into()].into_iter().chain(self.args.iter().skip(1)), + ) + } + + pub fn expect_projection_def_id(self) -> I::TraitAssocTermId { + match self.kind { + AliasTermKind::ProjectionTy { def_id } => def_id.into(), + AliasTermKind::ProjectionConst { def_id } => def_id.into(), + kind => panic!("expected projection alias, found {kind:?}"), + } + } + + pub fn trait_def_id(self, interner: I) -> I::TraitId { + interner.projection_parent(self.expect_projection_def_id()) + } + + /// Extracts the underlying trait reference and own args from this projection. + /// For example, if this is a projection of `::Item<'a>`, + /// then this function would return a `T: StreamingIterator` trait reference and + /// `['a]` as the own args. + pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::GenericArgsSlice) { + interner.trait_ref_and_own_args_for_alias(self.expect_projection_def_id(), self.args) + } + + /// Extracts the underlying trait reference from this projection. + /// For example, if this is a projection of `::Item`, + /// then this function would return a `T: Iterator` trait reference. + /// + /// WARNING: This will drop the args for generic associated types + /// consider calling [Self::trait_ref_and_own_args] to get those + /// as well. + pub fn trait_ref(self, interner: I) -> ty::TraitRef { + self.trait_ref_and_own_args(interner).0 + } + + /// Extract the own args from this projection. + /// For example, if this is a projection of `::Item<'a>`, + /// then this function would return the slice `['a]` as the own args. + pub fn own_args(self, interner: I) -> I::GenericArgsSlice { + self.trait_ref_and_own_args(interner).1 + } +} + +/// The following methods work only with inherent associated term projections. +// FIXME: Replace by an impl on Alias +impl AliasTerm { + pub fn expect_inherent_def_id(self) -> I::InherentAssocTermId { + match self.kind { + AliasTermKind::InherentTy { def_id } => def_id.into(), + AliasTermKind::InherentConst { def_id } => def_id.into(), + kind => panic!("expected inherent alias, found {kind:?}"), + } + } + + /// Transform the generic parameters to have the given `impl` args as the base and the GAT args on top of that. + /// + /// Does the following transformation: + /// + /// ```text + /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m] + /// + /// I_i impl args + /// P_j GAT args + /// ``` + pub fn rebase_inherent_args_onto_impl( + self, + impl_args: I::GenericArgs, + interner: I, + ) -> I::GenericArgs { + debug_assert!(matches!( + self.kind, + AliasTermKind::InherentTy { .. } | AliasTermKind::InherentConst { .. } + )); + interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1))) + } +} + +/// The following methods work only with free term aliases. +// FIXME: Replace by an impl on Alias +impl AliasTerm { + pub fn expect_free_def_id(self) -> I::FreeTermAliasId { + match self.kind { + AliasTermKind::FreeTy { def_id } => def_id.into(), + AliasTermKind::FreeConst { def_id } => def_id.into(), + kind => panic!("expected free alias, found {kind:?}"), + } + } +} + +impl From> for AliasTerm { + fn from(ty: ty::AliasTy) -> Self { + AliasTerm { args: ty.args, kind: AliasTermKind::from(ty.kind), _use_alias_new_instead: () } + } +} + +impl From> for AliasTerm { + fn from(ty: ty::AliasConst) -> Self { + AliasTerm { args: ty.args, kind: AliasTermKind::from(ty.kind), _use_alias_new_instead: () } + } +} diff --git a/compiler/rustc_type_ir/src/ty/alias.rs b/compiler/rustc_type_ir/src/ty/alias.rs index 2ca563c4e7db8..10fd4c6a25394 100644 --- a/compiler/rustc_type_ir/src/ty/alias.rs +++ b/compiler/rustc_type_ir/src/ty/alias.rs @@ -5,9 +5,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use crate::predicate::AliasTermKind; -use crate::ty_kind::AliasTyKind; -use crate::{Interner, UnevaluatedConstKind}; +use crate::{AliasConstKind, AliasTermKind, AliasTyKind, Interner}; /// Represents an alias of a type, constant, or other term-like item. /// @@ -57,4 +55,4 @@ pub type ProjectionAliasTy = Alias::TraitAssocTyId>; pub type InherentAliasTy = Alias::InherentAssocTyId>; pub type OpaqueAliasTy = Alias::OpaqueTyId>; pub type FreeAliasTy = Alias::FreeTyAliasId>; -pub type UnevaluatedConst = Alias>; +pub type AliasConst = Alias>; diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index de5d3b7b233b3..31824b61cfb27 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -107,6 +107,34 @@ impl AliasTyKind { } } +/// Whether an alias type is rigid or potentially normalizeable. +/// +/// This is not used by the old solver and is always `IsRigid::No` there. In the new solver, +/// aliases which cannot be further normalized in their current scope normalize to themselves +/// with `IsRigid::Yes`. At this point we no longer have to try and renormalize this alias +/// later on. +/// +/// FIXME(#155345): Alias handling is currently still in flux for the new trait +/// solver and this is currently somewhat messy. Please reach out on +/// #t-types/trait-system-refactor-initiative if you encounter this and it isn't +/// immediately clear what to do. +#[derive(Debug, Clone, Copy, Hash, PartialEq)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) +)] +pub enum IsRigid { + Yes, + No, +} + +impl IsRigid { + pub fn yes_if_next_solver(interner: I) -> IsRigid { + if interner.next_trait_solver_globally() { IsRigid::Yes } else { IsRigid::No } + } +} + /// Defines the kinds of types used by the type system. /// /// Types written by the user start out as `hir::TyKind` and get @@ -273,7 +301,7 @@ pub enum TyKind { /// /// All of these types are represented as pairs of def-id and args, and can /// be normalized, so they are grouped conceptually. - Alias(AliasTy), + Alias(IsRigid, AliasTy), /// A type parameter; for example, `T` in `fn f(x: T) {}`. Param(I::ParamTy), @@ -375,7 +403,7 @@ impl TyKind { ty::Error(_) | ty::Infer(_) - | ty::Alias(_) + | ty::Alias(ty::IsRigid::No | ty::IsRigid::Yes, _) | ty::Param(_) | ty::Bound(_, _) | ty::Placeholder(_) => false, @@ -440,7 +468,7 @@ impl fmt::Debug for TyKind { } write!(f, ")") } - Alias(a) => f.debug_tuple("Alias").field(&a).finish(), + Alias(is_rigid, a) => f.debug_tuple("Alias").field(&is_rigid).field(&a).finish(), Param(p) => write!(f, "{p:?}"), Bound(d, b) => crate::debug_bound_var(f, *d, b), Placeholder(p) => write!(f, "{p:?}"), @@ -478,8 +506,8 @@ impl AliasTy { matches!(self.kind, AliasTyKind::Opaque { .. }) } - pub fn to_ty(self, interner: I) -> I::Ty { - Ty::new_alias(interner, self) + pub fn to_ty(self, interner: I, is_rigid: ty::IsRigid) -> I::Ty { + Ty::new_alias(interner, is_rigid, self) } pub fn try_to_projection(self) -> Option> { @@ -860,8 +888,19 @@ pub struct TypeAndMut { impl Eq for TypeAndMut {} +/// Error type for splatted argument index errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SplattedArgIndexError { + /// The splatted argument index is invalid. + /// A `u8::MAX` argument index used to indicate that no argument is splatted. + /// Higher values are also not supported, for performance reasons. + InvalidIndex { splatted_arg_index: u8 }, + + /// The splatted argument index is outside the bounds of the function arguments. + OutOfBounds { splatted_arg_index: u8, args_len: u16 }, +} + /// Contains the packed non-type fields of a function signature. -// FIXME(splat): add the splatted argument index as a u16 #[derive_where(Copy, Clone, PartialEq, Eq, Hash; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( @@ -875,6 +914,15 @@ pub struct FnSigKind { #[type_visitable(ignore)] #[type_foldable(identity)] flags: u8, + + /// Which function argument is splatted into multiple arguments in callers, if any? + /// Splatting functions with `>= u8::MAX` arguments is not supported, for performance reasons. + /// (And spending an extra byte on an edge case is not worth the perf.) + #[lift(identity)] + #[type_visitable(ignore)] + #[type_foldable(identity)] + splatted: u8, + #[type_visitable(ignore)] #[type_foldable(identity)] _marker: PhantomData I>, @@ -894,12 +942,28 @@ impl fmt::Debug for FnSigKind { if self.c_variadic() { f.field(&"CVariadic"); - }; + } + + if let Some(index) = self.splatted() { + f.field(&format!("Splatted({})", index)); + } f.finish() } } +impl Default for FnSigKind { + /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic or splatted arguments. + /// To modify these flags, use the `set_*` methods, for readability. + fn default() -> Self { + Self { flags: 0, splatted: 0, _marker: PhantomData } + .set_abi(ExternAbi::Rust) + .set_safety(I::Safety::unsafe_mode()) + .set_c_variadic(false) + .set_no_splatted_args() + } +} + impl FnSigKind { /// Mask for the `ExternAbi` variant, including the unwind flag. const EXTERN_ABI_MASK: u8 = 0b111111; @@ -910,19 +974,34 @@ impl FnSigKind { /// Bitflag for a trailing C-style variadic argument. const C_VARIADIC_FLAG: u8 = 1 << 7; - /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic argument. - /// To modify these flags, use the `set_*` methods, for readability. - // FIXME: use Default instead when that trait is const stable. - pub fn default() -> Self { - Self { flags: 0, _marker: PhantomData } - .set_abi(ExternAbi::Rust) - .set_safety(I::Safety::unsafe_mode()) - .set_c_variadic(false) - } + /// The marker index for "no splatted arguments". Higher values are also not supported, for + /// performance reasons. + /// + /// Must have the same value as `FnDeclFlags::NO_SPLATTED_ARG_INDEX` and + /// `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`. + /// + /// This is an implementation detail, which should only be used in low-level encoding. + pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; - /// Create a new FnSigKind with the given ABI, safety, and C-style variadic flag. - pub fn new(abi: ExternAbi, safety: I::Safety, c_variadic: bool) -> Self { - Self::default().set_abi(abi).set_safety(safety).set_c_variadic(c_variadic) + /// Create a new FnSigKind with the given ABI, safety, C-style variadic, and splatted argument + /// index. + pub fn new( + abi: ExternAbi, + safety: I::Safety, + c_variadic: bool, + splatted: Option, + args_len: usize, + ) -> Result { + Self::default() + .set_abi(abi) + .set_safety(safety) + .set_c_variadic(c_variadic) + .set_splatted(splatted, args_len) + } + + /// Create a new safe FnSigKind with the `extern "Rust"` ABI, that isn't C-style variadic or splatted. + pub fn dummy() -> Self { + Self::default().set_safety(I::Safety::safe()) } /// Set the ABI, including the unwind flag. @@ -961,6 +1040,41 @@ impl FnSigKind { self } + /// Set the splatted argument index. + /// The number of function arguments is used for error checking. + #[must_use = "this method does not modify the receiver"] + pub fn set_splatted( + mut self, + splatted: Option, + args_len: usize, + ) -> Result { + if let Some(splatted_arg_index) = splatted { + if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX { + // This index value is used as a marker for "no splatting", so it is unsupported. + // Higher values are also not supported, for performance reasons. + return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index }); + } else if usize::from(splatted_arg_index) >= args_len { + return Err(SplattedArgIndexError::OutOfBounds { + splatted_arg_index, + args_len: args_len as u16, + }); + } + + self.splatted = splatted_arg_index; + } else { + self.splatted = Self::NO_SPLATTED_ARG_INDEX; + } + + Ok(self) + } + + /// Set the splatted argument index to "no splatted arguments". + #[must_use = "this method does not modify the receiver"] + pub fn set_no_splatted_args(mut self) -> Self { + self.splatted = Self::NO_SPLATTED_ARG_INDEX; + self + } + /// Get the ABI, including the unwind flag. pub fn abi(self) -> ExternAbi { let abi_index = self.flags & Self::EXTERN_ABI_MASK; @@ -981,6 +1095,11 @@ impl FnSigKind { pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } + + /// Get the index of the splatted argument, if any. + pub fn splatted(self) -> Option { + if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) } + } } #[derive_where(Clone, Copy, PartialEq, Hash; I: Interner)] @@ -1011,8 +1130,21 @@ impl FnSig { !self.c_variadic() && self.safety().is_safe() && self.abi() == ExternAbi::Rust } + /// Set the safety flag. + #[must_use = "this method does not modify the receiver"] pub fn set_safety(self, safety: I::Safety) -> Self { - Self { fn_sig_kind: FnSigKind::new(self.abi(), safety, self.c_variadic()), ..self } + Self { fn_sig_kind: self.fn_sig_kind.set_safety(safety), ..self } + } + + /// Set the splatted argument index. + /// The number of function arguments is used for error checking. + #[must_use = "this method does not modify the receiver"] + pub fn set_splatted( + self, + splatted: Option, + args_len: usize, + ) -> Result { + Ok(Self { fn_sig_kind: self.fn_sig_kind.set_splatted(splatted, args_len)?, ..self }) } pub fn safety(self) -> I::Safety { @@ -1027,11 +1159,14 @@ impl FnSig { self.fn_sig_kind.c_variadic() } + pub fn splatted(self) -> Option { + self.fn_sig_kind.splatted() + } + + /// Create a new safe FnSig with no arguments or return type, using the `extern "Rust"` ABI, + /// that isn't C-style variadic or splatted. pub fn dummy() -> Self { - Self { - inputs_and_output: Default::default(), - fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false), - } + Self { inputs_and_output: Default::default(), fn_sig_kind: FnSigKind::dummy() } } } @@ -1064,6 +1199,10 @@ impl ty::Binder> { self.skip_binder().c_variadic() } + pub fn splatted(self) -> Option { + self.skip_binder().splatted() + } + pub fn safety(self) -> I::Safety { self.skip_binder().safety() } @@ -1099,6 +1238,9 @@ impl fmt::Debug for FnSig { if i > 0 { write!(f, ", ")?; } + if Some(i) == fn_sig_kind.splatted().map(usize::from) { + write!(f, "#[splat] ")?; + } write!(f, "{ty:?}")?; } if fn_sig_kind.c_variadic() { @@ -1260,8 +1402,9 @@ impl FnHeader { self.fn_sig_kind.abi() } + /// Create a new safe FnHeader with the `extern "Rust"` ABI, that isn't C-style variadic or splatted. pub fn dummy() -> Self { - Self { fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false) } + Self { fn_sig_kind: FnSigKind::dummy() } } } diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index b688a001f73fa..9453d30ce91c9 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -364,7 +364,7 @@ pub struct CoroutineClosureSignature { // Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types // never actually differ. But we save them rather than recreating them // from scratch just for good measure. - /// Always safe, RustCall, non-c-variadic + /// Always safe, RustCall, non-c-variadic, non-splatted #[type_visitable(ignore)] #[type_foldable(identity)] pub fn_sig_kind: FnSigKind, diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 492c37481298b..b77782b09ef85 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -167,6 +167,12 @@ impl, E: TypeVisitable> TypeVisitable for } } +impl> TypeVisitable for &T { + fn visit_with>(&self, visitor: &mut V) -> V::Result { + (**self).visit_with(visitor) + } +} + impl> TypeVisitable for Arc { fn visit_with>(&self, visitor: &mut V) -> V::Result { (**self).visit_with(visitor) @@ -363,6 +369,16 @@ pub trait TypeVisitableExt: TypeVisitable { fn has_non_region_error(&self) -> bool { self.has_type_flags(TypeFlags::HAS_NON_REGION_ERROR) } + + /// True if an alias has `IsRigid::Yes`. Used for skipping normalization. + fn has_rigid_aliases(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RIGID_ALIAS) + } + + /// True if an alias has `IsRigid::No`. + fn has_non_rigid_aliases(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_NON_RIGID_ALIAS) + } } impl> TypeVisitableExt for T { diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 96ae6f1c06146..d2442358b13cb 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -106,7 +106,7 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg stack.push(ty.into()); stack.push(lt.into()); } - ty::Alias(alias) => { + ty::Alias(_, alias) => { stack.extend(alias.args.iter().rev()); } ty::Dynamic(obj, lt) => { @@ -160,7 +160,7 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg ty::ConstKind::Value(cv) => stack.push(cv.ty().into()), ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()), - ty::ConstKind::Unevaluated(ct) => { + ty::ConstKind::Alias(_, ct) => { stack.extend(ct.args.iter().rev()); } }, diff --git a/library/Cargo.lock b/library/Cargo.lock index 99e9765171c4a..c30ed6dc30c55 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -283,9 +283,9 @@ dependencies = [ [[package]] name = "rustc-literal-escaper" -version = "0.0.7" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be87abb9e40db7466e0681dc8ecd9dcfd40360cb10b4c8fe24a7c4c3669b198" +checksum = "bfe6f213fb658c8fb95baabd5420393438cf5a98d707f5dd701d9197c705f71e" dependencies = [ "rustc-std-workspace-core", ] diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3087a7d2004bc..40e0d8cacfff1 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1753,7 +1753,7 @@ impl Box { /// /// This method guarantees that for the purpose of the aliasing model, this method /// does not materialize a reference to the underlying memory, and thus the returned pointer - /// will remain valid when mixed with other calls to [`as_ptr`] and [`as_mut_ptr`]. + /// will remain valid when mixed with other calls to [`as_ptr`], [`as_mut_ptr`], and [`as_non_null`]. /// Note that calling other methods that materialize references to the memory /// may still invalidate this pointer. /// See the example below for how this guarantee can be used. @@ -1776,6 +1776,8 @@ impl Box { /// /// [`as_mut_ptr`]: Self::as_mut_ptr /// [`as_ptr`]: Self::as_ptr + /// [`as_non_null`]: Self::as_non_null + #[must_use] #[stable(feature = "box_as_ptr", since = "CURRENT_RUSTC_VERSION")] #[rustc_never_returns_null_ptr] #[rustc_as_ptr] @@ -1797,7 +1799,7 @@ impl Box { /// /// This method guarantees that for the purpose of the aliasing model, this method /// does not materialize a reference to the underlying memory, and thus the returned pointer - /// will remain valid when mixed with other calls to [`as_ptr`] and [`as_mut_ptr`]. + /// will remain valid when mixed with other calls to [`as_ptr`], [`as_mut_ptr`], and [`as_non_null`]. /// Note that calling other methods that materialize mutable references to the memory, /// as well as writing to this memory, may still invalidate this pointer. /// See the example below for how this guarantee can be used. @@ -1823,6 +1825,8 @@ impl Box { /// /// [`as_mut_ptr`]: Self::as_mut_ptr /// [`as_ptr`]: Self::as_ptr + /// [`as_non_null`]: Self::as_non_null + #[must_use] #[stable(feature = "box_as_ptr", since = "CURRENT_RUSTC_VERSION")] #[rustc_never_returns_null_ptr] #[rustc_as_ptr] @@ -1833,6 +1837,48 @@ impl Box { &raw const **b } + /// Returns a `NonNull` pointer to the `Box`'s contents. + /// + /// The caller must ensure that the `Box` outlives the pointer this + /// function returns, or else it will end up dangling. + /// + /// This method guarantees that for the purpose of the aliasing model, this method + /// does not materialize a reference to the underlying memory, and thus the returned pointer + /// will remain valid when mixed with other calls to [`as_ptr`], [`as_mut_ptr`], and [`as_non_null`]. + /// Note that calling other methods that materialize references to the memory + /// may still invalidate this pointer. + /// See the example below for how this guarantee can be used. + /// + /// # Examples + /// + /// Due to the aliasing guarantee, the following code is legal: + /// + /// ```rust + /// #![feature(box_as_non_null)] + /// + /// unsafe { + /// let mut b = Box::new(0); + /// let ptr1 = Box::as_non_null(&mut b); + /// ptr1.write(1); + /// let ptr2 = Box::as_non_null(&mut b); + /// ptr2.write(2); + /// // Notably, the write to `ptr2` did *not* invalidate `ptr1`: + /// ptr1.write(3); + /// } + /// ``` + /// + /// [`as_mut_ptr`]: Self::as_mut_ptr + /// [`as_ptr`]: Self::as_ptr + /// [`as_non_null`]: Self::as_non_null + #[must_use] + #[unstable(feature = "box_as_non_null", issue = "157345")] + #[rustc_as_ptr] + #[inline] + pub fn as_non_null(b: &mut Self) -> NonNull { + // SAFETY: `Box` is guaranteed to be non-null. + unsafe { NonNull::new_unchecked(Self::as_mut_ptr(b)) } + } + /// Returns a reference to the underlying allocator. /// /// Note: this is an associated function, which means that you have diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 5a900db9d1ce8..065cc128101ae 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -267,7 +267,7 @@ impl VecDeque { /// /// - Ranges must not overlap: `src.abs_diff(dst) >= count`. /// - Ranges must be in bounds of the logical buffer: `src + count <= self.capacity()` and `dst + count <= self.capacity()`. - /// - `head` must be in bounds: `head < self.capacity()`. + /// - `head` must be in bounds: `head < self.capacity()`, unless `self.capacity() == 0`, in which case `head == 0`. #[cfg(not(no_global_oom_handling))] unsafe fn nonoverlapping_ranges( &mut self, diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs new file mode 100644 index 0000000000000..eb02664ee7e46 --- /dev/null +++ b/library/alloc/src/io/error.rs @@ -0,0 +1,277 @@ +use core::io::Custom; +#[cfg_attr(no_global_oom_handling, expect(unused_imports))] +use core::io::CustomOwner; +use core::{error, result}; + +use crate::boxed::Box; +#[cfg_attr(any(no_rc, no_sync, no_global_oom_handling), expect(unused_imports))] +use crate::io::const_error; +use crate::io::{Error, ErrorKind}; + +impl Error { + /// Creates a new I/O error from a known kind of error as well as an + /// arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. The `error` argument is an arbitrary + /// payload which will be contained in this [`Error`]. + /// + /// Note that this function allocates memory on the heap. + /// If no extra payload is required, use the `From` conversion from + /// `ErrorKind`. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// // errors can be created from strings + /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); + /// + /// // creating an error without payload (and without memory allocation) + /// let eof_error = Error::from(ErrorKind::UnexpectedEof); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] + #[inline(never)] + #[rustc_allow_incoherent_impl] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + let custom = custom_owner_from_box(kind, error.into()); + + // SAFETY: `custom_owner` has been constructed from a `Box` from the `alloc` crate. + unsafe { Self::from_custom_owner(custom) } + } + + /// Creates a new I/O error from an arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. It is a shortcut for [`Error::new`][new] + /// with [`ErrorKind::Other`]. + /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method + /// [new]: struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::Error; + /// + /// // errors can be created from strings + /// let custom_error = Error::other("oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::other(custom_error); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "io_error_other", since = "1.74.0")] + #[rustc_allow_incoherent_impl] + pub fn other(error: E) -> Error + where + E: Into>, + { + Self::new(ErrorKind::Other, error) + } + + /// Consumes the `Error`, returning its inner error (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] or [`other`][other], + /// then this function will return [`Some`], + /// otherwise it will return [`None`]. + /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method + /// [new]: struct.Error.html#method.new + /// [other]: struct.Error.html#method.other + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// if let Some(inner_err) = err.into_inner() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + #[rustc_allow_incoherent_impl] + pub fn into_inner(self) -> Option> { + let custom_owner = self.into_custom_owner().ok()?; + + let ptr = custom_owner.into_raw().as_ptr(); + + // SAFETY: + // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`. + let custom = unsafe { Box::::from_raw(ptr) }; + + let ptr = custom.into_raw().as_ptr(); + + // SAFETY: + // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate + // to contain a `Custom` which itself was constructed with `Box::into_raw`. + Some(unsafe { Box::from_raw(ptr) }) + } + + /// Attempts to downcast the custom boxed error to `E`. + /// + /// If this [`Error`] contains a custom boxed error, + /// then it would attempt downcasting on the boxed error, + /// otherwise it will return [`Err`]. + /// + /// If the custom boxed error has the same type as `E`, it will return [`Ok`], + /// otherwise it will also return [`Err`]. + /// + /// This method is meant to be a convenience routine for calling + /// `Box::downcast` on the custom boxed error, returned by + /// [`Error::into_inner`][into_inner]. + /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method + /// [into_inner]: struct.Error.html#method.into_inner + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// use std::io; + /// use std::error::Error; + /// + /// #[derive(Debug)] + /// enum E { + /// Io(io::Error), + /// SomeOtherVariant, + /// } + /// + /// impl fmt::Display for E { + /// // ... + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # todo!() + /// # } + /// } + /// impl Error for E {} + /// + /// impl From for E { + /// fn from(err: io::Error) -> E { + /// err.downcast::() + /// .unwrap_or_else(E::Io) + /// } + /// } + /// + /// impl From for io::Error { + /// fn from(err: E) -> io::Error { + /// match err { + /// E::Io(io_error) => io_error, + /// e => io::Error::new(io::ErrorKind::Other, e), + /// } + /// } + /// } + /// + /// # fn main() { + /// let e = E::SomeOtherVariant; + /// // Convert it to an io::Error + /// let io_error = io::Error::from(e); + /// // Cast it back to the original variant + /// let e = E::from(io_error); + /// assert!(matches!(e, E::SomeOtherVariant)); + /// + /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); + /// // Convert it to E + /// let e = E::from(io_error); + /// // Cast it back to the original variant + /// let io_error = io::Error::from(e); + /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); + /// assert!(io_error.get_ref().is_none()); + /// assert!(io_error.raw_os_error().is_none()); + /// # } + /// ``` + #[stable(feature = "io_error_downcast", since = "1.79.0")] + #[rustc_allow_incoherent_impl] + pub fn downcast(self) -> result::Result + where + E: error::Error + Send + Sync + 'static, + { + if let Some(e) = self.get_ref() + && e.is::() + { + if let Some(b) = self.into_inner() + && let Ok(err) = b.downcast::() + { + Ok(*err) + } else { + // Safety: We have just checked that the condition is true + unsafe { core::hint::unreachable_unchecked() } + } + } else { + Err(self) + } + } +} + +#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] +#[stable(feature = "rust1", since = "1.0.0")] +impl From for Error { + /// Converts a [`crate::ffi::NulError`] into a [`Error`]. + fn from(_: crate::ffi::NulError) -> Error { + const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") + } +} + +#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] +impl From for Error { + /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. + /// + /// `TryReserveError` won't be available as the error `source()`, + /// but this may change in the future. + fn from(_: crate::collections::TryReserveError) -> Error { + // ErrorData::Custom allocates, which isn't great for handling OOM errors. + ErrorKind::OutOfMemory.into() + } +} + +#[cfg(not(no_global_oom_handling))] +fn custom_owner_from_box( + kind: ErrorKind, + error: Box, +) -> CustomOwner { + /// # Safety + /// + /// `ptr` must be valid to pass into `Box::from_raw`. + unsafe fn drop_box_raw(ptr: *mut T) { + // SAFETY + // Caller ensures `ptr` is valid to pass into `Box::from_raw`. + drop(unsafe { Box::from_raw(ptr) }) + } + + // SAFETY: the pointer returned by Box::into_raw is non-null. + let error = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(error)) }; + + // SAFETY: + // * `error` is valid up to a static lifetime, and owns its pointee. + // * `drop_box_raw` is safe to call for the pointer `error` exactly once. + // * `drop_box_raw` is safe to call on a pointer to this instance of `Custom`, + // and will be stored in a `CustomOwner`. + let custom = unsafe { Custom::from_raw(kind, error, drop_box_raw, drop_box_raw) }; + + // SAFETY: the pointer returned by Box::into_raw is non-null. + let custom = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(Box::new(custom))) }; + + // SAFETY: the `outer_drop` provided to `custom` is valid for itself. + unsafe { CustomOwner::from_raw(custom) } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs new file mode 100644 index 0000000000000..58e5ffa39d0c4 --- /dev/null +++ b/library/alloc/src/io/mod.rs @@ -0,0 +1,20 @@ +//! Traits, helpers, and type definitions for core I/O functionality. + +mod error; + +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use core::io::RawOsError; +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use core::io::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use core::io::const_error; +#[unstable(feature = "core_io_borrowed_buf", issue = "117693")] +pub use core::io::{BorrowedBuf, BorrowedCursor}; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use core::io::{ + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty, + repeat, sink, +}; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use core::io::{OsFunctions, chain, take}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index b0e8039263ffe..33fbcd8e32a6e 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -65,15 +65,17 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables, duplicate_features), deny(warnings))) )] -#![doc(auto_cfg(hide(no_global_oom_handling, no_rc, no_sync, target_has_atomic = "ptr")))] +#![doc(auto_cfg( + hide(no_global_oom_handling, no_rc, no_sync), + hide(target_has_atomic, values("ptr")), +))] #![doc(rust_logo)] #![feature(rustdoc_internals)] #![no_std] #![needs_allocator] // Lints: #![deny(unsafe_op_in_unsafe_fn)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![warn(deprecated_in_future)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] @@ -114,6 +116,9 @@ #![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] +#![feature(core_io)] +#![feature(core_io_borrowed_buf)] +#![feature(core_io_internals)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] #![feature(diagnostic_on_move)] @@ -131,12 +136,15 @@ #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] +#![feature(io_const_error)] +#![feature(io_const_error_internals)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(layout_for_ptr)] #![feature(legacy_receiver_trait)] #![feature(likely_unlikely)] #![feature(local_waker)] +#![feature(maybe_uninit_array_assume_init)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] @@ -145,6 +153,7 @@ #![feature(ptr_cast_slice)] #![feature(ptr_internals)] #![feature(ptr_metadata)] +#![feature(raw_os_error_ty)] #![feature(rev_into_inner)] #![feature(set_ptr_value)] #![feature(share_trait)] @@ -234,6 +243,8 @@ pub mod collections; pub mod ffi; pub mod fmt; pub mod intrinsics; +#[unstable(feature = "alloc_io", issue = "154046")] +pub mod io; #[cfg(not(no_rc))] pub mod rc; pub mod slice; diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 229514803bf6a..eb00db4568b63 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2239,15 +2239,33 @@ impl FromUtf8Error { /// invalid sequences, and [`String::from_utf8_lossy_owned`] for the /// `String` function which corresponds to this function. /// + /// This is useful in conjunction with [`String::from_utf8`] when you need + /// to branch on whether the bytes are valid UTF-8, but still want to + /// recover a lossily converted `String` in the error case. Use + /// [`String::from_utf8_lossy_owned`] if you always need a lossily converted + /// `String`. + /// + /// Since the original [`String::from_utf8`] error records where validation + /// stopped, this method does not need to re-check the already valid prefix + /// of the byte sequence. + /// /// # Examples /// /// ``` /// #![feature(string_from_utf8_lossy_owned)] /// // some invalid bytes /// let input: Vec = b"Hello \xF0\x90\x80World".into(); - /// let output = String::from_utf8(input).unwrap_or_else(|e| e.into_utf8_lossy()); + /// + /// let (output, had_invalid_utf8) = match String::from_utf8(input) { + /// Ok(output) => (output, false), + /// Err(error) => { + /// // The bytes were not valid UTF-8, but we can still recover a string. + /// (error.into_utf8_lossy(), true) + /// } + /// }; /// /// assert_eq!(String::from("Hello �World"), output); + /// assert!(had_invalid_utf8); /// ``` #[must_use] #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 97e4912596b94..b7fcecf11e059 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -445,6 +445,47 @@ impl DoubleEndedIterator for IntoIter { } } + #[inline] + fn next_chunk_back(&mut self) -> Result<[T; N], core::array::IntoIter> { + let mut raw_ary = [const { MaybeUninit::uninit() }; N]; + + let len = self.len(); + + if T::IS_ZST { + if len < N { + self.forget_remaining_elements(); + // Safety: ZSTs can be conjured ex nihilo, only the amount has to be correct + return Err(unsafe { array::IntoIter::new_unchecked(raw_ary, N - len..N) }); + } + + self.end = self.end.wrapping_byte_sub(N); + // Safety: ditto + return Ok(unsafe { MaybeUninit::array_assume_init(raw_ary) }); + } + + if len < N { + // Safety: `len` indicates that this many elements are available and we just checked that + // it fits into the array. + unsafe { + ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, len); + self.forget_remaining_elements(); + return Err(array::IntoIter::new_unchecked(raw_ary, 0..len)); + } + } + + // Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize + // the array. + unsafe { + ptr::copy_nonoverlapping( + self.ptr.add(len - N).as_ptr(), + raw_ary.as_mut_ptr() as *mut T, + N, + ); + self.end = self.end.sub(N); + Ok(MaybeUninit::array_assume_init(raw_ary)) + } + } + #[inline] fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { let step_size = self.len().min(n); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index b37f865020fd9..c3b86a635bd0f 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1760,7 +1760,11 @@ impl Vec { #[must_use] pub fn into_array(self) -> Result, Self> { if self.len() == N { - Ok(self.into_boxed_slice().into_array().ok().unwrap()) + // SAFETY: `Box::into_array` is guaranteed to return `Ok` if the + // length of the slice is equal to `N`. + // `self.into_boxed_slice().len()` is equal to `self.len()`, + // which we just checked. + Ok(unsafe { self.into_boxed_slice().into_array().unwrap_unchecked() }) } else { Err(self) } diff --git a/library/alloctests/benches/lib.rs b/library/alloctests/benches/lib.rs index 2be7a24d2de7b..4b7139d943593 100644 --- a/library/alloctests/benches/lib.rs +++ b/library/alloctests/benches/lib.rs @@ -6,8 +6,7 @@ #![feature(slice_partition_dedup)] #![feature(strict_provenance_lints)] #![feature(test)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] extern crate test; diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index f7feb3bbbca85..f89b974cd290c 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -43,8 +43,7 @@ #![feature(vec_try_remove)] #![feature(ptr_cast_slice)] #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] extern crate alloc; diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index ccf9b8095b290..fc58e4364fe66 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -1027,6 +1027,15 @@ fn test_into_iter_next_chunk() { assert_eq!(iter.next_chunk::<4>().unwrap_err().as_slice(), &[]); // N is explicitly 4 } +#[test] +fn test_into_iter_next_chunk_back() { + let mut iter = b"lorem".to_vec().into_iter(); + + assert_eq!(iter.next_chunk_back().unwrap(), [b'e', b'm']); // N is inferred as 2 + assert_eq!(iter.next_chunk_back().unwrap(), [b'l', b'o', b'r']); // N is inferred as 3 + assert_eq!(iter.next_chunk_back::<4>().unwrap_err().as_slice(), &[]); // N is explicitly 4 +} + #[test] fn test_into_iter_clone() { fn iter_equal>(it: I, slice: &[i32]) { @@ -1131,6 +1140,14 @@ fn test_into_iter_zst() { let mut it = vec![C, C].into_iter(); it.next_chunk::<4>().unwrap_err(); drop(it); + + let mut it = vec![C, C].into_iter(); + it.next_chunk_back::<1>().unwrap(); + drop(it); + + let mut it = vec![C, C].into_iter(); + it.next_chunk_back::<4>().unwrap_err(); + drop(it); } #[test] diff --git a/library/compiler-builtins/compiler-builtins/src/arm.rs b/library/compiler-builtins/compiler-builtins/src/arm.rs index 0c15b37df1dc1..f26fce2d912b6 100644 --- a/library/compiler-builtins/compiler-builtins/src/arm.rs +++ b/library/compiler-builtins/compiler-builtins/src/arm.rs @@ -84,7 +84,11 @@ intrinsics! { /// /// Usual `memcpy` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // SAFETY: memcpy preconditions apply. unsafe { crate::mem::memcpy(dst, src, n) }; } @@ -96,7 +100,11 @@ intrinsics! { /// Usual `memcpy` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy4(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy4( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // We are guaranteed 4-alignment, so accessing at u32 is okay. let mut dst = dst.cast::(); let mut src = src.cast::(); @@ -121,7 +129,7 @@ intrinsics! { } // SAFETY: `dst` and `src` will still be valid for `n` bytes - unsafe { __aeabi_memcpy(dst.cast::(), src.cast::(), n) }; + unsafe { __aeabi_memcpy(dst.cast::(), src.cast::(), n) }; } /// `memcpy` for 8-byte alignment. @@ -131,7 +139,11 @@ intrinsics! { /// Usual `memcpy` requirements apply. Additionally, `dest` and `src` must be aligned to /// eight bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy8(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy8( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(8)); debug_assert!(src.addr().is_multiple_of(8)); @@ -145,7 +157,11 @@ intrinsics! { /// /// Usual `memmove` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memmove(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // SAFETY: memmove preconditions apply. unsafe { crate::mem::memmove(dst, src, n) }; } @@ -157,7 +173,11 @@ intrinsics! { /// Usual `memmove` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memmove4(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove4( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(4)); debug_assert!(src.addr().is_multiple_of(4)); @@ -172,7 +192,11 @@ intrinsics! { /// Usual `memmove` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memmove8(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove8( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(8)); debug_assert!(src.addr().is_multiple_of(8)); @@ -186,7 +210,11 @@ intrinsics! { /// /// Usual `memset` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { // Note the different argument order // SAFETY: memset preconditions apply. unsafe { crate::mem::memset(dst, c, n) }; @@ -199,7 +227,11 @@ intrinsics! { /// Usual `memset` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset4(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset4( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { let mut dst = dst.cast::(); debug_assert!(dst.is_aligned()); let mut n = n; @@ -222,7 +254,7 @@ intrinsics! { } // SAFETY: `dst` will still be valid for `n` bytes - unsafe { __aeabi_memset(dst.cast::(), n, byte as i32) }; + unsafe { __aeabi_memset(dst.cast::(), n, byte as i32) }; } /// `memset` for 8-byte alignment. @@ -232,7 +264,11 @@ intrinsics! { /// Usual `memset` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset8(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset8( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { debug_assert!(dst.addr().is_multiple_of(8)); // SAFETY: memset preconditions apply, less strict alignment. @@ -245,7 +281,7 @@ intrinsics! { /// /// Usual `memclr` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memclr(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr(dst: *mut core::ffi::c_void, n: usize) { // SAFETY: memclr preconditions apply, less strict alignment. unsafe { __aeabi_memset(dst, n, 0) }; } @@ -257,7 +293,7 @@ intrinsics! { /// Usual `memclr` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memclr4(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr4(dst: *mut core::ffi::c_void, n: usize) { debug_assert!(dst.addr().is_multiple_of(4)); // SAFETY: memclr preconditions apply, less strict alignment. @@ -271,7 +307,7 @@ intrinsics! { /// Usual `memclr` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memclr8(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr8(dst: *mut core::ffi::c_void, n: usize) { debug_assert!(dst.addr().is_multiple_of(8)); // SAFETY: memclr preconditions apply, less strict alignment. diff --git a/library/compiler-builtins/compiler-builtins/src/mem/mod.rs b/library/compiler-builtins/compiler-builtins/src/mem/mod.rs index ac41cd33416f6..ef0f4da228e96 100644 --- a/library/compiler-builtins/compiler-builtins/src/mem/mod.rs +++ b/library/compiler-builtins/compiler-builtins/src/mem/mod.rs @@ -9,37 +9,57 @@ mod impls; intrinsics! { #[mem_builtin] - pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { - impls::copy_forward(dest, src, n); + pub unsafe extern "C" fn memcpy( + dest: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) -> *mut core::ffi::c_void { + impls::copy_forward(dest.cast(), src.cast(), n); dest } #[mem_builtin] - pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + pub unsafe extern "C" fn memmove( + dest: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) -> *mut core::ffi::c_void { let delta = (dest as usize).wrapping_sub(src as usize); if delta >= n { // We can copy forwards because either dest is far enough ahead of src, // or src is ahead of dest (and delta overflowed). - impls::copy_forward(dest, src, n); + impls::copy_forward(dest.cast(), src.cast(), n); } else { - impls::copy_backward(dest, src, n); + impls::copy_backward(dest.cast(), src.cast(), n); } dest } #[mem_builtin] - pub unsafe extern "C" fn memset(s: *mut u8, c: core::ffi::c_int, n: usize) -> *mut u8 { - impls::set_bytes(s, c as u8, n); + pub unsafe extern "C" fn memset( + s: *mut core::ffi::c_void, + c: core::ffi::c_int, + n: usize + ) -> *mut core::ffi::c_void { + impls::set_bytes(s.cast(), c as u8, n); s } #[mem_builtin] - pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> core::ffi::c_int { - impls::compare_bytes(s1, s2, n) + pub unsafe extern "C" fn memcmp( + s1: *const core::ffi::c_void, + s2: *const core::ffi::c_void, + n: usize + ) -> core::ffi::c_int { + impls::compare_bytes(s1.cast(), s2.cast(), n) } #[mem_builtin] - pub unsafe extern "C" fn bcmp(s1: *const u8, s2: *const u8, n: usize) -> core::ffi::c_int { + pub unsafe extern "C" fn bcmp( + s1: *const core::ffi::c_void, + s2: *const core::ffi::c_void, + n: usize + ) -> core::ffi::c_int { memcmp(s1, s2, n) } diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 8f435dd72d7a1..5421b1d2a2652 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -40,5 +40,8 @@ check-cfg = [ 'cfg(target_has_reliable_f128)', 'cfg(target_has_reliable_f128_math)', 'cfg(llvm_enzyme)', + # Prevents use of a static variable for providing platform specific RawOsError + # functionality + 'cfg(no_io_statics)', ] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 5784dbed03634..63a99349d7c9a 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -969,6 +969,54 @@ const impl Drop for Guard<'_, T> { } } +/// Panic guard for incremental initialization of arrays from the back. +/// +/// Elements of the array are populated starting from the end towards the beginning. +/// Disarm the guard with `mem::forget` once the array has been fully initialized. +/// +/// # Safety +/// +/// All write accesses to this structure are unsafe and must maintain a correct +/// count of `initialized` elements. +struct GuardBack<'a, T> { + /// The array to be initialized (will be filled from the end). + pub array_mut: &'a mut [MaybeUninit], + /// The number of items that have been initialized so far. + pub initialized: usize, +} + +impl GuardBack<'_, T> { + /// Adds an item to the array and updates the initialized item counter. + /// + /// # Safety + /// + /// No more than N elements must be initialized. + #[inline] + pub(crate) unsafe fn push_unchecked(&mut self, item: T) { + // SAFETY: If `initialized` was correct before and the caller does not + // invoke this method more than N times, then writes will be in-bounds + // and slots will not be initialized more than once. + unsafe { + let offset = self.initialized.unchecked_add(1); + let index = self.array_mut.len().unchecked_sub(offset); + self.array_mut.get_unchecked_mut(index).write(item); + self.initialized = offset; + } + } +} + +impl Drop for GuardBack<'_, T> { + #[inline] + fn drop(&mut self) { + debug_assert!(self.initialized <= self.array_mut.len()); + let len = self.array_mut.len(); + // SAFETY: this slice will contain only initialized objects. + unsafe { + self.array_mut.get_unchecked_mut(len - self.initialized..len).assume_init_drop(); + } + } +} + /// Pulls `N` items from `iter` and returns them as an array. If the iterator /// yields fewer than `N` items, `Err` is returned containing an iterator over /// the already yielded items. @@ -1076,3 +1124,109 @@ const fn iter_next_chunk_erased( mem::forget(guard); Ok(()) } + +/// Pulls `N` items from the back of `iter` and returns them as an array. +/// If the iterator yields fewer than `N` items, `Err` is returned containing +/// an iterator over the already yielded items. +/// +/// Since the iterator is passed as a mutable reference and this function calls +/// `next_back` at most `N` times, the iterator can still be used afterwards to +/// retrieve the remaining items. +/// +/// If `iter.next_back()` panics, all items already yielded by the iterator are +/// dropped. +/// +/// Used for [`DoubleEndedIterator::next_chunk_back`]. +#[inline] +pub(crate) fn iter_next_chunk_back( + iter: &mut impl DoubleEndedIterator, +) -> Result<[T; N], IntoIter> { + iter.spec_next_chunk_back() +} + +pub(crate) trait SpecNextChunkBack: + DoubleEndedIterator +{ + fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter>; +} + +impl, T, const N: usize> SpecNextChunkBack for I { + #[inline] + default fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter> { + let mut array = [const { MaybeUninit::uninit() }; N]; + let r = iter_next_chunk_back_erased(&mut array, self); + match r { + Ok(()) => { + // SAFETY: All elements of `array` were populated. + Ok(unsafe { MaybeUninit::array_assume_init(array) }) + } + Err(initialized) => { + // SAFETY: Only the last `initialized` elements were populated + Err(unsafe { IntoIter::new_unchecked(array, N - initialized..N) }) + } + } + } +} + +impl + TrustedLen, T, const N: usize> SpecNextChunkBack + for I +{ + fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter> { + let len = (*self).size_hint().0; + let mut array = [const { MaybeUninit::uninit() }; N]; + if len < N { + // SAFETY: `TrustedLen`, an unsafe trait, requires that i can get len items out of it. + unsafe { write_back(&mut array, self, len) }; + // SAFETY: Only the last `len` elements were populated + Err(unsafe { IntoIter::new_unchecked(array, N - len..N) }) + } else { + // SAFETY: `TrustedLen`, an unsafe trait, requires that i can get N items out of it. + unsafe { write_back(&mut array, self, N) }; + // SAFETY: All N items were populated + Ok(unsafe { MaybeUninit::array_assume_init(array) }) + } + } +} + +// SAFETY: `from` must have len items, and len items must be < N. +unsafe fn write_back( + to: &mut [MaybeUninit; N], + from: &mut impl DoubleEndedIterator, + len: usize, +) { + let mut guard = GuardBack { array_mut: to, initialized: 0 }; + while guard.initialized < len { + // SAFETY: caller has guaranteed, from has len items. + let item = unsafe { from.next_back().unwrap_unchecked() }; + // SAFETY: guard.initialized < len < N + unsafe { guard.push_unchecked(item) }; + } + crate::mem::forget(guard); +} + +/// Version of [`iter_next_chunk_back`] using a passed-in slice +/// in order to avoid needing to monomorphize for every array length. +/// +/// Unfortunately this loop has two exit conditions, the buffer filling up +/// or the iterator running out of items, making it tend to optimize poorly. +#[inline] +fn iter_next_chunk_back_erased( + buffer: &mut [MaybeUninit], + iter: &mut impl DoubleEndedIterator, +) -> Result<(), usize> { + // if `Iterator::next_back` panics, this guard will drop already initialized items + let mut guard = GuardBack { array_mut: buffer, initialized: 0 }; + while guard.initialized < guard.array_mut.len() { + let Some(item) = iter.next_back() else { + let initialized = guard.initialized; + mem::forget(guard); + return Err(initialized); + }; + + // SAFETY: The loop condition ensures we have space to push the item + unsafe { guard.push_unchecked(item) }; + } + + mem::forget(guard); + Ok(()) +} diff --git a/library/core/src/attribute_docs.rs b/library/core/src/attribute_docs.rs new file mode 100644 index 0000000000000..e8d36483f3139 --- /dev/null +++ b/library/core/src/attribute_docs.rs @@ -0,0 +1,337 @@ +#[doc(attribute = "must_use")] +// +/// Warn when a value is ignored. +/// +/// The `must_use` attribute applies to values where simply creating or returning them is +/// often not enough. If a value marked with `#[must_use]` is produced and then ignored, the +/// compiler warns through the [`unused_must_use`] lint. +/// +/// This is most common on types that represent an important state or outcome. For example, +/// [`Result`] is marked `#[must_use]` because ignoring an error value can hide a failed operation. +/// In the following example, the returned `Result` is the only sign that writing the message +/// might have failed: +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// fn write_message() -> std::io::Result<()> { +/// // Write the message... +/// Ok(()) +/// } +/// +/// write_message(); +/// ``` +/// +/// Ignoring that `Result` triggers this warning: +/// +/// ```text +/// warning: unused `Result` that must be used +/// = note: this `Result` may be an `Err` variant, which should be handled +/// = note: `#[warn(unused_must_use)]` (part of `#[warn(unused)]`) on by default +/// help: use `let _ = ...` to ignore the resulting value +/// ``` +/// +/// Future values are also `#[must_use]`: creating a future does not run it, so ignoring one often +/// means the intended asynchronous work never happens. +/// +/// You can also place `#[must_use]` on a function, method, or trait declaration. On a function or +/// method, the warning is tied to ignoring that call's return value: +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// #[must_use] +/// fn make_token() -> String { +/// String::from("token") +/// } +/// +/// // Ignoring this call's return value triggers `unused_must_use`. +/// make_token(); +/// ``` +/// +/// On a trait, the warning applies when a function returns an opaque type (`impl Trait`) or trait +/// object (`dyn Trait`) whose bounds include that trait. This is how futures warn if you create one +/// but never poll or await it, since an `async fn` returns an opaque type implementing [`Future`]. +/// +/// The attribute can include a message explaining what the caller should do with the value: +/// +/// ```rust +/// # #![allow(dead_code)] +/// #[must_use = "call `.finish()` to complete the operation"] +/// fn start_operation() -> Operation { +/// Operation +/// } +/// +/// struct Operation; +/// ``` +/// +/// If intentionally ignoring the value is correct, bind it to `_` or call [`drop`]: +/// +/// ```rust +/// # #[must_use] +/// # fn make_token() -> String { +/// # String::from("token") +/// # } +/// let _ = make_token(); +/// drop(make_token()); +/// ``` +/// +/// The attribute is a warning tool, not a type-system rule. Code can still explicitly discard a +/// `#[must_use]` value, and the compiler does not require callers to inspect or otherwise act on +/// the value. +/// +/// For more information, see the Reference on [the `must_use` attribute]. +/// +/// [`Result`]: result::Result +/// [`Future`]: future::Future +/// [`unused_must_use`]: ../rustc/lints/listing/warn-by-default.html#unused-must-use +/// [the `must_use` attribute]: ../reference/attributes/diagnostics.html#the-must_use-attribute +mod must_use_attribute {} + +#[doc(attribute = "allow")] +// +/// The `allow` attribute suppresses lint diagnostics that would otherwise produce +/// warnings or errors. It can be used on any lint or lint group (except those +/// set to `forbid`). +/// +/// ```rust +/// #[allow(dead_code)] +/// fn unused_function() { +/// // ... +/// } +/// +/// fn main() { +/// // `unused_function` does not generate a compiler warning. +/// } +/// ``` +/// +/// Without `#[allow(dead_code)]`, the example above would emit: +/// +/// ```text +/// warning: function `unused_function` is never used +/// --> main.rs:1:4 +/// | +/// 1 | fn unused_function() { +/// | ^^^^^^^^^^^^^^^ +/// | +/// = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default +/// +/// warning: 1 warning emitted +/// ``` +/// +/// Multiple lints can be set to `allow` at once with commas: +/// +/// ```rust +/// #[allow(unused_variables, unused_mut)] +/// fn main() { +/// let mut x: u32 = 42; +/// } +/// ``` +/// +/// This is mostly used to prevent lint warnings or errors while still under development. +/// +/// It cannot override a lint that has been set to `forbid`. +/// +/// It's also important to consider that overusing `allow` could make code harder to maintain +/// and possibly hide issues. To mitigate this issue, using the `expect` attribute is preferred. +/// +/// `allow` can be overridden by `warn`, `deny`, and `forbid`. +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `allow` attribute]. +/// +/// [the `allow` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod allow_attribute {} + +#[doc(attribute = "cfg")] +// +/// Used for conditional compilation. +/// +/// The `cfg` attribute allows compiling an item under specific conditions, otherwise it +/// will be ignored. +/// +/// ```rust +/// // Only compiles this function for Linux. +/// #[cfg(target_os = "linux")] +/// fn platform_specific() { +/// println!("Running on Linux"); +/// } +/// +/// // Only compiles this function if not for Linux. +/// #[cfg(not(target_os = "linux"))] +/// fn platform_specific() { +/// println!("Running on something else"); +/// } +/// ``` +/// +/// Depending on the platform you're targeting, only one of these two functions will be considered +/// during the compilation. +/// +/// Conditions can also be combined with `all(...)`, `any(...)`, and `not(...)`. +/// +/// * `all`: True if all given predicates are true. +/// * `any`: True if at least one of the given predicates is true. +/// * `not`: True if the predicate is false and false if the predicate is true. +/// +/// ```rust +/// #[cfg(all(unix, target_pointer_width = "64"))] +/// fn unix_64bit() { +/// } +/// ``` +/// +/// If you want to use this mechanism in an `if` condition in your code, you +/// can use the [`cfg!`] macro. To conditionally apply an attribute, +/// see [`cfg_attr`]. +/// +/// For more information, see the Reference on [the `cfg` attribute]. +/// +/// [`cfg_attr`]: ../reference/conditional-compilation.html#the-cfg_attr-attribute +/// [the `cfg` attribute]: ../reference/conditional-compilation.html#the-cfg-attribute +mod cfg_attribute {} + +#[doc(attribute = "deny")] +// +/// Emits an error, preventing the compilation from finishing, when a lint check has failed. +/// This is useful for enforcing rules or preventing certain patterns: +/// +/// ```rust,compile_fail +/// #[deny(unused)] +/// fn foo() { +/// let x = 42; // Emits an error because x is unused. +/// } +/// ``` +/// +/// `deny` can be overridden by `allow`, `warn`, and `forbid`: +/// +/// ```rust +/// #![deny(unused)] +/// +/// #[allow(unused)] // We override the `deny` for this function. +/// fn foo() { +/// let x = 42; // No lint emitted even though `x` is unused. +/// } +/// ``` +/// +/// Multiple lints can also be set to `deny` at once: +/// +/// ```rust,compile_fail +/// #![deny(unused_imports, unused_variables)] +/// use std::collections::*; +/// +/// fn main() { +/// let mut x = 10; +/// } +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `deny` attribute]. +/// +/// [the `deny` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod deny_attribute {} + +#[doc(attribute = "forbid")] +// +/// Emits an error, preventing the compilation from finishing, when a lint check has failed. +/// +/// A lint set to `forbid` cannot be overridden by `allow` or `warn`. +/// Attempting either will result in a compilation error. Writing `#[deny(...)]` on the same lint inside a +/// `forbid` scope is permitted, but has no effect; the lint remains at the `forbid` level. +/// +/// This is useful for enforcing strict policies that should not be relaxed +/// anywhere in the codebase. Example: +/// +/// ```rust +/// #![forbid(unsafe_code)] +/// +/// // This would cause a compilation error if uncommented: +/// // #[allow(unsafe_code)] // error: cannot override `forbid` +/// ``` +/// +/// Multiple lints can be set to `forbid` at once: +/// +/// ```rust +/// #![forbid(unsafe_code, unused)] +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `forbid` attribute]. +/// +/// [the `forbid` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod forbid_attribute {} + +#[doc(attribute = "deprecated")] +// +/// Emits a warning during compilation when an item with this attribute is used. +/// `since` and `note` are optional fields giving more detail about why the item is deprecated. +/// +/// * `since`: the version since when the item is deprecated. +/// * `note`: the reason why an item is deprecated. +/// +/// Example: +/// +/// ```rust +/// #[deprecated(since = "1.0.0", note = "Use bar instead")] +/// struct Foo; +/// struct Bar; +/// ``` +/// +/// `deprecated` attribute helps developers transition away from old code by providing warnings when +/// deprecated items are used. Note that during `Cargo` builds, warnings on dependencies get silenced +/// by default, so you may not see a deprecation warning unless you build that dependency directly. +/// +/// For more information, see the Reference on [the `deprecated` attribute]. +/// +/// [the `deprecated` attribute]: ../reference/attributes/diagnostics.html#the-deprecated-attribute +mod deprecated_attribute {} + +#[doc(attribute = "warn")] +// +/// Emits a warning during compilation when a lint check failed. +/// +/// Unlike `deny` or `forbid`, `warn` does not produce a hard error: the compilation continues, but +/// the compiler emits a warning message. `warn` can be overridden by `allow`, `deny`, and `forbid`. +/// +/// Example: +/// +/// ```rust,compile_fail +/// #![allow(unused)] +/// +/// #[warn(unused)] // We override the allowed `unused` lint. +/// fn foo() { +/// // This lint warns by default even without #[warn(unused)] being explicitly set +/// let x = 42; // warning: unused variable `x` +/// } +/// ``` +/// +/// +/// Many lints, including `unused`, are already set to `warn` by default so this attribute is +/// mainly useful for lints that are normally `allow` by default. +/// +/// Multiple lints can be set to `warn` at once: +/// +/// ```rust,compile_fail +/// #[warn(unused_mut, unused_variables)] +/// fn main() { +/// let mut x = 42; +/// } +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `warn` attribute]. +/// +/// [the `warn` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod warn_attribute {} diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 8c5f9d472bfa8..8517df4da2afc 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -495,7 +495,7 @@ impl char { || args.escape_grapheme_extender && self.is_grapheme_extender() || self.is_default_ignorable() || self.is_format_control() - || self.is_unassigned() => + || !self.is_assigned() => { EscapeDebug::unicode(self) } @@ -1139,8 +1139,9 @@ impl char { /// [`UnicodeData.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt /// #[must_use] + #[unstable(feature = "char_unassigned_private_use", issue = "158322")] #[inline] - const fn is_private_use(self) -> bool { + pub const fn is_private_use(self) -> bool { // According to // https://www.unicode.org/policies/stability_policy.html#Property_Value, // the set of codepoints in `Co` will never change. @@ -1176,54 +1177,56 @@ impl char { self > '\u{AC}' && unicode::Cf(self) } - /// Returns `true` if this `char` has not yet been assigned a meaning by Unicode, as of + /// Returns `true` if this `char` has been assigned a meaning by Unicode, as of /// [`UNICODE_VERSION`]. /// /// [`UNICODE_VERSION`]: Self::UNICODE_VERSION /// - /// These characters may have a meaning assigned in the future, - /// except for the 66 [noncharacters] which will never be assigned a meaning. - /// - /// [noncharacters]: https://www.unicode.org/faq/private_use#noncharacters - /// /// Many of Unicode's [stability policies] apply only to assigned characters. /// /// [stability policies]: https://www.unicode.org/policies/stability_policy.html /// - /// Unassigned characters (code points with the general category of `Cn`) are [described] in Chapter 4 - /// (Character Properties) of the Unicode Standard, and [specified] in the Unicode Character Database - /// by their exclusion from [`UnicodeData.txt`]. + /// Currently unassigned characters (characters for which this method returns `false`) + /// may have a meaning assigned in a future version of Unicode, + /// except for the 66 [noncharacters] which will never be assigned a meaning. + /// + /// [noncharacters]: https://www.unicode.org/faq/private_use.html#noncharacters + /// + /// A character is considered assigned if it is present in [`UnicodeData.txt`]. + /// Unassigned characters have general category `Cn`, as [described] in Chapter 4 + /// (Character Properties) of the Unicode Standard. /// - /// [described]: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153 - /// [specified]: https://www.unicode.org/reports/tr44/#GC_Values_Table /// [`UnicodeData.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt + /// [described]: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153 /// /// # Examples /// /// Basic usage: /// - /// ```ignore(private) - /// assert!('\u{FFFE}'.is_unassigned()); // noncharacter, will never be assigned - /// - /// //assert!('\u{7AAAA}'.is_unassigned()); // not currently assigned, but may be in the future, - /// // so we shouldn't rely on the current status + /// ``` + /// #![feature(char_unassigned_private_use)] + /// assert!('γ'.is_assigned()); // once a character is assigned, it stays assigned forever + /// assert!(!'\u{FFFE}'.is_assigned()); // noncharacter, will never be assigned /// - /// assert!(!'γ'.is_unassigned()); // once a character is assigned, it stays assigned forever + /// // Not currently assigned, but may be in the future, + /// // so we shouldn't rely on the current status + /// /* assert!(!'\u{7AAAA}'.is_assigned()); */ /// ``` #[must_use] + #[unstable(feature = "char_unassigned_private_use", issue = "158322")] #[inline] - fn is_unassigned(self) -> bool { + pub fn is_assigned(self) -> bool { match self { - '\0'..='\u{377}' => false, - '\u{378}'..='\u{3FFFD}' => unicode::Cn_planes_0_3(self), + '\0'..='\u{377}' => true, + '\u{378}'..='\u{3FFFD}' => !unicode::Cn_planes_0_3(self), // Assigned character ranges in planes 4 and above. // `src/tools/unicode-table-generator/src/main.rs` asserts that this is correct '\u{E0001}' | '\u{E0020}'..='\u{E007F}' | '\u{E0100}'..='\u{E01EF}' | '\u{F0000}'..='\u{FFFFD}' - | '\u{100000}'..='\u{10FFFD}' => false, - _ => true, + | '\u{100000}'..='\u{10FFFD}' => true, + _ => false, } } diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 3f1aa54050a31..df6396b84264d 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -85,3 +85,30 @@ impl fmt::Debug for c_void { )] #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] unsafe extern "C" {} + +// Used by rustc for checking the definitions of other function with the same symbol names +// +// See the `invalid_runtime_symbols_definitions` lint. +mod runtime_symbols { + use crate::ffi::{c_char, c_int, c_void}; + + unsafe extern "C" { + #[lang = "memcpy_fn"] + fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + #[lang = "memmove_fn"] + fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + #[lang = "memset_fn"] + fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + + #[lang = "memcmp_fn"] + fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + #[lang = "bcmp_fn"] + fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + #[lang = "strlen_fn"] + fn strlen(s: *const c_char) -> usize; + } +} diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 838dab90a6de0..4671e04062fb0 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -332,9 +332,7 @@ mod flags { } impl FormattingOptions { - /// Construct a new `FormatterBuilder` with the supplied `Write` trait - /// object for output that is equivalent to the `{}` formatting - /// specifier: + /// Construct a new `FormattingOptions` representing the plain `{}` formatting specifier: /// /// - no flags, /// - filled with spaces, @@ -518,7 +516,7 @@ impl FormattingOptions { pub const fn get_precision(&self) -> Option { if self.flags & flags::PRECISION_FLAG != 0 { Some(self.precision) } else { None } } - /// Returns the current precision. + /// Returns the current `x?` or `X?` flag. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_debug_as_hex(&self) -> Option { if self.flags & flags::DEBUG_LOWER_HEX_FLAG != 0 { diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 6c639a8b092b3..050822da8f12a 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -361,7 +361,7 @@ macro_rules! impl_Display { #[cfg(feature = "optimize_for_size")] fn ${concat($fmt_fn, _small)}(n: $T, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const MAX_DEC_N: usize = $T::MAX.ilog(10) as usize + 1; + const MAX_DEC_N: usize = $T::MAX.ilog10() as usize + 1; let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; let offset = ${concat($fmt_fn, _in_buf_small)}(n, &mut buf); diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index af8acda6633c5..624a953062332 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -17,13 +17,13 @@ macro_rules! impl_NumBufferTrait { #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] impl NumBufferTrait for $signed { // `+ 2` and not `+ 1` to include the `-` character. - const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $signed::MAX.ilog(10) as usize + 2]; - type Buf = [MaybeUninit; $signed::MAX.ilog(10) as usize + 2]; + const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $signed::MAX.ilog10() as usize + 2]; + type Buf = [MaybeUninit; $signed::MAX.ilog10() as usize + 2]; } #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] impl NumBufferTrait for $unsigned { - const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $unsigned::MAX.ilog(10) as usize + 1]; - type Buf = [MaybeUninit; $unsigned::MAX.ilog(10) as usize + 1]; + const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $unsigned::MAX.ilog10() as usize + 1]; + type Buf = [MaybeUninit; $unsigned::MAX.ilog10() as usize + 1]; } )* } @@ -74,7 +74,6 @@ impl NumBuffer { #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] pub const fn new() -> Self { - // FIXME: Once const generics feature is working, use `T::BUF_SIZE` instead of 40. NumBuffer { buf: T::DEFAULT, phantom: core::marker::PhantomData } } } diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index ebbec6c5e8210..24d3bfc67b988 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -16,6 +16,7 @@ /// code, but use an in-memory buffer in our tests. We can do this with /// `Cursor`: /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html @@ -80,6 +81,7 @@ impl Cursor { /// is not empty. So writing to cursor starts with overwriting [`Vec`] /// content, not with appending to it. /// + // FIXME(#74481): Hard-links required to link from `core` to `alloc` /// [`Vec`]: ../../alloc/vec/struct.Vec.html /// /// # Examples diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 67cd1eebf44f2..2d15307daf153 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,6 +1,714 @@ #![unstable(feature = "core_io", issue = "154046")] -use crate::fmt; +// On 64-bit platforms, `io::Error` may use a bit-packed representation to +// reduce size. However, this representation assumes that error codes are +// always 32-bit wide. +// +// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. +// Therefore, the packed representation is explicitly disabled for UEFI +// targets, and the unpacked representation must be used instead. +#[cfg_attr( + all(target_pointer_width = "64", not(target_os = "uefi")), + path = "error/repr_bitpacked.rs" +)] +#[cfg_attr( + not(all(target_pointer_width = "64", not(target_os = "uefi"))), + path = "error/repr_unpacked.rs" +)] +mod repr; + +#[cfg_attr( + all(target_has_atomic_load_store = "ptr", not(no_io_statics)), + path = "error/os_functions_atomic.rs" +)] +#[cfg_attr( + not(all(target_has_atomic_load_store = "ptr", not(no_io_statics))), + path = "error/os_functions.rs" +)] +mod os_functions; + +use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; +use self::repr::Repr; +use crate::{error, fmt, result}; + +/// A specialized [`Result`] type for I/O operations. +/// +/// This type is broadly used across [`std::io`] for any operation which may +/// produce an error. +/// +/// This type alias is generally used to avoid writing out [`io::Error`] directly and +/// is otherwise a direct mapping to [`Result`]. +/// +/// While usual Rust style is to import types directly, aliases of [`Result`] +/// often are not, to make it easier to distinguish between them. [`Result`] is +/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias +/// will generally use `io::Result` instead of shadowing the [prelude]'s import +/// of [`core::result::Result`][`Result`]. +/// +// FIXME(#74481): Hard-links required to link from `core` to `std` +/// [`std::io`]: ../../std/io/index.html +/// [`io::Error`]: Error +/// [`Result`]: crate::result::Result +/// [prelude]: crate::prelude +/// +/// # Examples +/// +/// A convenience function that bubbles an `io::Result` to its caller: +/// +/// ``` +/// use std::io; +/// +/// fn get_string() -> io::Result { +/// let mut buffer = String::new(); +/// +/// io::stdin().read_line(&mut buffer)?; +/// +/// Ok(buffer) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(search_unbox)] +pub type Result = result::Result; + +/// The error type for I/O operations of the [`Read`][Read], [`Write`][Write], [`Seek`][Seek], and +/// associated traits. +/// +/// Errors mostly originate from the underlying OS, but custom instances of +/// `Error` can be created with crafted error messages and a particular value of +/// [`ErrorKind`]. +/// +// FIXME(#74481): Hard-links required to link from `core` to `std` +/// [Read]: ../../std/io/trait.Read.html +/// [Write]: ../../std/io/trait.Write.html +/// [Seek]: ../../std/io/trait.Seek.html +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_has_incoherent_inherent_impls] +pub struct Error { + repr: Repr, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.repr, f) + } +} + +/// Common errors constants for use in std +#[doc(hidden)] +impl Error { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const INVALID_UTF8: Self = + const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const READ_EXACT_EOF: Self = + const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNKNOWN_THREAD_COUNT: Self = const_error!( + ErrorKind::NotFound, + "the number of hardware threads is not known for the target platform", + ); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNSUPPORTED_PLATFORM: Self = + const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const WRITE_ALL_EOF: Self = + const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const ZERO_TIMEOUT: Self = + const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const NO_ADDRESSES: Self = + const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); +} + +// Only derive debug in tests, to make sure it +// doesn't accidentally get printed. +#[cfg_attr(test, derive(Debug))] +enum ErrorData { + Os(RawOsError), + Simple(ErrorKind), + SimpleMessage(&'static SimpleMessage), + Custom(C), +} + +// `#[repr(align(4))]` is probably redundant, it should have that value or +// higher already. We include it just because repr_bitpacked.rs's encoding +// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the +// alignment required by the struct, only increase it). +// +// If we add more variants to ErrorData, this can be increased to 8, but it +// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or +// whatever cfg we're using to enable the `repr_bitpacked` code, since only the +// that version needs the alignment, and 8 is higher than the alignment we'll +// have on 32 bit platforms. +// +// (For the sake of being explicit: the alignment requirement here only matters +// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't +// matter at all) +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +#[repr(align(4))] +#[derive(Debug)] +pub struct SimpleMessage { + pub kind: ErrorKind, + pub message: &'static str, +} + +/// Creates a new I/O error from a known kind of error and a string literal. +/// +/// Contrary to [`Error::new`][new], this macro does not allocate and can be used in +/// `const` contexts. +/// +// FIXME(#74481): Hard-links required to link from `core` to `alloc` for incoherent method +/// [new]: ../../alloc/io/struct.Error.html#method.new +/// +/// # Example +/// ``` +/// #![feature(io_const_error)] +/// use std::io::{const_error, Error, ErrorKind}; +/// +/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); +/// +/// fn not_here() -> Result<(), Error> { +/// Err(FAIL) +/// } +/// ``` +#[rustc_macro_transparency = "semiopaque"] +#[unstable(feature = "io_const_error", issue = "133448")] +#[allow_internal_unstable(core_io, hint_must_use, io_const_error_internals)] +pub macro const_error($kind:expr, $message:expr $(,)?) { + $crate::hint::must_use($crate::io::Error::from_static_message( + const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, + )) +} + +/// Intended for use for errors not exposed to the user, where allocating onto +/// the heap (for normal construction via Error::new) is too costly. +#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] +impl From for Error { + /// Converts an [`ErrorKind`] into an [`Error`]. + /// + /// This conversion creates a new error with a simple representation of error kind. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// let not_found = ErrorKind::NotFound; + /// let error = Error::from(not_found); + /// assert_eq!("entity not found", format!("{error}")); + /// ``` + #[inline] + fn from(kind: ErrorKind) -> Error { + Error { repr: Repr::new_simple(kind) } + } +} + +impl Error { + /// # Safety + /// + /// The provided `CustomOwner` must have been constructed from a `Box` from the `alloc` crate. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_custom_owner(custom: CustomOwner) -> Error { + Error { repr: Repr::new_custom(custom) } + } + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub fn into_custom_owner(self) -> result::Result { + if matches!(self.repr.data(), ErrorData::Custom(..)) { + let ErrorData::Custom(c) = self.repr.into_data() else { + // SAFETY: Checked above using `matches!`. + unsafe { crate::hint::unreachable_unchecked() } + }; + Ok(c) + } else { + Err(self) + } + } + + /// Creates a new I/O error from a known kind of error as well as a constant + /// message. + /// + /// This function does not allocate. + /// + /// You should not use this directly, and instead use the `const_error!` + /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. + /// + /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. + #[inline] + #[doc(hidden)] + #[unstable(feature = "io_const_error_internals", issue = "none")] + pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { + Self { repr: Repr::new_simple_message(msg) } + } + + /// # Safety + /// + /// `functions` must point to data that is entirely constant; it must + /// not be created during runtime. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_raw_os_error_with_functions( + code: RawOsError, + functions: &'static OsFunctions, + ) -> Error { + // SAFETY: Caller ensures `functions` is a constant not created at runtime. + unsafe { + set_functions(functions); + } + Error { repr: Repr::new_os(code) } + } + + /// Returns the OS error that this error represents (if any). + /// + /// If this [`Error`] was constructed via [`last_os_error`][last_os_error] or + /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise + /// it will return [`None`]. + /// + // FIXME(#74481): Hard-links required to link from `core` to `std` for incoherent method + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_os_error(err: &Error) { + /// if let Some(raw_os_err) = err.raw_os_error() { + /// println!("raw OS error: {raw_os_err:?}"); + /// } else { + /// println!("Not an OS error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "raw OS error: ...". + /// print_os_error(&Error::last_os_error()); + /// // Will print "Not an OS error". + /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn raw_os_error(&self) -> Option { + match self.repr.data() { + ErrorData::Os(i) => Some(i), + ErrorData::Custom(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + } + } + + /// Returns a reference to the inner error wrapped by this error (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [new]: ../../alloc/io/struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err:?}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error_ref()), + } + } + + /// Returns a mutable reference to the inner error wrapped by this error + /// (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + // FIXME(#74481): Hard-links required to link from `core` to `std` + /// [new]: ../../alloc/io/struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// use std::{error, fmt}; + /// use std::fmt::Display; + /// + /// #[derive(Debug)] + /// struct MyError { + /// v: String, + /// } + /// + /// impl MyError { + /// fn new() -> MyError { + /// MyError { + /// v: "oh no!".to_string() + /// } + /// } + /// + /// fn change_message(&mut self, new_message: &str) { + /// self.v = new_message.to_string(); + /// } + /// } + /// + /// impl error::Error for MyError {} + /// + /// impl Display for MyError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "MyError: {}", self.v) + /// } + /// } + /// + /// fn change_error(mut err: Error) -> Error { + /// if let Some(inner_err) = err.get_mut() { + /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); + /// } + /// err + /// } + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&change_error(Error::last_os_error())); + /// // Will print "Inner error: ...". + /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { + match self.repr.data_mut() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error_mut()), + } + } + + /// Returns the corresponding [`ErrorKind`] for this error. + /// + /// This may be a value set by Rust code constructing custom `io::Error`s, + /// or if this `io::Error` was sourced from the operating system, + /// it will be a value inferred from the system's error encoding. + /// See [`last_os_error`][last_os_error] for more details. + /// + // FIXME(#74481): Hard-links required to link from `core` to `std` + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// println!("{:?}", err.kind()); + /// } + /// + /// fn main() { + /// // As no error has (visibly) occurred, this may print anything! + /// // It likely prints a placeholder for unidentified (non-)errors. + /// print_error(Error::last_os_error()); + /// // Will print "AddrInUse". + /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn kind(&self) -> ErrorKind { + match self.repr.data() { + ErrorData::Os(code) => decode_error_kind(code), + ErrorData::Custom(c) => c.kind, + ErrorData::Simple(kind) => kind, + ErrorData::SimpleMessage(m) => m.kind, + } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[doc(hidden)] + #[inline] + pub fn is_interrupted(&self) -> bool { + match self.repr.data() { + ErrorData::Os(code) => is_interrupted(code), + ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, + ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, + ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, + } + } +} + +impl fmt::Debug for Repr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.data() { + ErrorData::Os(code) => fmt + .debug_struct("Os") + .field("code", &code) + .field("kind", &decode_error_kind(code)) + .field( + "message", + &fmt::from_fn(|fmt| { + write!(fmt, "\"{}\"", fmt::from_fn(|fmt| format_os_error(code, fmt))) + }), + ) + .finish(), + ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), + ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + ErrorData::SimpleMessage(msg) => fmt + .debug_struct("Error") + .field("kind", &msg.kind) + .field("message", &msg.message) + .finish(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.repr.data() { + ErrorData::Os(code) => { + let detail = fmt::from_fn(|fmt| format_os_error(code, fmt)); + write!(fmt, "{detail} (os error {code})") + } + ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), + ErrorData::Simple(kind) => kind.fmt(fmt), + ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for Error { + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn error::Error> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error_ref().cause(), + } + } + + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error_ref().source(), + } + } +} + +fn _assert_error_is_sync_send() { + fn _is_sync_send() {} + _is_sync_send::(); +} + +#[doc(hidden)] +#[derive(Debug)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub struct OsFunctions { + pub format_os_error: fn(_: RawOsError, _: &mut fmt::Formatter<'_>) -> fmt::Result, + pub decode_error_kind: fn(_: RawOsError) -> ErrorKind, + pub is_interrupted: fn(_: RawOsError) -> bool, +} + +impl OsFunctions { + const DEFAULT: &'static OsFunctions = &OsFunctions { + format_os_error: |_, _| Ok(()), + decode_error_kind: |_| ErrorKind::Uncategorized, + is_interrupted: |_| false, + }; +} + +// As with `SimpleMessage`: `#[repr(align(4))]` here is just because +// repr_bitpacked's encoding requires it. In practice it almost certainly be +// already be this high or higher. +#[doc(hidden)] +#[repr(align(4))] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub struct Custom { + kind: ErrorKind, + error: crate::ptr::NonNull, + error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), + outer_drop: unsafe fn(*mut Self), +} + +// SAFETY: All members of `Custom` are `Send` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Send for Custom {} + +// SAFETY: All members of `Custom` are `Sync` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Sync for Custom {} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl fmt::Debug for Custom { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Custom").field("kind", &self.kind).field("error", self.error_ref()).finish() + } +} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl Drop for Custom { + fn drop(&mut self) { + // SAFETY: `Custom::from_raw` ensures this call is safe. + unsafe { + (self.error_drop)(self.error.as_ptr()); + } + } +} + +impl Custom { + /// # Safety + /// + /// * `error` must be valid for up to a static lifetime, and own its pointee. + /// * `error_drop` must be safe to call for the pointer `error` exactly once. + /// * `outer_drop` must be safe to call on a pointer to this instance of `Custom` + /// if it were stored within a [`CustomOwner`]. + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub unsafe fn from_raw( + kind: ErrorKind, + error: crate::ptr::NonNull, + error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), + outer_drop: unsafe fn(*mut Self), + ) -> Custom { + Custom { kind, error, error_drop, outer_drop } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn into_raw(self) -> crate::ptr::NonNull { + let ptr = self.error; + core::mem::forget(self); + ptr + } + + fn error_ref(&self) -> &(dyn error::Error + Send + Sync + 'static) { + // SAFETY: + // `from_raw` ensures `error` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.error.as_ref() } + } + + fn error_mut(&mut self) -> &mut (dyn error::Error + Send + Sync + 'static) { + // SAFETY: + // `from_raw` ensures `error` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.error.as_mut() } + } +} + +#[derive(Debug)] +#[repr(transparent)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +#[doc(hidden)] +pub struct CustomOwner(crate::ptr::NonNull); + +// SAFETY: Custom is `Send` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Send for CustomOwner {} + +// SAFETY: Custom is `Sync` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Sync for CustomOwner {} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl Drop for CustomOwner { + fn drop(&mut self) { + // SAFETY: `CustomOwner::from_raw` ensures this call is safe. + unsafe { + (self.0.as_ref().outer_drop)(self.0.as_ptr()); + } + } +} + +impl CustomOwner { + /// # Safety + /// + /// * The `outer_drop` of the provided `custom` must be safe to call exactly once. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub unsafe fn from_raw(custom: crate::ptr::NonNull) -> CustomOwner { + CustomOwner(custom) + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn into_raw(self) -> crate::ptr::NonNull { + let ptr = self.0; + core::mem::forget(self); + ptr + } + + #[allow(dead_code, reason = "only used for unpacked representation")] + fn custom_ref(&self) -> &Custom { + // SAFETY: + // `from_raw` ensures `0` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.0.as_ref() } + } + + #[allow(dead_code, reason = "only used for unpacked representation")] + fn custom_mut(&mut self) -> &mut Custom { + // SAFETY: + // `from_raw` ensures `0` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.0.as_mut() } + } +} /// The type of raw OS error codes. /// @@ -23,7 +731,7 @@ pub type RawOsError = cfg_select! { /// /// It is used with the [`io::Error`][error] type. /// -/// [error]: ../../std/io/struct.Error.html +/// [error]: Error /// /// # Handling errors and matching on `ErrorKind` /// @@ -238,6 +946,11 @@ pub enum ErrorKind { #[unstable(feature = "io_error_inprogress", issue = "130840")] InProgress, + /// The process or the whole system has reached its limit on the number of + /// open files or sockets. + #[unstable(feature = "io_error_too_many_open_files", issue = "158319")] + TooManyOpenFiles, + // "Unusual" error kinds which do not correspond simply to (sets // of) OS error codes, should be added just above this comment. // `Other` and `Uncategorized` should remain at the end: @@ -253,7 +966,7 @@ pub enum ErrorKind { /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. /// New [`ErrorKind`]s might be added in the future for some of those. /// - /// [error]: ../../std/io/struct.Error.html + /// [error]: Error #[stable(feature = "rust1", since = "1.0.0")] Other, @@ -268,7 +981,7 @@ pub enum ErrorKind { } impl ErrorKind { - pub(crate) const fn as_str(&self) -> &'static str { + const fn as_str(&self) -> &'static str { use ErrorKind::*; match *self { // tidy-alphabetical-start @@ -309,6 +1022,7 @@ impl ErrorKind { StorageFull => "no storage space", TimedOut => "timed out", TooManyLinks => "too many links", + TooManyOpenFiles => "too many open files", Uncategorized => "uncategorized error", UnexpectedEof => "unexpected end of file", Unsupported => "unsupported", @@ -322,9 +1036,8 @@ impl ErrorKind { // unsafe, or to hard-code max ErrorKind or its size in a way the compiler // couldn't verify. #[inline] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[doc(hidden)] - pub const fn from_prim(ek: u32) -> Option { + #[allow(dead_code, reason = "only used for packed representation")] + const fn from_prim(ek: u32) -> Option { macro_rules! from_prim { ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ // Force a compile error if the list gets out of date. @@ -379,6 +1092,7 @@ impl ErrorKind { Unsupported, OutOfMemory, InProgress, + TooManyOpenFiles, Uncategorized, }) } diff --git a/library/core/src/io/error/os_functions.rs b/library/core/src/io/error/os_functions.rs new file mode 100644 index 0000000000000..b34b7c7dc7926 --- /dev/null +++ b/library/core/src/io/error/os_functions.rs @@ -0,0 +1,30 @@ +use super::{ErrorKind, OsFunctions, RawOsError}; +use crate::fmt; + +/// # Safety +/// +/// The provided reference must point to data that is entirely constant; it must +/// not be created during runtime. +#[inline] +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { + // FIXME: externally implementable items may allow for weak linkage, allowing + // these methods to be overridden even when atomic pointers are not supported. +} + +#[inline] +pub(super) fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = OsFunctions::DEFAULT; + (f.format_os_error)(errno, fmt) +} + +#[inline] +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = OsFunctions::DEFAULT; + (f.decode_error_kind)(errno) +} + +#[inline] +pub(super) fn is_interrupted(errno: RawOsError) -> bool { + let f = OsFunctions::DEFAULT; + (f.is_interrupted)(errno) +} diff --git a/library/core/src/io/error/os_functions_atomic.rs b/library/core/src/io/error/os_functions_atomic.rs new file mode 100644 index 0000000000000..dfaf59ae742de --- /dev/null +++ b/library/core/src/io/error/os_functions_atomic.rs @@ -0,0 +1,61 @@ +//! OS-dependent functions +//! +//! `Error` needs OS functionalities to work interpret raw OS errors, but +//! we can't link to anythink here in `alloc`. Therefore, we restrict +//! creation of `Error` from raw OS errors in `std`, and require providing +//! a vtable of operations when creating one. + +// FIXME: replace this with externally implementable items once they are more stable + +use super::{ErrorKind, OsFunctions, RawOsError}; +use crate::fmt; +use crate::sync::atomic; + +/// These default functions are not reachable, but have them just to be safe. +static OS_FUNCTIONS: atomic::AtomicPtr = + atomic::AtomicPtr::new(OsFunctions::DEFAULT as *const _ as *mut _); + +fn get_os_functions() -> &'static OsFunctions { + // SAFETY: + // * `OS_FUNCTIONS` is initially a pointer to `OsFunctions::DEFAULT`, which is valid for a static lifetime. + // * `OS_FUNCTIONS` can only be changed by `set_functions`, which only accepts `&'static OsFunctions`. + // * Therefore, `OS_FUNCTIONS` must always contain a valid non-null pointer with a static lifetime. + // * `Relaxed` ordering is sufficient as the only way to write to `OS_FUNCTIONS` is through + // `set_functions`, which has as a safety precondition that any value passed in must + // be constant and not created during runtime. + unsafe { &*OS_FUNCTIONS.load(atomic::Ordering::Relaxed) } +} + +/// # Safety +/// +/// The provided reference must point to data that is entirely constant; it must +/// not be created during runtime. +#[inline] +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { + #[cold] + fn set_functions_inner(f: &'static OsFunctions) { + OS_FUNCTIONS.store(f as *const _ as *mut _, atomic::Ordering::Relaxed); + } + + if OS_FUNCTIONS.load(atomic::Ordering::Relaxed) != f as *const _ as *mut _ { + set_functions_inner(f); + } +} + +#[inline] +pub(super) fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = get_os_functions(); + (f.format_os_error)(errno, fmt) +} + +#[inline] +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = get_os_functions(); + (f.decode_error_kind)(errno) +} + +#[inline] +pub(super) fn is_interrupted(errno: RawOsError) -> bool { + let f = get_os_functions(); + (f.is_interrupted)(errno) +} diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/core/src/io/error/repr_bitpacked.rs similarity index 88% rename from library/std/src/io/error/repr_bitpacked.rs rename to library/core/src/io/error/repr_bitpacked.rs index 7c237825459af..22cb331924081 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/core/src/io/error/repr_bitpacked.rs @@ -106,7 +106,7 @@ use core::marker::PhantomData; use core::num::NonZeroUsize; use core::ptr::NonNull; -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use super::{Custom, CustomOwner, ErrorData, ErrorKind, RawOsError, SimpleMessage}; // The 2 least-significant bits are used as tag. const TAG_MASK: usize = 0b11; @@ -126,15 +126,15 @@ const TAG_SIMPLE: usize = 0b11; /// ``` #[repr(transparent)] #[rustc_insignificant_dtor] -pub(super) struct Repr(NonNull<()>, PhantomData>>); +pub(super) struct Repr(NonNull<()>, PhantomData>); // All the types `Repr` stores internally are Send + Sync, and so is it. unsafe impl Send for Repr {} unsafe impl Sync for Repr {} impl Repr { - pub(super) fn new_custom(b: Box) -> Self { - let p = Box::into_raw(b).cast::(); + pub(super) fn new_custom(b: CustomOwner) -> Self { + let p = b.into_raw().as_ptr().cast::(); // Should only be possible if an allocator handed out a pointer with // wrong alignment. debug_assert_eq!(p.addr() & TAG_MASK, 0); @@ -146,7 +146,8 @@ impl Repr { // (rather than `ptr::wrapping_add`), but it's unclear this would give // any benefit, so we just use `wrapping_add` instead. let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>(); - // Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, + // SAFETY: + // `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, // because `p`'s alignment means it isn't allowed to have any of the // `TAG_BITS` set (you can verify that addition and bitwise-or are the // same when the operands have no bits in common using a truth table). @@ -156,7 +157,8 @@ impl Repr { // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore, // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the // `new_unchecked` is safe. - let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData); + let ptr = unsafe { NonNull::new_unchecked(tagged) }; + let res = Self(ptr, PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed"); @@ -166,11 +168,9 @@ impl Repr { #[inline] pub(super) fn new_os(code: RawOsError) -> Self { let utagged = ((code as usize) << 32) | TAG_OS; - // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + // SAFETY: `TAG_OS` is not zero, so the result of the `|` is not 0. + let utagged = unsafe { NonZeroUsize::new_unchecked(utagged) }; + let res = Self(NonNull::without_provenance(utagged), PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!( @@ -183,11 +183,9 @@ impl Repr { #[inline] pub(super) fn new_simple(kind: ErrorKind) -> Self { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; - // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + // SAFETY: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. + let utagged = unsafe { NonZeroUsize::new_unchecked(utagged) }; + let res = Self(NonNull::without_provenance(utagged), PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!( @@ -200,38 +198,43 @@ impl Repr { #[inline] pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { - // Safety: References are never null. - Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) + // SAFETY: References are never null. + let ptr = unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }; + Self(ptr, PhantomData) } #[inline] pub(super) fn data(&self) -> ErrorData<&Custom> { - // Safety: We're a Repr, decode_repr is fine. + // SAFETY: We're a Repr, decode_repr is fine. unsafe { decode_repr(self.0, |c| &*c) } } #[inline] pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { - // Safety: We're a Repr, decode_repr is fine. + // SAFETY: We're a Repr, decode_repr is fine. unsafe { decode_repr(self.0, |c| &mut *c) } } #[inline] - pub(super) fn into_data(self) -> ErrorData> { + pub(super) fn into_data(self) -> ErrorData { let this = core::mem::ManuallyDrop::new(self); - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we prevent double-drop using `ManuallyDrop`. - unsafe { decode_repr(this.0, |p| Box::from_raw(p)) } + unsafe { + decode_repr(this.0, |p| CustomOwner::from_raw(core::ptr::NonNull::new_unchecked(p))) + } } } impl Drop for Repr { #[inline] fn drop(&mut self) { - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we're being dropped. unsafe { - let _ = decode_repr(self.0, |p| Box::::from_raw(p)); + let _ = decode_repr(self.0, |p| { + CustomOwner::from_raw(core::ptr::NonNull::new_unchecked(p)) + }); } } } @@ -255,7 +258,7 @@ where let kind_bits = (bits >> 32) as u32; let kind = ErrorKind::from_prim(kind_bits).unwrap_or_else(|| { debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits); - // This means the `ptr` passed in was not valid, which violates + // SAFETY: This means the `ptr` passed in was not valid, which violates // the unsafe contract of `decode_repr`. // // Using this rather than unwrap meaningfully improves the code @@ -306,7 +309,7 @@ static_assert!(@usize_eq: size_of::>(), size_of::()); // `Custom` and `SimpleMessage` need to be thin pointers. static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8); -static_assert!(@usize_eq: size_of::>(), 8); +static_assert!(@usize_eq: size_of::(), 8); static_assert!((TAG_MASK + 1).is_power_of_two()); // And they must have sufficient alignment. diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/core/src/io/error/repr_unpacked.rs similarity index 76% rename from library/std/src/io/error/repr_unpacked.rs rename to library/core/src/io/error/repr_unpacked.rs index b3e7b5f024ea0..a32ed58ce1b74 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/core/src/io/error/repr_unpacked.rs @@ -2,15 +2,15 @@ //! non-64bit targets, where the packed 64 bit representation wouldn't work, and //! would have no benefit. -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use super::{Custom, CustomOwner, ErrorData, ErrorKind, RawOsError, SimpleMessage}; -type Inner = ErrorData>; +type Inner = ErrorData; pub(super) struct Repr(Inner); impl Repr { #[inline] - pub(super) fn new_custom(b: Box) -> Self { + pub(super) fn new_custom(b: CustomOwner) -> Self { Self(Inner::Custom(b)) } #[inline] @@ -26,7 +26,7 @@ impl Repr { Self(Inner::SimpleMessage(m)) } #[inline] - pub(super) fn into_data(self) -> ErrorData> { + pub(super) fn into_data(self) -> ErrorData { self.0 } #[inline] @@ -35,7 +35,7 @@ impl Repr { Inner::Os(c) => ErrorData::Os(*c), Inner::Simple(k) => ErrorData::Simple(*k), Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&*m), + Inner::Custom(m) => ErrorData::Custom(m.custom_ref()), } } #[inline] @@ -44,7 +44,7 @@ impl Repr { Inner::Os(c) => ErrorData::Os(*c), Inner::Simple(k) => ErrorData::Simple(*k), Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&mut *m), + Inner::Custom(m) => ErrorData::Custom(m.custom_mut()), } } } diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 43b6e09bfc4f0..b3c8815cb062c 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -8,16 +8,22 @@ mod util; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::cursor::Cursor; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::error::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use self::error::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use self::error::const_error; #[unstable(feature = "core_io", issue = "154046")] -pub use self::io_slice::{IoSlice, IoSliceMut}; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use self::{ + cursor::Cursor, + error::{Error, ErrorKind, Result}, + io_slice::{IoSlice, IoSliceMut}, + util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, +}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::util::{chain, take}; +pub use self::{ + error::{Custom, CustomOwner, OsFunctions}, + util::{chain, take}, +}; diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index ce80874a23897..a16732e1d8c74 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -1,3 +1,4 @@ +use crate::array; use crate::marker::Destruct; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; @@ -95,6 +96,53 @@ pub const trait DoubleEndedIterator: [const] Iterator { #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option; + /// Advances from the back of the iterator and returns an array containing the next + /// `N` values in sequence. + /// + /// If there are not enough elements to fill the array then `Err` is returned + /// containing an iterator over the remaining elements. + /// + /// Note: This is not equivalent to doing `iter.rev().next_chunk()` as this method + /// takes elements from the back of the iterator and preserves the order that the + /// elements were seen in the original iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_next_chunk)] + /// + /// let mut iter = "lorem".chars(); + /// + /// assert_eq!(iter.next_chunk_back().unwrap(), ['e', 'm']); // N is inferred as 2 + /// assert_eq!(iter.next_chunk_back().unwrap(), ['l', 'o', 'r']); // N is inferred as 3 + /// assert_eq!(iter.next_chunk_back::<4>().unwrap_err().as_slice(), &[]); // N is explicitly 4 + /// ``` + /// + /// Split a string and get the last three items in sequence. + /// + /// ``` + /// #![feature(iter_next_chunk)] + /// + /// let quote = "not all those who wander are lost"; + /// let [first, second, third] = quote.split_whitespace().next_chunk_back().unwrap(); + /// assert_eq!(first, "wander"); + /// assert_eq!(second, "are"); + /// assert_eq!(third, "lost"); + /// ``` + #[inline] + #[unstable(feature = "iter_next_chunk", issue = "98326")] + #[rustc_non_const_trait_method] + fn next_chunk_back( + &mut self, + ) -> Result<[Self::Item; N], array::IntoIter> + where + Self: Sized, + { + crate::array::iter_next_chunk_back(self) + } + /// Advances the iterator from the back by `n` elements. /// /// `advance_back_by` is the reverse version of [`advance_by`]. This method will diff --git a/library/std/src/keyword_docs.rs b/library/core/src/keyword_docs.rs similarity index 98% rename from library/std/src/keyword_docs.rs rename to library/core/src/keyword_docs.rs index 5f94a13dad22a..596765be5e2dd 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/core/src/keyword_docs.rs @@ -195,7 +195,7 @@ mod break_keyword {} /// to be most things that would be reasonable to have in a constant (barring `const fn`s). For /// example, you can't have a [`File`] as a `const`. /// -/// [`File`]: crate::fs::File +/// [`File`]: ../std/fs/struct.File.html /// /// The only lifetime allowed in a constant is `'static`, which is the lifetime that encompasses /// all others in a Rust program. For example, if you wanted to define a constant string, it would @@ -484,7 +484,7 @@ mod extern_keyword {} #[doc(keyword = "false")] // -/// A value of type [`bool`] representing logical **false**. +/// A value of type [`prim@bool`] representing logical **false**. /// /// `false` is the logical opposite of [`true`]. /// @@ -1060,7 +1060,8 @@ mod mod_keyword {} /// /// `move` is often used when [threads] are involved. /// -/// ```rust +#[cfg_attr(target_os = "wasi", doc = "```rust,ignore (thread::spawn not supported)")] +#[cfg_attr(not(target_os = "wasi"), doc = "```rust")] /// let data = vec![1, 2, 3]; /// /// std::thread::spawn(move || { @@ -1235,31 +1236,18 @@ mod ref_keyword {} /// `return` returns from the function immediately (an "early return"): /// /// ```no_run -/// use std::fs::File; -/// use std::io::{Error, ErrorKind, Read, Result}; -/// -/// fn main() -> Result<()> { -/// let mut file = match File::open("foo.txt") { -/// Ok(f) => f, -/// Err(e) => return Err(e), -/// }; +/// fn main() -> Result<(), &'static str> { +/// let contents = "Hello, world!"; /// -/// let mut contents = String::new(); -/// let size = match file.read_to_string(&mut contents) { -/// Ok(s) => s, -/// Err(e) => return Err(e), -/// }; +/// if contents.contains("impossible!") { +/// return Err("oh no!"); +/// } /// -/// if contents.contains("impossible!") { -/// return Err(Error::new(ErrorKind::Other, "oh no!")); -/// } +/// if contents.len() > 9000 { +/// return Err("over 9000!"); +/// } /// -/// if size > 9000 { -/// return Err(Error::new(ErrorKind::Other, "over 9000!")); -/// } -/// -/// assert_eq!(contents, "Hello, world!"); -/// Ok(()) +/// Ok(()) /// } /// ``` /// @@ -1306,7 +1294,8 @@ mod return_keyword {} /// manner to computed goto). /// /// Example of using `become` to implement functional-style `fold`: -/// ``` +/// +/// ```ignore-wasm (tail-call target feature not enabled by default on wasm) /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1360,7 +1349,8 @@ mod return_keyword {} /// (unless it's coerced to a function pointer) /// /// It is possible to tail-call a function pointer: -/// ``` +/// +/// ```ignore-wasm (tail-call target feature not enabled by default on wasm) /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1631,8 +1621,8 @@ mod self_upper_keyword {} /// [`extern`]: keyword.extern.html /// [`mut`]: keyword.mut.html /// [`unsafe`]: keyword.unsafe.html -/// [`Mutex`]: sync::Mutex -/// [`OnceLock`]: sync::OnceLock +/// [`Mutex`]: ../std/sync/struct.Mutex.html +/// [`OnceLock`]: ../std/sync/struct.OnceLock.html /// [`RefCell`]: cell::RefCell /// [atomic]: sync::atomic /// [Reference]: ../reference/items/static-items.html @@ -1959,7 +1949,7 @@ mod trait_keyword {} #[doc(keyword = "true")] // -/// A value of type [`bool`] representing logical **true**. +/// A value of type [`prim@bool`] representing logical **true**. /// /// Logically `true` is not equal to [`false`]. /// @@ -2312,6 +2302,7 @@ mod type_keyword {} /// [`static`]: keyword.static.html /// [`union`]: keyword.union.html /// [`impl`]: keyword.impl.html +/// [`Vec::set_len`]: ../std/vec/struct.Vec.html#method.set_len /// [raw pointers]: ../reference/types/pointer.html /// [memory safety]: ../book/ch19-01-unsafe-rust.html /// [Rustonomicon]: ../nomicon/index.html @@ -2502,7 +2493,7 @@ mod use_keyword {} /// ``` /// /// `where` is available anywhere generic and lifetime parameters are available, -/// as can be seen with the [`Cow`](crate::borrow::Cow) type from the standard +/// as can be seen with the [`Cow`](../std/borrow/enum.Cow.html) type from the standard /// library: /// /// ```rust diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a26304c46ecea..8e310cc7d2155 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -51,27 +51,16 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut, duplicate_features))) )] #![doc(rust_logo)] -#![doc(auto_cfg(hide( - no_fp_fmt_parse, - target_pointer_width = "16", - target_pointer_width = "32", - target_pointer_width = "64", - target_has_atomic = "8", - target_has_atomic = "16", - target_has_atomic = "32", - target_has_atomic = "64", - target_has_atomic = "ptr", - target_has_atomic_primitive_alignment = "8", - target_has_atomic_primitive_alignment = "16", - target_has_atomic_primitive_alignment = "32", - target_has_atomic_primitive_alignment = "64", - target_has_atomic_primitive_alignment = "ptr", - target_has_atomic_load_store = "8", - target_has_atomic_load_store = "16", - target_has_atomic_load_store = "32", - target_has_atomic_load_store = "64", - target_has_atomic_load_store = "ptr", -)))] +#![doc(auto_cfg( + hide(no_fp_fmt_parse), + hide(target_pointer_width, values("16", "32", "64")), + hide( + target_has_atomic, + target_has_atomic_primitive_alignment, + target_has_atomic_load_store, + values("8", "16", "32", "64", "ptr"), + ), +))] #![no_core] #![rustc_coherence_is_core] #![rustc_preserve_ub_checks] @@ -79,8 +68,7 @@ // Lints: #![deny(rust_2021_incompatible_or_patterns)] #![deny(unsafe_op_in_unsafe_fn)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![warn(deprecated_in_future)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] @@ -108,6 +96,7 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] +#![feature(io_const_error)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(pattern_type_macro)] @@ -397,4 +386,17 @@ pub mod simd { pub use crate::core_simd::simd::*; } +// Include private modules that exist solely to provide rustdoc +// documentation for built-in attributes. Using `include!` because rustdoc +// only looks for these modules at the crate level. +include!("attribute_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for the existing keywords. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("keyword_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. include!("primitive_docs.rs"); diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index c21191dbc19be..932a2fc0bad92 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -40,7 +40,7 @@ macro_rules! panic { #[rustc_diagnostic_item = "assert_eq_macro"] #[allow_internal_unstable(panic_internals)] macro_rules! assert_eq { - ($left:expr, $right:expr $(,)?) => { + ($left:expr, $right:expr $(,)?) => {{ match (&$left, &$right) { (left_val, right_val) => { if !(*left_val == *right_val) { @@ -52,8 +52,8 @@ macro_rules! assert_eq { } } } - }; - ($left:expr, $right:expr, $($arg:tt)+) => { + }}; + ($left:expr, $right:expr, $($arg:tt)+) => {{ match (&$left, &$right) { (left_val, right_val) => { if !(*left_val == *right_val) { @@ -65,7 +65,7 @@ macro_rules! assert_eq { } } } - }; + }}; } /// Asserts that two expressions are not equal to each other (using [`PartialEq`]). @@ -96,7 +96,7 @@ macro_rules! assert_eq { #[rustc_diagnostic_item = "assert_ne_macro"] #[allow_internal_unstable(panic_internals)] macro_rules! assert_ne { - ($left:expr, $right:expr $(,)?) => { + ($left:expr, $right:expr $(,)?) => {{ match (&$left, &$right) { (left_val, right_val) => { if *left_val == *right_val { @@ -108,8 +108,8 @@ macro_rules! assert_ne { } } } - }; - ($left:expr, $right:expr, $($arg:tt)+) => { + }}; + ($left:expr, $right:expr, $($arg:tt)+) => {{ match (&($left), &($right)) { (left_val, right_val) => { if *left_val == *right_val { @@ -121,7 +121,7 @@ macro_rules! assert_ne { } } } - }; + }}; } /// Asserts that an expression matches the provided pattern. diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 744bd41d1139e..45b8b266a2671 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1066,7 +1066,7 @@ pub const trait Destruct: PointeeSized {} /// /// The implementation of this trait is built-in and cannot be implemented /// for any user type. -#[unstable(feature = "tuple_trait", issue = "none")] +#[unstable(feature = "tuple_trait", issue = "157987")] #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] #[rustc_deny_explicit_impl] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 9889f32c502ad..9bc8b0d128d2b 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,7 +1,7 @@ use crate::any::type_name; use crate::clone::TrivialClone; use crate::marker::Destruct; -use crate::mem::ManuallyDrop; +use crate::mem::{ManuallyDrop, transmute_neo}; use crate::{fmt, intrinsics, ptr, slice}; /// A wrapper type to construct uninitialized instances of `T`. @@ -724,9 +724,9 @@ impl MaybeUninit { // This also means that `self` must be a `value` variant. unsafe { intrinsics::assert_inhabited::(); - // We do this via a raw ptr read instead of `ManuallyDrop::into_inner` so that there's + // We do this via a transmute instead of `ManuallyDrop::into_inner` so that there's // no trace of `ManuallyDrop` in Miri's error messages here. - (&raw const self.value).cast::().read() + transmute_neo(self) } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 828572df6968b..a00a6c4783ba6 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1196,6 +1196,9 @@ pub const unsafe fn transmute_prefix(src: Src) -> Dst { /// New version of `transmute`, exposed under this name so it can be iterated upon /// without risking breakage to uses of "real" transmute. /// +/// Uses a `const`-`assert` to check the sizes instead of typeck hacks, +/// but is semantially identical to `transmute` otherwise. +/// /// It will not be stabilized under this name. /// /// # Examples @@ -1214,6 +1217,8 @@ pub const unsafe fn transmute_prefix(src: Src) -> Dst { /// unsafe { transmute_neo::(123) }; /// ``` #[unstable(feature = "transmute_neo", issue = "155079")] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[inline] pub const unsafe fn transmute_neo(src: Src) -> Dst { const { assert!(Src::SIZE == Dst::SIZE) }; diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 9ec36561839b5..7614adbcc532b 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -215,7 +215,7 @@ pub struct Variant { pub name: &'static str, /// All fields of the variant. pub fields: &'static [Field], - /// Whether the enum variant fields is non-exhaustive. + /// Whether the enum variant fields are non-exhaustive. pub non_exhaustive: bool, } @@ -342,6 +342,22 @@ pub struct FnPtr { /// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...); pub variadic: bool, + + // FIXME(splat): should these fields be private, or merged into an Option? + /// Is any function argument splatted? + pub is_splatted: bool, + + /// The index of the splatted function argument in `inputs`, only valid if `is_splatted` is true. + /// e.g. in `fn overload(a: u8, #[splat] b: (f32, usize))` the index is 1, and it can be called + /// as `overload(a, 1.0, 2)`. + pub splatted_index: u8, +} + +impl FnPtr { + /// Returns the splatted function argument index, or `None` if no argument is splatted. + pub const fn splatted(&self) -> Option { + if self.is_splatted { Some(self.splatted_index) } else { None } + } } #[derive(Debug, Default)] diff --git a/library/core/src/net/parser.rs b/library/core/src/net/parser.rs index 3aab24a90d817..a6473735dd455 100644 --- a/library/core/src/net/parser.rs +++ b/library/core/src/net/parser.rs @@ -63,11 +63,6 @@ impl<'a> Parser<'a> { if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind)) } - /// Peek the next character from the input - fn peek_char(&self) -> Option { - self.state.first().map(|&b| char::from(b)) - } - /// Reads the next character from the input fn read_char(&mut self) -> Option { self.state.split_first().map(|(&b, tail)| { @@ -100,60 +95,56 @@ impl<'a> Parser<'a> { }) } - // Read a number off the front of the input in the given radix, stopping - // at the first non-digit character or eof. Fails if the number has more - // digits than max_digits or if there is no number. - // - // INVARIANT: `max_digits` must be less than the number of digits that `u32` - // can represent. - fn read_number>( + /// Reads a number off the front of the input in the given radix, stopping at the first + /// non-digit character or eof. Fails if the number has more digits than `max_digits`, if there + /// is no number, if the number overflows `T`, or if there are leading zeros but + /// `allow_zero_prefix` is false. + /// + /// `max_digits` must be in 1..=6. + fn read_radix_max_digits>( &mut self, radix: u32, - max_digits: Option, + max_digits: u32, allow_zero_prefix: bool, ) -> Option { - self.read_atomically(move |p| { - let mut digit_count = 0; - let has_leading_zero = p.peek_char() == Some('0'); - - // If max_digits.is_some(), then we are parsing a `u8` or `u16` and - // don't need to use checked arithmetic since it fits within a `u32`. - let result = if let Some(max_digits) = max_digits { - // u32::MAX = 4_294_967_295u32, which is 10 digits long. - // `max_digits` must be less than 10 to not overflow a `u32`. - debug_assert!(max_digits < 10); - - let mut result = 0_u32; - while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { - result *= radix; - result += digit; - digit_count += 1; - - if digit_count > max_digits { - return None; - } - } - - result.try_into().ok() - } else { - let mut result = T::ZERO; + debug_assert!(1 <= max_digits); + debug_assert!(max_digits <= 6); // Works for any radix in u32 + self.read_atomically(|p| { + let first = p.read_char()?.to_digit(radix)?; + let mut result = first; + let mut digit_count = 1; - while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { - result = result.checked_mul(radix)?; - result = result.checked_add(digit)?; - digit_count += 1; + while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { + if digit_count >= max_digits { + return None; } + result *= radix; + result += digit; + digit_count += 1; + } - Some(result) - }; - - if digit_count == 0 { - None - } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { + if !allow_zero_prefix && first == 0 && digit_count > 1 { None } else { - result + result.try_into().ok() + } + }) + } + + /// Reads a decimal number off the front of the input, stopping at the first non-digit character + /// or eof. Fails if there is no number, or if the number overflows `T`. Allows an arbitrary + /// amount of leading zeros. + fn read_decimal(&mut self) -> Option { + self.read_atomically(|p| { + let first = p.read_char()?.to_digit(10)?; + let mut result = T::ZERO.checked_add(first)?; + + while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(10)) { + result = result.checked_mul(10)?; + result = result.checked_add(digit)?; } + + Some(result) }) } @@ -166,7 +157,7 @@ impl<'a> Parser<'a> { *slot = p.read_separator('.', i, |p| { // Disallow octal number in IP string. // https://tools.ietf.org/html/rfc6943#section-3.1.1 - p.read_number(10, Some(3), false) + p.read_radix_max_digits(10, 3, false) })?; } @@ -198,7 +189,7 @@ impl<'a> Parser<'a> { } } - let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true)); + let group = p.read_separator(':', i, |p| p.read_radix_max_digits(16, 4, true)); match group { Some(g) => *slot = g, @@ -250,7 +241,7 @@ impl<'a> Parser<'a> { fn read_port(&mut self) -> Option { self.read_atomically(|p| { p.read_given_char(':')?; - p.read_number(10, None, true) + p.read_decimal() }) } @@ -258,7 +249,7 @@ impl<'a> Parser<'a> { fn read_scope_id(&mut self) -> Option { self.read_atomically(|p| { p.read_given_char('%')?; - p.read_number(10, None, true) + p.read_decimal() }) } diff --git a/library/core/src/num/imp/overflow_panic.rs b/library/core/src/num/imp/overflow_panic.rs index f62d09ea041ab..fd5b88ad324c6 100644 --- a/library/core/src/num/imp/overflow_panic.rs +++ b/library/core/src/num/imp/overflow_panic.rs @@ -20,12 +20,6 @@ pub(in crate::num) const fn mul() -> ! { panic!("attempt to multiply with overflow") } -#[cold] -#[track_caller] -pub(in crate::num) const fn div() -> ! { - panic!("attempt to divide with overflow") -} - #[cold] #[track_caller] pub(in crate::num) const fn rem() -> ! { diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 34f16184e4ec3..8a374f015a958 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -983,9 +983,12 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// [`MIN`](Self::MIN) is the negative minimal value for the type); the result of this is `-MIN`, a positive value /// that is too large to represent in the type. /// + /// Note that this is equivalent to normal division: `MIN / -1` will also panic both in + /// debug and release builds. + /// /// # Examples /// /// ``` @@ -1010,8 +1013,8 @@ macro_rules! int_impl { #[inline] #[track_caller] pub const fn strict_div(self, rhs: Self) -> Self { - let (a, b) = self.overflowing_div(rhs); - if b { imp::overflow_panic::div() } else { a } + // Normal division already checks for "div-by-minus-1". + self / rhs } /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, @@ -1050,9 +1053,12 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// [`MIN`](Self::MIN) is the negative minimal value for the type); the result of this is `-MIN`, a positive value /// that is too large to represent in the type. /// + /// Note that this is equivalent to `div_euclid`: `MIN.div_euclid(-1)` will also panic both + /// in debug and release builds. + /// /// # Examples /// /// ``` @@ -1077,8 +1083,8 @@ macro_rules! int_impl { #[inline] #[track_caller] pub const fn strict_div_euclid(self, rhs: Self) -> Self { - let (a, b) = self.overflowing_div_euclid(rhs); - if b { imp::overflow_panic::div() } else { a } + // Normal `div_euclid` already checks for "div-by-minus-1". + self.div_euclid(rhs) } /// Checked integer division without remainder. Computes `self / rhs`, @@ -1181,7 +1187,7 @@ macro_rules! int_impl { ( lhs: $SelfT = self, rhs: $SelfT = rhs, - ) => rhs > 0 && lhs % rhs == 0 && (lhs != <$SelfT>::MIN || rhs != -1), + ) => rhs != 0 && lhs % rhs == 0 && (lhs != <$SelfT>::MIN || rhs != -1), ); // SAFETY: Same precondition unsafe { intrinsics::exact_div(self, rhs) } @@ -1223,7 +1229,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is `x % y` for `MIN / -1` on a - /// signed type (where `MIN` is the negative minimal value), which is invalid due to implementation artifacts. + /// signed type (where [`MIN`](Self::MIN) is the negative minimal value), which is invalid due to implementation artifacts. /// /// # Examples /// @@ -1289,7 +1295,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is `x % y` for `MIN / -1` on a - /// signed type (where `MIN` is the negative minimal value), which is invalid due to implementation artifacts. + /// signed type (where [`MIN`](Self::MIN) is the negative minimal value), which is invalid due to implementation artifacts. /// /// # Examples /// @@ -2259,8 +2265,8 @@ macro_rules! int_impl { /// boundary of the type. /// /// The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value - /// that is too large to represent in the type. In such a case, this function returns `MIN` itself. + /// [`MIN`](Self::MIN) is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// that is too large to represent in the type. In such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Panics /// @@ -2284,9 +2290,9 @@ macro_rules! int_impl { /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, /// wrapping around at the boundary of the type. /// - /// Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value + /// Wrapping will only occur in `MIN / -1` on a signed type (where [`MIN`](Self::MIN) is the negative minimal value /// for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the - /// type. In this case, this method returns `MIN` itself. + /// type. In this case, this method returns [`MIN`](Self::MIN) itself. /// /// # Panics /// @@ -2311,7 +2317,7 @@ macro_rules! int_impl { /// boundary of the type. /// /// Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` - /// invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, + /// invalid for `MIN / -1` on a signed type (where [`MIN`](Self::MIN) is the negative minimal value). In such a case, /// this function returns `0`. /// /// # Panics @@ -2336,8 +2342,8 @@ macro_rules! int_impl { /// Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around /// at the boundary of the type. /// - /// Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value - /// for the type). In this case, this method returns 0. + /// Wrapping will only occur in `MIN % -1` on a signed type (where [`MIN`](Self::MIN) is + /// the negative minimal value for the type). In this case, this method returns 0. /// /// # Panics /// @@ -2361,9 +2367,9 @@ macro_rules! int_impl { /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary /// of the type. /// - /// The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` + /// The only case where such wrapping can occur is when one negates [`MIN`](Self::MIN) on a signed type (where [`MIN`](Self::MIN) /// is the negative minimal value for the type); this is a positive value that is too large to represent - /// in the type. In such a case, this function returns `MIN` itself. + /// in the type. In such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Examples /// @@ -2460,7 +2466,7 @@ macro_rules! int_impl { /// /// The only case where such wrapping can occur is when one takes the absolute value of the negative /// minimal value for the type; this is a positive value that is too large to represent in the type. In - /// such a case, this function returns `MIN` itself. + /// such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Examples /// @@ -2772,7 +2778,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared among integer types, which is why `i32` is used. + /// Please note that this example is shared among integer types, which is why [`i32`] is used. /// /// ``` /// #![feature(signed_bigint_helpers)] @@ -2950,7 +2956,7 @@ macro_rules! int_impl { /// Negates self, overflowing if this is equal to the minimum value. /// /// Returns a tuple of the negated version of self along with a boolean indicating whether an overflow - /// happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the + /// happened. If `self` is the minimum value (e.g., [`i32::MIN`] for values of type [`i32`]), then the /// minimum value will be returned again and `true` will be returned for an overflow happening. /// /// # Examples @@ -3020,7 +3026,7 @@ macro_rules! int_impl { /// /// Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow /// happened. If self is the minimum value - #[doc = concat!("(e.g., ", stringify!($SelfT), "::MIN for values of type ", stringify!($SelfT), "),")] + #[doc = concat!("(e.g., [`", stringify!($SelfT), "::MIN`] for values of type [`", stringify!($SelfT), "`]),")] /// then the minimum value will be returned again and true will be returned /// for an overflow happening. /// @@ -3177,7 +3183,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3215,7 +3221,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` and + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] and /// `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3262,7 +3268,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3304,7 +3310,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3441,8 +3447,8 @@ macro_rules! int_impl { /// rounded down. /// /// This method might not be optimized owing to implementation details; - /// `ilog2` can produce results more efficiently for base 2, and `ilog10` - /// can produce results more efficiently for base 10. + /// [`ilog2`][Self::ilog2] can produce results more efficiently for base 2, + /// and [`ilog10`](Self::ilog10) can produce results more efficiently for base 10. /// /// # Panics /// diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 4053134d89d43..cfb5eb54fb6bc 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1721,8 +1721,8 @@ macro_rules! uint_impl { /// rounded down. /// /// This method might not be optimized owing to implementation details; - /// `ilog2` can produce results more efficiently for base 2, and `ilog10` - /// can produce results more efficiently for base 10. + /// [`ilog2`](Self::ilog2) can produce results more efficiently for base 2, + /// and [`ilog10`](Self::ilog10) can produce results more efficiently for base 10. /// /// # Panics /// diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index 25664a51a1d7b..15b54243c1eed 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -67,7 +67,7 @@ use crate::marker::Tuple; // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` @@ -154,7 +154,7 @@ pub const trait Fn: [const] FnMut { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` @@ -233,7 +233,7 @@ pub const trait FnMut: FnOnce { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` diff --git a/library/core/src/option.rs b/library/core/src/option.rs index c11b2354d1638..163e8bf714b1d 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -581,6 +581,7 @@ use crate::clone::TrivialClone; use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; +use crate::num::NonZero; use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; @@ -2666,18 +2667,111 @@ impl Iterator for OptionFlatten { type Item = A::Item; fn next(&mut self) -> Option { - self.iter.as_mut()?.next() + match &mut self.iter { + Some(iter) => iter.next(), + None => None, + } } fn size_hint(&self) -> (usize, Option) { - self.iter.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0))) + match &self.iter { + Some(iter) => iter.size_hint(), + None => (0, Some(0)), + } + } + + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + match &mut self.iter { + Some(iter) => iter.advance_by(n), + None => NonZero::new(n).map_or(Ok(()), Err), + } + } + + fn nth(&mut self, n: usize) -> Option { + match &mut self.iter { + Some(iter) => iter.nth(n), + None => None, + } + } + + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.iter { + Some(iter) => iter.fold(init, fold), + None => init, + } + } + + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + match &mut self.iter { + Some(iter) => iter.try_fold(init, fold), + None => try { init }, + } + } + + fn count(self) -> usize { + match self.iter { + Some(iter) => iter.count(), + None => 0, + } + } + + fn last(self) -> Option { + match self.iter { + Some(iter) => iter.last(), + None => None, + } } } #[unstable(feature = "option_into_flat_iter", issue = "148441")] impl DoubleEndedIterator for OptionFlatten { fn next_back(&mut self) -> Option { - self.iter.as_mut()?.next_back() + match &mut self.iter { + Some(iter) => iter.next_back(), + None => None, + } + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + match &mut self.iter { + Some(iter) => iter.advance_back_by(n), + None => NonZero::new(n).map_or(Ok(()), Err), + } + } + + fn nth_back(&mut self, n: usize) -> Option { + match &mut self.iter { + Some(iter) => iter.nth_back(n), + None => None, + } + } + + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.iter { + Some(iter) => iter.rfold(init, fold), + None => init, + } + } + + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + match &mut self.iter { + Some(iter) => iter.try_rfold(init, fold), + None => try { init }, + } } } diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 9d53567a26fd9..ee22521cb014c 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -13,7 +13,7 @@ use crate::panic::Location; #[derive(Debug)] pub struct PanicInfo<'a> { message: &'a fmt::Arguments<'a>, - location: &'a Location<'a>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, } @@ -33,7 +33,7 @@ impl<'a> PanicInfo<'a> { #[inline] pub(crate) fn new( message: &'a fmt::Arguments<'a>, - location: &'a Location<'a>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, ) -> Self { @@ -88,10 +88,10 @@ impl<'a> PanicInfo<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'_>> { + pub fn location(&self) -> Option<&'static Location<'static>> { // NOTE: If this is changed to sometimes return None, // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. - Some(&self.location) + Some(self.location) } /// Returns the payload associated with the panic. diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index b7fcd6a5cd3de..56f6a561f4bb9 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -183,7 +183,7 @@ impl *const T { /// [`with_exposed_provenance`]: with_exposed_provenance #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] - #[expect(lossy_provenance_casts, reason = "this *is* the replacement")] + #[expect(implicit_provenance_casts, reason = "this *is* the replacement")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } @@ -1497,9 +1497,10 @@ impl *const [T] { /// Returns a raw pointer to an element or subslice, without doing bounds /// checking. /// - /// Calling this method with an out-of-bounds index or when `self` is not dereferenceable + /// Calling this method with an [out-of-bounds index] or when `self` is not dereferenceable /// is *[undefined behavior]* even if the resulting pointer is not used. /// + /// [out-of-bounds index]: #method.add /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 593011edecf27..66dde8377a37a 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1000,7 +1000,7 @@ pub const fn dangling_mut() -> *mut T { #[stable(feature = "exposed_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "const_exposed_provenance", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead +#[allow(implicit_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance(addr: usize) -> *const T { addr as *const T } @@ -1041,7 +1041,7 @@ pub const fn with_exposed_provenance(addr: usize) -> *const T { #[stable(feature = "exposed_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "const_exposed_provenance", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead +#[allow(implicit_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance_mut(addr: usize) -> *mut T { addr as *mut T } @@ -1810,16 +1810,24 @@ pub const unsafe fn read(src: *const T) -> T { #[track_caller] #[rustc_diagnostic_item = "ptr_read_unaligned"] pub const unsafe fn read_unaligned(src: *const T) -> T { - let mut tmp = MaybeUninit::::uninit(); + // Always true thanks to the repr, but to demonstrate + const { + assert!(mem::offset_of!(Packed::, 0) == 0); + assert!(size_of::() == size_of::>()); + } + + let src = src.cast::>(); // SAFETY: the caller must guarantee that `src` is valid for reads. - // `src` cannot overlap `tmp` because `tmp` was just allocated on - // the stack as a separate allocation. + // Reading it as `Packed` instead of `T` reads those same bytes because + // it's the same size (thus zero offset), but with alignment 1 instead. // - // Also, since we just wrote a valid value into `tmp`, it is guaranteed - // to be properly initialized. + // Similarly, because it's the same bytes it's sound to transmute from the + // `Packed` to `T`. Transmute is a value-based (not a place-based) + // operation that doesn't care about alignment. unsafe { - copy_nonoverlapping(src as *const u8, tmp.as_mut_ptr() as *mut u8, size_of::()); - tmp.assume_init() + let packed = read(src); + // Can't just destructure because that's not allowed in const fn + mem::transmute_neo(packed) } } @@ -2012,14 +2020,18 @@ pub const unsafe fn write(dst: *mut T, src: T) { #[rustc_diagnostic_item = "ptr_write_unaligned"] #[track_caller] pub const unsafe fn write_unaligned(dst: *mut T, src: T) { - // SAFETY: the caller must guarantee that `dst` is valid for writes. - // `dst` cannot overlap `src` because the caller has mutable access - // to `dst` while `src` is owned by this function. - unsafe { - copy_nonoverlapping((&raw const src) as *const u8, dst as *mut u8, size_of::()); - // We are calling the intrinsic directly to avoid function calls in the generated code. - intrinsics::forget(src); + // Always true thanks to the repr, but to demonstrate + const { + assert!(mem::offset_of!(Packed::, 0) == 0); + assert!(size_of::() == size_of::>()); } + + let dst = dst.cast::>(); + let src = Packed(src); + // SAFETY: the caller must guarantee that `dst` is valid for writes. + // Writing it as `Packed` instead of `T` writes those same bytes because + // it's the same size (thus zero offset), but with alignment 1 instead. + unsafe { write(dst, src) } } /// Performs a volatile read of the value from `src` without moving it. @@ -2809,3 +2821,6 @@ pub macro addr_of($place:expr) { pub macro addr_of_mut($place:expr) { &raw mut $place } + +#[repr(C, packed)] +struct Packed(T); diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index fd8dafa18f56b..b333f8217b2c0 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -174,7 +174,7 @@ impl *mut T { /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] - #[expect(lossy_provenance_casts, reason = "this *is* the replacement")] + #[expect(implicit_provenance_casts, reason = "this *is* the replacement")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index bafc37469b32f..6ec34206a4248 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1573,9 +1573,10 @@ impl NonNull<[T]> { /// Returns a raw pointer to an element or subslice, without doing bounds /// checking. /// - /// Calling this method with an out-of-bounds index or when `self` is not dereferenceable + /// Calling this method with an [out-of-bounds index] or when `self` is not dereferenceable /// is *[undefined behavior]* even if the resulting pointer is not used. /// + /// [out-of-bounds index]: #method.add /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 51ab9b5fd1eac..b82d79232becc 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -139,6 +139,8 @@ pub impl(crate) const unsafe trait SliceIndex { /// Returns a pointer to the output at this location, without /// performing any bounds checking. /// + /// # Safety + /// /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting pointer is not used. /// @@ -149,6 +151,8 @@ pub impl(crate) const unsafe trait SliceIndex { /// Returns a mutable pointer to the output at this location, without /// performing any bounds checking. /// + /// # Safety + /// /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting pointer is not used. /// diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 9b077f1f9c1bd..4b02e73a4dcc3 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2530,6 +2530,7 @@ impl [T] { F: FnMut(&T) -> bool, { let index = self.iter().position(pred)?; + // Slice bounds checks optimized are away (as of June 2026) Some((&self[..index], &self[index + 1..])) } @@ -2558,6 +2559,7 @@ impl [T] { F: FnMut(&T) -> bool, { let index = self.iter().rposition(pred)?; + // Slice bounds checks optimized are away (as of June 2026) Some((&self[..index], &self[index + 1..])) } @@ -2736,10 +2738,12 @@ impl [T] { /// Returns a subslice with the prefix and suffix removed. /// - /// If the slice starts with `prefix` and ends with `suffix`, returns the subslice after the - /// prefix and before the suffix, wrapped in `Some`. + /// If the slice starts with `prefix`, ends with `suffix`, and + /// the prefix and suffix don't overlap, returns the subslice after + /// the prefix and before the suffix, wrapped in `Some`. /// - /// If the slice does not start with `prefix` or does not end with `suffix`, returns `None`. + /// If the slice does not start with `prefix`, does not end with `suffix`, + /// or the prefix and suffix overlap in the slice, returns `None`. /// /// # Examples /// @@ -2752,6 +2756,7 @@ impl [T] { /// assert_eq!(v.strip_circumfix(&[10], &[40]), None); /// assert_eq!(v.strip_circumfix(&[], &[40, 30]), Some(&[10, 50][..])); /// assert_eq!(v.strip_circumfix(&[10, 50], &[]), Some(&[40, 30][..])); + /// assert_eq!(v.strip_circumfix(&[10, 50, 40], &[50, 40, 30]), None); /// ``` #[must_use = "returns the subslice without modifying the original"] #[stable(feature = "strip_circumfix", since = "CURRENT_RUSTC_VERSION")] diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 78cb8ca665e1e..2128ce328a9c6 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2492,12 +2492,14 @@ impl str { /// Returns a string slice with the prefix and suffix removed. /// - /// If the string starts with the pattern `prefix` and ends with the pattern `suffix`, returns + /// If the string starts with the pattern `prefix` and ends with + /// the pattern `suffix`, and the prefix and suffix don't overlap, returns /// the substring after the prefix and before the suffix, wrapped in `Some`. /// Unlike [`trim_start_matches`] and [`trim_end_matches`], this method removes both the prefix /// and suffix exactly once. /// - /// If the string does not start with `prefix` or does not end with `suffix`, returns `None`. + /// If the string does not start with `prefix`, does not end with `suffix`, + /// or the prefix and suffix overlap in the string, returns `None`. /// /// Each [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. @@ -2513,6 +2515,7 @@ impl str { /// assert_eq!("bar:hello:foo".strip_circumfix("bar:", ":foo"), Some("hello")); /// assert_eq!("bar:foo".strip_circumfix("foo", "foo"), None); /// assert_eq!("foo:bar;".strip_circumfix("foo:", ';'), Some("bar")); + /// assert_eq!("foo:bar:baz".strip_circumfix("foo:bar:", ":bar:baz"), None); /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index e8865823deb98..802d38e3994b3 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -608,7 +608,6 @@ impl AtomicBool { /// # Examples /// /// ``` - /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let mut some_bool = true; @@ -618,7 +617,7 @@ impl AtomicBool { /// ``` #[inline] #[cfg(target_has_atomic_primitive_alignment = "8")] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn from_mut(v: &mut bool) -> &mut Self { // SAFETY: the mutable reference guarantees unique ownership, and // alignment of both `bool` and `Self` is 1. @@ -633,7 +632,6 @@ impl AtomicBool { /// # Examples /// /// ```ignore-wasm - /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let mut some_bools = [const { AtomicBool::new(false) }; 10]; @@ -653,7 +651,7 @@ impl AtomicBool { /// }); /// ``` #[inline] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn get_mut_slice(this: &mut [Self]) -> &mut [bool] { // SAFETY: the mutable reference guarantees unique ownership. unsafe { &mut *(this as *mut [Self] as *mut [bool]) } @@ -664,7 +662,6 @@ impl AtomicBool { /// # Examples /// /// ```rust,ignore-wasm - /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let mut some_bools = [false; 10]; @@ -678,7 +675,7 @@ impl AtomicBool { /// ``` #[inline] #[cfg(target_has_atomic_primitive_alignment = "8")] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn from_mut_slice(v: &mut [bool]) -> &mut [Self] { // SAFETY: the mutable reference guarantees unique ownership, and // alignment of both `bool` and `Self` is 1. @@ -1577,7 +1574,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicPtr, Ordering}; /// /// let mut data = 123; @@ -1589,7 +1585,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic_primitive_alignment = "ptr")] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn from_mut(v: &mut *mut T) -> &mut Self { let [] = [(); align_of::>() - align_of::<*mut ()>()]; // SAFETY: @@ -1607,7 +1603,6 @@ impl AtomicPtr { /// # Examples /// /// ```ignore-wasm - /// #![feature(atomic_from_mut)] /// use std::ptr::null_mut; /// use std::sync::atomic::{AtomicPtr, Ordering}; /// @@ -1633,7 +1628,7 @@ impl AtomicPtr { /// }); /// ``` #[inline] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn get_mut_slice(this: &mut [Self]) -> &mut [*mut T] { // SAFETY: the mutable reference guarantees unique ownership. unsafe { &mut *(this as *mut [Self] as *mut [*mut T]) } @@ -1646,7 +1641,6 @@ impl AtomicPtr { /// # Examples /// /// ```ignore-wasm - /// #![feature(atomic_from_mut)] /// use std::ptr::null_mut; /// use std::sync::atomic::{AtomicPtr, Ordering}; /// @@ -1668,7 +1662,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic_primitive_alignment = "ptr")] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn from_mut_slice(v: &mut [*mut T]) -> &mut [Self] { // SAFETY: // - the mutable reference guarantees unique ownership. @@ -2721,7 +2715,6 @@ macro_rules! atomic_int { /// # Examples /// /// ``` - /// #![feature(atomic_from_mut)] #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] /// /// let mut some_int = 123; @@ -2732,7 +2725,7 @@ macro_rules! atomic_int { /// #[inline] #[$cfg_align] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn from_mut(v: &mut $int_type) -> &mut Self { let [] = [(); align_of::() - align_of::<$int_type>()]; // SAFETY: @@ -2750,7 +2743,6 @@ macro_rules! atomic_int { /// # Examples /// /// ```ignore-wasm - /// #![feature(atomic_from_mut)] #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] /// #[doc = concat!("let mut some_ints = [const { ", stringify!($atomic_type), "::new(0) }; 10];")] @@ -2772,7 +2764,7 @@ macro_rules! atomic_int { /// }); /// ``` #[inline] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn get_mut_slice(this: &mut [Self]) -> &mut [$int_type] { // SAFETY: the mutable reference guarantees unique ownership. unsafe { &mut *(this as *mut [Self] as *mut [$int_type]) } @@ -2791,7 +2783,6 @@ macro_rules! atomic_int { /// # Examples /// /// ```ignore-wasm - /// #![feature(atomic_from_mut)] #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] /// /// let mut some_ints = [0; 10]; @@ -2807,7 +2798,7 @@ macro_rules! atomic_int { /// ``` #[inline] #[$cfg_align] - #[unstable(feature = "atomic_from_mut", issue = "76314")] + #[stable(feature = "atomic_from_mut", since = "CURRENT_RUSTC_VERSION")] pub fn from_mut_slice(v: &mut [$int_type]) -> &mut [Self] { let [] = [(); align_of::() - align_of::<$int_type>()]; // SAFETY: diff --git a/library/coretests/benches/net/addr_parser.rs b/library/coretests/benches/net/addr_parser.rs index bbf2ea3eb9796..d44ea98bfcbec 100644 --- a/library/coretests/benches/net/addr_parser.rs +++ b/library/coretests/benches/net/addr_parser.rs @@ -3,76 +3,210 @@ use core::str::FromStr; use test::{Bencher, black_box}; -const IPV4_STR: &str = "192.168.0.1"; -const IPV4_STR_PORT: &str = "192.168.0.1:8080"; - -const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1"; -const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1"; -const IPV6_STR_V4: &str = "2001:db8::192.168.0.1"; -const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080"; -const IPV6_STR_PORT_SCOPE_ID: &str = "[2001:db8::c0a8:1%1337]:8080"; +const IPV4: &[&str] = &[ + "192.168.0.1", + "8.8.8.8", + "127.0.0.1", + "255.255.255.255", + "0.0.0.0", + "10.0.0.1", + "203.0.113.42", + "172.16.254.1", + "100.64.0.1", + "1.2.3.4", +]; + +const IPV4_PORT: &[&str] = &[ + "192.168.0.1:8080", + "8.8.8.8:53", + "127.0.0.1:65535", + "255.255.255.255:1", + "0.0.0.0:80", + "10.0.0.1:443", + "203.0.113.42:22", + "172.16.254.1:3306", + "100.64.0.1:8443", + "1.2.3.4:0", +]; + +const IPV6_FULL: &[&str] = &[ + "2001:db8:0:0:0:0:c0a8:1", + "2001:db8:85a3:8d3:1319:8a2e:370:7348", + "fe80:0:0:0:0:0:0:1", + "ff02:0:0:0:0:0:0:101", + "2001:4860:4860:0:0:0:0:8888", + "2606:4700:4700:0:0:0:0:1111", + "fd00:0:0:0:0:0:0:1", + "fec0:0:0:0:0:0:0:abcd", + "1:2:3:4:5:6:7:8", + "abcd:ef01:2345:6789:abcd:ef01:2345:6789", +]; + +const IPV6_COMPRESS: &[&str] = &[ + "2001:db8::c0a8:1", + "::1", + "fe80::1", + "2001:db8::", + "ff02::1:2", + "64:ff9b::", + "::", + "2001:4860:4860::8888", + "2606:4700:4700::1111", + "fe80::1ff:fe23:4567:890a", +]; + +const IPV6_V4: &[&str] = &[ + "2001:db8::192.168.0.1", + "::ffff:192.168.0.1", + "64:ff9b::192.0.2.33", + "::ffff:8.8.8.8", + "::192.168.0.1", + "::ffff:255.255.255.255", + "2001:db8:0:0:0:0:192.168.0.1", + "64:ff9b::10.0.0.1", + "::ffff:1.2.3.4", + "::ffff:127.0.0.1", +]; + +const IPV6_PORT: &[&str] = &[ + "[2001:db8::c0a8:1]:8080", + "[::1]:443", + "[fe80::1]:53", + "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:22", + "[::]:80", + "[2001:4860:4860::8888]:443", + "[2606:4700:4700::1111]:53", + "[fe80::1ff:fe23:4567:890a]:8080", + "[64:ff9b::192.0.2.33]:443", + "[::ffff:8.8.8.8]:53", +]; + +const IPV6_PORT_SCOPE_ID: &[&str] = &[ + "[2001:db8::c0a8:1%1337]:8080", + "[fe80::1%1]:53", + "[fe80::1%999999]:443", + "[fe80::1%0]:80", + "[fe80::1ff:fe23:4567:890a%2]:8080", + "[::1%1]:443", + "[fe80::abcd%15]:22", + "[ff02::1%42]:5353", + "[fe80::1%4294967295]:443", + "[2001:db8::1%100]:8080", +]; #[bench] fn bench_parse_ipv4(b: &mut Bencher) { - b.iter(|| Ipv4Addr::from_str(black_box(IPV4_STR))); + b.iter(|| { + for s in IPV4 { + let _ = black_box(Ipv4Addr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_ipv6_full(b: &mut Bencher) { - b.iter(|| Ipv6Addr::from_str(black_box(IPV6_STR_FULL))); + b.iter(|| { + for s in IPV6_FULL { + let _ = black_box(Ipv6Addr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_ipv6_compress(b: &mut Bencher) { - b.iter(|| Ipv6Addr::from_str(black_box(IPV6_STR_COMPRESS))); + b.iter(|| { + for s in IPV6_COMPRESS { + let _ = black_box(Ipv6Addr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_ipv6_v4(b: &mut Bencher) { - b.iter(|| Ipv6Addr::from_str(black_box(IPV6_STR_V4))); + b.iter(|| { + for s in IPV6_V4 { + let _ = black_box(Ipv6Addr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_ipaddr_v4(b: &mut Bencher) { - b.iter(|| IpAddr::from_str(black_box(IPV4_STR))); + b.iter(|| { + for s in IPV4 { + let _ = black_box(IpAddr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_ipaddr_v6_full(b: &mut Bencher) { - b.iter(|| IpAddr::from_str(black_box(IPV6_STR_FULL))); + b.iter(|| { + for s in IPV6_FULL { + let _ = black_box(IpAddr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_ipaddr_v6_compress(b: &mut Bencher) { - b.iter(|| IpAddr::from_str(black_box(IPV6_STR_COMPRESS))); + b.iter(|| { + for s in IPV6_COMPRESS { + let _ = black_box(IpAddr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_ipaddr_v6_v4(b: &mut Bencher) { - b.iter(|| IpAddr::from_str(black_box(IPV6_STR_V4))); + b.iter(|| { + for s in IPV6_V4 { + let _ = black_box(IpAddr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_socket_v4(b: &mut Bencher) { - b.iter(|| SocketAddrV4::from_str(black_box(IPV4_STR_PORT))); + b.iter(|| { + for s in IPV4_PORT { + let _ = black_box(SocketAddrV4::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_socket_v6(b: &mut Bencher) { - b.iter(|| SocketAddrV6::from_str(black_box(IPV6_STR_PORT))); + b.iter(|| { + for s in IPV6_PORT { + let _ = black_box(SocketAddrV6::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_socket_v6_scope_id(b: &mut Bencher) { - b.iter(|| SocketAddrV6::from_str(black_box(IPV6_STR_PORT_SCOPE_ID))); + b.iter(|| { + for s in IPV6_PORT_SCOPE_ID { + let _ = black_box(SocketAddrV6::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_socketaddr_v4(b: &mut Bencher) { - b.iter(|| SocketAddr::from_str(black_box(IPV4_STR_PORT))); + b.iter(|| { + for s in IPV4_PORT { + let _ = black_box(SocketAddr::from_str(black_box(s))); + } + }); } #[bench] fn bench_parse_socketaddr_v6(b: &mut Bencher) { - b.iter(|| SocketAddr::from_str(black_box(IPV6_STR_PORT))); + b.iter(|| { + for s in IPV6_PORT { + let _ = black_box(SocketAddr::from_str(black_box(s))); + } + }); } diff --git a/library/coretests/tests/iter/adapters/filter.rs b/library/coretests/tests/iter/adapters/filter.rs index 167851e33336e..14cd3cbd58f10 100644 --- a/library/coretests/tests/iter/adapters/filter.rs +++ b/library/coretests/tests/iter/adapters/filter.rs @@ -63,3 +63,15 @@ fn test_next_chunk_does_not_leak() { assert_eq!(Rc::strong_count(w), 1); } } + +#[test] +fn test_next_chunk_back_does_not_leak() { + let drop_witness: [_; 5] = std::array::from_fn(|_| Rc::new(())); + + let v = (0..5).map(|i| drop_witness[i].clone()).collect::>(); + let _ = v.into_iter().filter(|_| false).next_chunk_back::<1>(); + + for ref w in drop_witness { + assert_eq!(Rc::strong_count(w), 1); + } +} diff --git a/library/coretests/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs index 386946461e9fe..0850b1e4edc26 100644 --- a/library/coretests/tests/iter/traits/iterator.rs +++ b/library/coretests/tests/iter/traits/iterator.rs @@ -619,6 +619,18 @@ fn test_next_chunk() { assert_eq!(it.next_chunk::<0>().unwrap(), []); } +#[test] +fn test_next_chunk_back() { + let mut it = 0..12; + assert_eq!(it.next_chunk_back().unwrap(), [8, 9, 10, 11]); + assert_eq!(it.next_chunk_back().unwrap(), []); + assert_eq!(it.next_chunk_back().unwrap(), [2, 3, 4, 5, 6, 7]); + assert_eq!(it.next_chunk_back::<4>().unwrap_err().as_slice(), &[0, 1]); + + let mut it = std::iter::once_with(|| panic!()); + assert_eq!(it.next_chunk_back::<0>().unwrap(), []); +} + #[test] fn test_next_chunk_untrusted() { struct Untrusted(I); @@ -636,6 +648,28 @@ fn test_next_chunk_untrusted() { assert_eq!(it.next_chunk::<4>().unwrap_err().as_slice(), &[10, 11]); } +#[test] +fn test_next_chunk_back_untrusted() { + struct Untrusted(I); + impl Iterator for Untrusted { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.0.next() + } + } + impl DoubleEndedIterator for Untrusted { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + let mut it = Untrusted(0..12); + assert_eq!(it.next_chunk_back().unwrap(), [8, 9, 10, 11]); + assert_eq!(it.next_chunk_back().unwrap(), []); + assert_eq!(it.next_chunk_back().unwrap(), [2, 3, 4, 5, 6, 7]); + assert_eq!(it.next_chunk::<4>().unwrap_err().as_slice(), &[0, 1]); +} + #[test] fn test_collect_into_tuples() { let a = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index ec2201aa7b9da..92fccea38bac9 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -128,8 +128,7 @@ #![feature(unwrap_infallible)] // tidy-alphabetical-end #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] /// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. diff --git a/library/coretests/tests/macros.rs b/library/coretests/tests/macros.rs index 9f73ebd253c3b..86419bb41e527 100644 --- a/library/coretests/tests/macros.rs +++ b/library/coretests/tests/macros.rs @@ -241,7 +241,19 @@ fn temporary_scope_introduction() { (assert_matches!(*MutRefWithDrop(&mut val).0, 0), std::mem::take(&mut val)); (assert_matches!(*MutRefWithDrop(&mut val).0, 0, "msg"), std::mem::take(&mut val)); - (debug_assert_matches!(*MutRefWithDrop(&mut val).0, 0), std::mem::take(&mut val)); (debug_assert_matches!(*MutRefWithDrop(&mut val).0, 0, "msg"), std::mem::take(&mut val)); + + (assert_eq!(*MutRefWithDrop(&mut val).0, 0), std::mem::take(&mut val)); + (assert_eq!(*MutRefWithDrop(&mut val).0, 0, "msg"), std::mem::take(&mut val)); + (debug_assert_eq!(*MutRefWithDrop(&mut val).0, 0), std::mem::take(&mut val)); + (debug_assert_eq!(*MutRefWithDrop(&mut val).0, 0, "msg"), std::mem::take(&mut val)); + + (assert_ne!(*MutRefWithDrop(&mut val).0, 1), std::mem::take(&mut val)); + (assert_ne!(*MutRefWithDrop(&mut val).0, 1, "msg"), std::mem::take(&mut val)); + (debug_assert_ne!(*MutRefWithDrop(&mut val).0, 1), std::mem::take(&mut val)); + (debug_assert_ne!(*MutRefWithDrop(&mut val).0, 1, "msg"), std::mem::take(&mut val)); + + (assert!(*MutRefWithDrop(&mut val).0 == 0), std::mem::take(&mut val)); + (assert!(*MutRefWithDrop(&mut val).0 == 0, "msg"), std::mem::take(&mut val)); } diff --git a/library/coretests/tests/mem/fn_ptr.rs b/library/coretests/tests/mem/fn_ptr.rs index 1d50a2552a193..6e7e170917f55 100644 --- a/library/coretests/tests/mem/fn_ptr.rs +++ b/library/coretests/tests/mem/fn_ptr.rs @@ -5,6 +5,7 @@ const STRING_TY: TypeId = const { TypeId::of::() }; const U8_TY: TypeId = const { TypeId::of::() }; const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() }; const UNIT_TY: TypeId = const { TypeId::of::<()>() }; +const TUPLE_STRING_U8_TY: TypeId = const { TypeId::of::<(String, u8)>() }; #[test] fn test_fn_ptrs() { @@ -14,6 +15,8 @@ fn test_fn_ptrs() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -31,6 +34,8 @@ fn test_ref() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -61,6 +66,8 @@ fn test_unsafe() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -75,6 +82,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -87,6 +96,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -99,6 +110,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -114,6 +127,8 @@ fn test_inputs() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -128,6 +143,8 @@ fn test_inputs() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -145,6 +162,8 @@ fn test_output() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of:: u8>().kind }) else { panic!(); @@ -160,6 +179,8 @@ fn test_variadic() { inputs: [ty1], output, variadic: true, + is_splatted: false, + splatted_index: _, }) = &(const { Type::of::().kind }) else { panic!(); @@ -167,3 +188,48 @@ fn test_variadic() { assert_eq!(output, &UNIT_TY); assert_eq!(*ty1, U8_TY); } + +#[test] +fn test_splat() { + #[rustfmt::skip] + let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { + panic!(); + }; + let FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: [ty1], + output, + variadic: false, + is_splatted: true, + splatted_index: 0, + } = fn_ptr_ty + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, TUPLE_STRING_U8_TY); + assert_eq!(fn_ptr_ty.splatted(), Some(0)); +} + +#[test] +fn test_not_splat() { + let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { + panic!(); + }; + let FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: [ty1], + output, + variadic: false, + is_splatted: false, + splatted_index: _, + } = fn_ptr_ty + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, TUPLE_STRING_U8_TY); + assert_eq!(fn_ptr_ty.splatted(), None); +} diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index 1e5046ca61c39..286080e1bea2e 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -9,7 +9,7 @@ std = { path = "../std" } # `core` when resolving doc links. Without this line a different `core` will be # loaded from sysroot causing duplicate lang items and other similar errors. core = { path = "../core" } -rustc-literal-escaper = { version = "0.0.7", features = ["rustc-dep-of-std"] } +rustc-literal-escaper = { version = "0.0.8", features = ["rustc-dep-of-std"] } [features] default = ["rustc-dep-of-std"] diff --git a/library/proc_macro/src/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs index 3760749d83a54..3176b9ea35c8c 100644 --- a/library/proc_macro/src/bridge/buffer.rs +++ b/library/proc_macro/src/bridge/buffer.rs @@ -1,8 +1,7 @@ //! Buffer management for same-process client<->server communication. -use std::io::{self, Write}; use std::mem::{self, ManuallyDrop}; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; use std::slice; #[repr(C)] @@ -32,13 +31,6 @@ impl Deref for Buffer { } } -impl DerefMut for Buffer { - #[inline] - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.data, self.len) } - } -} - impl Buffer { #[inline] pub(super) fn new() -> Self { @@ -99,25 +91,6 @@ impl Buffer { } } -impl Write for Buffer { - #[inline] - fn write(&mut self, xs: &[u8]) -> io::Result { - self.extend_from_slice(xs); - Ok(xs.len()) - } - - #[inline] - fn write_all(&mut self, xs: &[u8]) -> io::Result<()> { - self.extend_from_slice(xs); - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - impl Drop for Buffer { #[inline] fn drop(&mut self) { @@ -128,8 +101,7 @@ impl Drop for Buffer { impl From> for Buffer { fn from(v: Vec) -> Self { - let mut v = ManuallyDrop::new(v); - let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity()); + let (data, len, capacity) = v.into_raw_parts(); // This utility function is nested in here because it can *only* // be safely called on `Buffer`s created by *this* `proc_macro`. diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index a5966f767596e..48e6245ecf98b 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -282,7 +282,7 @@ impl Client { pub const fn expand1(f: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy) -> Self { Client { run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { - run_client(bridge, |input| f(input)) + run_client(bridge, f) }), } } diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 3459426b5cd3f..2c72497ad2723 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -1,6 +1,5 @@ //! Serialization for client-server communication. -use std::io::Write; use std::num::NonZero; use super::buffer::Buffer; @@ -209,7 +208,7 @@ impl Encode for &str { fn encode(self, w: &mut Buffer, s: &mut S) { let bytes = self.as_bytes(); bytes.len().encode(w, s); - w.write_all(bytes).unwrap(); + w.extend_from_slice(bytes); } } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 98b00c600f5ef..2cba4b52fc276 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -37,7 +37,7 @@ #![warn(unreachable_pub)] #![deny(unsafe_op_in_unsafe_fn)] -#[unstable(feature = "proc_macro_internals", issue = "27812")] +#[unstable(feature = "proc_macro_internals", issue = "none")] #[doc(hidden)] pub mod bridge; @@ -296,7 +296,7 @@ impl TokenStream { /// Checks if this `TokenStream` is empty. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn is_empty(&self) -> bool { - self.0.as_ref().map(|h| BridgeMethods::ts_is_empty(h)).unwrap_or(true) + self.0.as_ref().map(BridgeMethods::ts_is_empty).unwrap_or(true) } /// Parses this `TokenStream` as an expression and attempts to expand any @@ -575,9 +575,7 @@ pub mod token_stream { type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { - IntoIter( - self.0.map(|v| BridgeMethods::ts_into_trees(v)).unwrap_or_default().into_iter(), - ) + IntoIter(self.0.map(BridgeMethods::ts_into_trees).unwrap_or_default().into_iter()) } } } @@ -595,7 +593,7 @@ pub macro quote($($t:tt)*) { /* compiler built-in */ } -#[unstable(feature = "proc_macro_internals", issue = "27812")] +#[unstable(feature = "proc_macro_internals", issue = "none")] #[doc(hidden)] mod quote; @@ -755,14 +753,14 @@ impl Span { // Used by the implementation of `Span::quote` #[doc(hidden)] - #[unstable(feature = "proc_macro_internals", issue = "27812")] + #[unstable(feature = "proc_macro_internals", issue = "none")] pub fn save_span(&self) -> usize { BridgeMethods::span_save_span(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] - #[unstable(feature = "proc_macro_internals", issue = "27812")] + #[unstable(feature = "proc_macro_internals", issue = "none")] pub fn recover_proc_macro_span(id: usize) -> Span { Span(BridgeMethods::span_recover_proc_macro_span(id)) } @@ -1296,7 +1294,7 @@ macro_rules! unsuffixed_int_literals { /// specified on this token, meaning that invocations like /// `Literal::i8_unsuffixed(1)` are equivalent to /// `Literal::u32_unsuffixed(1)`. - /// Literals created from negative numbers might not survive rountrips through + /// Literals created from negative numbers might not survive roundtrips through /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). /// /// Literals created through this method have the `Span::call_site()` @@ -1416,7 +1414,7 @@ impl Literal { /// This constructor is similar to those like `Literal::i8_unsuffixed` where /// the float's value is emitted directly into the token but no suffix is /// used, so it may be inferred to be a `f64` later in the compiler. - /// Literals created from negative numbers might not survive rountrips through + /// Literals created from negative numbers might not survive roundtrips through /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). /// /// # Panics @@ -1441,7 +1439,7 @@ impl Literal { /// specified is the preceding part of the token and `f32` is the suffix of /// the token. This token will always be inferred to be an `f32` in the /// compiler. - /// Literals created from negative numbers might not survive rountrips through + /// Literals created from negative numbers might not survive roundtrips through /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). /// /// # Panics @@ -1461,7 +1459,7 @@ impl Literal { /// This constructor is similar to those like `Literal::i8_unsuffixed` where /// the float's value is emitted directly into the token but no suffix is /// used, so it may be inferred to be a `f64` later in the compiler. - /// Literals created from negative numbers might not survive rountrips through + /// Literals created from negative numbers might not survive roundtrips through /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). /// /// # Panics @@ -1486,7 +1484,7 @@ impl Literal { /// specified is the preceding part of the token and `f64` is the suffix of /// the token. This token will always be inferred to be an `f64` in the /// compiler. - /// Literals created from negative numbers might not survive rountrips through + /// Literals created from negative numbers might not survive roundtrips through /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). /// /// # Panics diff --git a/library/std/build.rs b/library/std/build.rs index 5f2e441bf7d83..98856b58d890f 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -48,6 +48,7 @@ fn main() { || target_os == "vita" || target_os == "aix" || target_os == "nto" + || target_os == "qnx" || target_os == "xous" || target_os == "hurd" || target_os == "uefi" diff --git a/library/std/src/attribute_docs.rs b/library/std/src/attribute_docs.rs deleted file mode 100644 index 1d8dddb7ca54a..0000000000000 --- a/library/std/src/attribute_docs.rs +++ /dev/null @@ -1,87 +0,0 @@ -#[doc(attribute = "must_use")] -// -/// Warn when a value is ignored. -/// -/// The `must_use` attribute applies to values where simply creating or returning them is -/// often not enough. If a value marked with `#[must_use]` is produced and then ignored, the -/// compiler warns through the [`unused_must_use`] lint. -/// -/// This is most common on types that represent an important state or outcome. For example, -/// [`Result`] is marked `#[must_use]` because ignoring an error value can hide a failed operation. -/// In the following example, the returned `Result` is the only sign that writing the message -/// might have failed: -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// fn write_message() -> std::io::Result<()> { -/// // Write the message... -/// Ok(()) -/// } -/// -/// write_message(); -/// ``` -/// -/// Ignoring that `Result` triggers this warning: -/// -/// ```text -/// warning: unused `Result` that must be used -/// = note: this `Result` may be an `Err` variant, which should be handled -/// = note: `#[warn(unused_must_use)]` (part of `#[warn(unused)]`) on by default -/// help: use `let _ = ...` to ignore the resulting value -/// ``` -/// -/// Future values are also `#[must_use]`: creating a future does not run it, so ignoring one often -/// means the intended asynchronous work never happens. -/// -/// You can also place `#[must_use]` on a function, method, or trait declaration. On a function or -/// method, the warning is tied to ignoring that call's return value: -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// #[must_use] -/// fn make_token() -> String { -/// String::from("token") -/// } -/// -/// // Ignoring this call's return value triggers `unused_must_use`. -/// make_token(); -/// ``` -/// -/// On a trait, the warning applies when a function returns an opaque type (`impl Trait`) or trait -/// object (`dyn Trait`) whose bounds include that trait. This is how futures warn if you create one -/// but never poll or await it, since an `async fn` returns an opaque type implementing [`Future`]. -/// -/// The attribute can include a message explaining what the caller should do with the value: -/// -/// ```rust -/// # #![allow(dead_code)] -/// #[must_use = "call `.finish()` to complete the operation"] -/// fn start_operation() -> Operation { -/// Operation -/// } -/// -/// struct Operation; -/// ``` -/// -/// If intentionally ignoring the value is correct, bind it to `_` or call [`drop`]: -/// -/// ```rust -/// # #[must_use] -/// # fn make_token() -> String { -/// # String::from("token") -/// # } -/// let _ = make_token(); -/// drop(make_token()); -/// ``` -/// -/// The attribute is a warning tool, not a type-system rule. Code can still explicitly discard a -/// `#[must_use]` value, and the compiler does not require callers to inspect or otherwise act on -/// the value. -/// -/// For more information, see the Reference on [the `must_use` attribute]. -/// -/// [`Result`]: result::Result -/// [`Future`]: future::Future -/// [`unused_must_use`]: ../rustc/lints/listing/warn-by-default.html#unused-must-use -/// [the `must_use` attribute]: ../reference/attributes/diagnostics.html#the-must_use-attribute -mod must_use_attribute {} diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 1cbabf572367e..fa5dfd8b76ec1 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -1096,6 +1096,7 @@ pub mod consts { /// * `"netbsd"` /// * `"nto"` /// * `"openbsd"` + /// * `"qnx"` /// * `"redox"` /// * `"solaris"` /// * `"solid_asp3"` diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 360ca83c65a91..6dbe37ca9fba8 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,310 +1,20 @@ #[cfg(test)] mod tests; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::ErrorKind; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use core::io::RawOsError; - -// On 64-bit platforms, `io::Error` may use a bit-packed representation to -// reduce size. However, this representation assumes that error codes are -// always 32-bit wide. -// -// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. -// Therefore, the packed representation is explicitly disabled for UEFI -// targets, and the unpacked representation must be used instead. -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -mod repr_bitpacked; -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -use repr_bitpacked::Repr; - -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -mod repr_unpacked; -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -use repr_unpacked::Repr; - -use crate::{error, fmt, result, sys}; - -/// A specialized [`Result`] type for I/O operations. -/// -/// This type is broadly used across [`std::io`] for any operation which may -/// produce an error. -/// -/// This type alias is generally used to avoid writing out [`io::Error`] directly and -/// is otherwise a direct mapping to [`Result`]. -/// -/// While usual Rust style is to import types directly, aliases of [`Result`] -/// often are not, to make it easier to distinguish between them. [`Result`] is -/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias -/// will generally use `io::Result` instead of shadowing the [prelude]'s import -/// of [`std::result::Result`][`Result`]. -/// -/// [`std::io`]: crate::io -/// [`io::Error`]: Error -/// [`Result`]: crate::result::Result -/// [prelude]: crate::prelude -/// -/// # Examples -/// -/// A convenience function that bubbles an `io::Result` to its caller: -/// -/// ``` -/// use std::io; -/// -/// fn get_string() -> io::Result { -/// let mut buffer = String::new(); -/// -/// io::stdin().read_line(&mut buffer)?; -/// -/// Ok(buffer) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(search_unbox)] -pub type Result = result::Result; - -/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and -/// associated traits. -/// -/// Errors mostly originate from the underlying OS, but custom instances of -/// `Error` can be created with crafted error messages and a particular value of -/// [`ErrorKind`]. -/// -/// [`Read`]: crate::io::Read -/// [`Write`]: crate::io::Write -/// [`Seek`]: crate::io::Seek -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Error { - repr: Repr, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.repr, f) - } -} - -/// Common errors constants for use in std -#[allow(dead_code)] -impl Error { - pub(crate) const INVALID_UTF8: Self = - const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - - pub(crate) const READ_EXACT_EOF: Self = - const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - - pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( - ErrorKind::NotFound, - "the number of hardware threads is not known for the target platform", - ); - - pub(crate) const UNSUPPORTED_PLATFORM: Self = - const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - - pub(crate) const WRITE_ALL_EOF: Self = - const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - - pub(crate) const ZERO_TIMEOUT: Self = - const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - - pub(crate) const NO_ADDRESSES: Self = - const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for Error { - /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. - fn from(_: alloc::ffi::NulError) -> Error { - const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") - } -} - -#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] -impl From for Error { - /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. - /// - /// `TryReserveError` won't be available as the error `source()`, - /// but this may change in the future. - fn from(_: alloc::collections::TryReserveError) -> Error { - // ErrorData::Custom allocates, which isn't great for handling OOM errors. - ErrorKind::OutOfMemory.into() - } -} - -// Only derive debug in tests, to make sure it -// doesn't accidentally get printed. -#[cfg_attr(test, derive(Debug))] -enum ErrorData { - Os(RawOsError), - Simple(ErrorKind), - SimpleMessage(&'static SimpleMessage), - Custom(C), -} - -// `#[repr(align(4))]` is probably redundant, it should have that value or -// higher already. We include it just because repr_bitpacked.rs's encoding -// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the -// alignment required by the struct, only increase it). -// -// If we add more variants to ErrorData, this can be increased to 8, but it -// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or -// whatever cfg we're using to enable the `repr_bitpacked` code, since only the -// that version needs the alignment, and 8 is higher than the alignment we'll -// have on 32 bit platforms. -// -// (For the sake of being explicit: the alignment requirement here only matters -// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't -// matter at all) -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -#[repr(align(4))] -#[derive(Debug)] -pub struct SimpleMessage { - pub kind: ErrorKind, - pub message: &'static str, -} - -/// Creates a new I/O error from a known kind of error and a string literal. -/// -/// Contrary to [`Error::new`], this macro does not allocate and can be used in -/// `const` contexts. -/// -/// # Example -/// ``` -/// #![feature(io_const_error)] -/// use std::io::{const_error, Error, ErrorKind}; -/// -/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); -/// -/// fn not_here() -> Result<(), Error> { -/// Err(FAIL) -/// } -/// ``` -#[rustc_macro_transparency = "semiopaque"] -#[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(hint_must_use, io_const_error_internals)] -pub macro const_error($kind:expr, $message:expr $(,)?) { - $crate::hint::must_use($crate::io::Error::from_static_message( - const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, - )) -} - -// As with `SimpleMessage`: `#[repr(align(4))]` here is just because -// repr_bitpacked's encoding requires it. In practice it almost certainly be -// already be this high or higher. -#[derive(Debug)] -#[repr(align(4))] -struct Custom { - kind: ErrorKind, - error: Box, -} - -/// Intended for use for errors not exposed to the user, where allocating onto -/// the heap (for normal construction via Error::new) is too costly. -#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] -impl From for Error { - /// Converts an [`ErrorKind`] into an [`Error`]. - /// - /// This conversion creates a new error with a simple representation of error kind. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// let not_found = ErrorKind::NotFound; - /// let error = Error::from(not_found); - /// assert_eq!("entity not found", format!("{error}")); - /// ``` - #[inline] - fn from(kind: ErrorKind) -> Error { - Error { repr: Repr::new_simple(kind) } - } -} - +#[cfg_attr( + test, + expect(unused, reason = "only used in implementation for non-test compilation") +)] +use crate::{ + io::{Error, OsFunctions, RawOsError}, + sys::io::{decode_error_kind, errno, error_string, is_interrupted}, +}; + +// Because std is linked in during testing, these incoherent implementations would +// be duplicated if this was unconditionally included. +// See #2912 for details. +#[cfg(not(test))] impl Error { - /// Creates a new I/O error from a known kind of error as well as an - /// arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. The `error` argument is an arbitrary - /// payload which will be contained in this [`Error`]. - /// - /// Note that this function allocates memory on the heap. - /// If no extra payload is required, use the `From` conversion from - /// `ErrorKind`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// // errors can be created from strings - /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); - /// - /// // creating an error without payload (and without memory allocation) - /// let eof_error = Error::from(ErrorKind::UnexpectedEof); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] - #[inline(never)] - pub fn new(kind: ErrorKind, error: E) -> Error - where - E: Into>, - { - Self::_new(kind, error.into()) - } - - /// Creates a new I/O error from an arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. It is a shortcut for [`Error::new`] - /// with [`ErrorKind::Other`]. - /// - /// # Examples - /// - /// ``` - /// use std::io::Error; - /// - /// // errors can be created from strings - /// let custom_error = Error::other("oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::other(custom_error); - /// ``` - #[stable(feature = "io_error_other", since = "1.74.0")] - pub fn other(error: E) -> Error - where - E: Into>, - { - Self::_new(ErrorKind::Other, error.into()) - } - - fn _new(kind: ErrorKind, error: Box) -> Error { - Error { repr: Repr::new_custom(Box::new(Custom { kind, error })) } - } - - /// Creates a new I/O error from a known kind of error as well as a constant - /// message. - /// - /// This function does not allocate. - /// - /// You should not use this directly, and instead use the `const_error!` - /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. - /// - /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. - #[inline] - #[doc(hidden)] - #[unstable(feature = "io_const_error_internals", issue = "none")] - pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { - Self { repr: Repr::new_simple_message(msg) } - } - /// Returns an error representing the last OS error which occurred. /// /// This function reads the value of `errno` for the target platform (e.g. @@ -324,13 +34,14 @@ impl Error { /// let os_error = Error::last_os_error(); /// println!("last OS error: {os_error:?}"); /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "GetLastError")] #[doc(alias = "errno")] #[must_use] #[inline] pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::io::errno()) + Error::from_raw_os_error(errno()) } /// Creates a new instance of an [`Error`] from a particular OS error code. @@ -358,405 +69,18 @@ impl Error { /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); /// # } /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] pub fn from_raw_os_error(code: RawOsError) -> Error { - Error { repr: Repr::new_os(code) } - } + const FUNCTIONS: &'static OsFunctions = &OsFunctions { + format_os_error: |code, fmt| fmt.write_str(&error_string(code)), + decode_error_kind, + is_interrupted, + }; - /// Returns the OS error that this error represents (if any). - /// - /// If this [`Error`] was constructed via [`last_os_error`] or - /// [`from_raw_os_error`], then this function will return [`Some`], otherwise - /// it will return [`None`]. - /// - /// [`last_os_error`]: Error::last_os_error - /// [`from_raw_os_error`]: Error::from_raw_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_os_error(err: &Error) { - /// if let Some(raw_os_err) = err.raw_os_error() { - /// println!("raw OS error: {raw_os_err:?}"); - /// } else { - /// println!("Not an OS error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "raw OS error: ...". - /// print_os_error(&Error::last_os_error()); - /// // Will print "Not an OS error". - /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn raw_os_error(&self) -> Option { - match self.repr.data() { - ErrorData::Os(i) => Some(i), - ErrorData::Custom(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - } + // SAFETY: `FUNCTIONS` is a constant and not created at runtime. + unsafe { Error::from_raw_os_error_with_functions(code, FUNCTIONS) } } - - /// Returns a reference to the inner error wrapped by this error (if any). - /// - /// If this [`Error`] was constructed via [`new`] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err:?}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&*c.error), - } - } - - /// Returns a mutable reference to the inner error wrapped by this error - /// (if any). - /// - /// If this [`Error`] was constructed via [`new`] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// use std::{error, fmt}; - /// use std::fmt::Display; - /// - /// #[derive(Debug)] - /// struct MyError { - /// v: String, - /// } - /// - /// impl MyError { - /// fn new() -> MyError { - /// MyError { - /// v: "oh no!".to_string() - /// } - /// } - /// - /// fn change_message(&mut self, new_message: &str) { - /// self.v = new_message.to_string(); - /// } - /// } - /// - /// impl error::Error for MyError {} - /// - /// impl Display for MyError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "MyError: {}", self.v) - /// } - /// } - /// - /// fn change_error(mut err: Error) -> Error { - /// if let Some(inner_err) = err.get_mut() { - /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); - /// } - /// err - /// } - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&change_error(Error::last_os_error())); - /// // Will print "Inner error: ...". - /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { - match self.repr.data_mut() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&mut *c.error), - } - } - - /// Consumes the `Error`, returning its inner error (if any). - /// - /// If this [`Error`] was constructed via [`new`] or [`other`], - /// then this function will return [`Some`], - /// otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// [`other`]: Error::other - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// if let Some(inner_err) = err.into_inner() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use = "`self` will be dropped if the result is not used"] - #[inline] - pub fn into_inner(self) -> Option> { - match self.repr.into_data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error), - } - } - - /// Attempts to downcast the custom boxed error to `E`. - /// - /// If this [`Error`] contains a custom boxed error, - /// then it would attempt downcasting on the boxed error, - /// otherwise it will return [`Err`]. - /// - /// If the custom boxed error has the same type as `E`, it will return [`Ok`], - /// otherwise it will also return [`Err`]. - /// - /// This method is meant to be a convenience routine for calling - /// `Box::downcast` on the custom boxed error, returned by - /// [`Error::into_inner`]. - /// - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// use std::io; - /// use std::error::Error; - /// - /// #[derive(Debug)] - /// enum E { - /// Io(io::Error), - /// SomeOtherVariant, - /// } - /// - /// impl fmt::Display for E { - /// // ... - /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// # todo!() - /// # } - /// } - /// impl Error for E {} - /// - /// impl From for E { - /// fn from(err: io::Error) -> E { - /// err.downcast::() - /// .unwrap_or_else(E::Io) - /// } - /// } - /// - /// impl From for io::Error { - /// fn from(err: E) -> io::Error { - /// match err { - /// E::Io(io_error) => io_error, - /// e => io::Error::new(io::ErrorKind::Other, e), - /// } - /// } - /// } - /// - /// # fn main() { - /// let e = E::SomeOtherVariant; - /// // Convert it to an io::Error - /// let io_error = io::Error::from(e); - /// // Cast it back to the original variant - /// let e = E::from(io_error); - /// assert!(matches!(e, E::SomeOtherVariant)); - /// - /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); - /// // Convert it to E - /// let e = E::from(io_error); - /// // Cast it back to the original variant - /// let io_error = io::Error::from(e); - /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); - /// assert!(io_error.get_ref().is_none()); - /// assert!(io_error.raw_os_error().is_none()); - /// # } - /// ``` - #[stable(feature = "io_error_downcast", since = "1.79.0")] - pub fn downcast(self) -> result::Result - where - E: error::Error + Send + Sync + 'static, - { - if let ErrorData::Custom(c) = self.repr.data() - && c.error.is::() - { - if let ErrorData::Custom(b) = self.repr.into_data() - && let Ok(err) = b.error.downcast::() - { - Ok(*err) - } else { - // Safety: We have just checked that the condition is true - unsafe { crate::hint::unreachable_unchecked() } - } - } else { - Err(self) - } - } - - /// Returns the corresponding [`ErrorKind`] for this error. - /// - /// This may be a value set by Rust code constructing custom `io::Error`s, - /// or if this `io::Error` was sourced from the operating system, - /// it will be a value inferred from the system's error encoding. - /// See [`last_os_error`] for more details. - /// - /// [`last_os_error`]: Error::last_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// println!("{:?}", err.kind()); - /// } - /// - /// fn main() { - /// // As no error has (visibly) occurred, this may print anything! - /// // It likely prints a placeholder for unidentified (non-)errors. - /// print_error(Error::last_os_error()); - /// // Will print "AddrInUse". - /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn kind(&self) -> ErrorKind { - match self.repr.data() { - ErrorData::Os(code) => sys::io::decode_error_kind(code), - ErrorData::Custom(c) => c.kind, - ErrorData::Simple(kind) => kind, - ErrorData::SimpleMessage(m) => m.kind, - } - } - - #[inline] - pub(crate) fn is_interrupted(&self) -> bool { - match self.repr.data() { - ErrorData::Os(code) => sys::io::is_interrupted(code), - ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, - ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, - ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, - } - } -} - -impl fmt::Debug for Repr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.data() { - ErrorData::Os(code) => fmt - .debug_struct("Os") - .field("code", &code) - .field("kind", &sys::io::decode_error_kind(code)) - .field("message", &sys::io::error_string(code)) - .finish(), - ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), - ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), - ErrorData::SimpleMessage(msg) => fmt - .debug_struct("Error") - .field("kind", &msg.kind) - .field("message", &msg.message) - .finish(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.repr.data() { - ErrorData::Os(code) => { - let detail = sys::io::error_string(code); - write!(fmt, "{detail} (os error {code})") - } - ErrorData::Custom(ref c) => c.error.fmt(fmt), - ErrorData::Simple(kind) => kind.fmt(fmt), - ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for Error { - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn error::Error> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.cause(), - } - } - - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.source(), - } - } -} - -fn _assert_error_is_sync_send() { - fn _is_sync_send() {} - _is_sync_send::(); } diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index a8eef06381dae..a3a2f5830ae91 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,4 +1,4 @@ -use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; +use crate::io::{Error, ErrorKind, const_error}; use crate::sys::io::{decode_error_kind, error_string}; use crate::{assert_matches, error, fmt}; @@ -12,12 +12,7 @@ fn test_debug_error() { let code = 6; let msg = error_string(code); let kind = decode_error_kind(code); - let err = Error { - repr: Repr::new_custom(Box::new(Custom { - kind: ErrorKind::InvalidInput, - error: Box::new(Error { repr: super::Repr::new_os(code) }), - })), - }; + let err = Error::new(ErrorKind::InvalidInput, Error::from_raw_os_error(code)); let expected = format!( "Custom {{ \ kind: InvalidInput, \ @@ -70,10 +65,6 @@ fn test_os_packing() { for code in -20..20 { let e = Error::from_raw_os_error(code); assert_eq!(e.raw_os_error(), Some(code)); - assert_matches!( - e.repr.data(), - ErrorData::Os(c) if c == code, - ); } } @@ -82,28 +73,17 @@ fn test_errorkind_packing() { assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); - // Check that the innards look like what we want. - assert_matches!( - Error::from(ErrorKind::OutOfMemory).repr.data(), - ErrorData::Simple(ErrorKind::OutOfMemory), - ); } #[test] fn test_simple_message_packing() { - use super::ErrorKind::*; - use super::SimpleMessage; + use ErrorKind::*; macro_rules! check_simple_msg { ($err:expr, $kind:ident, $msg:literal) => {{ let e = &$err; // Check that the public api is right. assert_eq!(e.kind(), $kind); assert!(format!("{e:?}").contains($msg)); - // and we got what we expected - assert_matches!( - e.repr.data(), - ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg }) - ); }}; } @@ -128,14 +108,11 @@ impl fmt::Display for Bojji { #[test] fn test_custom_error_packing() { - use super::Custom; let test = Error::new(ErrorKind::Uncategorized, Bojji(true)); + assert_eq!(test.kind(), ErrorKind::Uncategorized); assert_matches!( - test.repr.data(), - ErrorData::Custom(Custom { - kind: ErrorKind::Uncategorized, - error, - }) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), + test.get_ref(), + Some(error) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), ); } @@ -181,11 +158,11 @@ fn test_std_io_error_downcast() { assert_eq!(kind, io_error.kind()); // Case 5: simple message - const SIMPLE_MESSAGE: SimpleMessage = - SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" }; - let io_error = Error::from_static_message(&SIMPLE_MESSAGE); + const KIND: ErrorKind = ErrorKind::Other; + const MESSAGE: &str = "simple message error test"; + let io_error = const_error!(KIND, MESSAGE); let io_error = io_error.downcast::().unwrap_err(); - assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); - assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); + assert_eq!(KIND, io_error.kind()); + assert_eq!(MESSAGE, format!("{io_error}")); } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 4a250281dea6c..dc623a3e82c2c 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,23 +297,27 @@ #[cfg(test)] mod tests; -#[unstable(feature = "read_buf", issue = "78485")] -pub use core::io::{BorrowedBuf, BorrowedCursor}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; -#[stable(feature = "iovec", since = "1.36.0")] -pub use core::io::{IoSlice, IoSliceMut}; use core::slice::memchr; -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -pub use self::buffered::WriterPanicked; +use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use self::error::RawOsError; +pub use alloc_crate::io::RawOsError; #[doc(hidden)] #[unstable(feature = "io_const_error_internals", issue = "none")] -pub use self::error::SimpleMessage; +pub use alloc_crate::io::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] -pub use self::error::const_error; +pub use alloc_crate::io::const_error; +#[unstable(feature = "read_buf", issue = "78485")] +pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::io::{ + Chain, Empty, Error, ErrorKind, Repeat, Result, Sink, Take, empty, repeat, sink, +}; +#[stable(feature = "iovec", since = "1.36.0")] +pub use alloc_crate::io::{IoSlice, IoSliceMut}; + +#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] +pub use self::buffered::WriterPanicked; #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] @@ -330,7 +334,6 @@ pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, cursor::Cursor, - error::{Error, ErrorKind, Result}, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c9e884d89c85e..afa30c8c00f27 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -245,8 +245,7 @@ #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] @@ -355,6 +354,7 @@ #![feature(int_from_ascii)] #![feature(io_error_inprogress)] #![feature(io_error_more)] +#![feature(io_error_too_many_open_files)] #![feature(io_error_uncategorized)] #![feature(io_slice_as_bytes)] #![feature(ip)] @@ -390,6 +390,7 @@ // // Library features (alloc): // tidy-alphabetical-start +#![feature(alloc_io)] #![feature(allocator_api)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] @@ -725,13 +726,7 @@ pub mod alloc; mod panicking; #[path = "../../backtrace/src/lib.rs"] -#[allow( - dead_code, - unused_attributes, - fuzzy_provenance_casts, - lossy_provenance_casts, - unsafe_op_in_unsafe_fn -)] +#[allow(dead_code, unused_attributes, implicit_provenance_casts, unsafe_op_in_unsafe_fn)] mod backtrace_rs; #[stable(feature = "cfg_select", since = "1.95.0")] @@ -777,20 +772,23 @@ pub mod from { pub use core::from::From; } -// Include a number of private modules that exist solely to provide -// the rustdoc documentation for primitive types. Using `include!` -// because rustdoc only looks for these modules at the crate level. -include!("../../core/src/primitive_docs.rs"); +// We include the following files here *again* (they are already included in libcore) +// so that they show up in search results for the std crate, and to avoid breaking +// existing links: + +// documentation for built-in attributes. Using `include!` because rustdoc +// only looks for these modules at the crate level. +include!("../../core/src/attribute_docs.rs"); // Include a number of private modules that exist solely to provide // the rustdoc documentation for the existing keywords. Using `include!` // because rustdoc only looks for these modules at the crate level. -include!("keyword_docs.rs"); +include!("../../core/src/keyword_docs.rs"); -// Include private modules that exist solely to provide rustdoc -// documentation for built-in attributes. Using `include!` because rustdoc -// only looks for these modules at the crate level. -include!("attribute_docs.rs"); +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("../../core/src/primitive_docs.rs"); // This is required to avoid an unstable error when `restricted-std` is not // enabled. The use of #![feature(restricted_std)] in rustc-std-workspace-std diff --git a/library/std/src/net/ip_addr/tests.rs b/library/std/src/net/ip_addr/tests.rs index 7bed6f8a0f523..666146319d161 100644 --- a/library/std/src/net/ip_addr/tests.rs +++ b/library/std/src/net/ip_addr/tests.rs @@ -1,5 +1,5 @@ use crate::net::Ipv4Addr; -use crate::net::test::{sa4, tsa}; +use crate::net::tests::{sa4, tsa}; #[test] fn to_socket_addr_socketaddr() { diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 3e4447eb33f2a..2a8b0f8ca9aad 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -43,7 +43,7 @@ mod ip_addr; mod socket_addr; mod tcp; #[cfg(test)] -pub(crate) mod test; +pub(crate) mod tests; mod udp; /// Possible values which can be passed to the [`TcpStream::shutdown`] method. diff --git a/library/std/src/net/socket_addr/tests.rs b/library/std/src/net/socket_addr/tests.rs index 6a065cfba2105..2f3e3add5c22d 100644 --- a/library/std/src/net/socket_addr/tests.rs +++ b/library/std/src/net/socket_addr/tests.rs @@ -1,4 +1,4 @@ -use crate::net::test::{sa4, sa6, tsa}; +use crate::net::tests::{sa4, sa6, tsa}; use crate::net::*; #[test] diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 8a3a7ee7c5cba..cada78a0d55ad 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -3,7 +3,7 @@ use rand::Rng; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; -use crate::net::test::{LOCALHOST_IP4, LOCALHOST_IP6}; +use crate::net::tests::{LOCALHOST_IP4, LOCALHOST_IP6}; use crate::net::*; use crate::sync::mpsc::channel; use crate::time::{Duration, Instant}; @@ -733,7 +733,13 @@ fn debug() { // no longer has rounding errors. // VxWorks ignores SO_SNDTIMEO. #[cfg_attr( - any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), + any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "vxworks", + target_os = "nto", + target_os = "qnx" + ), ignore )] #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 diff --git a/library/std/src/net/test.rs b/library/std/src/net/tests.rs similarity index 78% rename from library/std/src/net/test.rs rename to library/std/src/net/tests.rs index 5002bb5c8e083..cb1c1ca36b124 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/tests.rs @@ -36,3 +36,14 @@ pub fn compare_ignore_zoneid(a: &SocketAddr, b: &SocketAddr) -> bool { _ => a == b, } } + +#[test] +fn hostname_smoketest() { + // Just a smoke test to ensure it can be called. + let name = crate::net::hostname(); + if cfg!(any(all(windows, not(target_vendor = "win7")), unix)) { + // At least on Windows and Unix, this should succeed. + // The `win7` Windows targets do not support it yet though. + name.unwrap(); + } +} diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index ede0240fbda57..eeb6afdb072eb 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -1,5 +1,5 @@ use crate::io::ErrorKind; -use crate::net::test::{LOCALHOST_IP4, LOCALHOST_IP6, compare_ignore_zoneid}; +use crate::net::tests::{LOCALHOST_IP4, LOCALHOST_IP6, compare_ignore_zoneid}; use crate::net::*; use crate::sync::mpsc::channel; use crate::thread; @@ -188,7 +188,13 @@ fn debug() { // no longer has rounding errors. // VxWorks ignores SO_SNDTIMEO. #[cfg_attr( - any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), + any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "vxworks", + target_os = "nto", + target_os = "qnx" + ), ignore )] #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 76374402be4b3..a7ffd87d84d93 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -6,121 +6,83 @@ pub mod raw; -// The code below could be written clearer using `cfg_if!`. However, the items below are -// publicly exported by `std` and external tools can have trouble analysing them because of the use -// of a macro that is not vendored by Rust and included in the toolchain. -// See https://github.com/rust-analyzer/rust-analyzer/issues/6038. +// # Important platforms -// On certain platforms right now the "main modules" modules that are -// documented don't compile (missing things in `libc` which is empty), -// so just omit them with an empty module and add the "unstable" attribute. - -// darwin, unix, linux, wasi and windows are handled a bit differently. -#[cfg(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -))] -#[unstable(issue = "none", feature = "std_internals")] -pub mod darwin {} -#[cfg(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -))] -#[unstable(issue = "none", feature = "std_internals")] -pub mod unix {} -#[cfg(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -))] -#[unstable(issue = "none", feature = "std_internals")] -pub mod linux {} -#[cfg(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -))] -#[unstable(issue = "none", feature = "std_internals")] -pub mod wasi {} -#[cfg(all( - doc, +// We always want to show documentation for the most important platforms, +// so these are handled specially here. +// +// FIXME: On certain platforms compilation errors (due to empty `libc`), +// prevent this, so we substitute an unstable empty module. +#[cfg(doc)] +cfg_select! { any( all(target_arch = "wasm32", not(target_os = "wasi")), all(target_vendor = "fortanix", target_env = "sgx") - ) -))] -#[unstable(issue = "none", feature = "std_internals")] -pub mod windows {} + ) => { + #[unstable(issue = "none", feature = "std_internals")] + pub mod darwin {} -// darwin -#[cfg(not(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -)))] -#[cfg(any(target_vendor = "apple", doc))] -pub mod darwin; + #[unstable(issue = "none", feature = "std_internals")] + pub mod unix {} -// unix -#[cfg(not(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -)))] -#[cfg(all(not(target_os = "hermit"), any(unix, doc)))] -pub mod unix; + #[unstable(issue = "none", feature = "std_internals")] + pub mod linux {} -// linux -#[cfg(not(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -)))] -#[cfg(any(target_os = "linux", doc))] -pub mod linux; + #[unstable(issue = "none", feature = "std_internals")] + pub mod wasi {} -// wasi -#[cfg(not(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -)))] -#[cfg(any(target_os = "wasi", any(target_env = "p1", target_env = "p2"), doc))] -pub mod wasi; + #[unstable(issue = "none", feature = "std_internals")] + pub mod windows {} + } + _ => { + // important platforms + pub mod darwin; + pub mod linux; + pub mod unix; + pub mod wasi; + pub mod wasip2; + pub mod windows; + } +} +#[cfg(not(doc))] // to prevent double module declarations +cfg_select! { + target_family = "unix" => { + pub mod unix; + #[cfg(target_vendor = "apple")] + pub mod darwin; + #[cfg(target_os = "linux")] + pub mod linux; + } + target_family = "wasm" => { + #[cfg(any(target_env = "p1", target_env = "p2"))] + pub mod wasi; + #[cfg(target_env = "p2")] + pub mod wasip2; + } + target_family = "windows" => { + pub mod windows; + } + _ => { /* handled below */ } +} -#[cfg(any(all(target_os = "wasi", target_env = "p2"), doc))] -pub mod wasip2; +// # Special modules -// windows -#[cfg(not(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -)))] -#[cfg(any(windows, doc))] -pub mod windows; +#[cfg(any( + unix, + target_os = "hermit", + target_os = "trusty", + target_os = "wasi", + target_os = "motor", + doc +))] +pub mod fd; + +#[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] +mod net; + +// # Ordinary platforms +// `cfg(doc)` not handled specially -// Others. #[cfg(target_os = "aix")] pub mod aix; #[cfg(target_os = "android")] @@ -159,7 +121,7 @@ pub mod macos; pub mod motor; #[cfg(target_os = "netbsd")] pub mod netbsd; -#[cfg(target_os = "nto")] +#[cfg(any(target_os = "nto", target_os = "qnx"))] pub mod nto; #[cfg(target_os = "nuttx")] pub mod nuttx; @@ -183,16 +145,3 @@ pub mod vita; pub mod vxworks; #[cfg(target_os = "xous")] pub mod xous; - -#[cfg(any( - unix, - target_os = "hermit", - target_os = "trusty", - target_os = "wasi", - target_os = "motor", - doc -))] -pub mod fd; - -#[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] -mod net; diff --git a/library/std/src/os/net/linux_ext/tests.rs b/library/std/src/os/net/linux_ext/tests.rs index 02839ee6a2aad..a63524e63b6d8 100644 --- a/library/std/src/os/net/linux_ext/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -1,6 +1,6 @@ #[test] fn quickack() { - use crate::net::test::LOCALHOST_IP4; + use crate::net::tests::LOCALHOST_IP4; use crate::net::{TcpListener, TcpStream}; use crate::os::net::linux_ext::tcp::TcpStreamExt; @@ -29,7 +29,7 @@ fn quickack() { #[test] #[cfg(target_os = "linux")] fn deferaccept() { - use crate::net::test::LOCALHOST_IP4; + use crate::net::tests::LOCALHOST_IP4; use crate::net::{TcpListener, TcpStream}; use crate::os::net::linux_ext::tcp::TcpStreamExt; use crate::time::Duration; diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index e3f15a00a0944..25aa3bf7893f4 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -69,7 +69,7 @@ mod platform { pub use crate::os::linux::*; #[cfg(target_os = "netbsd")] pub use crate::os::netbsd::*; - #[cfg(target_os = "nto")] + #[cfg(any(target_os = "nto", target_os = "qnx"))] pub use crate::os::nto::*; #[cfg(target_os = "nuttx")] pub use crate::os::nuttx::*; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 6876446c25c5d..e7bcd70140d67 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -9,6 +9,7 @@ target_os = "illumos", target_os = "haiku", target_os = "nto", + target_os = "qnx", target_os = "cygwin" ))] use libc::MSG_NOSIGNAL; @@ -36,6 +37,7 @@ use crate::{fmt, io}; target_os = "illumos", target_os = "haiku", target_os = "nto", + target_os = "qnx", target_os = "cygwin" )))] const MSG_NOSIGNAL: core::ffi::c_int = 0x0; diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index a44b23a77d2d4..137088dd832f7 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -20,6 +20,7 @@ mod tests; target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_os = "qnx", target_vendor = "apple", target_os = "cygwin" ))] @@ -44,6 +45,7 @@ pub use self::stream::*; target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_os = "qnx", target_vendor = "apple", target_os = "cygwin", ))] diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index a4218bd4da62c..4dfa06d74ce7e 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -6,7 +6,7 @@ cfg_select! { target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", target_os = "haiku", target_os = "nto", - target_os = "cygwin", + target_os = "qnx", target_os = "cygwin", ) => { use libc::MSG_NOSIGNAL; } @@ -26,6 +26,7 @@ use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_wi target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_os = "qnx", target_vendor = "apple", target_os = "cygwin" ))] diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index d88c97113efeb..f703fc6d54c02 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -176,7 +176,7 @@ fn long_path() { } #[test] -#[cfg(not(target_os = "nto"))] +#[cfg(not(any(target_os = "nto", target_os = "qnx")))] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets #[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake #[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")] diff --git a/library/std/src/os/unix/net/ucred.rs b/library/std/src/os/unix/net/ucred.rs index 1395d2ef4be3c..36b67cb1163d8 100644 --- a/library/std/src/os/unix/net/ucred.rs +++ b/library/std/src/os/unix/net/ucred.rs @@ -30,7 +30,8 @@ pub(super) use self::impl_apple::peer_cred; target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "nto" + target_os = "nto", + target_os = "qnx" ))] pub(super) use self::impl_bsd::peer_cred; #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] @@ -79,6 +80,7 @@ mod impl_linux { target_os = "openbsd", target_os = "netbsd", target_os = "nto", + target_os = "qnx", ))] mod impl_bsd { use super::UCred; diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index b0a41502614f6..dcec7b2a1077c 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -7,6 +7,8 @@ use crate::ffi::OsStr; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; +#[cfg(doc)] +use crate::process::{ExitStatus, ExitStatusError}; use crate::sys::process::ChildPipe; use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, process, sys}; @@ -16,7 +18,7 @@ cfg_select! { type UserId = u16; type GroupId = u16; } - target_os = "nto" => { + any(target_os = "nto", target_os = "qnx") => { // Both IDs are signed, see `sys/target_nto.h` of the QNX Neutrino SDP. // Only positive values should be used, see e.g. // https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/s/setuid.html @@ -102,8 +104,8 @@ pub impl(self) trait CommandExt { /// [POSIX fork() specification]: /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html /// [`std::env`]: mod@crate::env - /// [`Error::new`]: crate::io::Error::new - /// [`Error::other`]: crate::io::Error::other + /// [`Error::new`]: ../../../io/struct.Error.html#method.new + /// [`Error::other`]: ../../../io/struct.Error.html#method.other #[stable(feature = "process_pre_exec", since = "1.34.0")] unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command where @@ -272,35 +274,105 @@ impl CommandExt for process::Command { } } -/// Unix-specific extensions to [`process::ExitStatus`] and -/// [`ExitStatusError`](process::ExitStatusError). +/// Unix-specific extensions to [`ExitStatus`] and [`ExitStatusError`]. /// -/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as +/// On Unix, [`ExitStatus`] **does not necessarily represent an exit status**, as /// passed to the `_exit` system call or returned by -/// [`ExitStatus::code()`](crate::process::ExitStatus::code). It represents **any wait status** -/// as returned by one of the `wait` family of system +/// [`ExitStatus::code()`](ExitStatus::code). It represents **any wait status** +/// as returned by one of the [`wait`] family of system /// calls. /// -/// A Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but can also +/// A Unix wait status (a Rust [`ExitStatus`]) can represent a Unix exit status, but can also /// represent other kinds of process event. +/// +/// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "rust1", since = "1.0.0")] pub impl(self) trait ExitStatusExt { - /// Creates a new `ExitStatus` or `ExitStatusError` from the raw underlying integer status - /// value from `wait` + /// Creates a new [`ExitStatus`] or [`ExitStatusError`] from the raw underlying integer status + /// value from [`wait`]. /// /// The value should be a **wait status, not an exit status**. /// + /// # Example + /// + /// A signal-terminated [`wait`] status carries the signal number, which [`ExitStatus::signal`] + /// recovers using the platform's [`WTERMSIG`][`wait`] macro. Note that the bit layout of a + /// wait status is **not** specified by POSIX and is platform-specific. By convention on most + /// Unix platforms, the signal number occupies the low 7 bits with the exit-code byte left + /// zero, so a bare signal number between 1 and 126 is treated as a signal-terminated wait + /// status. The following example relies on that convention and is therefore not guaranteed to + /// hold on every target: + /// + /// ``` + /// # if cfg!(target_os = "fuchsia") { return; } + /// use std::os::unix::process::ExitStatusExt; + /// use std::process::ExitStatus; + /// + /// let signal = 15; // SIGTERM + /// assert!(signal > 0 && signal < 0x7f, "not a valid Unix termination signal: {signal}"); + /// + /// let status = ExitStatus::from_raw(signal); + /// assert!(!status.success()); + /// assert_eq!(status.code(), None); + /// assert_eq!(status.signal(), Some(15)); + /// ``` + /// + /// Generating an [`ExitStatus`] with a given exit code (0-255) is system-dependent. + /// The value returned by [`ExitStatus::code`] is specified to come from applying the + /// [`WEXITSTATUS`][`wait`] macro, but there is no POSIX-specified constructor and the bit + /// layout is left unspecified. By near-universal convention every Unix libc stores the + /// 8-bit exit code in bits 8..16, so a status built with `(code & 0xff) << 8` will usually + /// round-trip back to the original exit code: + /// + /// ``` + /// # if cfg!(target_os = "fuchsia") { return; } + /// use std::os::unix::process::ExitStatusExt; + /// use std::process::ExitStatus; + /// + /// let code = 41; + /// let status = ExitStatus::from_raw((code & 0xff) << 8); + /// assert_eq!(status.code(), Some(41)); + /// assert!(!status.success()); + /// ``` + /// /// # Panics /// - /// Panics on an attempt to make an `ExitStatusError` from a wait status of `0`. + /// - `ExitStatusError::from_raw` panics on an attempt to make an [`ExitStatusError`] from a + /// [`wait`] status of `0`. + /// - `ExitStatus::from_raw` always succeeds and never panics. /// - /// Making an `ExitStatus` always succeeds and never panics. + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "exit_status_from", since = "1.12.0")] fn from_raw(raw: i32) -> Self; /// If the process was terminated by a signal, returns that signal. /// - /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`. + /// In other words, if [`WIFSIGNALED`][`wait`], this returns [`WTERMSIG`][`wait`]. For such a status, + /// [`ExitStatus::code`] returns `None`: + /// + /// ``` + /// # if cfg!(target_os = "fuchsia") { return; } + /// use std::os::unix::process::ExitStatusExt; + /// use std::process::ExitStatus; + /// + /// let sigterm = 15; + /// let status = ExitStatus::from_raw(sigterm); + /// assert_eq!(status.code(), None); + /// assert_eq!(status.signal(), Some(sigterm)); + /// ``` + /// + /// A process that receives a signal may catch and handle it, then exit normally with an + /// exit code. When that happens, `signal` returns `None`. + /// + /// Rust does not pass commands through a shell, such as `bash` and `sh`, but it + /// is possible to do so manually. When invoking a shell, the signal value indicates whether + /// the top-level shell itself received a terminating signal. If instead a command *within* + /// an invoked shell receives a terminating signal, many shells convert the signal number + /// into an exit code by adding 128. For example, a command run under `sh` that receives a + /// [`SIGTERM`] canonically causes the shell to report an exit code of `15 + 128`, i.e. `143`. + /// + /// [`SIGTERM`]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/kill.html + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "rust1", since = "1.0.0")] fn signal(&self) -> Option; @@ -310,21 +382,27 @@ pub impl(self) trait ExitStatusExt { /// If the process was stopped by a signal, returns that signal. /// - /// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`. This is only possible if the status came from - /// a `wait` system call which was passed `WUNTRACED`, and was then converted into an `ExitStatus`. + /// In other words, if [`WIFSTOPPED`][`wait`], this returns [`WSTOPSIG`][`wait`]. This is only possible if the status came from + /// a [`wait`] system call which was passed [`WUNTRACED`][`wait`], and was then converted into an [`ExitStatus`]. + /// + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "unix_process_wait_more", since = "1.58.0")] fn stopped_signal(&self) -> Option; /// Whether the process was continued from a stopped status. /// - /// Ie, `WIFCONTINUED`. This is only possible if the status came from a `wait` system call - /// which was passed `WCONTINUED`, and was then converted into an `ExitStatus`. + /// I.e. [`WIFCONTINUED`][`wait`]. This is only possible if the status came from a [`wait`] system call + /// which was passed [`WCONTINUED`][`wait`], and was then converted into an [`ExitStatus`]. + /// + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "unix_process_wait_more", since = "1.58.0")] fn continued(&self) -> bool; - /// Returns the underlying raw `wait` status. + /// Returns the underlying raw [`wait`] status. /// /// The returned integer is a **wait status, not an exit status**. + /// + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "unix_process_wait_more", since = "1.58.0")] fn into_raw(self) -> i32; } diff --git a/library/std/src/os/unix/thread.rs b/library/std/src/os/unix/thread.rs index efb349b18d6b5..c19ccaf742da9 100644 --- a/library/std/src/os/unix/thread.rs +++ b/library/std/src/os/unix/thread.rs @@ -34,12 +34,12 @@ impl JoinHandleExt for JoinHandle { // This is an int2ptr cast on some platforms (e.g., *-musl) where RawPthread // is an integer but libc::pthread_t is a pointer. Exposed provenance is the // safe choice here, but `as` also works when it's int2int or ptr2ptr. - #[allow(lossy_provenance_casts)] + #[allow(implicit_provenance_casts)] fn as_pthread_t(&self) -> RawPthread { self.as_inner().id() as RawPthread } - #[allow(lossy_provenance_casts)] // see above for why + #[allow(implicit_provenance_casts)] // see above for why fn into_pthread_t(self) -> RawPthread { self.into_inner().into_id() as RawPthread } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 28e972925e667..ae1b7eaee8d12 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -75,7 +75,7 @@ impl OwnedSocket { } // FIXME(strict_provenance_magic): we defined RawSocket to be a u64 ;-; - #[allow(fuzzy_provenance_casts)] + #[allow(implicit_provenance_casts)] #[cfg(not(target_vendor = "uwp"))] pub(crate) fn set_no_inherit(&self) -> io::Result<()> { cvt(unsafe { diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 658026a8020f9..e8394feb73f5d 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -41,7 +41,7 @@ pub type PanicInfo<'a> = PanicHookInfo<'a>; #[derive(Debug)] pub struct PanicHookInfo<'a> { payload: &'a (dyn Any + Send), - location: &'a Location<'a>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, } @@ -49,7 +49,7 @@ pub struct PanicHookInfo<'a> { impl<'a> PanicHookInfo<'a> { #[inline] pub(crate) fn new( - location: &'a Location<'a>, + location: &'static Location<'static>, payload: &'a (dyn Any + Send), can_unwind: bool, force_no_backtrace: bool, @@ -160,10 +160,10 @@ impl<'a> PanicHookInfo<'a> { #[must_use] #[inline] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'_>> { + pub fn location(&self) -> Option<&'static Location<'static>> { // NOTE: If this is changed to sometimes return None, // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. - Some(&self.location) + Some(self.location) } /// Returns whether the panic handler is allowed to unwind the stack from diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 6bd3d9a290e44..7db6e93e8bd02 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -415,6 +415,7 @@ pub mod panic_count { // // This also updates thread-local state to keep track of whether a panic // hook is currently executing. + #[must_use = "MustAbort may not be ignored"] pub fn increase(run_panic_hook: bool) -> Option { let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed); if global_count & ALWAYS_ABORT_FLAG != 0 { @@ -775,7 +776,7 @@ fn payload_as_str(payload: &dyn Any) -> &str { #[optimize(size)] fn panic_with_hook( payload: &mut dyn PanicPayload, - location: &Location<'_>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, ) -> ! { @@ -843,7 +844,18 @@ fn panic_with_hook( /// It just forwards the payload to the panic runtime. #[cfg_attr(panic = "immediate-abort", inline)] pub fn resume_unwind(payload: Box) -> ! { - panic_count::increase(false); + if let Some(must_abort) = panic_count::increase(false) { + match must_abort { + panic_count::MustAbort::PanicInHook => { + rtprintpanic!("thread panicked while processing panic. aborting.\n"); + } + panic_count::MustAbort::AlwaysAbort => { + rtprintpanic!("aborting due to panic\n"); + } + } + + crate::process::abort(); + } struct RewrapBox(Box); diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 9f40c01546632..ee7e5f8586700 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -57,9 +57,6 @@ //! //! [`Once`]: crate::sync::Once -// If we are not unwinding, `PoisonError` is uninhabited. -#![cfg_attr(not(panic = "unwind"), expect(unreachable_code))] - #[stable(feature = "rust1", since = "1.0.0")] pub use self::condvar::Condvar; #[unstable(feature = "mapped_lock_guards", issue = "117108")] diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs index 9403059e7c607..8c1e07a787716 100644 --- a/library/std/src/sys/args/sgx.rs +++ b/library/std/src/sys/args/sgx.rs @@ -1,5 +1,4 @@ -// FIXME: this module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] +#![allow(implicit_provenance_casts)] // FIXME: this module systematically confuses pointers and integers use crate::ffi::OsString; use crate::num::NonZero; diff --git a/library/std/src/sys/args/unix.rs b/library/std/src/sys/args/unix.rs index 7a592c2b079dd..197931d8cc8d2 100644 --- a/library/std/src/sys/args/unix.rs +++ b/library/std/src/sys/args/unix.rs @@ -81,6 +81,7 @@ pub fn args() -> Args { target_os = "horizon", target_os = "aix", target_os = "nto", + target_os = "qnx", target_os = "hurd", target_os = "rtems", target_os = "nuttx", diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs index 0c19fcc848f4d..b45acfe7c1ba7 100644 --- a/library/std/src/sys/env/sgx.rs +++ b/library/std/src/sys/env/sgx.rs @@ -1,55 +1,35 @@ -// FIXME: this module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] - pub use super::common::Env; use crate::collections::HashMap; use crate::ffi::{OsStr, OsString}; use crate::io; -use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; +use crate::sync::{Mutex, OnceLock}; + +type EnvStore = Mutex>; // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")] -static ENV: Atomic = AtomicUsize::new(0); -// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests -#[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")] -static ENV_INIT: Once = Once::new(); -type EnvStore = Mutex>; - -fn get_env_store() -> Option<&'static EnvStore> { - unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() } -} - -fn create_env_store() -> &'static EnvStore { - ENV_INIT.call_once(|| { - ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed) - }); - unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) } -} +static ENV: OnceLock = OnceLock::new(); pub fn env() -> Env { - let clone_to_vec = |map: &HashMap| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let env = get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default(); + let env = ENV + .get() + .map(|env| env.lock().unwrap().iter().map(|(k, v)| (k.clone(), v.clone())).collect()) + .unwrap_or_default(); Env::new(env) } pub fn getenv(k: &OsStr) -> Option { - get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) + ENV.get().and_then(|s| s.lock().unwrap().get(k).cloned()) } pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - create_env_store().lock().unwrap().insert(k, v); + ENV.get_or_init(|| EnvStore::default()).lock().unwrap().insert(k.to_owned(), v.to_owned()); Ok(()) } pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - if let Some(env) = get_env_store() { + if let Some(env) = ENV.get() { env.lock().unwrap().remove(k); } Ok(()) diff --git a/library/std/src/sys/env_consts.rs b/library/std/src/sys/env_consts.rs index 573f540483b1a..b14160cf4ad32 100644 --- a/library/std/src/sys/env_consts.rs +++ b/library/std/src/sys/env_consts.rs @@ -224,6 +224,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "qnx")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "qnx"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "nuttx")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index edb2ffc49995e..392e33a77dc50 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -76,6 +76,7 @@ const fn max_iov() -> usize { target_os = "emscripten", target_os = "linux", target_os = "nto", + target_os = "qnx", ))] const fn max_iov() -> usize { libc::UIO_MAXIOV as usize @@ -91,6 +92,7 @@ const fn max_iov() -> usize { target_os = "netbsd", target_os = "nuttx", target_os = "nto", + target_os = "qnx", target_os = "openbsd", target_os = "horizon", target_os = "vita", @@ -561,6 +563,7 @@ impl FileDesc { target_os = "redox", target_os = "vxworks", target_os = "nto", + target_os = "qnx", target_os = "wasi", )))] pub fn set_cloexec(&self) -> io::Result<()> { @@ -585,6 +588,7 @@ impl FileDesc { target_os = "redox", target_os = "vxworks", target_os = "nto", + target_os = "qnx", target_os = "wasi", ))] pub fn set_cloexec(&self) -> io::Result<()> { diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 3152a22534f6c..8fee56348c071 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -28,6 +28,7 @@ use libc::fstatat64; target_os = "fuchsia", target_os = "illumos", target_os = "nto", + target_os = "qnx", target_os = "redox", target_os = "solaris", target_os = "vita", @@ -45,6 +46,7 @@ use libc::readdir as readdir64; target_os = "l4re", target_os = "linux", target_os = "nto", + target_os = "qnx", target_os = "redox", target_os = "solaris", target_os = "vita", @@ -282,6 +284,7 @@ cfg_select! { target_os = "horizon", target_os = "vita", target_os = "nto", + target_os = "qnx", target_os = "vxworks", ) => { pub use crate::sys::fs::common::Dir; @@ -410,6 +413,7 @@ fn get_path_from_fd(fd: c_int) -> Option { target_os = "illumos", target_os = "linux", target_os = "nto", + target_os = "qnx", target_os = "redox", target_os = "solaris", target_os = "vita", @@ -436,6 +440,7 @@ pub struct DirEntry { target_os = "illumos", target_os = "linux", target_os = "nto", + target_os = "qnx", target_os = "redox", target_os = "solaris", target_os = "vita", @@ -448,6 +453,7 @@ struct dirent64_min { target_os = "illumos", target_os = "aix", target_os = "nto", + target_os = "qnx", target_os = "vita", )))] d_type: u8, @@ -462,6 +468,7 @@ struct dirent64_min { target_os = "illumos", target_os = "linux", target_os = "nto", + target_os = "qnx", target_os = "redox", target_os = "solaris", target_os = "vita", @@ -611,7 +618,13 @@ impl FileAttr { } } -#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix", target_os = "wasi")))] +#[cfg(not(any( + target_os = "netbsd", + target_os = "nto", + target_os = "qnx", + target_os = "aix", + target_os = "wasi" +)))] impl FileAttr { #[cfg(not(any( target_os = "vxworks", @@ -736,7 +749,7 @@ impl FileAttr { } } -#[cfg(any(target_os = "nto", target_os = "wasi"))] +#[cfg(any(target_os = "nto", target_os = "qnx", target_os = "wasi"))] impl FileAttr { pub fn modified(&self) -> io::Result { SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec.into()) @@ -854,6 +867,7 @@ impl Iterator for ReadDir { target_os = "illumos", target_os = "linux", target_os = "nto", + target_os = "qnx", target_os = "redox", target_os = "solaris", target_os = "vita", @@ -927,6 +941,7 @@ impl Iterator for ReadDir { target_os = "illumos", target_os = "aix", target_os = "nto", + target_os = "qnx", )))] d_type: (*entry_ptr).d_type as u8, }; @@ -952,6 +967,7 @@ impl Iterator for ReadDir { target_os = "illumos", target_os = "linux", target_os = "nto", + target_os = "qnx", target_os = "redox", target_os = "solaris", target_os = "vita", @@ -1015,6 +1031,7 @@ impl Drop for DirStream { miri, target_os = "redox", target_os = "nto", + target_os = "qnx", target_os = "vita", target_os = "hurd", target_os = "espidf", @@ -1103,6 +1120,7 @@ impl DirEntry { target_os = "vxworks", target_os = "aix", target_os = "nto", + target_os = "qnx", target_os = "vita", ))] pub fn file_type(&self) -> io::Result { @@ -1116,6 +1134,7 @@ impl DirEntry { target_os = "vxworks", target_os = "aix", target_os = "nto", + target_os = "qnx", target_os = "vita", )))] pub fn file_type(&self) -> io::Result { @@ -1146,6 +1165,7 @@ impl DirEntry { target_os = "l4re", target_os = "linux", target_os = "nto", + target_os = "qnx", target_os = "redox", target_os = "rtems", target_os = "solaris", @@ -1205,6 +1225,7 @@ impl DirEntry { target_os = "redox", target_os = "aix", target_os = "nto", + target_os = "qnx", target_os = "vita", target_os = "hurd", target_os = "wasi", @@ -1222,6 +1243,7 @@ impl DirEntry { target_os = "redox", target_os = "aix", target_os = "nto", + target_os = "qnx", target_os = "vita", target_os = "hurd", target_os = "wasi", @@ -1419,6 +1441,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_os = "qnx", target_os = "hurd", ))] unsafe fn os_datasync(fd: c_int) -> c_int { @@ -1433,6 +1456,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_os = "qnx", target_os = "hurd", target_vendor = "apple", )))] @@ -2405,6 +2429,7 @@ pub use remove_dir_impl::remove_dir_all; target_os = "horizon", target_os = "vita", target_os = "nto", + target_os = "qnx", target_os = "vxworks", miri ))] @@ -2419,6 +2444,7 @@ mod remove_dir_impl { target_os = "horizon", target_os = "vita", target_os = "nto", + target_os = "qnx", target_os = "vxworks", miri )))] diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 9de9570cb5b24..d13b28d2abffa 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -612,7 +612,7 @@ fn map_fresult(fresult: vex_sdk::FRESULT) -> io::Result<()> { Err(io::const_error!(io::ErrorKind::OutOfMemory, "not enough memory for the operation")) } vex_sdk::FRESULT::FR_TOO_MANY_OPEN_FILES => Err(io::const_error!( - io::ErrorKind::Uncategorized, + io::ErrorKind::TooManyOpenFiles, "maximum number of open files has been reached", )), vex_sdk::FRESULT::FR_INVALID_PARAMETER => { diff --git a/library/std/src/sys/io/error/unix.rs b/library/std/src/sys/io/error/unix.rs index b10343b2752c2..85e3c09782ae2 100644 --- a/library/std/src/sys/io/error/unix.rs +++ b/library/std/src/sys/io/error/unix.rs @@ -27,6 +27,7 @@ unsafe extern "C" { )] #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] + #[cfg_attr(target_os = "qnx", link_name = "__get_errno_ptr")] #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")] #[cfg_attr(target_os = "haiku", link_name = "_errnop")] #[cfg_attr(target_os = "aix", link_name = "_Errno")] @@ -137,6 +138,7 @@ pub fn decode_error_kind(errno: i32) -> io::ErrorKind { libc::ETXTBSY => ExecutableFileBusy, libc::EXDEV => CrossesDevices, libc::EINPROGRESS => InProgress, + libc::EMFILE | libc::ENFILE => TooManyOpenFiles, libc::EOPNOTSUPP => Unsupported, libc::EACCES | libc::EPERM => PermissionDenied, diff --git a/library/std/src/sys/io/error/wasi.rs b/library/std/src/sys/io/error/wasi.rs index 719fc0c900adc..3a0fa37e979bc 100644 --- a/library/std/src/sys/io/error/wasi.rs +++ b/library/std/src/sys/io/error/wasi.rs @@ -59,6 +59,7 @@ pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { libc::ETXTBSY => ExecutableFileBusy, libc::EXDEV => CrossesDevices, libc::EINPROGRESS => InProgress, + libc::EMFILE | libc::ENFILE => TooManyOpenFiles, libc::EOPNOTSUPP => Unsupported, libc::EACCES | libc::EPERM => PermissionDenied, libc::EWOULDBLOCK => WouldBlock, diff --git a/library/std/src/sys/io/error/windows.rs b/library/std/src/sys/io/error/windows.rs index 3c2f41a907813..9af11588fe322 100644 --- a/library/std/src/sys/io/error/windows.rs +++ b/library/std/src/sys/io/error/windows.rs @@ -61,6 +61,7 @@ pub fn decode_error_kind(errno: i32) -> io::ErrorKind { c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, c::ERROR_NOT_SAME_DEVICE => return CrossesDevices, c::ERROR_TOO_MANY_LINKS => return TooManyLinks, + c::ERROR_TOO_MANY_OPEN_FILES => return TooManyOpenFiles, c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename, c::ERROR_CANT_RESOLVE_FILENAME => return FilesystemLoop, _ => {} @@ -81,6 +82,7 @@ pub fn decode_error_kind(errno: i32) -> io::ErrorKind { c::WSAENETDOWN => NetworkDown, c::WSAENETUNREACH => NetworkUnreachable, c::WSAEDQUOT => QuotaExceeded, + c::WSAEMFILE => TooManyOpenFiles, // Not a perfect mapping but this error is only returned when writing to // a socket after shutting down the write-end. On Unix targets, EPIPE is // returned in those cases. diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 55c928dc6c5fc..66aa2a804db22 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -46,6 +46,7 @@ cfg_select! { target_os = "haiku", target_os = "l4re", target_os = "nto", + target_os = "qnx", target_os = "nuttx", target_vendor = "apple", ) => { @@ -65,7 +66,8 @@ cfg_select! { target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", - target_os = "haiku", target_os = "nto", + target_os = "haiku", + target_os = "nto", target_os = "qnx", target_os = "cygwin", ) => { use libc::MSG_NOSIGNAL; @@ -80,7 +82,7 @@ cfg_select! { target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", - target_os = "nto", + target_os = "nto", target_os = "qnx", ) => { use crate::ffi::c_uchar; type IpV4MultiCastType = c_uchar; diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 3336dc4cf9233..41850574c96fa 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -76,6 +76,7 @@ impl Socket { target_os = "openbsd", target_os = "cygwin", target_os = "nto", + target_os = "qnx", target_os = "solaris", ) => { // On platforms that support it we pass the SOCK_CLOEXEC @@ -124,6 +125,7 @@ impl Socket { target_os = "openbsd", target_os = "cygwin", target_os = "nto", + target_os = "qnx", ) => { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; diff --git a/library/std/src/sys/net/connection/xous/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs index b139376f59768..0ed15b28c36e1 100644 --- a/library/std/src/sys/net/connection/xous/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs @@ -43,9 +43,8 @@ impl Iterator for LookupHost { return None; } let mut new_addr = [0u8; 16]; - for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)] - .iter() - .zip(new_addr.iter_mut()) + for (src, octet) in + self.data.0[self.offset..(self.offset + 16)].iter().zip(new_addr.iter_mut()) { *octet = *src; } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 7b2c8e5a8024a..6ed6e39c61897 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -3,8 +3,7 @@ //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for Fortanix SGX. #![deny(unsafe_op_in_unsafe_fn)] -// FIXME: this entire module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] +#![allow(implicit_provenance_casts)] // FIXME: this entire module systematically confuses pointers and integers use crate::io; use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index 73768860723d0..51162296ea3fc 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -1,9 +1,9 @@ use super::Mutex; use crate::cell::UnsafeCell; use crate::pin::Pin; -#[cfg(not(target_os = "nto"))] +#[cfg(not(any(target_os = "nto", target_os = "qnx")))] use crate::sys::pal::time::TIMESPEC_MAX; -#[cfg(target_os = "nto")] +#[cfg(any(target_os = "nto", target_os = "qnx"))] use crate::sys::pal::time::TIMESPEC_MAX_CAPPED; use crate::time::Duration; @@ -69,10 +69,10 @@ impl Condvar { let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur); - #[cfg(not(target_os = "nto"))] + #[cfg(not(any(target_os = "nto", target_os = "qnx")))] let timeout = timeout.and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); - #[cfg(target_os = "nto")] + #[cfg(any(target_os = "nto", target_os = "qnx"))] let timeout = timeout.and_then(|t| t.to_timespec_capped()).unwrap_or(TIMESPEC_MAX_CAPPED); let r = unsafe { libc::pthread_cond_timedwait(self.raw(), mutex, &timeout) }; diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 26fb6d3d7cf2d..26296b5d169a1 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -16,7 +16,7 @@ pub const TIMESPEC_MAX: libc::timespec = { // This additional constant is only used when calling // `libc::pthread_cond_timedwait`. -#[cfg(target_os = "nto")] +#[cfg(any(target_os = "nto", target_os = "qnx"))] pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { tv_sec: (u64::MAX / NSEC_PER_SEC) as i64, tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, diff --git a/library/std/src/sys/paths/unix.rs b/library/std/src/sys/paths/unix.rs index e0b1aafda6eb0..36879b7b9bce3 100644 --- a/library/std/src/sys/paths/unix.rs +++ b/library/std/src/sys/paths/unix.rs @@ -269,7 +269,7 @@ pub fn current_exe() -> io::Result { } } -#[cfg(target_os = "nto")] +#[cfg(any(target_os = "nto", target_os = "qnx"))] pub fn current_exe() -> io::Result { let mut e = crate::fs::read("/proc/self/exefile")?; // Current versions of QNX Neutrino provide a null-terminated path. diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 529cdaf72cc6c..55906ed2854f2 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -20,7 +20,7 @@ use crate::sys::process::PidFd; use crate::{fmt, mem, sys}; cfg_select! { - target_os = "nto" => { + any(target_os = "nto", target_os = "qnx") => { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -183,7 +183,12 @@ impl Command { // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any(target_os = "watchos", target_os = "tvos", target_os = "nto")))] + #[cfg(not(any( + target_os = "watchos", + target_os = "tvos", + target_os = "nto", + target_os = "qnx" + )))] unsafe fn do_fork(&mut self) -> Result { cvt(libc::fork()) } @@ -192,7 +197,7 @@ impl Command { // or closed a file descriptor while the fork() was occurring". // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - #[cfg(target_os = "nto")] + #[cfg(any(target_os = "nto", target_os = "qnx"))] unsafe fn do_fork(&mut self) -> Result { use crate::sys::io::errno; @@ -424,6 +429,7 @@ impl Command { all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), target_os = "nto", + target_os = "qnx", target_vendor = "apple", target_os = "cygwin", )))] @@ -443,6 +449,7 @@ impl Command { all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), target_os = "nto", + target_os = "qnx", target_vendor = "apple", target_os = "cygwin", ))] @@ -510,8 +517,8 @@ impl Command { support = SPAWN; } } - Err(e) if e.raw_os_error() == Some(libc::EMFILE) => { - // We're temporarily(?) out of file descriptors. In this case pidfd_spawnp would also fail + Err(e) if matches!(e.raw_os_error(), Some(libc::EMFILE | libc::ENFILE | libc::ENOMEM)) => { + // We're temporarily(?) out of file descriptors or memory. In this case pidfd_spawnp would also fail // Don't update the support flag so we can probe again later. return Err(e) } @@ -550,7 +557,7 @@ impl Command { // or closed a file descriptor while the posix_spawn() was occurring". // Documentation says "... or try calling posix_spawn() again". This is what we do here. // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html - #[cfg(target_os = "nto")] + #[cfg(any(target_os = "nto", target_os = "qnx"))] unsafe fn retrying_libc_posix_spawnp( pid: *mut pid_t, file: *const c_char, @@ -763,9 +770,9 @@ impl Command { let _env_lock = sys::env::env_read_lock(); let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::env::environ() as *const _); - #[cfg(not(target_os = "nto"))] + #[cfg(not(any(target_os = "nto", target_os = "qnx")))] let spawn_fn = libc::posix_spawnp; - #[cfg(target_os = "nto")] + #[cfg(any(target_os = "nto", target_os = "qnx"))] let spawn_fn = retrying_libc_posix_spawnp; #[cfg(target_os = "linux")] @@ -822,7 +829,7 @@ impl Command { envp as *const _, ); - #[cfg(target_os = "nto")] + #[cfg(any(target_os = "nto", target_os = "qnx"))] let spawn_res = spawn_res?; cvt_nz(spawn_res)?; @@ -1202,7 +1209,12 @@ fn signal_string(signal: i32) -> &'static str { ) ))] libc::SIGSTKFLT => " (SIGSTKFLT)", - #[cfg(any(target_os = "linux", target_os = "nto", target_os = "cygwin"))] + #[cfg(any( + target_os = "linux", + target_os = "nto", + target_os = "qnx", + target_os = "cygwin" + ))] libc::SIGPWR => " (SIGPWR)", #[cfg(any( target_os = "freebsd", @@ -1210,6 +1222,7 @@ fn signal_string(signal: i32) -> &'static str { target_os = "openbsd", target_os = "dragonfly", target_os = "nto", + target_os = "qnx", target_vendor = "apple", target_os = "cygwin", ))] diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs index 9b6d16e196ac1..6a4e4f3e670db 100644 --- a/library/std/src/sys/random/linux.rs +++ b/library/std/src/sys/random/linux.rs @@ -123,7 +123,9 @@ fn getrandom(mut bytes: &mut [u8], insecure: bool) { GETRANDOM_AVAILABLE.store(false, Relaxed); break; } - _ => panic!("failed to generate random data"), + other => { + panic!("failed to generate random data: errno={other:?}, flags={flags:?}") + } } } } diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index 12346ef5a2edd..e5a66dc463c6b 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -54,6 +54,7 @@ cfg_select! { target_os = "hurd", target_os = "l4re", target_os = "nto", + target_os = "qnx", ) => { mod unix_legacy; pub use unix_legacy::fill_bytes; diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index eb3fcff05ea9e..e52ffff165beb 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -12,7 +12,12 @@ use crate::mem::{self, DropGuard, ManuallyDrop}; use crate::num::NonZero; #[cfg(all(target_os = "linux", target_env = "gnu"))] use crate::sys::weak::dlsym; -#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] +#[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "nto", + target_os = "qnx", +))] use crate::sys::weak::weak; use crate::thread::ThreadInit; use crate::time::Duration; @@ -392,6 +397,7 @@ pub fn current_os_id() -> Option { target_os = "vxworks", target_os = "cygwin", target_vendor = "apple", + target_os = "netbsd", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -463,7 +469,12 @@ pub fn set_name(name: &CStr) { #[cfg(target_os = "netbsd")] pub fn set_name(name: &CStr) { + // See https://github.com/NetBSD/src/blob/8d40872b4c550a802379f3b9c22a40212d5e149d/lib/libpthread/pthread.h#L281 + // FIXME: move to libc. + const PTHREAD_MAX_NAMELEN_NP: usize = 32; + unsafe { + let name = truncate_cstr::<{ PTHREAD_MAX_NAMELEN_NP }>(name); let res = libc::pthread_setname_np( libc::pthread_self(), c"%s".as_ptr(), diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 98f6b842d135f..6333b32ab8ad4 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -337,6 +337,8 @@ jobs: cargo run --bin=stdarch-gen-loongarch --release -- crates/stdarch-gen-loongarch/lasx.spec git diff --exit-code - name: Check hexagon + env: + STDARCH_GEN_MODE: check run: | cargo run -p stdarch-gen-hexagon --release git diff --exit-code diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index a1c31fa9f0cea..c8cfc21a5a1a1 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -268,6 +268,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -282,13 +298,14 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "getrandom" -version = "0.2.17" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "wasi", + "r-efi 5.3.0", + "wasip2", ] [[package]] @@ -299,7 +316,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "rand_core 0.10.0", "wasip2", "wasip3", @@ -445,6 +462,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "log" version = "0.4.29" @@ -457,6 +480,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "once_cell_polyfill" version = "1.70.2" @@ -529,7 +558,7 @@ checksum = "95c589f335db0f6aaa168a7cd27b1fc6920f5e1470c804f814d9cd6e62a0f70b" dependencies = [ "env_logger 0.11.10", "log", - "rand 0.10.0", + "rand 0.10.1", ] [[package]] @@ -541,6 +570,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" @@ -549,20 +584,19 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core 0.9.5", ] [[package]] name = "rand" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "getrandom 0.4.2", "rand_core 0.10.0", @@ -570,21 +604,21 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core 0.9.5", ] [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.3.4", ] [[package]] @@ -648,6 +682,19 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.23" @@ -787,11 +834,19 @@ dependencies = [ "walkdir", ] +[[package]] +name = "stdarch-gen-common" +version = "0.1.0" +dependencies = [ + "tempfile", +] + [[package]] name = "stdarch-gen-hexagon" version = "0.1.0" dependencies = [ "regex", + "stdarch-gen-common", ] [[package]] @@ -805,7 +860,7 @@ dependencies = [ name = "stdarch-gen-loongarch" version = "0.1.0" dependencies = [ - "rand 0.8.5", + "rand 0.9.4", ] [[package]] @@ -838,7 +893,7 @@ version = "0.0.0" dependencies = [ "core_arch", "quickcheck", - "rand 0.8.5", + "rand 0.9.4", ] [[package]] @@ -864,6 +919,19 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -921,12 +989,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasip2" version = "1.0.2+wasi-0.2.9" diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 0441611f38fd2..8de7a4cfa5221 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -1,13 +1,19 @@ #!/usr/bin/env sh -set -ex - if [ $# -lt 2 ]; then - >&2 echo "Usage: $0 " + >&2 echo "Usage: $0 <..args for \`cargo test\`..>" exit 1 fi -case ${2} in +set -ex + +# Pop both arguments and leave "$@" as containing args to be forwarded to `cargo test` +TARGET="$1" +shift +CC_KIND="$1" +shift + +case ${CC_KIND} in clang) export CC="${CLANG_PATH}" CC_ARG_STYLE=clang @@ -22,7 +28,7 @@ case ${2} in CC_ARG_STYLE=clang ;; *) - >&2 echo "Unknown compiler: ${2}" + >&2 echo "Unknown compiler: ${CC_KIND}" exit 1 ;; esac @@ -35,7 +41,7 @@ echo "PROFILE=${PROFILE}" INTRINSIC_TEST="--manifest-path=crates/intrinsic-test/Cargo.toml" -case ${1} in +case ${TARGET} in aarch64_be*) export CFLAGS="-I${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc/usr/include --sysroot={AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc -Wno-nonportable-vector-initialization" ARCH=aarch64_be @@ -60,24 +66,25 @@ case ${1} in esac -case "${1}" in +case "${TARGET}" in x86_64-unknown-linux-gnu*) env -u CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER \ cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/x86-intel.xml \ --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ - --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ - --target "${1}" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${CC_KIND}.txt" \ + --target "${TARGET}" \ --cc-arg-style "${CC_ARG_STYLE}" ;; *) cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ - --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ - --target "${1}" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${CC_KIND}.txt" \ + --target "${TARGET}" \ --cc-arg-style "${CC_ARG_STYLE}" ;; esac -cargo test --manifest-path=rust_programs/Cargo.toml --target "${1}" --profile "${PROFILE}" --tests +cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" \ + --tests "$@" diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index 8d701d9b88056..1b5b17e538bde 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -11903,7 +11903,7 @@ pub unsafe fn vluti4q_lane_s8(a: int8x16_t, b: uint8x8_t) -> in unsafe extern "unadjusted" { #[cfg_attr( any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.vluti4q.lane.v8i8" + link_name = "llvm.aarch64.neon.vluti4q.lane.v16i8" )] fn _vluti4q_lane_s8(a: int8x16_t, b: uint8x8_t, n: i32) -> int8x16_t; } diff --git a/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs b/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs index 4dcbc9549f115..88e5c0987f34d 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs @@ -2,7 +2,7 @@ use stdarch_test::assert_instr; unsafe extern "unadjusted" { - #[link_name = "llvm.prefetch"] + #[link_name = "llvm.prefetch.p0"] fn prefetch(p: *const i8, rw: i32, loc: i32, ty: i32); } diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs index e1157f3440cfb..ac3070918a025 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs @@ -1844,7 +1844,7 @@ pub fn svadrd_u64base_u64index(bases: svuint64_t, indices: svuint64_t) -> svuint #[cfg_attr(test, assert_instr(and))] pub fn svand_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.and.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.and.z.nxv16i1")] fn _svand_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svand_b_z(pg, op1, op2) } @@ -2936,7 +2936,7 @@ pub fn svasrd_n_s64_z(pg: svbool_t, op1: svint64_t) -> svint64_ #[cfg_attr(test, assert_instr(bic))] pub fn svbic_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.bic.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.bic.z.nxv16i1")] fn _svbic_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svbic_b_z(pg, op1, op2) } @@ -4560,7 +4560,7 @@ pub fn svcmla_lane_f32( unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fcmla.lane.x.nxv4f32" + link_name = "llvm.aarch64.sve.fcmla.lane.nxv4f32" )] fn _svcmla_lane_f32( op1: svfloat32_t, @@ -7658,7 +7658,10 @@ pub fn svcvt_f64_f32_z(pg: svbool_t, op: svfloat32_t) -> svfloat64_t { #[cfg_attr(test, assert_instr(scvtf))] pub fn svcvt_f32_s32_m(inactive: svfloat32_t, pg: svbool_t, op: svint32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i32")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.scvtf.nxv4f32.nxv4i32" + )] fn _svcvt_f32_s32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; } unsafe { _svcvt_f32_s32_m(inactive, pg.sve_into(), op) } @@ -7682,66 +7685,137 @@ pub fn svcvt_f32_s32_z(pg: svbool_t, op: svint32_t) -> svfloat32_t { svcvt_f32_s32_m(svdup_n_f32(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_m)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool_t, op: svuint32_t) -> svfloat32_t { + unsafe extern "unadjusted" { + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.ucvtf.nxv4f32.nxv4i32" + )] + fn _svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; + } + unsafe { _svcvt_f32_u32_m(inactive, pg.sve_into(), op.as_signed()) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_x)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_x(pg: svbool_t, op: svuint32_t) -> svfloat32_t { + unsafe { svcvt_f32_u32_m(transmute_unchecked(op), pg, op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_z)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat32_t { + svcvt_f32_u32_m(svdup_n_f32(0.0), pg, op) +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool_t, op: svint64_t) -> svfloat32_t { +pub fn svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool_t, op: svint64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i64")] - fn _svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool2_t, op: svint64_t) -> svfloat32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.scvtf.nxv2f64.nxv2i64" + )] + fn _svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; } - unsafe { _svcvt_f32_s64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_f64_s64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_x(pg: svbool_t, op: svint64_t) -> svfloat32_t { - unsafe { svcvt_f32_s64_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_f64_s64_x(pg: svbool_t, op: svint64_t) -> svfloat64_t { + unsafe { svcvt_f64_s64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_z(pg: svbool_t, op: svint64_t) -> svfloat32_t { - svcvt_f32_s64_m(svdup_n_f32(0.0), pg, op) +pub fn svcvt_f64_s64_z(pg: svbool_t, op: svint64_t) -> svfloat64_t { + svcvt_f64_s64_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool_t, op: svuint32_t) -> svfloat32_t { +pub fn svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool_t, op: svuint64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.ucvtf.f32i32")] - fn _svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.ucvtf.nxv2f64.nxv2i64" + )] + fn _svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; } - unsafe { _svcvt_f32_u32_m(inactive, pg.sve_into(), op.as_signed()) } + unsafe { _svcvt_f64_u64_m(inactive, pg.sve_into(), op.as_signed()) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_x(pg: svbool_t, op: svuint32_t) -> svfloat32_t { - unsafe { svcvt_f32_u32_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_f64_u64_x(pg: svbool_t, op: svuint64_t) -> svfloat64_t { + unsafe { svcvt_f64_u64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat32_t { - svcvt_f32_u32_m(svdup_n_f32(0.0), pg, op) +pub fn svcvt_f64_u64_z(pg: svbool_t, op: svuint64_t) -> svfloat64_t { + svcvt_f64_u64_m(svdup_n_f64(0.0), pg, op) +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_m)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool_t, op: svint64_t) -> svfloat32_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i64")] + fn _svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool2_t, op: svint64_t) -> svfloat32_t; + } + unsafe { _svcvt_f32_s64_m(inactive, pg.sve_into(), op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_x)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_x(pg: svbool_t, op: svint64_t) -> svfloat32_t { + unsafe { svcvt_f32_s64_m(transmute_unchecked(op), pg, op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_z)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_z(pg: svbool_t, op: svint64_t) -> svfloat32_t { + svcvt_f32_s64_m(svdup_n_f32(0.0), pg, op) } #[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u64]_m)"] @@ -7806,37 +7880,6 @@ pub fn svcvt_f64_s32_z(pg: svbool_t, op: svint32_t) -> svfloat64_t { svcvt_f64_s32_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_m)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool_t, op: svint64_t) -> svfloat64_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f64i64")] - fn _svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; - } - unsafe { _svcvt_f64_s64_m(inactive, pg.sve_into(), op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_x)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_x(pg: svbool_t, op: svint64_t) -> svfloat64_t { - unsafe { svcvt_f64_s64_m(transmute_unchecked(op), pg, op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_z)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_z(pg: svbool_t, op: svint64_t) -> svfloat64_t { - svcvt_f64_s64_m(svdup_n_f64(0.0), pg, op) -} -#[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u32]_m)"] #[inline] #[target_feature(enable = "sve")] @@ -7868,190 +7911,202 @@ pub fn svcvt_f64_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat64_t { svcvt_f64_u32_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool_t, op: svuint64_t) -> svfloat64_t { +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_m(inactive: svint32_t, pg: svbool_t, op: svfloat32_t) -> svint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.ucvtf.f64i64")] - fn _svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzs.nxv4i32.nxv4f32" + )] + fn _svcvt_s32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; } - unsafe { _svcvt_f64_u64_m(inactive, pg.sve_into(), op.as_signed()) } + unsafe { _svcvt_s32_f32_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_x(pg: svbool_t, op: svuint64_t) -> svfloat64_t { - unsafe { svcvt_f64_u64_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_x(pg: svbool_t, op: svfloat32_t) -> svint32_t { + unsafe { svcvt_s32_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_z(pg: svbool_t, op: svuint64_t) -> svfloat64_t { - svcvt_f64_u64_m(svdup_n_f64(0.0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_z(pg: svbool_t, op: svfloat32_t) -> svint32_t { + svcvt_s32_f32_m(svdup_n_s32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_m(inactive: svint32_t, pg: svbool_t, op: svfloat32_t) -> svint32_t { +pub fn svcvt_s64_f64_m(inactive: svint64_t, pg: svbool_t, op: svfloat64_t) -> svint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f32")] - fn _svcvt_s32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzs.nxv2i64.nxv2f64" + )] + fn _svcvt_s64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; } - unsafe { _svcvt_s32_f32_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_s64_f64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_x(pg: svbool_t, op: svfloat32_t) -> svint32_t { - unsafe { svcvt_s32_f32_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_s64_f64_x(pg: svbool_t, op: svfloat64_t) -> svint64_t { + unsafe { svcvt_s64_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_z(pg: svbool_t, op: svfloat32_t) -> svint32_t { - svcvt_s32_f32_m(svdup_n_s32(0), pg, op) +pub fn svcvt_s64_f64_z(pg: svbool_t, op: svfloat64_t) -> svint64_t { + svcvt_s64_f64_m(svdup_n_s64(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_m(inactive: svint32_t, pg: svbool_t, op: svfloat64_t) -> svint32_t { +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_m(inactive: svuint32_t, pg: svbool_t, op: svfloat32_t) -> svuint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f64")] - fn _svcvt_s32_f64_m(inactive: svint32_t, pg: svbool2_t, op: svfloat64_t) -> svint32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzu.nxv4i32.nxv4f32" + )] + fn _svcvt_u32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; } - unsafe { _svcvt_s32_f64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_u32_f32_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_x(pg: svbool_t, op: svfloat64_t) -> svint32_t { - unsafe { svcvt_s32_f64_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint32_t { + unsafe { svcvt_u32_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_z(pg: svbool_t, op: svfloat64_t) -> svint32_t { - svcvt_s32_f64_m(svdup_n_s32(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint32_t { + svcvt_u32_f32_m(svdup_n_u32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_m(inactive: svint64_t, pg: svbool_t, op: svfloat32_t) -> svint64_t { +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_m(inactive: svuint64_t, pg: svbool_t, op: svfloat64_t) -> svuint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f32")] - fn _svcvt_s64_f32_m(inactive: svint64_t, pg: svbool2_t, op: svfloat32_t) -> svint64_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzu.nxv2i64.nxv2f64" + )] + fn _svcvt_u64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; } - unsafe { _svcvt_s64_f32_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_u64_f64_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_x(pg: svbool_t, op: svfloat32_t) -> svint64_t { - unsafe { svcvt_s64_f32_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_x(pg: svbool_t, op: svfloat64_t) -> svuint64_t { + unsafe { svcvt_u64_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_z(pg: svbool_t, op: svfloat32_t) -> svint64_t { - svcvt_s64_f32_m(svdup_n_s64(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_z(pg: svbool_t, op: svfloat64_t) -> svuint64_t { + svcvt_u64_f64_m(svdup_n_u64(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_m(inactive: svint64_t, pg: svbool_t, op: svfloat64_t) -> svint64_t { +pub fn svcvt_s32_f64_m(inactive: svint32_t, pg: svbool_t, op: svfloat64_t) -> svint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f64")] - fn _svcvt_s64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f64")] + fn _svcvt_s32_f64_m(inactive: svint32_t, pg: svbool2_t, op: svfloat64_t) -> svint32_t; } - unsafe { _svcvt_s64_f64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_s32_f64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_x(pg: svbool_t, op: svfloat64_t) -> svint64_t { - unsafe { svcvt_s64_f64_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_s32_f64_x(pg: svbool_t, op: svfloat64_t) -> svint32_t { + unsafe { svcvt_s32_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_z(pg: svbool_t, op: svfloat64_t) -> svint64_t { - svcvt_s64_f64_m(svdup_n_s64(0), pg, op) +pub fn svcvt_s32_f64_z(pg: svbool_t, op: svfloat64_t) -> svint32_t { + svcvt_s32_f64_m(svdup_n_s32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_m(inactive: svuint32_t, pg: svbool_t, op: svfloat32_t) -> svuint32_t { +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_m(inactive: svint64_t, pg: svbool_t, op: svfloat32_t) -> svint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzu.i32f32")] - fn _svcvt_u32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f32")] + fn _svcvt_s64_f32_m(inactive: svint64_t, pg: svbool2_t, op: svfloat32_t) -> svint64_t; } - unsafe { _svcvt_u32_f32_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } + unsafe { _svcvt_s64_f32_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint32_t { - unsafe { svcvt_u32_f32_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_x(pg: svbool_t, op: svfloat32_t) -> svint64_t { + unsafe { svcvt_s64_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint32_t { - svcvt_u32_f32_m(svdup_n_u32(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_z(pg: svbool_t, op: svfloat32_t) -> svint64_t { + svcvt_s64_f32_m(svdup_n_s64(0), pg, op) } #[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f64]_m)"] @@ -8115,37 +8170,6 @@ pub fn svcvt_u64_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint64_t { pub fn svcvt_u64_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint64_t { svcvt_u64_f32_m(svdup_n_u64(0), pg, op) } -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_m)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_m(inactive: svuint64_t, pg: svbool_t, op: svfloat64_t) -> svuint64_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzu.i64f64")] - fn _svcvt_u64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; - } - unsafe { _svcvt_u64_f64_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_x)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_x(pg: svbool_t, op: svfloat64_t) -> svuint64_t { - unsafe { svcvt_u64_f64_m(transmute_unchecked(op), pg, op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_z)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_z(pg: svbool_t, op: svfloat64_t) -> svuint64_t { - svcvt_u64_f64_m(svdup_n_u64(0), pg, op) -} #[doc = "Divide"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svdiv[_f32]_m)"] #[inline] @@ -10041,7 +10065,7 @@ pub fn svdupq_n_u8( #[cfg_attr(test, assert_instr(eor))] pub fn sveor_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.eor.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.eor.z.nxv16i1")] fn _sveor_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _sveor_b_z(pg, op1, op2) } @@ -10592,7 +10616,7 @@ pub fn svexpa_f32(op: svuint32_t) -> svfloat32_t { unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fexpa.x.nxv4f32 " + link_name = "llvm.aarch64.sve.fexpa.x.nxv4f32" )] fn _svexpa_f32(op: svint32_t) -> svfloat32_t; } @@ -10608,7 +10632,7 @@ pub fn svexpa_f64(op: svuint64_t) -> svfloat64_t { unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fexpa.x.nxv2f64 " + link_name = "llvm.aarch64.sve.fexpa.x.nxv2f64" )] fn _svexpa_f64(op: svint64_t) -> svfloat64_t; } @@ -27372,7 +27396,10 @@ pub fn svmls_lane_f64( #[cfg_attr(test, assert_instr(fmmla))] pub fn svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fmmla.nxv4f32")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fmmla.nxv4f32.nxv4f32" + )] fn _svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svfloat32_t; } unsafe { _svmmla_f32(op1, op2, op3) } @@ -27385,7 +27412,10 @@ pub fn svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svflo #[cfg_attr(test, assert_instr(fmmla))] pub fn svmmla_f64(op1: svfloat64_t, op2: svfloat64_t, op3: svfloat64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fmmla.nxv2f64")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fmmla.nxv2f64.nxv2f64" + )] fn _svmmla_f64(op1: svfloat64_t, op2: svfloat64_t, op3: svfloat64_t) -> svfloat64_t; } unsafe { _svmmla_f64(op1, op2, op3) } @@ -30261,7 +30291,7 @@ pub fn svnot_u64_z(pg: svbool_t, op: svuint64_t) -> svuint64_t { #[cfg_attr(test, assert_instr(orn))] pub fn svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orn.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orn.z.nxv16i1")] fn _svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svorn_b_z(pg, op1, op2) } @@ -30274,7 +30304,7 @@ pub fn svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(orr))] pub fn svorr_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orr.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orr.z.nxv16i1")] fn _svorr_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svorr_b_z(pg, op1, op2) } @@ -34341,10 +34371,7 @@ pub fn svrecps_f64(op1: svfloat64_t, op2: svfloat64_t) -> svfloat64_t { #[cfg_attr(test, assert_instr(frecpx))] pub fn svrecpx_f32_m(inactive: svfloat32_t, pg: svbool_t, op: svfloat32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr( - target_arch = "aarch64", - link_name = "llvm.aarch64.sve.frecpx.x.nxv4f32" - )] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.frecpx.nxv4f32")] fn _svrecpx_f32_m(inactive: svfloat32_t, pg: svbool4_t, op: svfloat32_t) -> svfloat32_t; } unsafe { _svrecpx_f32_m(inactive, pg.sve_into(), op) } @@ -34375,10 +34402,7 @@ pub fn svrecpx_f32_z(pg: svbool_t, op: svfloat32_t) -> svfloat32_t { #[cfg_attr(test, assert_instr(frecpx))] pub fn svrecpx_f64_m(inactive: svfloat64_t, pg: svbool_t, op: svfloat64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr( - target_arch = "aarch64", - link_name = "llvm.aarch64.sve.frecpx.x.nxv2f64" - )] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.frecpx.nxv2f64")] fn _svrecpx_f64_m(inactive: svfloat64_t, pg: svbool2_t, op: svfloat64_t) -> svfloat64_t; } unsafe { _svrecpx_f64_m(inactive, pg.sve_into(), op) } @@ -35202,19 +35226,6 @@ pub fn svreinterpret_u64_u64(op: svuint64_t) -> svuint64_t { unsafe { crate::intrinsics::transmute_unchecked(op) } } #[doc = "Reverse all elements"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(rev))] -pub fn svrev_b8(op: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv16i1")] - fn _svrev_b8(op: svbool_t) -> svbool_t; - } - unsafe { _svrev_b8(op) } -} -#[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -35222,10 +35233,10 @@ pub fn svrev_b8(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b16(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv8i1")] - fn _svrev_b16(op: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b16")] + fn _svrev_b16(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b16(op.sve_into()).sve_into() } + unsafe { _svrev_b16(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b32)"] @@ -35235,10 +35246,10 @@ pub fn svrev_b16(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b32(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv4i1")] - fn _svrev_b32(op: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b32")] + fn _svrev_b32(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b32(op.sve_into()).sve_into() } + unsafe { _svrev_b32(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b64)"] @@ -35248,10 +35259,10 @@ pub fn svrev_b32(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b64(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv2i1")] - fn _svrev_b64(op: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b64")] + fn _svrev_b64(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b64(op.sve_into()).sve_into() } + unsafe { _svrev_b64(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev[_f32])"] @@ -35367,6 +35378,19 @@ pub fn svrev_u32(op: svuint32_t) -> svuint32_t { pub fn svrev_u64(op: svuint64_t) -> svuint64_t { unsafe { svrev_s64(op.as_signed()).as_unsigned() } } +#[doc = "Reverse all elements"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(rev))] +pub fn svrev_b8(op: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv16i1")] + fn _svrev_b8(op: svbool_t) -> svbool_t; + } + unsafe { _svrev_b8(op) } +} #[doc = "Reverse bytes within elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrevb[_s16]_m)"] #[inline] @@ -43312,19 +43336,6 @@ pub fn svusmmla_s32(op1: svint32_t, op2: svuint8_t, op3: svint8_t) -> svint32_t unsafe { _svusmmla_s32(op1, op2.as_signed(), op3) } } #[doc = "Concatenate even elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(uzp1))] -pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv16i1")] - fn _svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svuzp1_b8(op1, op2) } -} -#[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -43332,10 +43343,10 @@ pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv8i1")] - fn _svuzp1_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b16")] + fn _svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b32)"] @@ -43345,10 +43356,10 @@ pub fn svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv4i1")] - fn _svuzp1_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b32")] + fn _svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b64)"] @@ -43358,10 +43369,10 @@ pub fn svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv2i1")] - fn _svuzp1_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b64")] + fn _svuzp1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1[_f32])"] @@ -43477,6 +43488,19 @@ pub fn svuzp1_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svuzp1_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp1_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Concatenate even elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(uzp1))] +pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv16i1")] + fn _svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svuzp1_b8(op1, op2) } +} #[doc = "Concatenate even quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1q[_f32])"] #[inline] @@ -43592,19 +43616,6 @@ pub fn svuzp1q_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp1q_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } #[doc = "Concatenate odd elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(uzp2))] -pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv16i1")] - fn _svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svuzp2_b8(op1, op2) } -} -#[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -43612,10 +43623,10 @@ pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv8i1")] - fn _svuzp2_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b16")] + fn _svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b32)"] @@ -43625,10 +43636,10 @@ pub fn svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv4i1")] - fn _svuzp2_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b32")] + fn _svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b64)"] @@ -43638,10 +43649,10 @@ pub fn svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv2i1")] - fn _svuzp2_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b64")] + fn _svuzp2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2[_f32])"] @@ -43757,6 +43768,19 @@ pub fn svuzp2_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svuzp2_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp2_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Concatenate odd elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(uzp2))] +pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv16i1")] + fn _svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svuzp2_b8(op1, op2) } +} #[doc = "Concatenate odd quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2q[_f32])"] #[inline] @@ -44397,19 +44421,6 @@ pub fn svwrffr(op: svbool_t) { unsafe { _svwrffr(op) } } #[doc = "Interleave elements from low halves of two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(zip1))] -pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv16i1")] - fn _svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svzip1_b8(op1, op2) } -} -#[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -44417,10 +44428,10 @@ pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv8i1")] - fn _svzip1_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b16")] + fn _svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b32)"] @@ -44430,10 +44441,10 @@ pub fn svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv4i1")] - fn _svzip1_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b32")] + fn _svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b64)"] @@ -44443,10 +44454,10 @@ pub fn svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv2i1")] - fn _svzip1_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b64")] + fn _svzip1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1[_f32])"] @@ -44562,6 +44573,19 @@ pub fn svzip1_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svzip1_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip1_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave elements from low halves of two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(zip1))] +pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv16i1")] + fn _svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svzip1_b8(op1, op2) } +} #[doc = "Interleave quadwords from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1q[_f32])"] #[inline] @@ -44677,19 +44701,6 @@ pub fn svzip1q_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip1q_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } #[doc = "Interleave elements from high halves of two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(zip2))] -pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv16i1")] - fn _svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svzip2_b8(op1, op2) } -} -#[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -44697,10 +44708,10 @@ pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv8i1")] - fn _svzip2_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b16")] + fn _svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b32)"] @@ -44710,10 +44721,10 @@ pub fn svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv4i1")] - fn _svzip2_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b32")] + fn _svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b64)"] @@ -44723,10 +44734,10 @@ pub fn svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv2i1")] - fn _svzip2_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b64")] + fn _svzip2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2[_f32])"] @@ -44842,6 +44853,19 @@ pub fn svzip2_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svzip2_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip2_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave elements from high halves of two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(zip2))] +pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv16i1")] + fn _svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svzip2_b8(op1, op2) } +} #[doc = "Interleave quadwords from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2q[_f32])"] #[inline] diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs index a3f70ab61c40f..f11ca660b15e2 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs @@ -28,6 +28,14 @@ pub(super) trait SveInto: Sized { unsafe fn sve_into(self) -> T; } +impl SveInto for T { + #[inline] + #[target_feature(enable = "sve")] + unsafe fn sve_into(self) -> T { + self + } +} + macro_rules! impl_sve_type { ($(($v:vis, $elem_type:ty, $name:ident, $elt:literal))*) => ($( #[doc = concat!("Scalable vector of type ", stringify!($elem_type))] @@ -130,7 +138,7 @@ macro_rules! impl_internal_sve_predicate { #[target_feature(enable = "sve")] unsafe fn sve_into(self) -> svbool_t { #[allow(improper_ctypes)] - unsafe extern "C" { + unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", link_name = concat!("llvm.aarch64.sve.convert.to.svbool.nxv", $elt, "i1") @@ -147,7 +155,7 @@ macro_rules! impl_internal_sve_predicate { #[target_feature(enable = "sve")] unsafe fn sve_into(self) -> $name { #[allow(improper_ctypes)] - unsafe extern "C" { + unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", link_name = concat!("llvm.aarch64.sve.convert.from.svbool.nxv", $elt, "i1") diff --git a/library/stdarch/crates/core_arch/src/hexagon/v128.rs b/library/stdarch/crates/core_arch/src/hexagon/v128.rs index 1f0566af78ef3..1a1826ddda316 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/v128.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/v128.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon HVX 128-byte vector mode intrinsics //! //! This module provides intrinsics for the Hexagon Vector Extensions (HVX) diff --git a/library/stdarch/crates/core_arch/src/hexagon/v64.rs b/library/stdarch/crates/core_arch/src/hexagon/v64.rs index e9b18b2fd8efe..3ac8b47079333 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/v64.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/v64.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon HVX 64-byte vector mode intrinsics //! //! This module provides intrinsics for the Hexagon Vector Extensions (HVX) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index 7e9f45f5e7b04..ffaa4225ba99d 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -62,56 +62,56 @@ unsafe extern "unadjusted" { /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_b_w(a: i8, b: i32) -> i32 { - unsafe { __crc_w_b_w(a.cast_unsigned() as i32, b) } + unsafe { __crc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_h_w(a: i16, b: i32) -> i32 { - unsafe { __crc_w_h_w(a.cast_unsigned() as i32, b) } + unsafe { __crc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_w_w(a: i32, b: i32) -> i32 { unsafe { __crc_w_w_w(a, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_d_w(a: i64, b: i32) -> i32 { unsafe { __crc_w_d_w(a, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_b_w(a: i8, b: i32) -> i32 { - unsafe { __crcc_w_b_w(a.cast_unsigned() as i32, b) } + unsafe { __crcc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_h_w(a: i16, b: i32) -> i32 { - unsafe { __crcc_w_h_w(a.cast_unsigned() as i32, b) } + unsafe { __crcc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_w_w(a: i32, b: i32) -> i32 { unsafe { __crcc_w_w_w(a, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_d_w(a: i64, b: i32) -> i32 { unsafe { __crcc_w_d_w(a, b) } } diff --git a/library/stdarch/crates/core_arch/src/mips/msa.rs b/library/stdarch/crates/core_arch/src/mips/msa.rs index bc601baef9e2a..dab64319ccc3b 100644 --- a/library/stdarch/crates/core_arch/src/mips/msa.rs +++ b/library/stdarch/crates/core_arch/src/mips/msa.rs @@ -45,7 +45,7 @@ types! { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.mips.add.a.b"] fn msa_add_a_b(a: v16i8, b: v16i8) -> v16i8; #[link_name = "llvm.mips.add.a.h"] diff --git a/library/stdarch/crates/core_arch/src/nvptx/mod.rs b/library/stdarch/crates/core_arch/src/nvptx/mod.rs index b63a5d01a7a22..d22f3a25bf70e 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/mod.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/mod.rs @@ -19,9 +19,9 @@ mod packed; pub use packed::*; #[allow(improper_ctypes)] -unsafe extern "C" { - #[link_name = "llvm.nvvm.barrier0"] - fn syncthreads() -> (); +unsafe extern "unadjusted" { + #[link_name = "llvm.nvvm.barrier.cta.sync.aligned.all"] + fn syncthreads(a: u32) -> (); #[link_name = "llvm.nvvm.read.ptx.sreg.ntid.x"] fn block_dim_x() -> u32; #[link_name = "llvm.nvvm.read.ptx.sreg.ntid.y"] @@ -54,7 +54,7 @@ unsafe extern "C" { #[inline] #[unstable(feature = "stdarch_nvptx", issue = "111199")] pub unsafe fn _syncthreads() -> () { - syncthreads() + syncthreads(0) } /// x-th thread-block dimension. diff --git a/library/stdarch/crates/core_arch/src/nvptx/packed.rs b/library/stdarch/crates/core_arch/src/nvptx/packed.rs index 1c7e81268fc99..7e6c14bc993d9 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/packed.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/packed.rs @@ -7,7 +7,7 @@ use crate::intrinsics::simd::*; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.minimum.v2f16"] fn llvm_f16x2_minimum(a: f16x2, b: f16x2) -> f16x2; #[link_name = "llvm.maximum.v2f16"] diff --git a/library/stdarch/crates/core_arch/src/powerpc/altivec.rs b/library/stdarch/crates/core_arch/src/powerpc/altivec.rs index 78ec39f91ff33..ccfed88a93c2a 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/altivec.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/altivec.rs @@ -96,7 +96,7 @@ impl From for m32x4 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.altivec.lvx"] fn lvx(p: *const i8) -> vector_unsigned_int; diff --git a/library/stdarch/crates/core_arch/src/powerpc/vsx.rs b/library/stdarch/crates/core_arch/src/powerpc/vsx.rs index 4a7b561a20c55..60cb2ad44cd4e 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/vsx.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/vsx.rs @@ -52,7 +52,7 @@ impl From for m64x2 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.altivec.vperm"] fn vperm( a: vector_signed_int, diff --git a/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs b/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs index 7b42be8653c55..9032e7795980f 100644 --- a/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs +++ b/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs @@ -17,7 +17,7 @@ use stdarch_test::assert_instr; use crate::mem::transmute; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.vsx.lxvl"] fn lxvl(a: *const u8, l: usize) -> vector_signed_int; diff --git a/library/stdarch/crates/core_arch/src/wasm32/memory.rs b/library/stdarch/crates/core_arch/src/wasm32/memory.rs index 90e9075e5136b..9d7b7287ede1b 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/memory.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/memory.rs @@ -2,9 +2,11 @@ use stdarch_test::assert_instr; unsafe extern "unadjusted" { - #[link_name = "llvm.wasm.memory.grow"] + #[cfg_attr(target_pointer_width = "32", link_name = "llvm.wasm.memory.grow.i32")] + #[cfg_attr(target_pointer_width = "64", link_name = "llvm.wasm.memory.grow.i64")] fn llvm_memory_grow(mem: u32, pages: usize) -> usize; - #[link_name = "llvm.wasm.memory.size"] + #[cfg_attr(target_pointer_width = "32", link_name = "llvm.wasm.memory.size.i32")] + #[cfg_attr(target_pointer_width = "64", link_name = "llvm.wasm.memory.size.i64")] fn llvm_memory_size(mem: u32) -> usize; } diff --git a/library/stdarch/crates/core_arch/src/x86/aes.rs b/library/stdarch/crates/core_arch/src/x86/aes.rs index d07ab4dc2a01e..ecb09a6a9099f 100644 --- a/library/stdarch/crates/core_arch/src/x86/aes.rs +++ b/library/stdarch/crates/core_arch/src/x86/aes.rs @@ -13,7 +13,7 @@ use crate::core_arch::x86::__m128i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.aesni.aesdec"] fn aesdec(a: __m128i, round_key: __m128i) -> __m128i; #[link_name = "llvm.x86.aesni.aesdeclast"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx.rs b/library/stdarch/crates/core_arch/src/x86/avx.rs index ef434205b52a0..f5ddff6457aea 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx.rs @@ -3292,7 +3292,7 @@ pub const fn _mm256_cvtss_f32(a: __m256) -> f32 { // LLVM intrinsics used in the above functions #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx.round.pd.256"] fn roundpd256(a: __m256d, b: i32) -> __m256d; #[link_name = "llvm.x86.avx.round.ps.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx2.rs b/library/stdarch/crates/core_arch/src/x86/avx2.rs index b49ad9522a412..6925ba8e27028 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx2.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx2.rs @@ -3906,7 +3906,7 @@ pub const fn _mm256_extract_epi16(a: __m256i) -> i32 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx2.pmadd.wd"] fn pmaddwd(a: i16x16, b: i16x16) -> i32x8; #[link_name = "llvm.x86.avx2.pmadd.ub.sw"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs b/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs index 8d944f5ba817b..230a4f3754cad 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs @@ -9,7 +9,7 @@ use crate::intrinsics::simd::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512bf16.cvtne2ps2bf16.128"] fn cvtne2ps2bf16(a: f32x4, b: f32x4) -> i16x8; #[link_name = "llvm.x86.avx512bf16.cvtne2ps2bf16.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs b/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs index dd211854afbb4..344130fb6e720 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs @@ -27,7 +27,7 @@ use crate::mem::transmute; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpshufbitqmb.512"] fn bitshuffle_512(data: i8x64, indices: i8x64) -> __mmask64; #[link_name = "llvm.x86.avx512.vpshufbitqmb.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bw.rs b/library/stdarch/crates/core_arch/src/x86/avx512bw.rs index 659d6c3be88e7..50d57b4964489 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bw.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bw.rs @@ -12764,7 +12764,7 @@ pub unsafe fn _mm_mask_cvtusepi16_storeu_epi8(mem_addr: *mut i8, k: __mmask8, a: } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.pmul.hr.sw.512"] fn vpmulhrsw(a: i16x32, b: i16x32) -> i16x32; diff --git a/library/stdarch/crates/core_arch/src/x86/avx512cd.rs b/library/stdarch/crates/core_arch/src/x86/avx512cd.rs index 4082433e70759..378bc3617ed63 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512cd.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512cd.rs @@ -563,7 +563,7 @@ pub const fn _mm_maskz_lzcnt_epi64(k: __mmask8, a: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.conflict.d.512"] fn vpconflictd(a: i32x16) -> i32x16; #[link_name = "llvm.x86.avx512.conflict.d.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512dq.rs b/library/stdarch/crates/core_arch/src/x86/avx512dq.rs index 0b322c8b83c7b..5fe40b7541549 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512dq.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512dq.rs @@ -7235,7 +7235,7 @@ pub fn _mm_mask_fpclass_ss_mask(k1: __mmask8, a: __m128) -> __m } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.sitofp.round.v2f64.v2i64"] fn vcvtqq2pd_128(a: i64x2, rounding: i32) -> f64x2; #[link_name = "llvm.x86.avx512.sitofp.round.v4f64.v4i64"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index 9b90ad60a39fb..76e7297bcda40 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -44215,7 +44215,7 @@ pub const _MM_PERM_DDDC: _MM_PERM_ENUM = 0xFE; pub const _MM_PERM_DDDD: _MM_PERM_ENUM = 0xFF; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.sqrt.ps.512"] fn vsqrtps(a: f32x16, rounding: i32) -> f32x16; #[link_name = "llvm.x86.avx512.sqrt.pd.512"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs b/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs index 5ce28565d1085..262215e62c43d 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs @@ -347,7 +347,7 @@ pub fn _mm_maskz_madd52lo_epu64(k: __mmask8, a: __m128i, b: __m128i, c: __m128i) } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpmadd52l.uq.128"] fn vpmadd52luq_128(z: __m128i, x: __m128i, y: __m128i) -> __m128i; #[link_name = "llvm.x86.avx512.vpmadd52h.uq.128"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs b/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs index d9ad14ef00ddb..141a8c8aec2bc 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs @@ -453,7 +453,7 @@ pub fn _mm_maskz_multishift_epi64_epi8(k: __mmask16, a: __m128i, b: __m128i) -> } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpermi2var.qi.512"] fn vpermi2b(a: i8x64, idx: i8x64, b: i8x64) -> i8x64; #[link_name = "llvm.x86.avx512.vpermi2var.qi.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs b/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs index 78a50b90c8614..0a4accc2f70fb 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs @@ -2383,7 +2383,7 @@ pub const fn _mm_maskz_shrdi_epi16( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.mask.compress.store.w.512"] fn vcompressstorew(mem: *mut i8, data: i16x32, mask: u32); #[link_name = "llvm.x86.avx512.mask.compress.store.w.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs b/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs index 8cd8764f24868..b9084aec59344 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs @@ -873,7 +873,7 @@ pub fn _mm256_dpwuuds_epi32(src: __m256i, a: __m256i, b: __m256i) -> __m256i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpdpwssd.512"] fn vpdpwssd(src: i32x16, a: i16x32, b: i16x32) -> i32x16; #[link_name = "llvm.x86.avx512.vpdpwssd.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs b/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs index 4dd7412e9e330..30fcfce63ccf4 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs @@ -110,7 +110,7 @@ pub unsafe fn _mm512_2intersect_epi64( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vp2intersect.d.128"] fn vp2intersectd_128(a: i32x4, b: i32x4) -> (u8, u8); #[link_name = "llvm.x86.avx512.vp2intersect.q.128"] diff --git a/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs b/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs index 861213eb4257f..b17d3de48b595 100644 --- a/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs +++ b/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs @@ -176,7 +176,7 @@ pub fn _mm256_cvtneps_avx_pbh(a: __m256) -> __m128bh { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.vbcstnebf162ps128"] fn bcstnebf162ps_128(a: *const bf16) -> __m128; #[link_name = "llvm.x86.vbcstnebf162ps256"] diff --git a/library/stdarch/crates/core_arch/src/x86/bmi1.rs b/library/stdarch/crates/core_arch/src/x86/bmi1.rs index 432051abd1cfa..21da03667f849 100644 --- a/library/stdarch/crates/core_arch/src/x86/bmi1.rs +++ b/library/stdarch/crates/core_arch/src/x86/bmi1.rs @@ -131,7 +131,7 @@ pub const fn _mm_tzcnt_32(x: u32) -> i32 { x.trailing_zeros() as i32 } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bextr.32"] fn x86_bmi_bextr_32(x: u32, y: u32) -> u32; } diff --git a/library/stdarch/crates/core_arch/src/x86/bmi2.rs b/library/stdarch/crates/core_arch/src/x86/bmi2.rs index 5320640d96873..dae4133d63bec 100644 --- a/library/stdarch/crates/core_arch/src/x86/bmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86/bmi2.rs @@ -67,7 +67,7 @@ pub fn _pext_u32(a: u32, mask: u32) -> u32 { unsafe { x86_bmi2_pext_32(a, mask) } } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bzhi.32"] fn x86_bmi2_bzhi_32(x: u32, y: u32) -> u32; #[link_name = "llvm.x86.bmi.pdep.32"] diff --git a/library/stdarch/crates/core_arch/src/x86/fxsr.rs b/library/stdarch/crates/core_arch/src/x86/fxsr.rs index 08619efe7c9ef..74a95e192a3af 100644 --- a/library/stdarch/crates/core_arch/src/x86/fxsr.rs +++ b/library/stdarch/crates/core_arch/src/x86/fxsr.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.fxsave"] fn fxsave(p: *mut u8); #[link_name = "llvm.x86.fxrstor"] diff --git a/library/stdarch/crates/core_arch/src/x86/gfni.rs b/library/stdarch/crates/core_arch/src/x86/gfni.rs index e9ee27a7b823b..8cebd04a4b836 100644 --- a/library/stdarch/crates/core_arch/src/x86/gfni.rs +++ b/library/stdarch/crates/core_arch/src/x86/gfni.rs @@ -23,7 +23,7 @@ use crate::mem::transmute; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.vgf2p8affineinvqb.512"] fn vgf2p8affineinvqb_512(x: i8x64, a: i8x64, imm8: u8) -> i8x64; #[link_name = "llvm.x86.vgf2p8affineinvqb.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs b/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs index 0f2769257f958..454785c5e0c89 100644 --- a/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs +++ b/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs @@ -11,7 +11,7 @@ use crate::core_arch::x86::__m128i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.pclmulqdq"] fn pclmulqdq(a: __m128i, round_key: __m128i, imm8: u8) -> __m128i; } diff --git a/library/stdarch/crates/core_arch/src/x86/rtm.rs b/library/stdarch/crates/core_arch/src/x86/rtm.rs index c88bd6592d784..f09b95d76bdb3 100644 --- a/library/stdarch/crates/core_arch/src/x86/rtm.rs +++ b/library/stdarch/crates/core_arch/src/x86/rtm.rs @@ -16,7 +16,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xbegin"] fn x86_xbegin() -> i32; #[link_name = "llvm.x86.xend"] diff --git a/library/stdarch/crates/core_arch/src/x86/sha.rs b/library/stdarch/crates/core_arch/src/x86/sha.rs index f8a3295d19589..f7d8c3c0b2408 100644 --- a/library/stdarch/crates/core_arch/src/x86/sha.rs +++ b/library/stdarch/crates/core_arch/src/x86/sha.rs @@ -1,7 +1,7 @@ use crate::core_arch::{simd::*, x86::*}; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sha1msg1"] fn sha1msg1(a: i32x4, b: i32x4) -> i32x4; #[link_name = "llvm.x86.sha1msg2"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse.rs b/library/stdarch/crates/core_arch/src/x86/sse.rs index 867387290119b..e31175d48cf94 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse.rs @@ -1987,7 +1987,7 @@ pub const fn _MM_TRANSPOSE4_PS( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse.rcp.ss"] fn rcpss(a: __m128) -> __m128; #[link_name = "llvm.x86.sse.rcp.ps"] @@ -2040,7 +2040,7 @@ unsafe extern "C" { fn stmxcsr(p: *mut i8); #[link_name = "llvm.x86.sse.ldmxcsr"] fn ldmxcsr(p: *const i8); - #[link_name = "llvm.prefetch"] + #[link_name = "llvm.prefetch.p0"] fn prefetch(p: *const i8, rw: i32, loc: i32, ty: i32); #[link_name = "llvm.x86.sse.cmp.ss"] fn cmpss(a: __m128, b: __m128, imm8: i8) -> __m128; diff --git a/library/stdarch/crates/core_arch/src/x86/sse2.rs b/library/stdarch/crates/core_arch/src/x86/sse2.rs index 62719700ad905..bd7e4df2138d5 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse2.rs @@ -3242,7 +3242,7 @@ pub const fn _mm_unpacklo_pd(a: __m128d, b: __m128d) -> __m128d { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse2.pause"] fn pause(); #[link_name = "llvm.x86.sse2.clflush"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse3.rs b/library/stdarch/crates/core_arch/src/x86/sse3.rs index e4c75702544d9..2f6fa35cb616a 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse3.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse3.rs @@ -178,7 +178,7 @@ pub const fn _mm_moveldup_ps(a: __m128) -> __m128 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse3.ldu.dq"] fn lddqu(mem_addr: *const i8) -> i8x16; } diff --git a/library/stdarch/crates/core_arch/src/x86/sse41.rs b/library/stdarch/crates/core_arch/src/x86/sse41.rs index 4ebf7d3bd39a8..d981166ff08c9 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse41.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse41.rs @@ -1181,7 +1181,7 @@ pub unsafe fn _mm_stream_load_si128(mem_addr: *const __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse41.insertps"] fn insertps(a: __m128, b: __m128, imm8: u8) -> __m128; #[link_name = "llvm.x86.sse41.dppd"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse42.rs b/library/stdarch/crates/core_arch/src/x86/sse42.rs index 55e22592637f1..0534fe9379a18 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse42.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse42.rs @@ -569,7 +569,7 @@ pub const fn _mm_cmpgt_epi64(a: __m128i, b: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { // SSE 4.2 string and text comparison ops #[link_name = "llvm.x86.sse42.pcmpestrm128"] fn pcmpestrm128(a: i8x16, la: i32, b: i8x16, lb: i32, imm8: i8) -> u8x16; diff --git a/library/stdarch/crates/core_arch/src/x86/sse4a.rs b/library/stdarch/crates/core_arch/src/x86/sse4a.rs index f36b879a030e3..14e9b56dcb2ed 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse4a.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse4a.rs @@ -6,7 +6,7 @@ use crate::core_arch::{simd::*, x86::*}; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse4a.extrq"] fn extrq(x: i64x2, y: i8x16) -> i64x2; #[link_name = "llvm.x86.sse4a.extrqi"] diff --git a/library/stdarch/crates/core_arch/src/x86/ssse3.rs b/library/stdarch/crates/core_arch/src/x86/ssse3.rs index 1d7a97944a37b..b0152c81596bd 100644 --- a/library/stdarch/crates/core_arch/src/x86/ssse3.rs +++ b/library/stdarch/crates/core_arch/src/x86/ssse3.rs @@ -345,7 +345,7 @@ pub fn _mm_sign_epi32(a: __m128i, b: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.ssse3.pshuf.b.128"] fn pshufb128(a: u8x16, b: u8x16) -> u8x16; diff --git a/library/stdarch/crates/core_arch/src/x86/tbm.rs b/library/stdarch/crates/core_arch/src/x86/tbm.rs index 0ba4572dcd029..2a44579408364 100644 --- a/library/stdarch/crates/core_arch/src/x86/tbm.rs +++ b/library/stdarch/crates/core_arch/src/x86/tbm.rs @@ -13,7 +13,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.tbm.bextri.u32"] fn bextri_u32(a: u32, control: u32) -> u32; } diff --git a/library/stdarch/crates/core_arch/src/x86/vaes.rs b/library/stdarch/crates/core_arch/src/x86/vaes.rs index 864b1d56d1057..72dd387f900d5 100644 --- a/library/stdarch/crates/core_arch/src/x86/vaes.rs +++ b/library/stdarch/crates/core_arch/src/x86/vaes.rs @@ -14,7 +14,7 @@ use crate::core_arch::x86::__m512i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.aesni.aesenc.256"] fn aesenc_256(a: __m256i, round_key: __m256i) -> __m256i; #[link_name = "llvm.x86.aesni.aesenclast.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs b/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs index ad44e59f3ada1..4c39ae6ee7aa3 100644 --- a/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs +++ b/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs @@ -12,7 +12,7 @@ use crate::core_arch::x86::__m512i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.pclmulqdq.256"] fn pclmulqdq_256(a: __m256i, round_key: __m256i, imm8: u8) -> __m256i; #[link_name = "llvm.x86.pclmulqdq.512"] diff --git a/library/stdarch/crates/core_arch/src/x86/xsave.rs b/library/stdarch/crates/core_arch/src/x86/xsave.rs index e22d3580ff463..395e2c64be3a0 100644 --- a/library/stdarch/crates/core_arch/src/x86/xsave.rs +++ b/library/stdarch/crates/core_arch/src/x86/xsave.rs @@ -5,7 +5,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xsave"] fn xsave(p: *mut u8, hi: u32, lo: u32); #[link_name = "llvm.x86.xrstor"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index 0fd9b09363d4b..e26bfc5c1ca4f 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -527,7 +527,7 @@ pub fn _mm_cvtt_roundss_u64(a: __m128) -> u64 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vcvtss2si64"] fn vcvtss2si64(a: f32x4, rounding: i32) -> i64; #[link_name = "llvm.x86.avx512.vcvtss2usi64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs index 2a511328bb382..5baf1c9036e19 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs @@ -211,7 +211,7 @@ pub fn _mm_cvtt_roundsh_u64(a: __m128h) -> u64 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512fp16.vcvtsi642sh"] fn vcvtsi642sh(a: __m128h, b: i64, rounding: i32) -> __m128h; #[link_name = "llvm.x86.avx512fp16.vcvtusi642sh"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/bmi.rs b/library/stdarch/crates/core_arch/src/x86_64/bmi.rs index 8d2b22089ac10..f082953f9df90 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/bmi.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/bmi.rs @@ -122,7 +122,7 @@ pub const fn _mm_tzcnt_64(x: u64) -> i64 { x.trailing_zeros() as i64 } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bextr.64"] fn x86_bmi_bextr_64(x: u64, y: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs b/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs index 6151eee8bdbb5..81ab9e05add96 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs @@ -69,7 +69,7 @@ pub fn _pext_u64(a: u64, mask: u64) -> u64 { unsafe { x86_bmi2_pext_64(a, mask) } } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bzhi.64"] fn x86_bmi2_bzhi_64(x: u64, y: u64) -> u64; #[link_name = "llvm.x86.bmi.pdep.64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs b/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs index 28bf1951167a5..f50dacb4bee4f 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.fxsave64"] fn fxsave64(p: *mut u8); #[link_name = "llvm.x86.fxrstor64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse.rs b/library/stdarch/crates/core_arch/src/x86_64/sse.rs index 81e1070b55691..521652c126914 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse.rs @@ -6,7 +6,7 @@ use crate::core_arch::x86::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse.cvtss2si64"] fn cvtss2si64(a: __m128) -> i64; #[link_name = "llvm.x86.sse.cvttss2si64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs index 08dabf053d428..c4768cedbfa6b 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs @@ -6,7 +6,7 @@ use crate::core_arch::x86::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse2.cvtsd2si64"] fn cvtsd2si64(a: __m128d) -> i64; #[link_name = "llvm.x86.sse2.cvttsd2si64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse42.rs b/library/stdarch/crates/core_arch/src/x86_64/sse42.rs index cd32c149aff5b..307e6167aabae 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse42.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse42.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse42.crc32.64.64"] fn crc32_64_64(crc: u64, v: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/tbm.rs b/library/stdarch/crates/core_arch/src/x86_64/tbm.rs index fe12538b07a06..b4ec50a66fa0b 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/tbm.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/tbm.rs @@ -13,7 +13,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.tbm.bextri.u64"] fn bextri_u64(a: u64, control: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/xsave.rs b/library/stdarch/crates/core_arch/src/x86_64/xsave.rs index 30a7123315e5f..c307d40073433 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/xsave.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/xsave.rs @@ -6,7 +6,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xsave64"] fn xsave64(p: *mut u8, hi: u32, lo: u32); #[link_name = "llvm.x86.xrstor64"] diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs deleted file mode 100644 index 87b8ebfa183e7..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/arm/config.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub const NOTICE: &str = "\ -// This is a transient test file, not intended for distribution. Some aspects of the -// test are derived from a JSON specification, published under the same license as the -// `intrinsic-test` crate.\n"; - -pub const PLATFORM_RUST_DEFINITIONS: &str = ""; - -pub const PLATFORM_RUST_CFGS: &str = r#" -#![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))] -#![cfg_attr(target_arch = "arm", feature(stdarch_aarch32_crc32))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_dotprod))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_feat_lut))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fp8))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(faminmax))] -#![feature(stdarch_neon_f16)] - -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] -use core_arch::arch::aarch64::*; - -#[cfg(target_arch = "arm")] -use core_arch::arch::arm::*; -"#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index a54e5857192e0..bcbee3503b9ad 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -2,9 +2,9 @@ use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] -pub struct ArmIntrinsicType(pub IntrinsicType); +pub struct ArmType(pub IntrinsicType); -impl Deref for ArmIntrinsicType { +impl Deref for ArmType { type Target = IntrinsicType; fn deref(&self) -> &Self::Target { @@ -12,7 +12,7 @@ impl Deref for ArmIntrinsicType { } } -impl DerefMut for ArmIntrinsicType { +impl DerefMut for ArmType { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 06cf78a422285..26f861ca64b62 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -1,4 +1,5 @@ -use super::intrinsic::ArmIntrinsicType; +use super::intrinsic::ArmType; +use crate::arm::Arm; use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; @@ -59,7 +60,7 @@ struct JsonIntrinsic { pub fn get_neon_intrinsics( filename: &Path, -) -> Result>, Box> { +) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let json: Vec = serde_json::from_reader(reader).expect("Couldn't parse JSON"); @@ -79,10 +80,10 @@ pub fn get_neon_intrinsics( fn json_to_intrinsic( mut intr: JsonIntrinsic, -) -> Result, Box> { +) -> Result, Box> { let name = intr.name.replace(['[', ']'], ""); - let result_ty = ArmIntrinsicType(parse_intrinsic_type(&intr.return_type.value)?); + let result_ty = ArmType(parse_intrinsic_type(&intr.return_type.value)?); let args = intr .arguments @@ -120,12 +121,8 @@ fn json_to_intrinsic( } }); - let mut arg = Argument::::new( - i, - String::from(arg_name), - ArmIntrinsicType(arg_ty), - constraint, - ); + let mut arg = + Argument::::new(i, String::from(arg_name), ArmType(arg_ty), constraint); // The JSON doesn't list immediates as const let IntrinsicType { @@ -138,13 +135,14 @@ fn json_to_intrinsic( }) .collect(); - let arguments = ArgumentList:: { args }; + let arguments = ArgumentList:: { args }; Ok(Intrinsic { name, arguments, results: result_ty, arch_tags: intr.architectures, + extension: intr.simd_isa, }) } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 378f23ba7c361..a60e0ff1551a3 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -1,42 +1,48 @@ -mod config; mod intrinsic; mod json_parser; mod types; -use crate::common::SupportedArchitectureTest; +use crate::common::SupportedArchitecture; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::TypeKind; -use intrinsic::ArmIntrinsicType; +use crate::common::intrinsic_helpers::{SimdLen, TypeKind}; +use intrinsic::ArmType; use json_parser::get_neon_intrinsics; -pub struct ArmArchitectureTest { - intrinsics: Vec>, -} +#[derive(PartialEq)] +pub struct Arm(Vec>); -impl SupportedArchitectureTest for ArmArchitectureTest { - type IntrinsicImpl = ArmIntrinsicType; +impl SupportedArchitecture for Arm { + type Type = ArmType; - fn intrinsics(&self) -> &[Intrinsic] { - &self.intrinsics + fn intrinsics(&self) -> &[Intrinsic] { + &self.0 } - const NOTICE: &str = config::NOTICE; - - const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const NOTICE: &str = r#" +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from a JSON specification, published under the same license as the +// `intrinsic-test` crate. +"#; - const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; - const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; + const C_PRELUDE: &str = r#" +#include +#include +#include +"#; + const RUST_PRELUDE: &str = RUST_PRELUDE; - fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { // GCC uses an extra `-` in the arch name match cli_options.cc_arg_style { CcArgStyle::Clang => vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"], + // SVE tests aren't run under GCC so there are no target features added for SVE CcArgStyle::Gcc => vec!["-march=armv8.6-a+crypto+crc+dotprod+fp16+sha3+sm4"], } } fn create(cli_options: &ProcessedCli) -> Self { + let big_endian = cli_options.target.starts_with("aarch64_be"); let a32 = cli_options.target.starts_with("armv7"); let mut intrinsics = get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); @@ -54,6 +60,53 @@ impl SupportedArchitectureTest for ArmArchitectureTest { // Skip bfloat intrinsics - not currently supported .filter(|i| i.results.kind() != TypeKind::BFloat) .filter(|i| !i.arguments.iter().any(|a| a.ty.kind() == TypeKind::BFloat)) + // Skip SVE intrinsics that have `f16` - not yet implemented! + .filter(|i| { + let has_f16_arg = i + .arguments + .iter() + .any(|a| a.ty.kind() == TypeKind::Float && a.ty.bit_len == Some(16)); + let has_sve_arg = i + .arguments + .iter() + .any(|a| a.ty.simd_len == Some(SimdLen::Scalable)); + !(has_f16_arg && has_sve_arg) + }) + .filter(|i| { + let has_f16_ret = + i.results.kind() == TypeKind::Float && i.results.bit_len == Some(16); + let has_sve_ret = i.results.simd_len == Some(SimdLen::Scalable); + !(has_f16_ret && has_sve_ret) + }) + // Skip `svqcvtn{u,}n*_x2` intrinsics - not yet implemented! + .filter(|i| !(i.name.starts_with("svqcvtn") && i.name.ends_with("_x2"))) + // Skip `svqrshr{u,}n*_x2` intrinsics - not yet implemented! + .filter(|i| !(i.name.starts_with("svqrshrn") && i.name.ends_with("_x2"))) + .filter(|i| !(i.name.starts_with("svqrshrun") && i.name.ends_with("_x2"))) + // Skip `svclamp*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svclamp")) + // Skip `svdot{_lane,}_{s,u}32_{s,u}16` intrinsics - not yet implemented! + .filter(|i| { + i.name != "svdot_lane_u32_u16" + && i.name != "svdot_lane_s32_s16" + && i.name != "svdot_u32_u16" + && i.name != "svdot_s32_s16" + }) + // Skip `svrevd*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svrevd")) + // Skip `svpsel_lane_b*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svpsel_lane_b")) + // Skip `svundef*` intrinsics - to avoid undefined behaviour in Rust, these return + // zeroed vectors in Rust, which are inherently going to be different than the + // undefined vectors returned by the C intrinsics. + .filter(|i| !i.name.starts_with("svundef")) + // Skip `sveorv` intrinsics - the code produced by `intrinsic-test` for these + // miscompiles and the Rust intrinsic call gets replaced by a constant zero (see + // llvm/llvm-project#203921). + .filter(|i| !i.name.starts_with("sveorv")) + // These load intrinsics expect each element in the scalable vector `bases` argument to + // be able to be cast to a pointer, which we don't support generating tests for yet. + .filter(|i| !(i.name.starts_with("svld") && i.name.contains("_gather_"))) // Skip pointers for now, we would probably need to look at the return // type to work out how many elements we need to point to. .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) @@ -63,9 +116,36 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .filter(|i| !cli_options.skip.contains(&i.name)) // Skip A64-specific intrinsics on A32 .filter(|i| !(a32 && i.arch_tags == vec!["A64".to_string()])) + // Skip SVE intrinsics on big endian + .filter(|i| !(big_endian && (i.extension == "SVE" || i.extension == "SVE2"))) + // Skip SVE intrinsics when testing against GCC as our wrappers run into ICEs + // See + .filter(|i| { + !(matches!(cli_options.cc_arg_style, CcArgStyle::Gcc) + && (i.extension == "SVE" || i.extension == "SVE2")) + }) .take(sample_size) .collect::>(); - Self { intrinsics } + Self(intrinsics) } } + +const RUST_PRELUDE: &str = r#" +#![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))] +#![cfg_attr(target_arch = "arm", feature(stdarch_aarch32_crc32))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_feat_lut))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fp8))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(faminmax))] +#![feature(stdarch_neon_f16)] + +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] +use core_arch::arch::aarch64::*; + +#[cfg(target_arch = "arm")] +use core_arch::arch::arm::*; +"#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index cd420f10678fc..7754e9ec2d0dd 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,28 +1,37 @@ -use super::intrinsic::ArmIntrinsicType; -use crate::common::intrinsic_helpers::{ - IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, -}; +use super::intrinsic::ArmType; +use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; -impl IntrinsicTypeDefinition for ArmIntrinsicType { +impl TypeDefinition for ArmType { /// Gets a string containing the typename for this type in C format. fn c_type(&self) -> String { let prefix = self.kind.c_prefix(); - if let Some(bit_len) = self.bit_len { - match (self.simd_len, self.vec_len) { - (None, None) => format!("{prefix}{bit_len}_t"), - (Some(SimdLen::Fixed(simd)), None) => format!("{prefix}{bit_len}x{simd}_t"), - (Some(SimdLen::Fixed(simd)), Some(vec)) => { - format!("{prefix}{bit_len}x{simd}x{vec}_t") - } - (Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), - (Some(SimdLen::Scalable), Some(vec)) => { - format!("sv{prefix}{bit_len}x{vec}_t") - } - (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case + match (self.bit_len, self.simd_len, self.vec_len) { + // e.g. `bool` + (Some(_), None, None) if matches!(self.kind, TypeKind::Bool) => { + format!("{prefix}") } - } else { - todo!("{self:#?}") + // e.g. `float32_t`, `int64_t` + (Some(bit_len), None, None) => format!("{prefix}{bit_len}_t"), + // e.g. `float32x2_t`, `int64x2_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), None) => { + format!("{prefix}{bit_len}x{simd}_t") + } + // e.g. `float32x2x3_t`, `int64x2x3_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{prefix}{bit_len}x{simd}x{vec}_t") + } + // e.g. `svbool_t` + (Some(_), Some(SimdLen::Scalable), None) if matches!(self.kind, TypeKind::Bool) => { + format!("sv{prefix}_t") + } + // e.g. `svfloat32_t`, `svint64_t` + (Some(bit_len), Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), + // e.g. `svfloat32x3_t`, `svint64x3_t` + (Some(bit_len), Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{prefix}{bit_len}x{vec}_t") + } + _ => todo!("{self:#?}"), } } @@ -30,26 +39,39 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { let rust_prefix = self.kind.rust_prefix(); let c_prefix = self.kind.c_prefix(); - if let Some(bit_len) = self.bit_len { - match (self.simd_len, self.vec_len) { - (None, None) => format!("{rust_prefix}{bit_len}"), - (Some(SimdLen::Fixed(simd)), None) => format!("{c_prefix}{bit_len}x{simd}_t"), - (Some(SimdLen::Fixed(simd)), Some(vec)) => { - format!("{c_prefix}{bit_len}x{simd}x{vec}_t") - } - (Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), - (Some(SimdLen::Scalable), Some(vec)) => { - format!("sv{c_prefix}{bit_len}x{vec}_t") - } - (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case + match (self.bit_len, self.simd_len, self.vec_len) { + // e.g. `svpattern` + (None, _, _) => format!("{rust_prefix}"), + // e.g. `bool` + (Some(_), None, None) if matches!(self.kind, TypeKind::Bool) => { + format!("{rust_prefix}") } - } else { - todo!("{self:#?}") + // e.g. `i32` + (Some(bit_len), None, None) => format!("{rust_prefix}{bit_len}"), + // e.g. `int32x2_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), None) => { + format!("{c_prefix}{bit_len}x{simd}_t") + } + // e.g. `int32x2x3_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{c_prefix}{bit_len}x{simd}x{vec}_t") + } + // e.g. `svbool_t` + (Some(_), Some(SimdLen::Scalable), None) if matches!(self.kind, TypeKind::Bool) => { + format!("sv{c_prefix}_t") + } + // e.g. `svint32_t` + (Some(bit_len), Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), + // e.g. `svint32x3_t` + (Some(bit_len), Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{c_prefix}{bit_len}x{vec}_t") + } + (Some(_), None, Some(_)) => todo!("{self:#?}"), } } /// Determines the load function for this type. - fn get_load_function(&self) -> String { + fn load_function(&self) -> String { if let IntrinsicType { kind: k, bit_len: Some(bl), @@ -73,7 +95,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { len = vec_len.unwrap_or(1), ) } else { - todo!("get_load_function IntrinsicType: {self:#?}") + todo!("load_function IntrinsicType: {self:#?}") } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index eaec5b71c47e3..4d38bce327930 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -1,30 +1,31 @@ use itertools::Itertools; +use crate::common::SupportedArchitecture; use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::test_values_array_name; use super::PASSES; use super::constraint::Constraint; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use super::intrinsic_helpers::TypeDefinition; /// An argument for the intrinsic. #[derive(Debug, PartialEq, Clone)] -pub struct Argument { +pub struct Argument { /// The argument's index in the intrinsic function call. pub pos: usize, /// The argument name. pub name: String, /// The type of the argument. - pub ty: T, + pub ty: A::Type, /// Any constraints that are on this argument pub constraint: Option, } -impl Argument +impl Argument where - T: IntrinsicTypeDefinition, + A: SupportedArchitecture, { - pub fn new(pos: usize, name: String, ty: T, constraint: Option) -> Self { + pub fn new(pos: usize, name: String, ty: A::Type, constraint: Option) -> Self { Argument { pos, name, @@ -63,13 +64,13 @@ where /// Arguments of an intrinsic - including parameters that end up being const generics. #[derive(Debug, PartialEq, Clone)] -pub struct ArgumentList { - pub args: Vec>, +pub struct ArgumentList { + pub args: Vec>, } -impl ArgumentList +impl ArgumentList where - T: IntrinsicTypeDefinition, + A: SupportedArchitecture, { /// Returns a string with the arguments in `self` as a parameter list for a wrapper fn /// definition in C (e.g. `$ty1 $arg1, $ty2 $arg2`). @@ -179,14 +180,14 @@ where .map(|(idx, arg)| { if arg.is_simd() { format!( - "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", + "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);", name = arg.generate_name(), vals_name = test_values_array_name(&arg.ty), - load = arg.ty.get_load_function(), + load = arg.ty.load_function(), ) } else { format!( - "let {name} = {vals_name}[(i+{idx}) % {PASSES}];\n", + "let {name} = {vals_name}[(i+{idx}) % {PASSES}];", name = arg.generate_name(), vals_name = test_values_array_name(&arg.ty), ) @@ -196,7 +197,7 @@ where } /// Returns an iterator over the contained arguments - pub fn iter(&self) -> std::slice::Iter<'_, Argument> { + pub fn iter(&self) -> std::slice::Iter<'_, Argument> { self.args.iter() } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 24756324c48e4..21cc5cfa2e597 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -1,8 +1,8 @@ use itertools::Itertools; -use crate::common::intrinsic::Intrinsic; +use crate::common::{SupportedArchitecture, intrinsic::Intrinsic}; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use super::intrinsic_helpers::TypeDefinition; /// Generates a C source file containing wrapper functions around each specialisation of each /// intrinsic (that is, intrinsics with specific values for the the immediate arguments). Each @@ -14,20 +14,15 @@ use super::intrinsic_helpers::IntrinsicTypeDefinition; /// *__dst = __crc32cd(a, b); /// } /// ``` -pub fn write_wrapper_c( +pub fn write_wrapper_c( w: &mut impl std::io::Write, - notice: &str, - platform_headers: &[&str], - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { - write!(w, "{notice}")?; + write!(w, "{}", A::NOTICE)?; writeln!(w, "#include ")?; writeln!(w, "#include ")?; - - for header in platform_headers { - writeln!(w, "#include <{header}>")?; - } + writeln!(w, "{}", A::C_PRELUDE)?; for intrinsic in intrinsics { intrinsic.iter_specializations(|imm_values| { diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 02f6e40dc06a6..baa11511ec219 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -2,12 +2,11 @@ use std::process::Command; use itertools::Itertools; -use super::intrinsic_helpers::IntrinsicTypeDefinition; -use crate::common::PASSES; +use super::intrinsic_helpers::TypeDefinition; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::{test_values_array_name, test_values_array_static}; +use crate::common::{PASSES, SupportedArchitecture}; /// Rust definitions that are included verbatim in the generated source. In particular, defines /// a wrapper around float types that defines `NaN`s to be equal reflexively to enable @@ -32,12 +31,6 @@ macro_rules! wrap_partialeq { wrap_partialeq!(NanEqF16(f16), NanEqF32(f32), NanEqF64(f64)); "#; -macro_rules! concatln { - ($($lines:expr),* $(,)?) => { - concat!($( $lines, "\n" ),*) - }; -} - /// Run rustfmt on the generated source code pub fn run_rustfmt(source_path: &str) { let output = Command::new("rustfmt") @@ -65,11 +58,14 @@ pub fn write_bin_cargo_toml( w: &mut impl std::io::Write, module_count: usize, ) -> std::io::Result<()> { - write!(w, concatln!("[workspace]", "members = ["))?; - for i in 0..module_count { - writeln!(w, " \"mod_{i}\",")?; - } - writeln!(w, "]") + write!( + w, + r#" +[workspace] +members = [{members}] +"#, + members = (0..module_count).format_with(",", |i, fmt| fmt(&format_args!("\"mod_{i}\""))) + ) } /// Writes a `Cargo.toml` for a crate with name `name` to `w` that will contain a single Rust source @@ -77,21 +73,20 @@ pub fn write_bin_cargo_toml( pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io::Result<()> { write!( w, - concatln!( - "[package]", - "name = \"{name}\"", - "version = \"{version}\"", - "authors = [{authors}]", - "license = \"{license}\"", - "edition = \"2018\"", - "", - "[dependencies]", - "core_arch = {{ path = \"../../crates/core_arch\" }}", - "", - "[build-dependencies]", - "cc = \"1\"" - ), - name = name, + r#" +[package] +name = "{name}" +version = "{version}" +authors = [{authors}] +license = "{license}" +edition = "2018" + +[dependencies] +core_arch = {{ path = "../../crates/core_arch" }} + +[build-dependencies] +cc = "1" +"#, version = env!("CARGO_PKG_VERSION"), authors = env!("CARGO_PKG_AUTHORS") .split(":") @@ -102,30 +97,30 @@ pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io: /// Writes a Rust source file into `w` with common definitions, static arrays with test values, /// declarations of C wrapper functions for FFI and Rust test functions. -pub fn write_lib_rs( +pub fn write_lib_rs( w: &mut impl std::io::Write, - notice: &str, - cfg: &str, - definitions: &str, i: usize, - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { - write!(w, "{notice}")?; - - writeln!(w, "#![feature(simd_ffi)]")?; - writeln!(w, "#![feature(f16)]")?; - writeln!(w, "#![allow(unused)]")?; - - // Cargo will spam the logs if these warnings are not silenced. - writeln!(w, "#![allow(non_upper_case_globals)]")?; - writeln!(w, "#![allow(non_camel_case_types)]")?; - writeln!(w, "#![allow(non_snake_case)]")?; - - writeln!(w, "{cfg}")?; - - writeln!(w, "{}", COMMON_RUST_DEFINITIONS)?; - - writeln!(w, "{definitions}")?; + writeln!( + w, + r#" +{notice} +#![feature(simd_ffi)] +#![feature(f16)] +#![allow(unused)] + +// Cargo will spam the logs if these warnings are not silenced. +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +{prelude} +{COMMON_RUST_DEFINITIONS} +"#, + notice = A::NOTICE, + prelude = A::RUST_PRELUDE, + )?; let mut seen = std::collections::HashSet::new(); @@ -157,9 +152,9 @@ pub fn write_lib_rs( /// (first loop) `PASSES` number of times (second loop). For a given iteration of a given /// specialisation, test values are loaded for each argument and passed to the Rust intrinsic /// and the C wrapper function, and the results are compared. -fn generate_rust_test_loop( +fn generate_rust_test_loop( w: &mut impl std::io::Write, - intrinsic: &Intrinsic, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { let intrinsic_name = &intrinsic.name; @@ -199,67 +194,45 @@ fn generate_rust_test_loop( writeln!(w, " ];")?; } - let (cast_prefix, cast_suffix) = if intrinsic.results.is_simd() { - ( - format!( - "std::mem::transmute::<_, [{}; {}]>(", - intrinsic.results.rust_scalar_type().replace("f", "NanEqF"), - intrinsic.results.num_lanes() * intrinsic.results.num_vectors() - ), - ")", - ) - } else if intrinsic.results.kind == TypeKind::Float { - ( - match intrinsic.results.inner_size() { - 16 => format!("NanEqF16("), - 32 => format!("NanEqF32("), - 64 => format!("NanEqF64("), - _ => unimplemented!(), - }, - ")", - ) - } else { - ("".to_string(), "") - }; - write!( w, - concatln!( - " for (id, rust, c) in specializations {{", - " for i in 0..{passes} {{", - " unsafe {{", - "{loaded_args}", - " let __rust_return_value = rust({rust_args});", - "", - " let mut __c_return_value = std::mem::MaybeUninit::uninit();", - " c(__c_return_value.as_mut_ptr(){c_args});", - " let __c_return_value = __c_return_value.assume_init();", - "", - " assert_eq!({cast_prefix}__rust_return_value{cast_suffix}, {cast_prefix}__c_return_value{cast_suffix}, \"{{id}}\");", - " }}", - " }}", - " }}", - ), + r#" +for (id, rust, c) in specializations {{ + for i in 0..{PASSES} {{ + unsafe {{ + {loaded_args} + let __rust_return_value = rust({rust_args}); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + c(__c_return_value.as_mut_ptr(){c_args}); + let __c_return_value = __c_return_value.assume_init(); + + {comparison} + }} + }} +}} +"#, loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), - passes = PASSES, - cast_prefix = cast_prefix, - cast_suffix = cast_suffix, + comparison = intrinsic.results.comparison_function(), ) } /// Writes a test function for an given intrinsic to `w`, with a body generated by /// `generate_rust_test_loop`. -fn create_rust_test( +fn create_rust_test( w: &mut impl std::io::Write, - intrinsic: &Intrinsic, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { trace!("generating `{}`", intrinsic.name); write!( w, - concatln!("#[test]", "fn test_{intrinsic_name}() {{"), + r#" +#[test] +fn test_{intrinsic_name}() {{ +"#, intrinsic_name = intrinsic.name, )?; @@ -272,26 +245,25 @@ fn create_rust_test( /// Writes an `extern "C"` block with function declarations for each of the C wrapper functions into /// `w`. -pub fn write_bindings_rust( +pub fn write_bindings_rust( w: &mut impl std::io::Write, i: usize, - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!( w, - concatln!( - "#[allow(improper_ctypes)]", - "#[link(name = \"wrapper_{i}\")]", - "unsafe extern \"C\" {{" - ), - i = i + r#" +#[allow(improper_ctypes)] +#[link(name = "wrapper_{i}")] +unsafe extern "C" {{ +"#, )?; for intrinsic in intrinsics { intrinsic.iter_specializations(|imm_values| { writeln!( w, - " fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", + "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", return_ty = intrinsic.results.rust_type(), name = intrinsic.name, imm_arglist = imm_values @@ -326,32 +298,22 @@ pub fn write_build_rs( write!( w, - concatln!( - "fn main() {{", - " cc::Build::new()", - " .file(\"../../c_programs/wrapper_{i}.c\")", - " .opt_level(2)", - " .flags(&[", - ), - i = i - )?; - - let compiler_specific_flags = match cli_options.cc_arg_style { - CcArgStyle::Gcc => GCC_FLAGS, - CcArgStyle::Clang => CLANG_FLAGS, - }; - - for flag in COMMON_FLAGS - .iter() - .chain(compiler_specific_flags) - .chain(arch_flags) - { - writeln!(w, "\"{flag}\",")?; - } - - write!( - w, - concatln!(" ])", " .compile(\"wrapper_{i}\");", "}}"), - i = i + r#" +fn main() {{ + cc::Build::new() + .file("../../c_programs/wrapper_{i}.c") + .opt_level(2) + .flags(&[{flags}]) + .compile("wrapper_{i}"); +}} +"#, + flags = COMMON_FLAGS + .iter() + .chain(match cli_options.cc_arg_style { + CcArgStyle::Gcc => GCC_FLAGS, + CcArgStyle::Clang => CLANG_FLAGS, + }) + .chain(arch_flags) + .format_with(",", |flag, fmt| fmt(&format_args!("\"{flag}\""))), ) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index d69644388a830..0c5bf43069d00 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,22 +1,23 @@ -use crate::common::constraint::Constraint; - use super::argument::ArgumentList; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use crate::common::{SupportedArchitecture, constraint::Constraint}; /// An intrinsic #[derive(Debug, PartialEq, Clone)] -pub struct Intrinsic { +pub struct Intrinsic { /// The function name of this intrinsic. pub name: String, /// Any arguments for this intrinsic. - pub arguments: ArgumentList, + pub arguments: ArgumentList, /// The return type of this intrinsic. - pub results: T, + pub results: A::Type, /// Any architecture-specific tags. pub arch_tags: Vec, + + /// Specific extension that the intrinsic is from + pub extension: String, } /// Invokes `f` for each combination of the values in the constraint ranges. @@ -40,7 +41,7 @@ fn recurse_specializations<'a, E>( } } -impl Intrinsic { +impl Intrinsic { /// Invokes `f` for "specialisation" of the intrinsic - a specific instantiation of the /// constant generics of the intrinsic. `f` takes a slice where the `i`th element corresponds /// to the value of the `i`th const generic argument of the intrinsic. diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index ca5aeba86dab9..b9f30af7dfd51 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -1,6 +1,6 @@ use std::cmp; use std::fmt; -use std::ops::Deref; +use std::ops::DerefMut; use std::str::FromStr; #[derive(Debug, PartialEq, Copy, Clone)] @@ -90,9 +90,13 @@ impl TypeKind { } } - /// Returns the Rust prefix for this type kind i.e. `i`, `u`, or `f`. + /// Returns the Rust prefix for this type kind (i.e. `i` for `i16`, or `u` for `u16`). For type + /// kinds without any bit length at the end (e.g. `bool`), returns the whole type name. pub fn rust_prefix(&self) -> &str { match self { + Self::Bool => "bool", + Self::SvPattern => "svpattern", + Self::SvPrefetchOp => "svprfop", Self::BFloat => "bf", Self::Float => "f", Self::Int(Sign::Signed) => "i", @@ -101,7 +105,7 @@ impl TypeKind { Self::Char(Sign::Unsigned) => "u", Self::Char(Sign::Signed) => "i", Self::Mask => "u", - _ => unreachable!("Unused type kind: {self:#?}"), + _ => unreachable!("type kind without Rust prefix: {self:#?}"), } } } @@ -195,9 +199,19 @@ impl IntrinsicType { } } -pub trait IntrinsicTypeDefinition: Deref { +pub trait TypeDefinition: Clone + DerefMut { /// Determines the load function for this type. - fn get_load_function(&self) -> String; + fn load_function(&self) -> String; + + /// Determines the comparison function for this type. + fn comparison_function(&self) -> String { + match self.simd_len { + Some(SimdLen::Scalable) => unimplemented!("architecture-specific"), + Some(SimdLen::Fixed(_)) | None => { + default_fixed_vector_comparison(self, self.num_lanes()) + } + } + } /// Gets a string containing the typename for this type in C. fn c_type(&self) -> String; @@ -208,14 +222,49 @@ pub trait IntrinsicTypeDefinition: Deref { /// Gets a string containing the name of the scalar type corresponding to this type if it is a /// vector. fn rust_scalar_type(&self) -> String { - if self.is_simd() { - format!( - "{prefix}{bits}", - prefix = self.kind().rust_prefix(), - bits = self.inner_size() - ) - } else { - self.rust_type() - } + let mut ty = self.clone(); + ty.simd_len = None; + ty.vec_len = None; + ty.rust_type() } } + +/// Returns the default comparison between results of an intrinsic - casting the vectors to arrays +/// and using `assert_eq` - using `NanEqF*` where required for floats. +pub(crate) fn default_fixed_vector_comparison( + ty: &Ty, + num_lanes: u32, +) -> String { + let (cast_prefix, cast_suffix) = if ty.is_simd() { + ( + format!( + "std::mem::transmute::<_, [{}; {}]>(", + ty.rust_scalar_type().replace("f", "NanEqF"), + num_lanes * ty.num_vectors() + ), + ")", + ) + } else if ty.kind == TypeKind::Float { + ( + match ty.inner_size() { + 16 => format!("NanEqF16("), + 32 => format!("NanEqF32("), + 64 => format!("NanEqF64("), + _ => unimplemented!(), + }, + ")", + ) + } else { + ("".to_string(), "") + }; + + format!( + r#" +assert_eq!( + {cast_prefix}__rust_return_value{cast_suffix}, + {cast_prefix}__c_return_value{cast_suffix}, + "{{id}}" +); +"#, + ) +} diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index b577491454e44..73daabbd6661a 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -10,7 +10,7 @@ use crate::common::{ run_rustfmt, write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs, }, intrinsic::Intrinsic, - intrinsic_helpers::IntrinsicTypeDefinition, + intrinsic_helpers::TypeDefinition, }; pub mod argument; @@ -29,21 +29,19 @@ pub(crate) const PASSES: u32 = 20; /// Architectures must support this trait /// to be successfully tested. -pub trait SupportedArchitectureTest { - type IntrinsicImpl: IntrinsicTypeDefinition + Sync; +pub trait SupportedArchitecture: Sized { + type Type: TypeDefinition + std::fmt::Debug + PartialEq + Sync; - fn intrinsics(&self) -> &[Intrinsic]; + fn intrinsics(&self) -> &[Intrinsic]; fn create(cli_options: &ProcessedCli) -> Self; const NOTICE: &str; - const PLATFORM_C_HEADERS: &[&str]; + const C_PRELUDE: &str; + const RUST_PRELUDE: &str; - const PLATFORM_RUST_CFGS: &str; - const PLATFORM_RUST_DEFINITIONS: &str; - - fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; fn generate_c_file(&self) { let (max_chunk_size, _chunk_count) = manual_chunk(self.intrinsics().len()); @@ -55,14 +53,14 @@ pub trait SupportedArchitectureTest { .map(|(i, chunk)| { let c_filename = format!("c_programs/wrapper_{i}.c"); let mut file = File::create(&c_filename).unwrap(); - write_wrapper_c(&mut file, Self::NOTICE, Self::PLATFORM_C_HEADERS, chunk) + write_wrapper_c(&mut file, chunk) }) .collect::>() .unwrap(); } fn generate_rust_file(&self, cli_options: &ProcessedCli) { - let arch_flags = self.arch_flags(cli_options); + let arch_flags = self.c_compiler_flags(cli_options); std::fs::create_dir_all("rust_programs").unwrap(); @@ -81,14 +79,7 @@ pub trait SupportedArchitectureTest { trace!("generating `{rust_filename}`"); let mut file = File::create(&rust_filename)?; - write_lib_rs( - &mut file, - Self::NOTICE, - Self::PLATFORM_RUST_CFGS, - Self::PLATFORM_RUST_DEFINITIONS, - i, - chunk, - )?; + write_lib_rs(&mut file, i, chunk)?; run_rustfmt(&rust_filename); let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs index 4c3dd078e034f..8c549346ce6ed 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/values.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs @@ -2,7 +2,7 @@ use itertools::Itertools as _; use crate::common::{ PASSES, - intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind}, + intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}, }; /// Maximum size of a SVE vector @@ -18,13 +18,13 @@ pub const MAX_SVE_BITS: u32 = 2048; /// 0x80, 0x3b, 0xff, /// ]; /// ``` -pub fn test_values_array_static( +pub fn test_values_array_static( w: &mut impl std::io::Write, ty: &T, ) -> std::io::Result<()> { writeln!( w, - "static {name}: [{ty}; {load_size}] = {values};\n", + "static {name}: [{ty}; {load_size}] = {values};", name = test_values_array_name(ty), ty = ty.rust_scalar_type(), load_size = test_values_array_length(&ty), @@ -34,7 +34,7 @@ pub fn test_values_array_static( /// Returns a string with the name of the static variable containing test values for intrinsic /// arguments of this type. -pub fn test_values_array_name(ty: &T) -> String { +pub fn test_values_array_name(ty: &T) -> String { format!( "{ty}_{load_size}", ty = ty.rust_scalar_type().to_uppercase(), diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index 4c0136041fc35..e25eb48a456d1 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -5,10 +5,10 @@ mod arm; mod common; mod x86; -use arm::ArmArchitectureTest; -use common::SupportedArchitectureTest; +use arm::Arm; +use common::SupportedArchitecture; use common::cli::{Cli, ProcessedCli}; -use x86::X86ArchitectureTest; +use x86::X86; fn main() { pretty_env_logger::init(); @@ -18,21 +18,15 @@ fn main() { if processed_cli_options.target.starts_with("arm") | processed_cli_options.target.starts_with("aarch64") { - run( - ArmArchitectureTest::create(&processed_cli_options), - processed_cli_options, - ) + run(Arm::create(&processed_cli_options), processed_cli_options) } else if processed_cli_options.target.starts_with("x86") { - run( - X86ArchitectureTest::create(&processed_cli_options), - processed_cli_options, - ) + run(X86::create(&processed_cli_options), processed_cli_options) } else { unimplemented!("Unsupported target {}", processed_cli_options.target) } } -fn run(test_environment: impl SupportedArchitectureTest, processed_cli_options: ProcessedCli) { +fn run(test_environment: impl SupportedArchitecture, processed_cli_options: ProcessedCli) { info!("building C binaries"); test_environment.generate_c_file(); diff --git a/library/stdarch/crates/intrinsic-test/src/x86/config.rs b/library/stdarch/crates/intrinsic-test/src/x86/config.rs deleted file mode 100644 index 68737ab5ac4c8..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/x86/config.rs +++ /dev/null @@ -1,138 +0,0 @@ -pub const NOTICE: &str = "\ -// This is a transient test file, not intended for distribution. Some aspects of the -// test are derived from an XML specification, published under the same license as the -// `intrinsic-test` crate.\n"; - -pub const PLATFORM_RUST_DEFINITIONS: &str = r#" -use core_arch::arch::x86_64::*; - -#[inline] -unsafe fn _mm_loadu_ph_to___m128i(mem_addr: *const f16) -> __m128i { - _mm_castph_si128(_mm_loadu_ph(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_ph_to___m256i(mem_addr: *const f16) -> __m256i { - _mm256_castph_si256(_mm256_loadu_ph(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_ph_to___mm512i(mem_addr: *const f16) -> __m512i { - _mm512_castph_si512(_mm512_loadu_ph(mem_addr)) -} - - -#[inline] -unsafe fn _mm_loadu_ps_to___m128h(mem_addr: *const f32) -> __m128h { - _mm_castps_ph(_mm_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_ps_to___m256h(mem_addr: *const f32) -> __m256h { - _mm256_castps_ph(_mm256_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_ps_to___m512h(mem_addr: *const f32) -> __m512h { - _mm512_castps_ph(_mm512_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi16_to___m128d(mem_addr: *const i16) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi16_to___m256d(mem_addr: *const i16) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi16_to___m512d(mem_addr: *const i16) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi32_to___m128d(mem_addr: *const i32) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi32_to___m256d(mem_addr: *const i32) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi32_to___m512d(mem_addr: *const i32) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi64_to___m128d(mem_addr: *const i64) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi64_to___m256d(mem_addr: *const i64) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi64_to___m512d(mem_addr: *const i64) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi64(mem_addr)) -} - -// === -#[inline] -unsafe fn _mm_loadu_epi16_to___m128(mem_addr: *const i16) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi16_to___m256(mem_addr: *const i16) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi16_to___m512(mem_addr: *const i16) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi32_to___m128(mem_addr: *const i32) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi32_to___m256(mem_addr: *const i32) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi32_to___m512(mem_addr: *const i32) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi64_to___m128(mem_addr: *const i64) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi64_to___m256(mem_addr: *const i64) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi64_to___m512(mem_addr: *const i64) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi64(mem_addr)) -} - -"#; - -pub const PLATFORM_RUST_CFGS: &str = r#" -#![feature(stdarch_x86_avx512_bf16)] -#![feature(stdarch_x86_avx512_f16)] -#![feature(stdarch_x86_rtm)] -#![feature(x86_amx_intrinsics)] -"#; diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index 288bd8bdf8961..ce4955032916d 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -1,35 +1,38 @@ -mod config; mod constraint; mod intrinsic; mod types; mod xml_parser; -use crate::common::SupportedArchitectureTest; +use crate::common::SupportedArchitecture; use crate::common::cli::ProcessedCli; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use intrinsic::X86IntrinsicType; use xml_parser::get_xml_intrinsics; -pub struct X86ArchitectureTest { - intrinsics: Vec>, +pub struct X86 { + intrinsics: Vec>, } -impl SupportedArchitectureTest for X86ArchitectureTest { - type IntrinsicImpl = X86IntrinsicType; +impl SupportedArchitecture for X86 { + type Type = X86IntrinsicType; - fn intrinsics(&self) -> &[Intrinsic] { + fn intrinsics(&self) -> &[Intrinsic] { &self.intrinsics } - const NOTICE: &str = config::NOTICE; + const NOTICE: &str = r#" +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from an XML specification, published under the same license as the +// `intrinsic-test` crate. +"#; - const PLATFORM_C_HEADERS: &[&str] = &["immintrin.h"]; + const C_PRELUDE: &str = r#" +#include +"#; + const RUST_PRELUDE: &str = RUST_PRELUDE; - const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; - const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; - - fn arch_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { + fn c_compiler_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { vec![ "-maes", "-mf16c", @@ -97,3 +100,134 @@ impl SupportedArchitectureTest for X86ArchitectureTest { Self { intrinsics } } } + +const RUST_PRELUDE: &str = r#" +#![feature(stdarch_x86_avx512_bf16)] +#![feature(stdarch_x86_avx512_f16)] +#![feature(stdarch_x86_rtm)] +#![feature(x86_amx_intrinsics)] + +use core_arch::arch::x86_64::*; + +#[inline] +unsafe fn _mm_loadu_ph_to___m128i(mem_addr: *const f16) -> __m128i { + _mm_castph_si128(_mm_loadu_ph(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_ph_to___m256i(mem_addr: *const f16) -> __m256i { + _mm256_castph_si256(_mm256_loadu_ph(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_ph_to___mm512i(mem_addr: *const f16) -> __m512i { + _mm512_castph_si512(_mm512_loadu_ph(mem_addr)) +} + + +#[inline] +unsafe fn _mm_loadu_ps_to___m128h(mem_addr: *const f32) -> __m128h { + _mm_castps_ph(_mm_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_ps_to___m256h(mem_addr: *const f32) -> __m256h { + _mm256_castps_ph(_mm256_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_ps_to___m512h(mem_addr: *const f32) -> __m512h { + _mm512_castps_ph(_mm512_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi16_to___m128d(mem_addr: *const i16) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi16_to___m256d(mem_addr: *const i16) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi16_to___m512d(mem_addr: *const i16) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi32_to___m128d(mem_addr: *const i32) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi32_to___m256d(mem_addr: *const i32) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi32_to___m512d(mem_addr: *const i32) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi64_to___m128d(mem_addr: *const i64) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi64_to___m256d(mem_addr: *const i64) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi64_to___m512d(mem_addr: *const i64) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi64(mem_addr)) +} + +// === +#[inline] +unsafe fn _mm_loadu_epi16_to___m128(mem_addr: *const i16) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi16_to___m256(mem_addr: *const i16) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi16_to___m512(mem_addr: *const i16) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi32_to___m128(mem_addr: *const i32) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi32_to___m256(mem_addr: *const i32) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi32_to___m512(mem_addr: *const i32) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi64_to___m128(mem_addr: *const i64) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi64_to___m256(mem_addr: *const i64) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi64_to___m512(mem_addr: *const i64) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi64(mem_addr)) +} +"#; diff --git a/library/stdarch/crates/intrinsic-test/src/x86/types.rs b/library/stdarch/crates/intrinsic-test/src/x86/types.rs index a0e14c77d6b5e..2cba54a73f767 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/types.rs @@ -3,12 +3,10 @@ use std::str::FromStr; use itertools::Itertools; use super::intrinsic::X86IntrinsicType; -use crate::common::intrinsic_helpers::{ - IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, -}; +use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; use crate::x86::xml_parser::Parameter; -impl IntrinsicTypeDefinition for X86IntrinsicType { +impl TypeDefinition for X86IntrinsicType { /// Gets a string containing the type in C format. /// This function assumes that this value is present in the metadata hashmap. fn c_type(&self) -> String { @@ -50,7 +48,7 @@ impl IntrinsicTypeDefinition for X86IntrinsicType { } /// Determines the load function for this type. - fn get_load_function(&self) -> String { + fn load_function(&self) -> String { let type_value = self.param.type_data.clone(); if type_value.len() == 0 { unimplemented!("the value for key 'type' is not present!"); diff --git a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs index 6006d7919f875..52f6da786e3d7 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs @@ -1,6 +1,7 @@ use crate::common::argument::{Argument, ArgumentList}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; +use crate::x86::X86; use crate::x86::constraint::map_constraints; use regex::Regex; @@ -56,13 +57,13 @@ pub struct Parameter { pub fn get_xml_intrinsics( filename: &Path, -) -> Result>, Box> { +) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let data: Data = quick_xml::de::from_reader(reader).expect("failed to deserialize the source XML file"); - let parsed_intrinsics: Vec> = data + let parsed_intrinsics: Vec> = data .intrinsics .into_iter() .filter(|intrinsic| { @@ -84,9 +85,7 @@ pub fn get_xml_intrinsics( Ok(parsed_intrinsics) } -fn xml_to_intrinsic( - intr: XMLIntrinsic, -) -> Result, Box> { +fn xml_to_intrinsic(intr: XMLIntrinsic) -> Result, Box> { let name = intr.name; let result = X86IntrinsicType::from_param(&intr.return_data); let args_check = intr.parameters.into_iter().enumerate().map(|(i, param)| { @@ -100,12 +99,7 @@ fn xml_to_intrinsic( param.imm_width }; let constraint = map_constraints(&name, ¶m.imm_type, effective_imm_width); - let arg = Argument::::new( - i, - param.var_name.clone(), - ty.unwrap(), - constraint, - ); + let arg = Argument::::new(i, param.var_name.clone(), ty.unwrap(), constraint); Some(arg) } }); @@ -125,8 +119,8 @@ fn xml_to_intrinsic( // if one of the args has etype="MASK" and type="__md", // then set the bit_len and simd_len accordingly let re = Regex::new(r"__m\d+").unwrap(); - let is_mask = |arg: &Argument| arg.ty.param.etype.as_str() == "MASK"; - let is_vector = |arg: &Argument| re.is_match(arg.ty.param.type_data.as_str()); + let is_mask = |arg: &Argument| arg.ty.param.etype.as_str() == "MASK"; + let is_vector = |arg: &Argument| re.is_match(arg.ty.param.type_data.as_str()); let pos = args_test.position(|arg| is_mask(arg) && is_vector(arg)); if let Some(index) = pos { args[index].ty.bit_len = args[0].ty.bit_len; @@ -134,7 +128,7 @@ fn xml_to_intrinsic( args.iter_mut().for_each(|arg| arg.ty.update_simd_len()); - let arguments = ArgumentList:: { args }; + let arguments = ArgumentList:: { args }; if let Err(message) = result { return Err(Box::from(message)); @@ -145,5 +139,6 @@ fn xml_to_intrinsic( arguments, results: result.unwrap(), arch_tags: intr.cpuid, + extension: intr.tech, }) } diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index 03fea5e0a4af3..e5ce77ed8b33f 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -13951,7 +13951,7 @@ intrinsics: - 'b: {neon_type[1]}' - 'n: i32' links: - - link: "llvm.aarch64.neon.vluti4q.lane.{neon_type[1]}" + - link: "llvm.aarch64.neon.vluti4q.lane.{neon_type[0]}" arch: aarch64,arm64ec - FnCall: ['_vluti4{neon_type[0].lane_nox}', [a, b, LANE]] @@ -14002,7 +14002,7 @@ intrinsics: - 'b: {neon_type[1]}' - 'n: i32' links: - - link: "llvm.aarch64.neon.vluti4q.laneq.{neon_type[1]}" + - link: "llvm.aarch64.neon.vluti4q.laneq.{neon_type[0]}" arch: aarch64,arm64ec - FnCall: ['_vluti4{neon_type[0].laneq_nox}', [a, b, LANE]] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml index 29dd3a095d7e6..138d5ba31175b 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml @@ -115,7 +115,7 @@ intrinsics: assert_instr: [[fcmla, "IMM_INDEX = 0, IMM_ROTATION = 90"]] compose: - LLVMLink: - name: fcmla.lane.x.{sve_type} + name: fcmla.lane.{sve_type} arguments: - "op1: {sve_type}" - "op2: {sve_type}" @@ -1021,7 +1021,7 @@ intrinsics: doc: Interleave elements from low halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [zip1] compose: - LLVMLink: { name: "zip1.{sve_type}" } @@ -1031,10 +1031,13 @@ intrinsics: doc: Interleave elements from low halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [zip1] compose: - - LLVMLink: { name: "zip1.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.zip1.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svzip1q[_{type}] attr: [*sve-unstable] @@ -1052,7 +1055,7 @@ intrinsics: doc: Interleave elements from high halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [zip2] compose: - LLVMLink: { name: "zip2.{sve_type}" } @@ -1062,10 +1065,13 @@ intrinsics: doc: Interleave elements from high halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [zip2] compose: - - LLVMLink: { name: "zip2.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.zip2.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svzip2q[_{type}] attr: [*sve-unstable] @@ -1083,7 +1089,7 @@ intrinsics: doc: Concatenate even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [uzp1] compose: - LLVMLink: { name: "uzp1.{sve_type}" } @@ -1093,10 +1099,13 @@ intrinsics: doc: Concatenate even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [uzp1] compose: - - LLVMLink: { name: "uzp1.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.uzp1.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svuzp1q[_{type}] attr: [*sve-unstable] @@ -1114,7 +1123,7 @@ intrinsics: doc: Concatenate odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [uzp2] compose: - LLVMLink: { name: "uzp2.{sve_type}" } @@ -1124,10 +1133,13 @@ intrinsics: doc: Concatenate odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [uzp2] compose: - - LLVMLink: { name: "uzp2.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.uzp2.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svuzp2q[_{type}] attr: [*sve-unstable] @@ -1207,7 +1219,7 @@ intrinsics: doc: Reverse all elements arguments: ["op: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [rev] compose: - LLVMLink: { name: "llvm.vector.reverse.{sve_type}" } @@ -1217,10 +1229,13 @@ intrinsics: doc: Reverse all elements arguments: ["op: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [rev] compose: - - LLVMLink: { name: "llvm.vector.reverse.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.rev.b{size}" + arguments: ["op: svbool_t"] + return_type: "svbool_t" - name: svrevb[_{type}]{_mxz} attr: [*sve-unstable] @@ -4179,7 +4194,8 @@ intrinsics: ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] return_type: "{sve_type[0]}" types: - - [[f32, f64], [i32, u32, i64, u64]] + - [f32, [i64, u64]] + - [f64, [i32, u32]] zeroing_method: { drop: inactive } substitutions: convert_from: { match_kind: "{type[1]}", default: s, unsigned: u } @@ -4187,6 +4203,23 @@ intrinsics: compose: - LLVMLink: name: "{convert_from}cvtf.{type[0]}{type[1]}" + + - name: svcvt_{type[0]}[_{type[1]}]{_mxz} + attr: [*sve-unstable] + doc: Floating-point convert + arguments: + ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] + return_type: "{sve_type[0]}" + types: + - [f32, [i32, u32]] + - [f64, [i64, u64]] + zeroing_method: { drop: inactive } + substitutions: + convert_from: { match_kind: "{type[1]}", default: s, unsigned: u } + assert_instr: ["{convert_from}cvtf"] + compose: + - LLVMLink: + name: "{convert_from}cvtf.{sve_type[0]}.{sve_type[1]}" - name: svcvt_{type[0]}[_{type[1]}]{_mxz} attr: [*sve-unstable] @@ -4195,13 +4228,30 @@ intrinsics: ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] return_type: "{sve_type[0]}" types: - - [[i32, u32, i64, u64], [f32, f64]] + - [[i32, u32], f64] + - [[i64, u64], f32] zeroing_method: { drop: inactive } substitutions: convert_to: { match_kind: "{type[0]}", default: s, unsigned: u } assert_instr: ["fcvtz{convert_to}"] compose: - LLVMLink: { name: "fcvtz{convert_to}.{type[0]}{type[1]}" } + + - name: svcvt_{type[0]}[_{type[1]}]{_mxz} + attr: [*sve-unstable] + doc: Floating-point convert + arguments: + ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] + return_type: "{sve_type[0]}" + types: + - [[i32, u32], f32] + - [[i64, u64], f64] + zeroing_method: { drop: inactive } + substitutions: + convert_to: { match_kind: "{type[0]}", default: s, unsigned: u } + assert_instr: ["fcvtz{convert_to}"] + compose: + - LLVMLink: { name: "fcvtz{convert_to}.{sve_type[0]}.{sve_type[1]}" } - name: svcvt_{type[0]}[_{type[1]}]{_mxz} attr: [*sve-unstable] @@ -4356,7 +4406,7 @@ intrinsics: return_type: svbool_t assert_instr: [and] compose: - - LLVMLink: { name: "and.z.nvx16i1" } + - LLVMLink: { name: "and.z.nxv16i1" } - name: svmov[_b]_z attr: [*sve-unstable] @@ -4386,7 +4436,7 @@ intrinsics: return_type: svbool_t assert_instr: [bic] compose: - - LLVMLink: { name: "bic.z.nvx16i1" } + - LLVMLink: { name: "bic.z.nxv16i1" } - name: sveor[{_n}_{type}]{_mxz} attr: [*sve-unstable] @@ -4417,7 +4467,7 @@ intrinsics: return_type: svbool_t assert_instr: [eor] compose: - - LLVMLink: { name: "eor.z.nvx16i1" } + - LLVMLink: { name: "eor.z.nxv16i1" } - name: svnot[_{type}]{_mxz} attr: [*sve-unstable] @@ -4497,7 +4547,7 @@ intrinsics: return_type: svbool_t assert_instr: [orr] compose: - - LLVMLink: { name: "orr.z.nvx16i1" } + - LLVMLink: { name: "orr.z.nxv16i1" } - name: svorn[_b]_z attr: [*sve-unstable] @@ -4506,7 +4556,7 @@ intrinsics: return_type: svbool_t assert_instr: [orn] compose: - - LLVMLink: { name: "orn.z.nvx16i1" } + - LLVMLink: { name: "orn.z.nxv16i1" } - name: svlsl[{_n}_{type[0]}]{_mxz} attr: [*sve-unstable] @@ -4749,7 +4799,7 @@ intrinsics: assert_instr: [frecpx] zeroing_method: { drop: inactive } compose: - - LLVMLink: { name: "frecpx.x.{sve_type}" } + - LLVMLink: { name: "frecpx.{sve_type}" } - name: svrsqrte[_{type}] attr: [*sve-unstable] @@ -5115,7 +5165,7 @@ intrinsics: types: [[f32, u32], [f64, u64]] assert_instr: [fexpa] compose: - - LLVMLink: { name: "fexpa.x.{sve_type[0]} " } + - LLVMLink: { name: "fexpa.x.{sve_type[0]}" } - name: svscale[{_n}_{type[0]}]{_mxz} attr: [*sve-unstable] @@ -5139,7 +5189,7 @@ intrinsics: types: [f32] assert_instr: [fmmla] compose: - - LLVMLink: { name: "fmmla.{sve_type}" } + - LLVMLink: { name: "fmmla.{sve_type}.{sve_type}" } - name: svmmla[_{type}] attr: [*sve-unstable] @@ -5150,7 +5200,7 @@ intrinsics: types: [f64] assert_instr: [fmmla] compose: - - LLVMLink: { name: "fmmla.{sve_type}" } + - LLVMLink: { name: "fmmla.{sve_type}.{sve_type}" } - name: svmmla[_{type[0]}] attr: [*sve-unstable] diff --git a/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs b/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs index 72fb97fee1f08..f96f05dfec680 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs @@ -1604,6 +1604,7 @@ impl Intrinsic { (Some(BaseTypeKind::Float), Some(BaseTypeKind::Float)) => ex, (Some(BaseTypeKind::UInt), Some(BaseTypeKind::UInt)) => ex, (Some(BaseTypeKind::Poly), Some(BaseTypeKind::Poly)) => ex, + (Some(BaseTypeKind::Bool), Some(BaseTypeKind::Bool)) => ex, (None, None) => ex, _ => unreachable!( diff --git a/library/stdarch/crates/stdarch-gen-common/Cargo.toml b/library/stdarch/crates/stdarch-gen-common/Cargo.toml new file mode 100644 index 0000000000000..691be14971a0d --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-common/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "stdarch-gen-common" +version = "0.1.0" +edition = "2024" + +[dependencies] +tempfile = "3" \ No newline at end of file diff --git a/library/stdarch/crates/stdarch-gen-common/src/lib.rs b/library/stdarch/crates/stdarch-gen-common/src/lib.rs new file mode 100644 index 0000000000000..13d788594ca55 --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-common/src/lib.rs @@ -0,0 +1,352 @@ +//! Shared check/bless harness for stdarch generators. + +use std::error::Error as StdError; +use std::fmt; +use std::fs; +use std::io; +use std::io::Read; +use std::path::{Path, PathBuf}; + +/// First-line marker identifying an auto-generated file. Generators emit this +/// as the first line of every file they produce; the harness uses it to +/// discover which files in `committed` are owned by the generator. +pub const GENERATED_MARKER: &str = "// This code is automatically generated. DO NOT MODIFY."; + +/// Controls what `run_generator` does with the generator's output. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Mode { + /// Verify that the `committed` matches the generator's output for owned files. + /// + /// Runs the generator into a temp directory, then compares each produced + /// file against the committed copy. Returns an error on the first mismatch. + Check, + /// Update the `committed` to match the generator's output for owned files. + /// + /// Runs the generator into a temp directory and copies each produced file + /// into `committed`. If the generator no longer produces an owned file, the + /// committed copy is deleted. Files in `committed` that are not owned + /// are left untouched. + Bless, +} + +impl Mode { + /// Read the mode from the `STDARCH_GEN_MODE` environment variable. + /// + /// Recognized values: + /// - `"check"` → [`Mode::Check`] + /// - `"bless"` → [`Mode::Bless`] + /// - unset → [`Mode::Bless`] + /// - any other value → panic + pub fn from_env() -> Self { + match std::env::var("STDARCH_GEN_MODE").as_deref() { + Ok("check") => Mode::Check, + Ok("bless") => Mode::Bless, + Ok(other) => panic!("unknown STDARCH_GEN_MODE value: {other:?}"), + Err(_) => Mode::Bless, + } + } +} + +#[derive(Debug)] +pub enum Error { + Io(io::Error), + Mismatch { path: PathBuf, kind: MismatchKind }, + Generator(Box), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MismatchKind { + /// Owned file produced by the generator but absent from the `committed`. + /// Means the `committed` needs to be regenerated. + MissingInCommitted, + /// Owned file present in the `committed` but the generator no longer + /// produces it. The file must be removed from the `committed` . + ExtraInCommitted, + /// Owned file exists on both sides but contents differ. + ContentsDiffer, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Io(e) => write!(f, "I/O error: {e}"), + Error::Mismatch { path, kind } => match kind { + MismatchKind::MissingInCommitted => { + write!(f, "{}: generated but not committed", path.display()) + } + MismatchKind::ExtraInCommitted => { + write!(f, "{}: committed but no longer generated", path.display()) + } + MismatchKind::ContentsDiffer => write!(f, "{}: contents differ", path.display()), + }, + Error::Generator(e) => write!(f, "generator failed: {e}"), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(e) => Some(e), + Error::Generator(e) => Some(&**e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +pub type Result = std::result::Result; + +/// Run a generator under the chosen `mode`, reconciling its output with `committed`. +/// +/// Arguments: +/// - `committed` — the directory holding the in-tree (committed) source files. +/// Files inside `committed` whose first line begins with [`GENERATED_MARKER`] +/// are treated as owned by the generator. Anything else is treated as +/// hand-written and is left untouched by `Bless` and ignored by `Check`. +/// So generated files coexist with hand-written files in the same directory. +/// - `mode` — what to do with the generator's output. +/// - `generate` — closure that writes the generator's output into the +/// directory it is given. Its error is wrapped in [`Error::Generator`]. +/// +/// Behavior per mode: +/// - [`Mode::Check`]: runs the generator into a temp dir and compares owned +/// files against the committed copies. Mismatch returns [`Error::Mismatch`]. +/// - [`Mode::Bless`]: runs the generator into a temp dir and copies owned +/// files into `committed`, or removes `committed`'s copy if the generator no +/// longer produces them. +pub fn run_generator(committed: &Path, mode: Mode, generate: F) -> Result<()> +where + F: FnOnce(&Path) -> std::result::Result<(), E>, + E: Into>, +{ + let scratch = tempfile::tempdir()?; + generate(scratch.path()).map_err(|e| Error::Generator(e.into()))?; + + let owned = discover_owned(committed)?; + let produced = discover_all(scratch.path())?; + + let mut names: Vec<&String> = owned.iter().chain(produced.iter()).collect(); + names.sort(); + names.dedup(); + + for name in names { + match mode { + Mode::Check => compare(scratch.path(), committed, name)?, + Mode::Bless => apply_bless(scratch.path(), committed, name)?, + } + } + Ok(()) +} + +/// Returns the names of files in `dir` whose first line begins with +/// [`GENERATED_MARKER`]. Files without the marker are skipped. +fn discover_owned(dir: &Path) -> Result> { + let mut out = Vec::new(); + for entry in fs::read_dir(dir)? { + let entry = entry?; + if !entry.file_type()?.is_file() { + continue; + } + let Ok(mut file) = fs::File::open(entry.path()) else { + continue; + }; + let mut buf = [0u8; GENERATED_MARKER.len()]; + if file.read_exact(&mut buf).is_err() { + continue; + } + if buf == *GENERATED_MARKER.as_bytes() + && let Some(name) = entry.file_name().to_str() + { + out.push(name.to_owned()); + } + } + out.sort(); + Ok(out) +} + +/// Returns every file name in `dir` (scratch dir — all files are generator output). +fn discover_all(dir: &Path) -> Result> { + let mut out = Vec::new(); + for entry in fs::read_dir(dir)? { + let entry = entry?; + if entry.file_type()?.is_file() + && let Some(name) = entry.file_name().to_str() + { + out.push(name.to_string()); + } + } + Ok(out) +} + +fn compare(generated_dir: &Path, committed_dir: &Path, filename: &str) -> Result<()> { + let rel_path = PathBuf::from(filename); + let gen_path = generated_dir.join(&rel_path); + let comm_path = committed_dir.join(&rel_path); + match (gen_path.exists(), comm_path.exists()) { + (true, false) => Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::MissingInCommitted, + }), + (false, true) => Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ExtraInCommitted, + }), + (false, false) => Ok(()), + (true, true) => { + if fs::read(&gen_path)? != fs::read(&comm_path)? { + Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ContentsDiffer, + }) + } else { + Ok(()) + } + } + } +} + +fn apply_bless(generated_dir: &Path, committed_dir: &Path, filename: &str) -> Result<()> { + fs::create_dir_all(committed_dir)?; + let rel_path = PathBuf::from(filename); + let from = generated_dir.join(&rel_path); + let to = committed_dir.join(&rel_path); + if from.exists() { + if let Some(parent) = to.parent() { + fs::create_dir_all(parent)?; + } + fs::copy(&from, &to)?; + } else if to.exists() { + fs::remove_file(&to)?; + } + Ok(()) +} + +// Skipped on iOS because these tests fail on the `x86_64-apple-ios-macabi` CI runner +// with `Os { code: 17, kind: AlreadyExists, message: "File exists" }`. +#[cfg(all(test, not(target_os = "ios")))] +mod tests { + use super::*; + + fn write_marker(p: &Path, body: &[u8]) { + if let Some(d) = p.parent() { + fs::create_dir_all(d).unwrap(); + } + let mut bytes = Vec::new(); + bytes.extend_from_slice(GENERATED_MARKER.as_bytes()); + bytes.push(b'\n'); + bytes.extend_from_slice(body); + fs::write(p, bytes).unwrap(); + } + + #[test] + fn check_fails_on_byte_diff() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("a.txt"), b"hi"); + let e = run_generator( + &committed, + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("a.txt"), b"HI"); + Ok(()) + }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::ContentsDiffer, + .. + } + )); + } + + #[test] + fn check_fails_when_owned_file_missing_from_generated() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("a.txt"), b"hi"); + let e = run_generator( + &committed, + Mode::Check, + |_| -> std::result::Result<(), io::Error> { Ok(()) }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::ExtraInCommitted, + .. + } + )); + } + + #[test] + fn check_fails_when_owned_file_missing_from_committed() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + fs::create_dir_all(&committed).unwrap(); + let e = run_generator( + &committed, + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("a.txt"), b"hi"); + Ok(()) + }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::MissingInCommitted, + .. + } + )); + } + + #[test] + fn bless_deletes_files_no_longer_produced() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("keep.txt"), b""); + write_marker(&committed.join("stale.txt"), b""); + run_generator( + &committed, + Mode::Bless, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("keep.txt"), b""); + Ok(()) + }, + ) + .unwrap(); + assert!(committed.join("keep.txt").exists()); + assert!(!committed.join("stale.txt").exists()); + } + + #[test] + fn bless_preserves_unowned_files() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + fs::create_dir_all(&committed).unwrap(); + fs::write(committed.join("mod.rs"), b"hand-written").unwrap(); + fs::write(committed.join("old.txt"), b"old").unwrap(); + run_generator( + &committed, + Mode::Bless, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("new.txt"), b"new"); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(fs::read(committed.join("mod.rs")).unwrap(), b"hand-written"); + assert_eq!(fs::read(committed.join("old.txt")).unwrap(), b"old"); + assert!(committed.join("new.txt").exists()); + } +} diff --git a/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml b/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml index 397c7816f8d1e..c7dfce2c0fd7b 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] regex = "1.10" +stdarch-gen-common = { path = "../stdarch-gen-common" } diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs index 7a1c3030c0042..c3ad153ab0a7a 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs @@ -21,6 +21,7 @@ use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::Write; use std::path::Path; +use stdarch_gen_common::{run_generator, Mode, GENERATED_MARKER}; /// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics. /// These intrinsics have equivalent semantics and can be lowered to the generic form. @@ -1609,6 +1610,7 @@ fn generate_module_file( let mut output = File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; + writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?; @@ -1691,23 +1693,21 @@ fn main() -> Result<(), String> { } // Generate output files - let hexagon_dir = std::env::args() - .nth(1) - .map(std::path::PathBuf::from) - .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); - std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; - - // Generate v64.rs (64-byte vector mode) - let v64_path = hexagon_dir.join("v64.rs"); - println!("\nStep 3: Generating v64.rs (64-byte mode)..."); - generate_module_file(&intrinsics, &v64_path, VectorMode::V64)?; - println!(" Output: {}", v64_path.display()); - - // Generate v128.rs (128-byte vector mode) - let v128_path = hexagon_dir.join("v128.rs"); - println!("\nStep 4: Generating v128.rs (128-byte mode)..."); - generate_module_file(&intrinsics, &v128_path, VectorMode::V128)?; - println!(" Output: {}", v128_path.display()); + let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); + + // Either "check" to check the output versus the committed output, or "bless" + // to update the output. + let mode = Mode::from_env(); + println!("\nStep 3: Generating v64.rs and v128.rs (mode: {mode:?})..."); + run_generator(&hexagon_dir, mode, |out_dir| -> Result<(), String> { + for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { + let path = out_dir.join(filename); + generate_module_file(&intrinsics, &path, vmode)?; + println!(" Output: {}", hexagon_dir.join(filename).display()); + } + Ok(()) + }) + .map_err(|e| e.to_string())?; println!("\n=== Results ==="); println!( diff --git a/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml b/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml index d3ac607c5576c..1a8c052ebe318 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml @@ -7,4 +7,4 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = "0.8.5" +rand = "0.9.3" diff --git a/library/stdarch/examples/Cargo.toml b/library/stdarch/examples/Cargo.toml index c4fc4c7e374c8..8752f206526c7 100644 --- a/library/stdarch/examples/Cargo.toml +++ b/library/stdarch/examples/Cargo.toml @@ -13,7 +13,7 @@ default-run = "hex" [dependencies] core_arch = { path = "../crates/core_arch" } quickcheck = "1.0" -rand = "0.8" +rand = "0.9.3" [[bin]] name = "hex" diff --git a/library/stdarch/examples/connect5.rs b/library/stdarch/examples/connect5.rs index f24657b148394..2df294b16b71e 100644 --- a/library/stdarch/examples/connect5.rs +++ b/library/stdarch/examples/connect5.rs @@ -33,8 +33,8 @@ #![cfg_attr(target_arch = "x86_64", feature(stdarch_internal))] #![feature(stmt_expr_attributes)] +use rand::rng; use rand::seq::SliceRandom; -use rand::thread_rng; use std::cmp; use std::time::Instant; @@ -374,7 +374,7 @@ impl List { } pub fn shuffle(&mut self) { - let mut rng = thread_rng(); + let mut rng = rng(); let num = self.p_size as usize; self.p_move[..num].shuffle(&mut rng); diff --git a/library/stdarch/examples/hex.rs b/library/stdarch/examples/hex.rs index 21827b375adaf..6417562074d9c 100644 --- a/library/stdarch/examples/hex.rs +++ b/library/stdarch/examples/hex.rs @@ -354,9 +354,9 @@ mod benches { len: usize, f: for<'a> unsafe fn(&[u8], &'a mut [u8]) -> Result<&'a str, usize>, ) { - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let input = std::iter::repeat(()) - .map(|()| rng.r#gen::()) + .map(|()| rng.r#random::()) .take(len) .collect::>(); let mut dst = vec![0; input.len() * 2]; diff --git a/library/stdarch/intrinsics_data/arm_intrinsics.json b/library/stdarch/intrinsics_data/arm_intrinsics.json index fab6da7f2c16c..7f749fe4d8842 100644 --- a/library/stdarch/intrinsics_data/arm_intrinsics.json +++ b/library/stdarch/intrinsics_data/arm_intrinsics.json @@ -51174,6 +51174,12 @@ "svfloat16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51188,6 +51194,12 @@ "svfloat32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51202,6 +51214,12 @@ "svfloat64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51216,6 +51234,12 @@ "svint16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint16_t" }, @@ -51230,6 +51254,12 @@ "svint32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint32_t" }, @@ -51244,6 +51274,12 @@ "svint64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint64_t" }, @@ -51258,6 +51294,12 @@ "svint8x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint8_t" }, @@ -51272,6 +51314,12 @@ "svuint16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51286,6 +51334,12 @@ "svuint32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51300,6 +51354,12 @@ "svuint64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51314,6 +51374,12 @@ "svuint8x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint8_t" }, @@ -51328,6 +51394,12 @@ "svfloat16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51342,6 +51414,12 @@ "svfloat32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51356,6 +51434,12 @@ "svfloat64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51370,6 +51454,12 @@ "svint16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint16_t" }, @@ -51384,6 +51474,12 @@ "svint32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint32_t" }, @@ -51398,6 +51494,12 @@ "svint64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint64_t" }, @@ -51412,6 +51514,12 @@ "svint8x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint8_t" }, @@ -51426,6 +51534,12 @@ "svuint16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51440,6 +51554,12 @@ "svuint32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51454,6 +51574,12 @@ "svuint64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51468,6 +51594,12 @@ "svuint8x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint8_t" }, @@ -51482,6 +51614,12 @@ "svfloat16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51496,6 +51634,12 @@ "svfloat32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51510,6 +51654,12 @@ "svfloat64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51524,6 +51674,12 @@ "svint16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint16_t" }, @@ -51538,6 +51694,12 @@ "svint32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint32_t" }, @@ -51552,6 +51714,12 @@ "svint64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint64_t" }, @@ -51566,6 +51734,12 @@ "svint8x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint8_t" }, @@ -51580,6 +51754,12 @@ "svuint16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51594,6 +51774,12 @@ "svuint32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51608,6 +51794,12 @@ "svuint64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51622,6 +51814,12 @@ "svuint8x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint8_t" }, @@ -162999,6 +163197,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat16x2_t" }, @@ -163014,6 +163218,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat32x2_t" }, @@ -163029,6 +163239,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat64x2_t" }, @@ -163044,6 +163260,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint16x2_t" }, @@ -163059,6 +163281,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint32x2_t" }, @@ -163074,6 +163302,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint64x2_t" }, @@ -163089,6 +163323,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint8x2_t" }, @@ -163104,6 +163344,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint16x2_t" }, @@ -163119,6 +163365,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint32x2_t" }, @@ -163134,6 +163386,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint64x2_t" }, @@ -163149,6 +163407,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint8x2_t" }, @@ -163164,6 +163428,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat16x3_t" }, @@ -163179,6 +163449,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat32x3_t" }, @@ -163194,6 +163470,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat64x3_t" }, @@ -163209,6 +163491,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint16x3_t" }, @@ -163224,6 +163512,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint32x3_t" }, @@ -163239,6 +163533,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint64x3_t" }, @@ -163254,6 +163554,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint8x3_t" }, @@ -163269,6 +163575,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint16x3_t" }, @@ -163284,6 +163596,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint32x3_t" }, @@ -163299,6 +163617,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint64x3_t" }, @@ -163314,6 +163638,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint8x3_t" }, @@ -163329,6 +163659,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat16x4_t" }, @@ -163344,6 +163680,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat32x4_t" }, @@ -163359,6 +163701,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat64x4_t" }, @@ -163374,6 +163722,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint16x4_t" }, @@ -163389,6 +163743,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint32x4_t" }, @@ -163404,6 +163764,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint64x4_t" }, @@ -163419,6 +163785,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint8x4_t" }, @@ -163434,6 +163806,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint16x4_t" }, @@ -163449,6 +163827,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint32x4_t" }, @@ -163464,6 +163848,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint64x4_t" }, @@ -163479,6 +163869,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint8x4_t" }, diff --git a/library/stdarch/rust-version b/library/stdarch/rust-version index 59e9e5a0e6ee9..e8cec4e0ee9c9 100644 --- a/library/stdarch/rust-version +++ b/library/stdarch/rust-version @@ -1 +1 @@ -045b17737dab5fcc28e4cbee0cfe2ce4ed363b32 +8e150217bafcaaaa0c45bf685c55fd56cec48598 diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 4e380d8894781..cb4a3593c51d6 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -193,7 +193,7 @@ unsafe extern "C" {} #[link(name = "unwind")] unsafe extern "C" {} -#[cfg(target_os = "nto")] +#[cfg(any(target_os = "nto", target_os = "qnx"))] cfg_select! { target_env = "nto70" => { #[link(name = "gcc")] diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index d675181f19411..4d4b37eec08b0 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -112,9 +112,9 @@ TEST_SET2 := --skip=tests --skip=coverage-map --skip=coverage-run --skip=library # this intentionally doesn't use `$(BOOTSTRAP)` so we can test the shebang on Windows ci-msvc-py: - $(Q)$(CFG_SRC_DIR)/x.py test --stage 2 $(TEST_SET1) + $(Q)$(CFG_SRC_DIR)/x.py test --stage 2 $(TEST_SET1) --skip=src/tools/linkchecker ci-msvc-ps1: - $(Q)$(CFG_SRC_DIR)/x.ps1 test --stage 2 $(TEST_SET2) + $(Q)$(CFG_SRC_DIR)/x.ps1 test --stage 2 $(TEST_SET2) --skip=src/tools/linkchecker ci-msvc: ci-msvc-py ci-msvc-ps1 ## MingW native builders diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index b0feea13b0485..114387d963a6d 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1201,11 +1201,6 @@ impl Step for Src { // not needed and contains symlinks which rustup currently // chokes on when unpacking. "library/backtrace/crates", - // these are 30MB combined and aren't necessary for building - // the standard library. - "library/stdarch/Cargo.toml", - "library/stdarch/crates/stdarch-verify", - "library/stdarch/crates/intrinsic-test", ], &dst_src, ); diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 0948742de27a7..0f051232757e8 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1392,6 +1392,13 @@ impl Step for RustcBook { /// in the "md-doc" directory in the build output directory. Then /// "rustbook" is used to convert it to HTML. fn run(self, builder: &Builder<'_>) { + // FIXME: Temporary workaround for https://github.com/rust-lang/rust/issues/158378 + #[cfg(not(test))] // So this check doesn't affect the bootstrap tests + if self.target == "i686-pc-windows-msvc" { + eprintln!("WARNING: Skipping rustc book build to work around #158378"); + return; + } + let out_base = builder.md_doc_out(self.target).join("rustc"); t!(fs::create_dir_all(&out_base)); let out_listing = out_base.join("src/lints"); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 9891f54907c7c..087a395a067f0 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -169,7 +169,7 @@ pub fn prebuilt_llvm_config( generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project"), - &builder.llvm_cache_key(), + builder.in_tree_llvm_info.sha().unwrap_or_default(), ) }); @@ -999,7 +999,7 @@ impl Step for OmpOffload { generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project/offload"), - &builder.llvm_cache_key(), + builder.in_tree_llvm_info.sha().unwrap_or_default(), ) }); let stamp = BuildStamp::new(&out_dir).with_prefix("offload").add_stamp(smart_stamp_hash); @@ -1166,8 +1166,8 @@ impl Step for Enzyme { // Enzyme links against LLVM. If we update the LLVM submodule libLLVM might get a new // version number, in which case Enzyme will now fail to find LLVM. By including the LLVM // hash into the Enzyme hash we force a rebuild of Enzyme when updating LLVM. - let enzyme_hash_input = - builder.llvm_cache_key() + builder.enzyme_info.sha().unwrap_or_default(); + let enzyme_hash_input = builder.in_tree_llvm_info.sha().unwrap_or_default().to_owned() + + builder.enzyme_info.sha().unwrap_or_default(); static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { @@ -1430,7 +1430,7 @@ impl Step for Sanitizers { generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project/compiler-rt"), - &builder.llvm_cache_key(), + builder.in_tree_llvm_info.sha().unwrap_or_default(), ) }); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f7f708902c095..a2d05f8347cbe 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -10,6 +10,7 @@ use std::collections::HashSet; use std::env::split_paths; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; +use std::process::Command; use std::{env, fs, iter}; use build_helper::exit; @@ -2474,7 +2475,13 @@ Please disable assertions with `rust.debug-assertions = false`. "-Lnative={}", builder.test_helpers_out(test_compiler.host).display() )); - targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); + let target_helpers = builder.test_helpers_out(target); + targetflags.push(format!("-Lnative={}", target_helpers.display())); + if target.is_pauthtest() { + // For the pauthtest target, embed an rpath to the directory containing the helper + // dynamic library. + targetflags.push(format!("-Clink-arg=-Wl,-rpath,{}", target_helpers.display())); + } } for flag in hostflags { @@ -4120,32 +4127,54 @@ impl Step for TestHelpers { }; let dst = builder.test_helpers_out(target); let src = builder.src.join("tests/auxiliary/rust_test_helpers.c"); - if up_to_date(&src, &dst.join("librust_test_helpers.a")) { - return; - } - let _guard = builder.msg_unstaged(Kind::Build, "test helpers", target); t!(fs::create_dir_all(&dst)); - let mut cfg = cc::Build::new(); - // We may have found various cross-compilers a little differently due to our - // extra configuration, so inform cc of these compilers. Note, though, that - // on MSVC we still need cc's detection of env vars (ugh). - if !target.is_msvc() { - if let Some(ar) = builder.ar(target) { - cfg.archiver(ar); + if !up_to_date(&src, &dst.join("librust_test_helpers.a")) { + let mut cfg = cc::Build::new(); + + // We may have found various cross-compilers a little differently due to our + // extra configuration, so inform cc of these compilers. Note, though, that + // on MSVC we still need cc's detection of env vars (ugh). + if !target.is_msvc() { + if let Some(ar) = builder.ar(target) { + cfg.archiver(ar); + } + cfg.compiler(builder.cc(target)); + } + cfg.cargo_metadata(false) + .out_dir(&dst) + .target(&target.triple) + .host(&builder.config.host_target.triple) + .opt_level(0) + .warnings(false) + .debug(false) + .file(builder.src.join("tests/auxiliary/rust_test_helpers.c")) + .compile("rust_test_helpers"); + } + if target.is_pauthtest() { + let so = dst.join("librust_test_helpers.so"); + if up_to_date(&src, &so) { + return; } - cfg.compiler(builder.cc(target)); - } - cfg.cargo_metadata(false) - .out_dir(&dst) - .target(&target.triple) - .host(&builder.config.host_target.triple) - .opt_level(0) - .warnings(false) - .debug(false) - .file(builder.src.join("tests/auxiliary/rust_test_helpers.c")) - .compile("rust_test_helpers"); + + let status = Command::new(builder.cc(target)) + .arg("-target") + .arg(target.triple) + .arg("-march=armv8.3-a+pauth") + .arg("-fPIC") + .arg("-shared") + .arg("-O0") // Use O0 to match what static library is compiled at. + .arg("-o") + .arg(&so) + .arg(&src) + .status() + .unwrap_or_else(|_| panic!("Failed to run clang for {} toolchain", target.triple)); + + if !status.success() { + panic!("Linking of librust_test_helpers.so failed (target: {})", target.triple); + } + } } } diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e7fe8bd1f3858..72094373b5719 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -14,8 +14,11 @@ use crate::{ RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, }; -/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler -/// later. +/// Represents flag values in `String` form with a `\x1f` delimiter to pass to the compiler later. +/// +/// Flags are emitted via `CARGO_ENCODED_RUSTFLAGS` / `CARGO_ENCODED_RUSTDOCFLAGS`, +/// which use `\x1f` (ASCII Unit Separator) as the delimiter and therefore allow spaces +/// within individual flag values (e.g. paths from `llvm-config --libdir`). /// /// `-Z crate-attr` flags will be applied recursively on the target code using the /// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more @@ -51,11 +54,16 @@ impl Rustflags { } fn arg(&mut self, arg: &str) -> &mut Self { - assert_eq!(arg.split(' ').count(), 1); - if !self.0.is_empty() { - self.0.push(' '); + assert!( + !arg.contains('\x1f'), + "rustflag must not contain the ASCII unit separator (\\x1f): {arg:?}" + ); + if !arg.is_empty() { + if !self.0.is_empty() { + self.0.push('\x1f'); + } + self.0.push_str(arg); } - self.0.push_str(arg); self } @@ -457,14 +465,21 @@ impl From for BootstrapCommand { cargo.command.args(cargo.args); + // Always unset the plain RUSTFLAGS/RUSTDOCFLAGS so that downstream + // tools (e.g. build.rs scripts) see only the encoded form. Any flags + // from the caller's environment have already been folded into the + // Rustflags struct via `propagate_cargo_env`. + cargo.command.env_remove("RUSTFLAGS"); + cargo.command.env_remove("RUSTDOCFLAGS"); + let rustflags = &cargo.rustflags.0; if !rustflags.is_empty() { - cargo.command.env("RUSTFLAGS", rustflags); + cargo.command.env("CARGO_ENCODED_RUSTFLAGS", rustflags); } let rustdocflags = &cargo.rustdocflags.0; if !rustdocflags.is_empty() { - cargo.command.env("RUSTDOCFLAGS", rustdocflags); + cargo.command.env("CARGO_ENCODED_RUSTDOCFLAGS", rustdocflags); } let encoded_hostflags = cargo.hostflags.encode(); @@ -918,7 +933,10 @@ impl Builder<'_> { } let rustdoc_path = match cmd_kind { - Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc_for_compiler(compiler), + Kind::Doc => self.rustdoc_for_compiler(compiler), + Kind::Test | Kind::MiriTest if self.test_target.runs_doctests() => { + self.rustdoc_for_compiler(compiler) + } _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), }; @@ -1183,8 +1201,9 @@ impl Builder<'_> { if (mode == Mode::ToolRustcPrivate || mode == Mode::Codegen) && let Some(llvm_config) = self.llvm_config(target) { - let llvm_libdir = + let llvm_libdir_raw = command(llvm_config).cached().arg("--libdir").run_capture_stdout(self).stdout(); + let llvm_libdir = llvm_libdir_raw.trim(); if target.is_msvc() { rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); } else { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 6b2f526a8226d..66e14dae9b9ec 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1150,6 +1150,7 @@ impl<'a> Builder<'a> { /// compiler will run on, *not* the target it will build code for). Explicitly does not take /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, /// since it ensures that they are valid (i.e., built and assembled). + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1183,6 +1184,7 @@ impl<'a> Builder<'a> { /// /// However, without this optimization, we would also build stage 2 rustc for **target1**, /// which is completely wasteful. + #[track_caller] pub fn compiler_for_std(&self, stage: u32) -> Compiler { if compile::Std::should_be_uplifted_from_stage_1(self, stage) { self.compiler(1, self.host_target) @@ -1202,6 +1204,7 @@ impl<'a> Builder<'a> { /// sysroot. /// /// See `force_use_stage1` and `force_use_stage2` for documentation on what each argument is. + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1249,6 +1252,7 @@ impl<'a> Builder<'a> { /// Prefer using this method rather than manually invoking `Std::new`. /// /// Returns an optional build stamp, if libstd was indeed built. + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1297,17 +1301,20 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler } } + #[track_caller] pub fn sysroot(&self, compiler: Compiler) -> PathBuf { self.ensure(compile::Sysroot::new(compiler)) } /// Returns the bindir for a compiler's sysroot. + #[track_caller] pub fn sysroot_target_bindir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf { self.ensure(Libdir { compiler, target }).join(target).join("bin") } /// Returns the libdir where the standard library and other artifacts are /// found for a compiler's sysroot. + #[track_caller] pub fn sysroot_target_libdir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf { self.ensure(Libdir { compiler, target }).join(target).join("lib") } @@ -1416,6 +1423,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Returns a path to `Rustdoc` that "belongs" to the `target_compiler`. /// It can be either a stage0 rustdoc or a locally built rustdoc that *links* to /// `target_compiler`. + #[track_caller] pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf { self.ensure(tool::Rustdoc { target_compiler }) } @@ -1532,6 +1540,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Ensure that a given step is built, returning its output. This will /// cache the step, so it is safe (and good!) to call this as often as /// needed to ensure that all dependencies are built. + #[track_caller] pub fn ensure(&'a self, step: S) -> S::Output { { let mut stack = self.stack.borrow_mut(); @@ -1589,7 +1598,8 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler // in the step_name field. "step", step_name = pretty_step_name::(), - args = step_debug_args(&step) + args = step_debug_args(&step), + location = crate::utils::tracing::format_location(*std::panic::Location::caller()) ); span.entered() }; @@ -1687,16 +1697,6 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler pub fn exec_ctx(&self) -> &ExecutionContext { &self.config.exec_ctx } - - /// When to rebuild LLVM. Currently includes the LLVM commit hash and the configuration from - /// bootstrap.toml. - pub(crate) fn llvm_cache_key(&self) -> String { - format!( - "sha={sha}\nkey={key}", - sha = self.in_tree_llvm_info.sha().unwrap_or_default(), - key = self.config.llvm_cache_key, - ) - } } /// Return qualified step name, e.g. `compile::Rustc`. diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1a59e54a95cbf..176a61b15c988 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2423,6 +2423,19 @@ mod snapshot { } } + #[test] + fn test_library_tests_only_does_not_build_rustdoc() { + let ctx = TestCtx::new(); + let host = TargetSelection::from_user(&host_target()); + insta::assert_snapshot!( + ctx.config("test").args(&["--tests", "library/core"]).render_steps(), + @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + "); + } + #[test] fn test_cargo_stage_1() { let ctx = TestCtx::new(); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 0297907057ef9..1db44c2c8b39c 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -156,9 +156,6 @@ pub struct Config { pub backtrace_on_ice: bool, // llvm codegen options - /// Key used to decide when to rebuild LLVM. - pub llvm_cache_key: String, - pub llvm_assertions: bool, pub llvm_tests: bool, pub llvm_enzyme: bool, @@ -598,8 +595,6 @@ impl Config { rustflags: rust_rustflags, } = toml.rust.unwrap_or_default(); - let llvm = toml.llvm.unwrap_or_default(); - let llvm_cache_key = llvm.cache_key(); let Llvm { optimize: llvm_optimize, thin_lto: llvm_thin_lto, @@ -630,7 +625,7 @@ impl Config { enable_warnings: llvm_enable_warnings, download_ci_llvm: llvm_download_ci_llvm, build_config: llvm_build_config, - } = llvm; + } = toml.llvm.unwrap_or_default(); let Dist { sign_folder: dist_sign_folder, @@ -1211,7 +1206,9 @@ impl Config { | Subcommand::Install => { assert_eq!( stage, 2, - "x.py should be run with `--stage 2` on CI, but was run with `--stage {stage}`", + "\ +x.py was run under CI with an implicit `--stage {stage}`. This is probably wrong and you want stage 2. +NOTE: Please add `--stage 2` to your command line, or if you're sure you want to run stage {stage} then add `--stage {stage}` explicitly" ); } Subcommand::Clean { .. } @@ -1406,7 +1403,6 @@ impl Config { llvm_assertions, llvm_bitcode_linker_enabled: rust_llvm_bitcode_linker.unwrap_or(false), llvm_build_config: llvm_build_config.clone().unwrap_or(Default::default()), - llvm_cache_key, llvm_cflags, llvm_clang: llvm_clang.unwrap_or(false), llvm_clang_cl, diff --git a/src/bootstrap/src/core/config/target_selection.rs b/src/bootstrap/src/core/config/target_selection.rs index 8457607b897dd..365d6eff28ccf 100644 --- a/src/bootstrap/src/core/config/target_selection.rs +++ b/src/bootstrap/src/core/config/target_selection.rs @@ -78,6 +78,10 @@ impl TargetSelection { self.contains("msvc") } + pub fn is_pauthtest(&self) -> bool { + self.contains("pauthtest") + } + pub fn is_windows(&self) -> bool { self.contains("windows") } diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index c281aa94f64e6..89046c0eee779 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -11,6 +11,7 @@ use serde::Deserialize; use super::flags::Flags; use super::toml::change_id::ChangeIdWrapper; +use super::toml::rust::parse_codegen_backends; use super::{Config, RUSTC_IF_UNCHANGED_ALLOWED_PATHS}; use crate::ChangeId; use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; @@ -206,6 +207,15 @@ fn rust_optimize() { assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.get_opt_level(), Some("s".to_string())); } +#[test] +#[should_panic(expected = "Duplicate value 'llvm' for 'rust.codegen-backends'")] +fn rejects_duplicate_codegen_backends() { + parse_codegen_backends( + vec!["llvm", "llvm", "cranelift"].into_iter().map(str::to_owned).collect(), + "rust", + ); +} + #[test] #[should_panic] fn invalid_rust_optimize() { diff --git a/src/bootstrap/src/core/config/toml/llvm.rs b/src/bootstrap/src/core/config/toml/llvm.rs index e54c17c63aaae..5f08884e4ef71 100644 --- a/src/bootstrap/src/core/config/toml/llvm.rs +++ b/src/bootstrap/src/core/config/toml/llvm.rs @@ -43,93 +43,6 @@ define_config! { } } -impl Llvm { - /// A key that is used to determine whether LLVM should be rebuilt. - pub(crate) fn cache_key(&self) -> String { - let helper = || { - let mut key = String::with_capacity(512); - - let Self { - optimize, - thin_lto, - release_debuginfo, - assertions, - tests, - enzyme, - plugins, - static_libstdcpp, - libzstd, - ninja, - targets, - experimental_targets, - link_jobs: _, - link_shared, - version_suffix, - clang_cl, - cflags, - cxxflags, - ldflags, - use_libcxx, - use_linker, - allow_old_toolchain, - offload, - polly, - offload_clang_dir, - clang, - enable_warnings: _, - build_config, - download_ci_llvm: _, - } = self; - - use std::fmt::Write; - write!(key, "{:?}", optimize)?; - write!(key, "{:?}", thin_lto)?; - write!(key, "{:?}", release_debuginfo)?; - write!(key, "{:?}", assertions)?; - write!(key, "{:?}", tests)?; - write!(key, "{:?}", enzyme)?; - write!(key, "{:?}", plugins)?; - write!(key, "{:?}", static_libstdcpp)?; - write!(key, "{:?}", libzstd)?; - write!(key, "{:?}", ninja)?; - write!(key, "{:?}", targets)?; - write!(key, "{:?}", experimental_targets)?; - write!(key, "{:?}", link_shared)?; - write!(key, "{:?}", version_suffix)?; - write!(key, "{:?}", clang_cl)?; - write!(key, "{:?}", cflags)?; - write!(key, "{:?}", cxxflags)?; - write!(key, "{:?}", ldflags)?; - write!(key, "{:?}", use_libcxx)?; - write!(key, "{:?}", use_linker)?; - write!(key, "{:?}", allow_old_toolchain)?; - write!(key, "{:?}", offload)?; - write!(key, "{:?}", polly)?; - write!(key, "{:?}", offload_clang_dir)?; - write!(key, "{:?}", clang)?; - - match build_config { - None => { - write!(key, "None")?; - } - Some(c) => { - let mut build_config = c.iter().collect::>(); - build_config.sort(); - - for (k, v) in build_config { - write!(key, "{}: {}", k, v)?; - } - } - } - - Ok::<_, std::fmt::Error>(key) - }; - - // write! to a String always succeeds unless OOM. - helper().unwrap() - } -} - /// Compares the current `Llvm` options against those in the CI LLVM builder and detects any incompatible options. /// It does this by destructuring the `Llvm` instance to make sure every `Llvm` field is covered and not missing. #[cfg(not(test))] diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index a872671343405..d2d629e969f6c 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -422,23 +422,33 @@ pub(crate) fn parse_codegen_backends( Please, use '{stripped}' instead." ) } - if !BUILTIN_CODEGEN_BACKENDS.contains(&backend.as_str()) { + let backend = match backend.as_str() { + "llvm" => CodegenBackendKind::Llvm, + "cranelift" => CodegenBackendKind::Cranelift, + "gcc" => CodegenBackendKind::Gcc, + backend => CodegenBackendKind::Custom(backend.to_string()), + }; + + if found_backends.contains(&backend) { + panic!( + "Duplicate value '{}' for '{section}.codegen-backends'. \ + Each codegen backend should only be specified once.", + backend.name() + ); + } + + if !BUILTIN_CODEGEN_BACKENDS.contains(&backend.name()) { if CiEnv::is_rust_lang_managed_ci_job() { - eprintln!("Unknown codegen backend {backend}"); + eprintln!("Unknown codegen backend {}", backend.name()); exit!(1); } println!( - "HELP: '{backend}' for '{section}.codegen-backends' might fail. \ - List of known codegen backends: {BUILTIN_CODEGEN_BACKENDS:?}" + "HELP: '{}' for '{section}.codegen-backends' might fail. \ + List of known codegen backends: {BUILTIN_CODEGEN_BACKENDS:?}", + backend.name() ); } - let backend = match backend.as_str() { - "llvm" => CodegenBackendKind::Llvm, - "cranelift" => CodegenBackendKind::Cranelift, - "gcc" => CodegenBackendKind::Gcc, - backend => CodegenBackendKind::Custom(backend.to_string()), - }; found_backends.push(backend); } if found_backends.is_empty() { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 3c2348c9bf71c..99d9c8a04c5c1 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -21,7 +21,7 @@ use crate::builder::Kind; use crate::core::build_steps::tool; use crate::core::config::{CompilerBuiltins, Target}; use crate::utils::exec::command; -use crate::{Build, Subcommand}; +use crate::{Build, Subcommand, t}; pub struct Finder { cache: HashMap>, @@ -38,6 +38,9 @@ pub struct Finder { const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined "powerpc64-unknown-linux-gnuelfv2", + "aarch64-unknown-linux-pauthtest", // Stage 0 compiler is not guaranteed to see the target yet. + "aarch64-unknown-qnx", + "x86_64-pc-qnx", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM @@ -412,6 +415,53 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake { cmd_finder.must_have("wasm-component-ld"); } + + // aarch64-unknown-linux-pauthtest must use clang + if !skip_tools_checks && target.is_pauthtest() { + let cc_tool = build.cc_tool(*target); + let linker_path = build + .linker(*target) + .unwrap_or_else(|| panic!("{} requires an explicit clang linker", target.triple)); + + if !cc_tool.is_like_clang() { + panic!( + "Clang is required to build C code for {} target, got:\n\ + cc tool: `{}`,\n\ + linker: `{}`\n", + target.triple, + cc_tool.path().display(), + linker_path.display(), + ); + } + let cc_canon = t!(fs::canonicalize(cc_tool.path())); + let linker_canon = t!(fs::canonicalize(&linker_path)); + if cc_canon != linker_canon { + panic!( + "CC and Linker are expected to be the same for {} target, got:\n\ + CC: `{}`,\n\ + Linker: `{}`\n", + target.triple, + cc_canon.display(), + linker_canon.display(), + ); + } + + let output = + command(cc_tool.path()).arg("-dumpversion").run_capture_stdout(&build).stdout(); + let version_str = output.trim(); + let mut parts = version_str.split('.').map(|s| s.parse::().unwrap_or(0)); + let major = parts.next().unwrap_or(0); + let minor = parts.next().unwrap_or(0); + let patch = parts.next().unwrap_or(0); + if (major, minor, patch) < (22, 1, 0) { + panic!( + "clang version too old: {} ({} target trequires >= 22.1.0), path: {}", + target.triple, + version_str, + cc_tool.path().display() + ); + } + } } if let Some(ref s) = build.config.ccache { diff --git a/src/bootstrap/src/utils/step_graph.rs b/src/bootstrap/src/utils/step_graph.rs index cffe0dbb3e3a5..d6a2d89482ee4 100644 --- a/src/bootstrap/src/utils/step_graph.rs +++ b/src/bootstrap/src/utils/step_graph.rs @@ -1,10 +1,12 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::fmt::Debug; use std::io::BufWriter; +use std::panic::Location; use std::path::Path; use crate::core::builder::{AnyDebug, Step, pretty_step_name}; use crate::t; +use crate::utils::tracing::format_location; /// Records the executed steps and their dependencies in a directed graph, /// which can then be rendered into a DOT file for visualization. @@ -20,6 +22,7 @@ pub struct StepGraph { } impl StepGraph { + #[track_caller] pub fn register_step_execution( &mut self, step: &S, @@ -57,6 +60,7 @@ impl StepGraph { } } + #[track_caller] pub fn register_cached_step( &mut self, step: &S, @@ -97,12 +101,18 @@ struct Node { struct NodeHandle(usize); /// Represents a dependency between two bootstrap steps. -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] -struct Edge { - src: NodeHandle, - dst: NodeHandle, +#[derive(Default)] +struct EdgeData { // Was the corresponding execution of a step cached, or was the step actually executed? cached: bool, + // Locations from where the step was called + locations: Vec>, +} + +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +struct EdgeKey { + src: NodeHandle, + dst: NodeHandle, } // We could use a library for this, but they either: @@ -114,8 +124,8 @@ struct Edge { #[derive(Default)] struct DotGraph { nodes: Vec, + edges: HashMap, /// The `NodeHandle` represents an index within `self.nodes` - edges: HashSet, key_to_index: HashMap, } @@ -127,16 +137,19 @@ impl DotGraph { handle } + #[track_caller] fn add_edge(&mut self, src: NodeHandle, dst: NodeHandle) { - self.edges.insert(Edge { src, dst, cached: false }); + let key = EdgeKey { src, dst }; + let edge = self.edges.entry(key).or_default(); + edge.locations.push(*Location::caller()); } + #[track_caller] fn add_cached_edge(&mut self, src: NodeHandle, dst: NodeHandle) { - // There's no point in rendering both cached and uncached edge - let uncached = Edge { src, dst, cached: false }; - if !self.edges.contains(&uncached) { - self.edges.insert(Edge { src, dst, cached: true }); - } + let key = EdgeKey { src, dst }; + let edge = self.edges.entry(key).or_default(); + edge.cached = true; + edge.locations.push(*Location::caller()); } fn get_handle_by_key(&self, key: &str) -> Option { @@ -157,11 +170,23 @@ impl DotGraph { )?; } - let mut edges: Vec<&Edge> = self.edges.iter().collect(); - edges.sort(); - for edge in edges { - let style = if edge.cached { "dashed" } else { "solid" }; - writeln!(file, r#"{} -> {} [style="{style}"]"#, edge.src.0, edge.dst.0)?; + let mut edges: Vec<(&EdgeKey, &EdgeData)> = self.edges.iter().collect(); + edges.sort_by_key(|(key, _)| **key); + for (key, data) in edges { + let style = if data.cached { "dashed" } else { "solid" }; + let mut locations = data + .locations + .iter() + .map(|location| format_location(*location)) + .collect::>(); + locations.sort(); + locations.dedup(); + let locations = locations.join(", "); + writeln!( + file, + r#"{} -> {} [style="{style}", tooltip="{locations}"]"#, + key.src.0, key.dst.0, + )?; } writeln!(file, "}}") diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index b1226ed7de7be..4e75347f4cbae 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -351,7 +351,7 @@ mod inner { let field = &values.fields[0]; write!(writer, " {{{}}}", field.1)?; } - write_location(writer, span.metadata())?; + write_with_location(writer)?; } // Executed command COMMAND_SPAN_TARGET => { diff --git a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile index 9a3545dbad8d1..a06331d88490b 100644 --- a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile @@ -35,14 +35,14 @@ WORKDIR /build # the kernel. This file was generated by running `make vexpress_defconfig` # followed by `make menuconfig` and then enabling the IPv6 protocol page. COPY host-x86_64/armhf-gnu/vexpress_config /build/.config -RUN curl https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.14.320.tar.xz | \ - tar xJf - && \ - cd /build/linux-4.14.320 && \ +RUN curl https://ci-mirrors.rust-lang.org/rustc/linux-4.14.336.tar.gz | \ + tar xzf - && \ + cd /build/linux-4.14.336 && \ cp /build/.config . && \ make -j$(nproc) all && \ cp arch/arm/boot/zImage /tmp && \ cd /build && \ - rm -rf linux-4.14.320 + rm -rf linux-4.14.336 # Compile an instance of busybox as this provides a lightweight system and init # binary which we will boot into. Only trick here is configuring busybox to diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-redox/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-x86_64-redox/Dockerfile index 6a647ac162e4e..36ef0befb3337 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-redox/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-redox/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh WORKDIR /tmp diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile index f0d54b0d33d60..bd9937fdf6fc3 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh WORKDIR /build diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig index e7afdbe9d4dea..ba7bbf0630cf7 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig +++ b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig @@ -2,11 +2,14 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_ARM=y CT_ARCH_ARCH="armv6" CT_ARCH_FLOAT_SW=y CT_KERNEL_LINUX=y +CT_OBSOLETE=y CT_LINUX_V_3_2=y +CT_LINUX_VERSION="3.2.101" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_17=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile index 08b0c2ad04531..99cbdca526fbd 100644 --- a/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-armhf-linux/arm-linux-gnueabihf.defconfig b/src/ci/docker/host-x86_64/dist-armhf-linux/arm-linux-gnueabihf.defconfig index c7c5b4d5a2865..cfb6ca284be86 100644 --- a/src/ci/docker/host-x86_64/dist-armhf-linux/arm-linux-gnueabihf.defconfig +++ b/src/ci/docker/host-x86_64/dist-armhf-linux/arm-linux-gnueabihf.defconfig @@ -2,12 +2,15 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_ARM=y CT_ARCH_ARCH="armv6" CT_ARCH_FPU="vfp" CT_ARCH_FLOAT_HW=y CT_KERNEL_LINUX=y +CT_OBSOLETE=y CT_LINUX_V_3_2=y +CT_LINUX_VERSION="3.2.101" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_17=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile index c86bdbbc41b47..880966a2bd958 100644 --- a/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-armv7-linux/armv7-linux-gnueabihf.defconfig b/src/ci/docker/host-x86_64/dist-armv7-linux/armv7-linux-gnueabihf.defconfig index 3f37e546b4255..3f624aaa4342a 100644 --- a/src/ci/docker/host-x86_64/dist-armv7-linux/armv7-linux-gnueabihf.defconfig +++ b/src/ci/docker/host-x86_64/dist-armv7-linux/armv7-linux-gnueabihf.defconfig @@ -2,6 +2,7 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_ARM=y CT_ARCH_ARM_MODE_THUMB=y CT_ARCH_SUFFIX="v7" @@ -9,7 +10,9 @@ CT_ARCH_ARCH="armv7-a" CT_ARCH_FPU="vfpv3-d16" CT_ARCH_FLOAT_HW=y CT_KERNEL_LINUX=y +CT_OBSOLETE=y CT_LINUX_V_3_2=y +CT_LINUX_VERSION="3.2.101" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_17=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile index cf8ff07a5b07f..e80fe2a879368 100644 --- a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile @@ -22,6 +22,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig index f5f3cfd4b49f3..ac9c40f77873e 100644 --- a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig @@ -2,11 +2,14 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_X86=y CT_ARCH_ARCH="i586" CT_TARGET_CFLAGS="-Wa,-mrelax-relocations=no" CT_KERNEL_LINUX=y +CT_OBSOLETE=y CT_LINUX_V_3_2=y +CT_LINUX_VERSION="3.2.101" CT_BINUTILS_V_2_32=y CT_BINUTILS_EXTRA_CONFIG_ARRAY="--enable-compressed-debug-sections=none" CT_GLIBC_V_2_17=y diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile index 3ee8cb9ed7e8f..c4f8351bd9466 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig index 9ff9791c0b2cf..60c9cc7ef7252 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig @@ -3,6 +3,7 @@ CT_EXPERIMENTAL=y CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_LOONGARCH=y # CT_DEMULTILIB is not set CT_ARCH_USE_MMU=y @@ -11,6 +12,7 @@ CT_TARGET_CFLAGS="-mcmodel=medium" CT_TARGET_LDFLAGS="-mcmodel=medium" CT_KERNEL_LINUX=y CT_LINUX_V_5_19=y +CT_LINUX_VERSION="5.19.16" CT_GLIBC_V_2_36=y CT_BINUTILS_V_2_45=y CT_GCC_V_15=y diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile index 54c1869a2a04f..61b28e4189a4a 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig index 8c1562744fb52..73e29d7aca725 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig @@ -3,6 +3,7 @@ CT_EXPERIMENTAL=y CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_LOONGARCH=y # CT_DEMULTILIB is not set CT_ARCH_USE_MMU=y @@ -11,6 +12,7 @@ CT_TARGET_CFLAGS="-mcmodel=medium" CT_TARGET_LDFLAGS="-mcmodel=medium" CT_KERNEL_LINUX=y CT_LINUX_V_5_19=y +CT_LINUX_VERSION="5.19.16" CT_LIBC_MUSL=y CT_MUSL_V_1_2_5=y CT_BINUTILS_V_2_45=y diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile index 3d7f5f81fbec6..5f84f7eefcce9 100644 --- a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig index 94db07922a226..8c4e547a8beb4 100644 --- a/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig @@ -2,11 +2,13 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_MIPS=y CT_ARCH_mips_o32=y CT_ARCH_ARCH="mips32r2" CT_KERNEL_LINUX=y CT_LINUX_V_4_4=y +CT_LINUX_VERSION="4.4.302" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_23=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile index 3c798d737a48d..da841f6a19d9c 100644 --- a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig index f295a2fafc6b1..725ec99f58619 100644 --- a/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig @@ -2,12 +2,14 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_MIPS=y CT_ARCH_mips_n64=y CT_ARCH_64=y CT_ARCH_ARCH="mips64r2" CT_KERNEL_LINUX=y CT_LINUX_V_4_4=y +CT_LINUX_VERSION="4.4.302" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_23=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile index 32fd634555c2b..f509f17eefecc 100644 --- a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig index 47d6246356526..a577f0eb196da 100644 --- a/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig @@ -2,6 +2,7 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_MIPS=y CT_ARCH_mips_n64=y CT_ARCH_LE=y @@ -9,6 +10,7 @@ CT_ARCH_64=y CT_ARCH_ARCH="mips64r2" CT_KERNEL_LINUX=y CT_LINUX_V_4_4=y +CT_LINUX_VERSION="4.4.302" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_23=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile index 73ab0cf5bab65..f1fad8e2cc12e 100644 --- a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig index 5daa83ebc02a7..7497b6c45bfb4 100644 --- a/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig @@ -2,12 +2,14 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_MIPS=y CT_ARCH_mips_o32=y CT_ARCH_LE=y CT_ARCH_ARCH="mips32r2" CT_KERNEL_LINUX=y CT_LINUX_V_4_4=y +CT_LINUX_VERSION="4.4.302" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_23=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc-linux/Dockerfile index 825392414671d..4cef669c25d44 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc-linux/powerpc-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-powerpc-linux/powerpc-linux-gnu.defconfig index 6a0e96a3b67bc..1774a246c44bb 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc-linux/powerpc-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-powerpc-linux/powerpc-linux-gnu.defconfig @@ -2,10 +2,13 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_POWERPC=y CT_ARCH_CPU="powerpc" CT_KERNEL_LINUX=y +CT_OBSOLETE=y CT_LINUX_V_3_2=y +CT_LINUX_VERSION="3.2.101" CT_BINUTILS_V_2_30=y CT_GLIBC_V_2_17=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux-gnu/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64-linux-gnu/Dockerfile index ad0e210190170..4ee0bd829cfa1 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64-linux-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64-linux-gnu/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux-gnu/powerpc64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64-linux-gnu/powerpc64-linux-gnu.defconfig index 2b442cbe79467..c3b817afb30ed 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64-linux-gnu/powerpc64-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-powerpc64-linux-gnu/powerpc64-linux-gnu.defconfig @@ -2,12 +2,15 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_POWERPC=y CT_ARCH_CPU="power4" CT_ARCH_TUNE="power6" CT_ARCH_64=y CT_KERNEL_LINUX=y +CT_OBSOLETE=y CT_LINUX_V_3_2=y +CT_LINUX_VERSION="3.2.101" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_17=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64-linux-musl/Dockerfile index 17783b9c73588..bfbad2c70de61 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64-linux-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64-linux-musl/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux-musl/powerpc64-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64-linux-musl/powerpc64-unknown-linux-musl.defconfig index 4b7a9c3d671f9..6a9e38a8902a8 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64-linux-musl/powerpc64-unknown-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-powerpc64-linux-musl/powerpc64-unknown-linux-musl.defconfig @@ -3,12 +3,14 @@ CT_EXPERIMENTAL=y CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_POWERPC=y CT_ARCH_64=y CT_ARCH_ABI="elfv2" # CT_DEMULTILIB is not set CT_KERNEL_LINUX=y CT_LINUX_V_4_19=y +CT_LINUX_VERSION="4.19.325" CT_LIBC_MUSL=y CT_MUSL_V_1_2_5=y CT_BINUTILS_V_2_42=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile index 6a9573cb4231c..e3540e7b68128 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig index e1fd950922f71..3d0f885649a30 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig @@ -3,13 +3,16 @@ CT_EXPERIMENTAL=y CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_POWERPC=y CT_ARCH_LE=y CT_ARCH_64=y # CT_DEMULTILIB is not set CT_ARCH_ARCH="powerpc64le" CT_KERNEL_LINUX=y +CT_OBSOLETE=y CT_LINUX_V_3_10=y +CT_LINUX_VERSION="3.10.108" CT_GLIBC_V_2_17=y CT_BINUTILS_V_2_42=y CT_GCC_V_14=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile index be16966fbe1b4..615f41028dbc0 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig index a89e08572acab..c141dbb00eaca 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig @@ -3,6 +3,7 @@ CT_EXPERIMENTAL=y CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_POWERPC=y CT_ARCH_LE=y CT_ARCH_64=y @@ -10,6 +11,7 @@ CT_ARCH_64=y CT_ARCH_ARCH="powerpc64le" CT_KERNEL_LINUX=y CT_LINUX_V_4_19=y +CT_LINUX_VERSION="4.19.325" CT_LIBC_MUSL=y CT_MUSL_V_1_2_5=y CT_BINUTILS_V_2_42=y diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile index 51d9e8a5798aa..5186c99c4f430 100644 --- a/src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig index 75eb356bc9587..95df5205150d1 100644 --- a/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig @@ -3,6 +3,7 @@ CT_EXPERIMENTAL=y CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_RISCV=y # CT_DEMULTILIB is not set CT_ARCH_USE_MMU=y @@ -10,6 +11,7 @@ CT_ARCH_64=y CT_ARCH_ARCH="rv64gc" CT_KERNEL_LINUX=y CT_LINUX_V_4_20=y +CT_LINUX_VERSION="4.20.17" CT_BINUTILS_V_2_40=y CT_GLIBC_V_2_29=y CT_GCC_V_8=y diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile index 413249e02e87f..e9cf707b1911e 100644 --- a/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile @@ -4,6 +4,7 @@ COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/s390x-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-s390x-linux/s390x-linux-gnu.defconfig index 1fe21966600bb..2b27b00345d5c 100644 --- a/src/ci/docker/host-x86_64/dist-s390x-linux/s390x-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-s390x-linux/s390x-linux-gnu.defconfig @@ -2,10 +2,13 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_VERIFY_DOWNLOAD_DIGEST_SHA256=y CT_ARCH_S390=y CT_ARCH_64=y CT_KERNEL_LINUX=y +CT_OBSOLETE=y CT_LINUX_V_3_2=y +CT_LINUX_VERSION="3.2.101" CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_17=y CT_GCC_V_8=y diff --git a/src/ci/docker/scripts/crosstool-ng-sha256-20260705.diff b/src/ci/docker/scripts/crosstool-ng-sha256-20260705.diff new file mode 100644 index 0000000000000..6295edbbddb4e --- /dev/null +++ b/src/ci/docker/scripts/crosstool-ng-sha256-20260705.diff @@ -0,0 +1,77 @@ +diff --git a/packages/linux/3.10.108/chksum b/packages/linux/3.10.108/chksum +index 17c7f8ec..f6498231 100644 +--- a/packages/linux/3.10.108/chksum ++++ b/packages/linux/3.10.108/chksum +@@ -4,5 +4,5 @@ sha256 linux-3.10.108.tar.xz 3849ea8119517f605f9d53c57dd6c539af8d584c2f1d9031f4f + sha512 linux-3.10.108.tar.xz 2ed1bda124fbc0f03e7f4c4e5ddac73d15b82d24ff51f5dbb265cec7cc614b28f83923b5fc8df5e97a3244d2bbb861853f0f2a3894a0c64c5a08b489d4ae12ac + md5 linux-3.10.108.tar.gz b38e458f3a90ffa0815f2e2740bba7aa + sha1 linux-3.10.108.tar.gz 0b09e6fdc8152bea07a9d678119a23a35025d4d6 +-sha256 linux-3.10.108.tar.gz aff93454b2df33a0394253c8977c6d36c7205a423af6d8d4e3e1d67dfc9e07f8 ++sha256 linux-3.10.108.tar.gz b1711610cf3faf7194156dacdb98c63c1b7ffd02377269d7f75df63d823ccbba + sha512 linux-3.10.108.tar.gz 71c6f89a7620fecc8ce0f215b6aab52f6a6337b6474ae7447491a1ab4fc95eafa38e700cf667ec304220e9cbcaa669671b9ed15fe79cc679a4e4e9139b37ad8c +diff --git a/packages/linux/3.2.101/chksum b/packages/linux/3.2.101/chksum +index 1f9a68ba..7188ceeb 100644 +--- a/packages/linux/3.2.101/chksum ++++ b/packages/linux/3.2.101/chksum +@@ -4,5 +4,5 @@ sha256 linux-3.2.101.tar.xz 4453686b001685144f44c88d57c716fcb6e85ef8a2aad2f95d36 + sha512 linux-3.2.101.tar.xz 1058369878610cb9e42021dc7feb2e6b2fa67541b0fb4152e526f63e5dfee1d7a63b2f1fb0cfe59097824a743ba74f81526dc706bdb61fc7f1c5e29972b139ed + md5 linux-3.2.101.tar.gz ac6286e2ff2a66be117c1de20bdb6c72 + sha1 linux-3.2.101.tar.gz 8b2ab0dd4915e7517a26fa63112bde9b438d14d8 +-sha256 linux-3.2.101.tar.gz 0d02f47871e44fc12de452a8b41da119848a76d5f81159b8ae9ef57da5824712 ++sha256 linux-3.2.101.tar.gz 93e8391e029f131d5ba4b7ad76cc34b12f2c2244059604042f2297c4bde093f7 + sha512 linux-3.2.101.tar.gz 3951bda6970658c2915a0a8e8b022f7fc28d0fa4a5a8ef37a3a48a5d33a328dba3220a13979887b0df0cc2ba7f4ee92c32bc34ec84c6c1b0e4a1727765d412e0 +diff --git a/packages/linux/4.14.329/chksum b/packages/linux/4.14.329/chksum +index e5a318d3..44fa0469 100644 +--- a/packages/linux/4.14.329/chksum ++++ b/packages/linux/4.14.329/chksum +@@ -4,5 +4,5 @@ sha256 linux-4.14.329.tar.xz de36689d713ed17f8bc1286fe4b9afcdb9a3de3b6f1d6aff525 + sha512 linux-4.14.329.tar.xz b7851bfdd9b5b778c6dcd4fe0376ae3851a93d0030050e57e29472be11785e3fd01c8b167f26f2c04c1b05c89aed309b24d77407aabdcbfb76173545236c1c7b + md5 linux-4.14.329.tar.gz 0a75f96e4a19bfdeb41ff9289a111318 + sha1 linux-4.14.329.tar.gz 07f91c6f13ed3403d105d0a34b586f916f90c26c +-sha256 linux-4.14.329.tar.gz b0b08ea229defc872bef5c5dbe2fccde5ce739ba0c15dc3bce17aa26e7c24e43 ++sha256 linux-4.14.329.tar.gz 3850511cf1822f3bb7c4f2dcfb47a4575dfc2dd9510b3a22be1225a0b7a316b3 + sha512 linux-4.14.329.tar.gz 6056521d902d1681986a244f7edf12870eacfe39748d75a86ac1df0cec470f94eafe9fcd7c086fb55bdaff8eda97fdfbeaff3944b28500aa6a4334c511708508 +diff --git a/packages/linux/4.19.325/chksum b/packages/linux/4.19.325/chksum +index 9219f096..e938f889 100644 +--- a/packages/linux/4.19.325/chksum ++++ b/packages/linux/4.19.325/chksum +@@ -4,5 +4,5 @@ sha256 linux-4.19.325.tar.xz 607bed7de5cda31a443df4c8a78dbe5e8a9ad31afde2a4d28fe + sha512 linux-4.19.325.tar.xz 4a2c5f358e9caf47061d707db67738e0d1b1f24aebeeaab81dc2701cc3793ab574a7e0a3a5df2f99d6ff1d777acdca1d885da87ee31098c5b901853d8b580310 + md5 linux-4.19.325.tar.gz d8201f438135d2dc50302de5e6b7febc + sha1 linux-4.19.325.tar.gz 1a882796a187bbb2145d0e091ed370eed3d7f31b +-sha256 linux-4.19.325.tar.gz 8f360bb8ba9fb692507f27596422c7ceddec5f7fed4d8f3df2b7656b6042cfee ++sha256 linux-4.19.325.tar.gz 8753443636e475b506e08abd40059ec9b84904a115d206014f0c856dfe13a25e + sha512 linux-4.19.325.tar.gz 4e8cbfed3211b1c53122905ce588c421aedb05138581cd64986ddcba89b10ffcea8a3b1c63ec273abc02eac7c31f0f901e17677f4c3ed749b26fc47b75bce5f1 +diff --git a/packages/linux/4.20.17/chksum b/packages/linux/4.20.17/chksum +index 0d77f052..d64edcfd 100644 +--- a/packages/linux/4.20.17/chksum ++++ b/packages/linux/4.20.17/chksum +@@ -4,5 +4,5 @@ sha256 linux-4.20.17.tar.xz d011245629b980d4c15febf080b54804aaf215167b514a3577fe + sha512 linux-4.20.17.tar.xz d048b2e1e88be90c1983acbe71931fc0e716919b433990bd42ab1bb635fc4fc5953667966432b2bc1256fcd5afc211c12ca3e41b7feccdb39b4ced05e6e4071d + md5 linux-4.20.17.tar.gz 6b9d049ddeb6cb54ecd2bd3d5931ecda + sha1 linux-4.20.17.tar.gz 4743162961123c9c475628ecc31cfa80d822992a +-sha256 linux-4.20.17.tar.gz 830c472e5c0b4394936c347d70564dd661d8bed4ceb07f79dc8e8efdefd9bf73 ++sha256 linux-4.20.17.tar.gz 313b7bebb46084efbfcaf75f4ea6faf2e14c8cbc1711fcba483dc0a036c9acc1 + sha512 linux-4.20.17.tar.gz 8d4ff845e500e42908a3103a5c08ae3fa76265716cc0c4f37c4712a4d1f0721bd4adbac275136213ee40f490a5650de6e9d5e246e8c700ac11d65ad3ebb74425 +diff --git a/packages/linux/4.4.302/chksum b/packages/linux/4.4.302/chksum +index 9d2b041b..8dadd2f1 100644 +--- a/packages/linux/4.4.302/chksum ++++ b/packages/linux/4.4.302/chksum +@@ -4,5 +4,5 @@ sha256 linux-4.4.302.tar.xz 35017bb40b604e0b577fc2b87e727632b46608a2ba3a4f5858b9 + sha512 linux-4.4.302.tar.xz 2eb03f08bd0a8ee1630b20bc01bfe28f3ff396aca8c88247cc4db3a5c41bcabb6d8385dff90040480f4990f51b538415dedf283a5bc3807ef85e3d982b369570 + md5 linux-4.4.302.tar.gz 81f7f97ebc94e89311bb20e22b0e6004 + sha1 linux-4.4.302.tar.gz 2160955ff98a3694ae0574d247cea293d91292af +-sha256 linux-4.4.302.tar.gz 66271f9d9fce8596622e8154ca0ea160e46b78a5a6c967a15b55855f744d1b0b ++sha256 linux-4.4.302.tar.gz a22ceab143d40f511203265e5a70d6cc5ec39163cd54fa281346d19176f64451 + sha512 linux-4.4.302.tar.gz de6ad09783be1faf422e0b476d67768fff8bf1fcdc1e2ab27f915a26f7acdbecaa4a1a2385f78d3001e28ad83b3b55150eb2b996d1b64c6376f5f1d68af019f4 +diff --git a/packages/linux/5.19.16/chksum b/packages/linux/5.19.16/chksum +index c16d312c..64ee13fa 100644 +--- a/packages/linux/5.19.16/chksum ++++ b/packages/linux/5.19.16/chksum +@@ -4,5 +4,5 @@ sha256 linux-5.19.16.tar.xz a1ebaf667e1059ae2d72aa6374a4d6e2febc0b8ccda6a124687a + sha512 linux-5.19.16.tar.xz 1ff662424830fbd48ac1d8c1aabd4d11a8eabc284c4fe3fdf803064d4f56d9c1b631a0fb60679bac40a4e05b77d5e6706a8fd52a5e025ef4f1231a6cc84bbbde + md5 linux-5.19.16.tar.gz 15c43c56b85175aacafaaa4ef1983298 + sha1 linux-5.19.16.tar.gz cd7fb09335a4814a88fedf7ed064e6f10fd1c8db +-sha256 linux-5.19.16.tar.gz 9b835ea04b0210ad0748829367acd84e09357c3dd6d0f48e31737a7f21e990ff ++sha256 linux-5.19.16.tar.gz bbf0ead65559250e0784c13d4f9716b7f917a1d3a4e00f671f3994cc6990bb02 + sha512 linux-5.19.16.tar.gz 9b0330c7257aecba3f6f97566014c03d8012e1b2c5c1ee2c3b5ea1b7a2ca01addf346b5235d772b26e4312b0fc1bb529689390445668606ed96e66174cf93341 diff --git a/src/ci/docker/scripts/crosstool-ng.sh b/src/ci/docker/scripts/crosstool-ng.sh index 434c662c99c89..d0b0491bacba3 100644 --- a/src/ci/docker/scripts/crosstool-ng.sh +++ b/src/ci/docker/scripts/crosstool-ng.sh @@ -1,6 +1,8 @@ #!/bin/sh set -ex +# ignore-tidy-linelength + CT_NG=1.28.0 url="https://github.com/crosstool-ng/crosstool-ng/archive/crosstool-ng-$CT_NG.tar.gz" @@ -11,6 +13,12 @@ cd crosstool-ng-crosstool-ng-$CT_NG # "download source of zlib is invalid now" sed -e "s|zlib.net/'|zlib.net/fossils'|" -i packages/zlib/package.desc +# FIXME(#158718): patch crosstools-ng known-good kernel artifact SHA256 +# checksums to the artifacts we mirror in `ci-mirrors`. +# See +# . +patch -p1 /aarch64-linux-pauthtest/usr \ + -resource-dir /lib/clang/ \ + --rtlib=compiler-rt \ + --ld-path=/usr/bin/ld.lld \ + --unwindlib=libunwind \ + -Wl,--dynamic-linker=/aarch64-linux-pauthtest/usr/lib/libc.so \ + -Wl,--rpath=/aarch64-linux-pauthtest/usr/lib \ + "$@" +``` + +Bootstrap validates the name of the configured C compiler, so when using a +wrapper its name must contain `clang`. A recommended name is +`aarch64-unknown-linux-pauthtest-clang`. Update the script to set `--sysroot`, +`-resource-dir`, `--dynamic-linker` and `--rpath` correctly by replacing +`` with the directory produced by the build scripts and the +`` with LLVM's version. Make the wrapper executable. + +To verify that the toolchain layout is correct, check that: +* the sysroot contains a pointer-authentication-enabled version of libunwind + (`/aarch64-linux-pauthtest/usr/lib/libunwind.so`), +* the Clang resource directory contains the appropriate `compiler-rt` objects + (`/lib/clang//lib/aarch64-unknown-linux-pauthtest/{clang_rt.crtbegin.o,clang_rt.crtend.o}`) + +When using the AccessSoftek scripts to build the sysroot, the result includes a +Clang-based toolchain. In this case, no wrapper script is required, +`/bin/aarch64-linux-pauthtest-clang` can be used directly. + +## Building the target + +Introduction of `aarch64-unknown-linux-pauthtest` target needs to be propagated +to various crates/repos, so that they can correctly recognise and handle it. +Specifically: +* `cc-rs`: https://github.com/jchlanda/cc-rs/tree/jakub/cc-v1.2.28-pauthtest +* `libc`: https://github.com/jchlanda/libc/tree/jakub/0.2.183-pauthtest +* `backtrace`: https://github.com/jchlanda/backtrace-rs/tree/jakub/backtrace-v0.3.76-pauthtest + +The patched versions of `cc-rs` and `libc` will have to be registered through +`[patch.crates-io]` section of `Cargo.toml` files both in: +`/src/bootstrap/` and `/library/`. Check out `cc-rs` and +`libc` to `/patches` and update config files. See attached diff for +details: + +

+ +```diff +diff --git a/library/Cargo.toml b/library/Cargo.toml +index e30e6240942..fb5a12f0065 100644 +--- a/library/Cargo.toml ++++ b/library/Cargo.toml +@@ -59,3 +59,4 @@ rustflags = ["-Cpanic=abort"] + rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } + rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' } + rustc-std-workspace-std = { path = 'rustc-std-workspace-std' } ++libc = { path = '/patches/libc' } +diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml +index e1725db60cf..46763cdf9a4 100644 +--- a/src/bootstrap/Cargo.toml ++++ b/src/bootstrap/Cargo.toml +@@ -94,3 +94,6 @@ debug = 0 + [profile.dev.package] + # Only use debuginfo=1 to further reduce compile times. + bootstrap.debug = 1 ++ ++[patch.crates-io] ++cc = { path = '/patches/cc-rs' } +``` + +
+ +In contrast to `cc-rs` and `libc`, which are external crates resolved from +[crates.io](https://crates.io/) and can be overridden using `[patch.crates-io]`, +`backtrace` is included in the Rust repository as a git submodule under +`/library/backtrace`. At the time of writing, the necessary change +has not yet been committed there, which means an in-tree patch is currently +required. The patch: + +
+ +```diff +diff --git a/src/backtrace/libunwind.rs b/src/backtrace/libunwind.rs +index 0564f2e..a8a0d1a 100644 +--- a/src/backtrace/libunwind.rs ++++ b/src/backtrace/libunwind.rs +@@ -79,6 +79,18 @@ impl Frame { + // clause, and if this is fixed that test in theory can be run on macOS! + if cfg!(target_vendor = "apple") { + self.ip() ++ } else if cfg!(target_abi = "pauthtest") { ++ // NOTE: ip here is an unsigned (raw) pointer, so we must not use ++ // uw::_Unwind_FindEnclosingFunction. ++ // ++ // Otherwise, in the pointer-authentication-enabled reference ++ // toolchain, libunwind would attempt to authenticate and re-sign ++ // values. Performing signing here is not safe: it could create a ++ // signing oracle, and more importantly it is incorrect under the ++ // expected signing schema. ++ // The schema requires the stack pointer (SP) as the discriminator. ++ // However, the SP available at this point would not match the SP ++ // at authentication/re-sign time, since ++ // _Unwind_FindEnclosingFunction constructs a new unwind context. ++ // The SP used here would therefore correspond to a different frame. ++ // As a result, we must return the raw value. ++ self.ip() + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } + } +``` + +
+ +The target can be built by enabling it for a `rustc` build. + +```toml +[build] +target = ["aarch64-unknown-linux-pauthtest"] +``` + +Specify the binaries used by the target. + +```toml +[target.aarch64-unknown-linux-pauthtest] +cc = "/aarch64-unknown-linux-pauthtest-clang" +ar = "/llvm-ar" +ranlib = "/llvm-ranlib" +linker = "/aarch64-unknown-linux-pauthtest-clang" +``` + +Note that `cc` and `linker` must refer to the same binary (either Clang itself +or its wrapper). The bootstrap process will fail if they differ. On non-AArch64 +systems, ensure that QEMU is installed and that `binfmt_misc` is correctly +configured so that foreign architecture binaries can be executed transparently. + +## Building Rust programs + +Rust does not currently ship precompiled artifacts for this target. Programs +must be built using a locally compiled Rust toolchain, with +`aarch64-unknown-linux-pauthtest` target enabled. + +For a comprehensive example of how to interact between C and Rust programs +within the testing framework please consult +`/tests/run-make/pauth-quicksort-c-driver/rmake.rs`, the test builds +a C executable linked against Rust library. +`/tests/run-make/pauth-quicksort-rust-driver/rmake.rs` shows how to +link a Rust program against a library compiled from a C source file. + +### Minimal standalone Rust and C interoperability example + +A minimal standalone example demonstrating Rust and C interoperability on the +`aarch64-unknown-linux-pauthtest` target is listed below. + +
+ +* Project structure + +```text +rust_c_indirect/ + ┣━ Cargo.toml + ┣━ build.rs + ┣━ src/ + ┃ ┗━ main.rs + ┣━ c_src/ + ┃ ┗━ plugin.c + ┗━ target/ +``` + +* `Cargo.toml` + +```toml +[package] +name = "rust_c_indirect" +edition = "2024" +build = "build.rs" +``` + +* `build.rs` + +```rust, ignore (platform-specific) +use std::env; +use std::path::Path; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=c_src/plugin.c"); + + let clang = "/aarch64-unknown-linux-pauthtest-clang"; + + let out_dir = env::var("OUT_DIR").unwrap(); + let lib_path = Path::new(&out_dir).join("libplugin.so"); + let c_src = "c_src/plugin.c"; + + let status = Command::new(clang) + .args(["-shared", "-fPIC", c_src]) + .arg("-o") + .arg(&lib_path) + .status() + .unwrap_or_else(|_| panic!("failed to build shared library")); + assert!(status.success(), "failed to build shared library"); + + println!("cargo:rustc-link-arg=-Wl,--dynamic-linker=/aarch64-linux-pauthtest/usr/lib/libc.so"); + println!("cargo:rustc-link-arg=-Wl,-rpath,/aarch64-linux-pauthtest/usr/lib"); + println!("cargo:rustc-link-search=native={}", out_dir); + println!("cargo:rustc-link-lib=dylib=plugin"); +} + +``` + +* `src/main.rs` + +```rust, ignore (platform-specific) +use std::ptr; +use std::os::raw::c_int; + +unsafe extern "C" { + fn add(a: c_int, b: c_int) -> c_int; +} + +static OP: unsafe extern "C" fn(c_int, c_int) -> c_int = add; + +fn main() { + let a = 10; + let b = 32; + + let op = unsafe { ptr::read_volatile(&raw const OP) }; + let result = unsafe { op(a, b) }; + + println!("Result: {}", result); +} +``` + +* `c_src/plugin.c` + +```c +int add(int a, int b) { return a + b; } +``` + +
+ +* compile: `cargo build --target aarch64-unknown-linux-pauthtest --release` +* run: `./target/aarch64-unknown-linux-pauthtest/release/rust_c_indirect` + +Please make sure that `LD_LIBRARY_PATH` points to the directory containing +`libplugin.so`. For example: +`LD_LIBRARY_PATH=./target/aarch64-unknown-linux-pauthtest/release/build/rust_c_indirect-/out/`. + +To inspect pointer authentication behavior in IR, build with: +`RUSTFLAGS="--emit=llvm-ir"`. This generates an LLVM IR file, e.g.: +`target/aarch64-unknown-linux-pauthtest/release/deps/rust_c_indirect-*.ll`. +Relevant excerpt: + +```llvm +@_RNvCscVIHJvJIt8C_15rust_c_indirect2OP = internal constant ptr ptrauth (ptr @add, i32 0), align 8 + +%0 = load volatile ptr, ptr @_RNvCscVIHJvJIt8C_15rust_c_indirect2OP, align 8, !nonnull !5, !noundef !5 +%1 = tail call noundef i32 %0(i32 noundef 10, i32 noundef 32) #6 [ "ptrauth"(i32 0, i64 0) ] +``` + +Which shows that: +* function pointer (`@add`) is signed using `ptrauth`, when global variable is + initialized, +* the call is performed indirectly via a signed pointer, +* the `ptrauth` operand bundle enforces authentication at call time. + +Note, when building crates it is necessary to explicitly point Cargo to the +linker it has to use. This can be achieved by using a `config.toml` file (either +local to the project, or global), or by setting a +`CARGO_TARGET_AARCH64_UNKNOWN_LINUX_PAUTHTEST_LINKER` variable. For example: +* `.cargo/config.toml` + +```toml +[target.aarch64-unknown-linux-pauthtest] +linker = "/aarch64-unknown-linux-pauthtest-clang" +``` + +* `export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_PAUTHTEST_LINKER=/aarch64-unknown-linux-pauthtest-clang` + +Without it Cargo falls back to the system C toolchain (cc) and the compilation +fails. + +## Cross-compilation toolchains and C code + +This target supports interoperability with C code. A +pointer-authentication-enabled sysroot, built as described in the toolchain +build section of this document, is required. C code must be compiled with a +compiler configuration that supports pointer authentication. Mixed Rust/C +programs are supported and tested (e.g. quicksort examples). Pointer +authentication semantics must be consistent across Rust and C components. Only +dynamic linking is supported. + +The target can be cross-compiled from any Linux-based host, but execution +requires an AArch64 system that implements Pointer Authentication (PAC). In +practice, this means a CPU conforming to at least the Armv8.3-A architecture, +where the +[FEAT_PAuth](https://developer.arm.com/documentation/109697/2025_06/Feature-descriptions/The-Armv8-3-architecture-extension?lang=en#md448-the-armv83-architecture-extension__feat_FEAT_PAuth) +extension is defined. + +Cross-compilation has been successfully performed on both +`aarch64-unknown-linux-gnu` and `x86_64-unknown-linux-gnu` hosts. + +## Testing + +This target can be tested as normal with `x.py`. +The following categories are supported (all present in tree): +* Assembly tests + * pauth-basic.rs +* LLVM IR/codegen tests + * pauth-extern-c.rs + * pauth-extern-c-direct-indirect-call.rs + * pauth-extern-weak-global.rs + * pauth-init-fini.rs + * pauth-attr-special-funcs.rs +* End-to-end execution tests + * Rust-driven quicksort (pauth-quicksort-rust-driver) + * C-driven quicksort (pauth-quicksort-c-driver) +* UI error/warning reporting (the target does not support static linking) + * crt-static-pauthtest.rs + * pauth-static-link-warning + +All tests from `assembly-llvm`, `codegen-llvm`, `codegen-units`, `coverage`, +`crashes`, `incremental`, `library`, `mir-opt`, `run-make`, `ui` and +`ui-fulldeps` subsets are expected to pass. + +Command to run all passing tests (with tests added by this target explicitly +named for convenience): + +```sh +x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \ + codegen-llvm codegen-units coverage crashes incremental library mir-opt \ + run-make ui ui-fulldeps \ + tests/assembly-llvm/pauth-basic.rs \ + tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs \ + tests/codegen-llvm/pauth/pauth-extern-c.rs \ + tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs \ + tests/codegen-llvm/pauth/pauth-extern-weak-global.rs \ + tests/codegen-llvm/pauth/pauth-init-fini.rs \ + tests/run-make/pauth-quicksort-rust-driver \ + tests/run-make/pauth-quicksort-c-driver \ + tests/run-make/pauth-static-link-warning \ + tests/ui/statics/crt-static-pauthtest.rs +``` + +## Limitations + +Operand bundles should only be attached to indirect function calls. However, +function pointer signing is currently performed in `get_fn_addr`, which causes +the logic to be applied too broadly, including to function values (not just +pointers). As a result, direct calls using signed function values must also +receive operand bundles. Once this is resolved, we should analyze each call and +skip direct calls. +For more information please see the discussion in the [rust-lang issue +tracker](https://github.com/rust-lang/rust/issues/152532). + +The current version only supports C interoperability with pointer authentication +features explicitly mentioned at the beginning of this document. Further work is +needed to support configurable signing schemas (i.e. selection of signing keys, +discriminators, address diversity, and features opt-in/opt-out) as defined by +the LLVM pointer authentication model. + +C++ interoperability is not currently supported. Features such as signing C++ +member function pointers, virtual function pointers, and virtual table pointers +are not expected to work. diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 9f8960899c169..b3ab1c7c4485b 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -1,15 +1,22 @@ -# nto-qnx +# QNX **Tier: 3** -The [QNX®][qnx.com] Neutrino (nto) Real-time operating system. Known as QNX OS -from version 8 onwards. +Support for the [QNX®][qnx.com] [QNX Software Development Platform (SDP)], version 7.0, 7.1 and 8.0. -This support has been implemented jointly by [Elektrobit Automotive GmbH][Elektrobit] -and [QNX][qnx.com]. +[QNX Software Development Platform (SDP)]: https://qnx.software/en/software/products-and-solutions/qnx-software-development-platform -[qnx.com]: https://blackberry.qnx.com -[Elektrobit]: https://www.elektrobit.com +The [QNX Software Development Platform (SDP)] is a development environment that +you download and install on a host computer. It includes a C toolchain for your +host, an IDE, and various board support packages for different target platforms. +You can then use QNX SDP to build a custom run-time environment which you deploy +onto an embedded device. That run-time environment will include a microkernel, +whatever services you have selected, and perhaps one or more applications +written in Rust. + +In QNX SDP 7.x the run-time environment is based on QNX Neutrino RTOS 7.x, while +in QNX SDP 8.0 the run-time environment is based on QNX OS 8.0. The name change +reflects architectural changes in the RTOS, but both use a microkernel design. ## Target maintainers @@ -20,107 +27,52 @@ and [QNX][qnx.com]. ## Requirements -Currently, the following QNX versions and compilation targets are supported: +The following QNX SDP versions and compilation targets are supported: | Target Tuple | QNX Version | Target Architecture | Full support | `no_std` support | | ----------------------------------- | ----------------------------- | ------------------- | :----------: | :--------------: | -| `aarch64-unknown-nto-qnx800` | QNX OS 8.0 | AArch64 | ? | ✓ | -| `x86_64-pc-nto-qnx800` | QNX OS 8.0 | x86_64 | ? | ✓ | -| `aarch64-unknown-nto-qnx710` | QNX Neutrino 7.1 with io-pkt | AArch64 | ✓ | ✓ | -| `x86_64-pc-nto-qnx710` | QNX Neutrino 7.1 with io-pkt | x86_64 | ✓ | ✓ | -| `aarch64-unknown-nto-qnx710_iosock` | QNX Neutrino 7.1 with io-sock | AArch64 | ? | ✓ | -| `x86_64-pc-nto-qnx710_iosock` | QNX Neutrino 7.1 with io-sock | x86_64 | ? | ✓ | -| `aarch64-unknown-nto-qnx700` | QNX Neutrino 7.0 | AArch64 | ? | ✓ | -| `i686-pc-nto-qnx700` | QNX Neutrino 7.0 | x86 | | ✓ | - -On QNX Neutrino 7.0 and 7.1, `io-pkt` is used as network stack by default. -QNX Neutrino 7.1 includes the optional network stack `io-sock`. -QNX OS 8.0 always uses `io-sock`. QNX OS 8.0 support is currently work in progress. - -Adding other architectures that are supported by QNX is possible. - -In the table above, 'full support' indicates support for building Rust applications with the full standard library. A '?' means that support is in-progress. -'`no_std` support' is for building `#![no_std]` applications where only `core` and `alloc` are available. - -For building or using the Rust toolchain for QNX, the -[QNX Software Development Platform (SDP)](https://blackberry.qnx.com/en/products/foundation-software/qnx-software-development-platform) -must be installed and initialized. -Initialization is usually done by sourcing `qnxsdp-env.sh` (this will be installed as part of the SDP, see also installation instruction provided with the SDP). -Afterwards [`qcc`](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.utilities/topic/q/qcc.html) (QNX C/C++ compiler) -should be available (in the `$PATH` variable). -`qcc` will be called e.g. for linking executables. - -When linking `no_std` applications, they must link against `libc.so` (see example). This is -required because applications always link against the `crt` library and `crt` depends on `libc.so`. -This is done automatically when using the standard library. - -### Disabling RELocation Read-Only (RELRO) - -While not recommended by default, some QNX kernel setups may require the `RELRO` to be disabled with `-C relro_level=off`, e.g. by adding it to the `.cargo/config.toml` file: - -```toml -[target.aarch64-unknown-nto-qnx700] -rustflags = ["-C", "relro_level=off"] -``` - -If your QNX kernel does not allow it, and `relro` is not disabled, running compiled binary would fail with `syntax error: ... unexpected` or similar. This is due to kernel trying to interpret compiled binary with `/bin/sh`, and obviously failing. To verify that this is really the case, run your binary with the `DL_DEBUG=all` env var, and look for this output. If you see it, you should disable `relro` as described above. - -```text -Resolution scope for Executable->/bin/sh: - Executable->/bin/sh - libc.so.4->/usr/lib/ldqnx-64.so.2 -``` - -### Small example application - -Small `no_std` example is shown below. Applications using the standard library work as well. - -```rust,ignore (platform-specific) -#![no_std] -#![no_main] -#![feature(lang_items)] - -// We must always link against libc, even if no external functions are used -// "extern C" - Block can be empty but must be present -#[link(name = "c")] -extern "C" { - pub fn printf(format: *const core::ffi::c_char, ...) -> core::ffi::c_int; -} - -#[no_mangle] -pub extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { - const HELLO: &'static str = "Hello World, the answer is %d\n\0"; - unsafe { - printf(HELLO.as_ptr() as *const _, 42); - } - 0 -} - -use core::panic::PanicInfo; - -#[panic_handler] -fn panic(_panic: &PanicInfo<'_>) -> ! { - loop {} -} - -#[lang = "eh_personality"] -#[no_mangle] -pub extern "C" fn rust_eh_personality() {} -``` - -The QNX support in Rust has been tested with QNX Neutrino 7.0 and 7.1. Support for QNX OS 8.0 is a work in progress. - -There are no further known requirements. +| `aarch64-unknown-qnx` | QNX SDP 8.0+ | AArch64 | ? | ✓ | +| `x86_64-pc-qnx` | QNX SDP 8.0+ | x86_64 | ? | ✓ | +| `aarch64-unknown-nto-qnx710_iosock` | QNX SDP 7.1 with io-sock | AArch64 | ? | ✓ | +| `x86_64-pc-nto-qnx710_iosock` | QNX SDP 7.1 with io-sock | x86_64 | ? | ✓ | +| `aarch64-unknown-nto-qnx710` | QNX SDP 7.1 with io-pkt | AArch64 | ✓ | ✓ | +| `x86_64-pc-nto-qnx710` | QNX SDP 7.1 with io-pkt | x86_64 | ✓ | ✓ | +| `aarch64-unknown-nto-qnx700` | QNX SDP 7.0 | AArch64 | ? | ✓ | +| `i686-pc-nto-qnx700` | QNX SDP 7.0 | x86 | - | ✓ | + +* QNX SDP 7.0 only offers the `io-pkt` network stack +* QNX SDP 7.1 uses the `io-pkt` network stack by default, but also includes the optional `io-sock` network stack +* QNX SDP 8.0 only offers the `io-sock` network stack + +In the table above, 'full support' indicates support for building Rust +applications with the full standard library. A '?' means that support is +in-progress. `no_std` support is for building `#![no_std]` applications where +only `core` and `alloc` are available. + +For building or using the Rust toolchain for QNX, the relevant version of the +[QNX Software Development Platform (SDP)] must be installed and initialized. +Initialization is usually done by sourcing `qnxsdp-env.sh` (this will be +installed as part of the SDP, so see the installation instruction provided with +the SDP). Afterwards [`qcc`] (the QNX C/C++ compiler) should be available in +your system PATH because it will be called during Rust compilation (e.g. for +linking executables). + +[`qcc`]: https://www.qnx.com/developers/docs/latest/com.qnx.doc.neutrino.utilities/topic/q/qcc.html + +When linking `no_std` applications, they must link against `libc.so` (see +example). This is required because applications always link against the `crt` +library and `crt` depends on `libc.so`. This is done automatically when using +the standard library. ## Conditional compilation -For conditional compilation, following QNX specific attributes are defined: +For conditional compilation, the following QNX specific attributes are defined: - `target_os` = `"nto"` -- `target_env` = `"nto71"` (for QNX Neutrino 7.1 with "classic" network stack "io_pkt") -- `target_env` = `"nto71_iosock"` (for QNX Neutrino 7.1 with network stack "io_sock") -- `target_env` = `"nto70"` (for QNX Neutrino 7.0) -- `target_env` = `"nto80"` (for QNX OS 8.0) + - `target_env` = `"nto70"` (for QNX SDP 7.0) + - `target_env` = `"nto71"` (for QNX SDP 7.1 with "classic" network stack "io_pkt") + - `target_env` = `"nto71_iosock"` (for QNX SDP 7.1 with "new" network stack "io_sock") +- `target_os` = `"qnx"` (for QNX SDP 8.0 or higher) ## Building the target @@ -176,18 +128,37 @@ For conditional compilation, following QNX specific attributes are defined: rustc library/core library/alloc library/std ``` +## Building Rust programs + +Rust does not ship pre-compiled artifacts for this target. To compile for this +target, you must either build Rust with the target enabled (see "Building the +target" above), or build your own copy of `core` by using `build-std` or +similar. + +Compiled executables can run directly on QNX, either by including them in the +disk image, or copying them over the network to a running system. + +Compiling C code requires the same environment variables to be set as compiling +the Rust toolchain (see above), to ensure `qcc` is used with proper arguments. +To ensure compatibility, do not specify any further arguments that for example +change calling conventions or memory layout. + ## Running the Rust test suite -The test suites of the Rust compiler and standard library can be executed much like other Rust targets. -The environment for testing should match the one used during compiler compilation (refer to `build_env` and `qcc`/`PATH` above) with the -addition of the TEST_DEVICE_ADDR environment variable. -The TEST_DEVICE_ADDR variable controls the remote runner and should point to the target, despite localhost being shown in the following example. -Note that some tests are failing which is why they are currently excluded by the target maintainers which can be seen in the following example. +The test suites of the Rust compiler and standard library can be executed much +like other Rust targets. The environment for testing should match the one used +during compiler compilation (refer to `build_env` and `qcc`/`PATH` above) with +the addition of the `TEST_DEVICE_ADDR` environment variable. The +`TEST_DEVICE_ADDR` variable controls the remote runner and should point to a +target running the `remote-test-server` executable. + +Note that some tests are failing which is why they are currently excluded by the +target maintainers which can be seen in the following example. To run all tests on a x86_64 QNX Neutrino 7.1 target: ```bash -export TEST_DEVICE_ADDR="localhost:12345" # must address the test target, can be a SSH tunnel +export TEST_DEVICE_ADDR="1.2.3.4:12345" # must address the test target, can be a SSH tunnel export build_env= # Disable tests that only work on the host or don't make sense for this target. @@ -210,16 +181,6 @@ env $build_env \ --target x86_64-pc-nto-qnx710 ``` -## Building Rust programs - -Rust does not yet ship pre-compiled artifacts for this target. -To compile for this target, you must either build Rust with the target enabled (see "Building the target" above), -or build your own copy of `core` by using `build-std` or similar. - -## Testing - -Compiled executables can run directly on QNX. - ### Rust std library test suite The target needs sufficient resources to execute all tests. The commands below assume that a QEMU image @@ -279,8 +240,26 @@ is used. 64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=1 ms ``` -## Cross-compilation toolchains and C code +## Disabling RELocation Read-Only (RELRO) + +While not recommended by default, some QNX kernel setups may require the `RELRO` +to be disabled with `-C relro_level=off`, e.g. by adding it to the +`.cargo/config.toml` file: -Compiling C code requires the same environment variables to be set as compiling the Rust toolchain (see above), -to ensure `qcc` is used with proper arguments. -To ensure compatibility, do not specify any further arguments that for example change calling conventions or memory layout. +```toml +[target.aarch64-unknown-nto-qnx700] +rustflags = ["-C", "relro_level=off"] +``` + +If your QNX kernel does not allow it, and `relro` is not disabled, running the +compiled binary would fail with `syntax error: ... unexpected` or similar. This +is due to kernel trying to interpret the compiled binary with `/bin/sh`, and +obviously failing. To verify that this is really the case, run your binary with +the `DL_DEBUG=all` env var, and look for this output. If you see it, you should +disable `relro` as described above. + +```text +Resolution scope for Executable->/bin/sh: + Executable->/bin/sh + libc.so.4->/usr/lib/ldqnx-64.so.2 +``` diff --git a/src/doc/rustc/src/platform-support/oe-linux-gnu.md b/src/doc/rustc/src/platform-support/oe-linux-gnu.md new file mode 100644 index 0000000000000..353e909262fd0 --- /dev/null +++ b/src/doc/rustc/src/platform-support/oe-linux-gnu.md @@ -0,0 +1,41 @@ +# `*-oe-linux-gnu` + +**Tier: 3** + +Targets for OpenEmbedded/Yocto-based Linux systems. + +Target triplets available: + +* `x86_64-oe-linux-gnu` +* `aarch64-oe-linux-gnu` +* `i686-oe-linux-gnu` +* `armv7-oe-linux-gnueabihf` +* `riscv64-oe-linux-gnu` + +## Target maintainers + +[@DeepeshWR](https://github.com/DeepeshWR) + +## Requirements + +### Operating System + +These targets are intended for Linux systems built using the OpenEmbedded and Yocto Project build systems. + +### C Toolchain + +The targets use the architecture-specific OpenEmbedded GCC driver as the default linker and therefore require the corresponding OpenEmbedded cross-compilation toolchain to be available in the build environment. + +## Building + +These targets are intended for use with the OpenEmbedded/Yocto build system. They provide base target definitions from which downstream Yocto-generated targets may inherit. + +## Building Rust programs + +Rust does not currently ship precompiled artifacts for these targets. + +Downstream Yocto-generated targets may inherit from these base OpenEmbedded targets and customize metadata or vendor-specific configuration as needed. + +## Cross-compilation toolchains and C code + +The targets support linking with C code through the corresponding OpenEmbedded cross-compilation toolchains. diff --git a/src/doc/rustc/src/platform-support/windows-gnu.md b/src/doc/rustc/src/platform-support/windows-gnu.md index a867ebcfe2539..d7aec5af21dec 100644 --- a/src/doc/rustc/src/platform-support/windows-gnu.md +++ b/src/doc/rustc/src/platform-support/windows-gnu.md @@ -26,6 +26,18 @@ They follow Windows calling convention for `extern "C"`. Like with any other Windows target, created binaries are in PE format. +### C toolchain + +The targets are built and tested using a reasonably modern C toolchain, and it should be considered as the oldest supported version: + +* GNU Binutils 2.44 +* GCC 14.2 +* mingw-w64 12.0.0 +* MSVCRT library as the default + +Using older tools (especially Binutils) may not work properly, due to the number of issues plaguing older versions of Binutils. +The supported toolchain versions are subject to change. + ## Building Rust programs Rust does ship a pre-compiled std library for those targets. diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index f16f375a5a84b..cc6cf2f8a648c 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -850,12 +850,63 @@ pub mod futures { Then, the `unix` cfg will never be displayed into the documentation. -Rustdoc currently hides `doc` and `doctest` attributes by default and reserves the right to change the list of "hidden by default" attributes. +The syntax of `hide` is as follows: you can list as many `cfg` name as you want: -The attribute accepts only a list of identifiers or key/value items. So you can write: +```rust,ignore (nightly) +#[doc(auto_cfg(hide(feature, target_os)))] +``` + +With the above example, it means that `#[cfg(feature)]` and `#[cfg(target_os)]` won't be displayed in the docs. However, `#[cfg(target_os = "linux)]` or `#[cfg(feature = "something")]` will be displayed because only the key without values was marked as hidden. if you want to hide some values, you can do: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(feature, target_os, values("something", "linux"))))] +``` + +In this case, `#[cfg(feature = "linux")]`, `#[cfg(feature = "something")]`, `#[cfg(target_os = "something")]` and `#[cfg(target_os = "linux")]` will be hidden. All listed keys will be impacted by `values(...)`. You can split them by having two `hide`: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values("something")), + hide(target_os, values("linux")), +))] +``` + +Now, only `#[cfg(feature = "something")]` and `#[cfg(target_os = "linux")]` will be hidden. If you want to hide a key and all its values, you can use `any()`: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values(any())), +))] +``` + +If you want to hide only when there is no value you can use `none()`: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values("something", none())), +))] +``` + +So now, if you want to forbid all values for a key, but allow the key itself, you can do: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values(any())), // We completely hide "feature". + show(feature), // We show again "feature" (but not any value). +))] +``` + +If the previous example, both `#[cfg(feature)]` and `#[cfg(feature = "something")]` will be hidden. + +Rustdoc currently hides `test`, `doc` and `doctest` attributes by default and reserves the right to change the list of "hidden by default" attributes. + +The attribute accepts only a list of identifiers and `values()`. So you can write: ```rust,ignore (nightly) -#[doc(auto_cfg(hide(unix, doctest, feature = "something")))] +#[doc(auto_cfg( + hide(unix, doctest), + hide(feature, values("something")), +))] #[doc(auto_cfg(hide()))] ``` @@ -865,7 +916,7 @@ But you cannot write: #[doc(auto_cfg(hide(not(unix))))] ``` -So if we use `doc(auto_cfg(hide(unix)))`, it means it will hide all mentions of `unix`: +So if we use `doc(auto_cfg(hide(unix)))`, it means it will hide all mentions of `unix` without a value: ```rust,ignore (nightly) #[cfg(unix)] // nothing displayed @@ -879,14 +930,6 @@ However, it only impacts the `unix` cfg, not the feature: #[cfg(feature = "unix")] // `feature = "unix"` is displayed ``` -If `cfg_auto(show(...))` and `cfg_auto(hide(...))` are used to show/hide a same `cfg` on a same item, it'll emit an error. Example: - -```rust,ignore (nightly) -#[doc(auto_cfg(hide(unix)))] -#[doc(auto_cfg(show(unix)))] // Error! -pub fn foo() {} -``` - Using this attribute will re-enable `auto_cfg` if it was disabled at this location: ```rust,ignore (nightly) @@ -904,14 +947,6 @@ pub mod module { } ``` -However, using `doc(auto_cfg = ...)` and `doc(auto_cfg(...))` on the same item will emit an error: - -```rust,ignore (nightly) -#[doc(auto_cfg = false)] -#[doc(auto_cfg(hide(unix)))] // error -pub fn foo() {} -``` - The reason behind this is that `doc(auto_cfg = ...)` enables or disables the feature, whereas `doc(auto_cfg(...))` enables it unconditionally, making the first attribute to appear useless as it will be overidden by the next `doc(auto_cfg)` attribute. ### `#[doc(auto_cfg(show(...)))]` @@ -919,6 +954,8 @@ The reason behind this is that `doc(auto_cfg = ...)` enables or disables the fea This attribute does the opposite of `#[doc(auto_cfg(hide(...)))]`: if you used `#[doc(auto_cfg(hide(...)))]` and want to revert its effect on an item and its descendants, you can use `#[doc(auto_cfg(show(...)))]`. It only applies to `#[doc(auto_cfg = true)]`, not to `#[doc(cfg(...))]`. +It follows the same syntax rules as for `#[doc(auto_cfg(hide(...)))]`. + For example: ```rust,ignore (nightly) @@ -936,7 +973,10 @@ pub mod futures { The attribute accepts only a list of identifiers or key/value items. So you can write: ```rust,ignore (nightly) -#[doc(auto_cfg(show(unix, doctest, feature = "something")))] +#[doc(auto_cfg( + show(unix, doctest), + show(feature, values("something")), +))] #[doc(auto_cfg(show()))] ``` diff --git a/src/doc/unstable-book/src/compiler-flags/default-visibility.md b/src/doc/unstable-book/src/compiler-flags/default-visibility.md index ad9e5d84bba80..c401fd1d2d4dd 100644 --- a/src/doc/unstable-book/src/compiler-flags/default-visibility.md +++ b/src/doc/unstable-book/src/compiler-flags/default-visibility.md @@ -41,4 +41,4 @@ building for Linux. Other linkers such as LLD are not affected. Using `-Zdefault-visibility=interposable` will cause symbols to be emitted with "default" visibility. On platforms that support it, this makes it so that symbols can be interposed, which means that they can be overridden by symbols with the same name from the executable or by other -shared objects earier in the load order. +shared objects earlier in the load order. diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-mcount.md b/src/doc/unstable-book/src/compiler-flags/instrument-mcount.md new file mode 100644 index 0000000000000..376b509048dab --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/instrument-mcount.md @@ -0,0 +1,143 @@ +# `instrument-mcount` + +Insert calls to a counting function at the entry of each function. Traditionally, the name of this function was +mcount, but the exact name may vary depending on target and option usage. + +The counting function is a special function which does not typically follow a target's ABI. It generally takes +two arguments, the address of the calling function and the address of the called function. It was intially used +to profile applications, but has expanded to other usages (for example [ftrace on Linux](https://docs.kernel.org/trace/ftrace.html)). + +Supported options: + + - `no`, `n`, `off`: Do no enable instrumentation. The default option. This requires, and enables frame pointer generation. + - `yes`, `y`, `on`: Enable mcount based function instrumentation. + - `fentry`: Enable fentry based function instrument, where supported. The calling conventions for this are different than mcount, with less overhead, and no frame pointer requirements. This counting function is always named `__fentry__`. This is only available on x86 and s390x targets. + +|target |mcount function|supports fentry|ABI notes| +|--- |--- |--- |--- | +|aarch64-apple-darwin | `\u{1}mcount` | | | +|aarch64-pc-windows-msvc | `mcount` | | | +|aarch64-unknown-linux-gnu| `_mcount` | | | +|i686-pc-windows-msvc | `mcount` | x| | +|i686-unknown-linux-gnu | `mcount` | x| | +|x86_64-pc-windows-gnu | `_mcount` | x| | +|x86_64-pc-windows-msvc | `mcount` | x| | +|x86_64-unknown-linux-gnu | `mcount` | x| 1| + +On arm eabi targets, the mcount function is usually named `__gnu_mcount_nc`, though some targets may use different names. Implementers of counting function should consult the target specific documentation for quirks of each ABI function. + +1. On x86-64, mcount and fentry must preserve the argument registers `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`. When using fentry, the stack pointer `rsp` may need aligned to meet ABI requirements. + +## Implementing custom counting functions + +In essence, this is implementing the function `fn mcount(caller: *const std::ffi::c_void, callee: *const std::ffi::c_void)`. The calling convention for mcount follows its own ABI, which isn't usually the standard ABI for the target, but is enforced by preexisting convention. + +A trivial example on x86_64-unknown-linux-gnu looks something like the following. The `#[instrument_fn]` attribute can be used to disable profiling to simplify writing counting functions, but implementors must be very careful when calling other functions (or closures which fail to inline) as they may also call mcount. + +The following example can be compiled with `-Zinstrument-mcount=yes` or `-Zinstrument-mcount=fentry` on an x86_64-unknown-linux-gnu target. It is also acceptable to link objects with different usages of `-Zinstrument-mcount`, however doing so will require implementing both `__fentry__` and `mcount` on targets which support both. + +```rust +#![feature(instrument_fn)] +#![feature(abi_custom)] + +fn main() { + // Ensure all the early startup occurs before attempting to call this trivial, single-threaded + // counting function. + unsafe { + PROFILING_ENABLED = true; + } + println!("main() called"); + unsafe { + PROFILING_ENABLED = false; + } +} + +// This example is not threadsafe. +pub static mut IN_MCOUNT: isize = 0; +pub static mut PROFILING_ENABLED: bool = false; + +#[unsafe(no_mangle)] +#[instrument_fn = "off"] +unsafe extern "C" fn __count_fn(caller: u64, callee: u64) { + unsafe { + if IN_MCOUNT == 0 && PROFILING_ENABLED { + IN_MCOUNT += 1; + { + println!("mcount: call from 0x{caller:x} to 0x{callee:x}"); + } + IN_MCOUNT -= 1; + } + } +} + +// Define a custom mcount function. This may partially or fully override the glibc +// implementation depending on linker options. +#[unsafe(naked)] +#[unsafe(no_mangle)] +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +unsafe extern "custom" fn mcount() { + core::arch::naked_asm!( + // A simplified version based on the glibc x86-64 mcount wrapper. + // Save register arguments to the stack, and call the mcount above. + "push rax", + "push rcx", + "push rdx", + "push rsi", + "push rdi", + "push r8", + "push r9", + "mov rsi, 56[rsp]", + "mov rdi, 8[rbp]", + "call __count_fn", + "pop r9", + "pop r8", + "pop rdi", + "pop rsi", + "pop rdx", + "pop rcx", + "pop rax", + "ret", + ) +} + +// Supply a custom __fentry__ instead of glibc's. This has the same linker +// restrictions as noted with mcount, but does not require a frame pointer. +#[unsafe(naked)] +#[unsafe(no_mangle)] +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +unsafe extern "custom" fn __fentry__() { + core::arch::naked_asm!( + // __fentry__ is called before any other prologue actions, be careful + // with stack alignment. The stack slots look something like: + // [...] + // [caller return address] + // [callee return address] <- top of stack + "sub rsp, 8", + "push rax", + "push rcx", + "push rdx", + "push rsi", + "push rdi", + "push r8", + "push r9", + "mov rsi, 64[rsp]", + "mov rdi, 72[rsp]", + "call __count_fn", + "pop r9", + "pop r8", + "pop rdi", + "pop rsi", + "pop rdx", + "pop rcx", + "pop rax", + "add rsp, 8", + "ret", + ) +} +``` + +When run, the above program should produce output similar to: +```txt +mcount: call from 0x5614c97d778a to 0x5614c97d76e5 +main() called +``` diff --git a/src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md b/src/doc/unstable-book/src/compiler-flags/min-recursion-limit.md similarity index 100% rename from src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md rename to src/doc/unstable-book/src/compiler-flags/min-recursion-limit.md diff --git a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md index 4a9bf47a29011..80f0e54907872 100644 --- a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md +++ b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md @@ -2,10 +2,13 @@ -------------------- -The `-Z patchable-function-entry=total_nops,prefix_nops` or `-Z patchable-function-entry=total_nops` +The `-Z patchable-function-entry=total_nops,prefix_nops,record_section`, + `-Z patchable-function-entry=total_nops,prefix_nops`, or + `-Z patchable-function-entry=total_nops` compiler flag enables nop padding of function entries with 'total_nops' nops, with -an offset for the entry of the function at 'prefix_nops' nops. In the second form, -'prefix_nops' defaults to 0. +an offset for the entry of the function at 'prefix_nops' nops. In the third form, +'prefix_nops' defaults to 0. record\_section can specify a specific linker section +to place entry record in, the default is `__patchable_function_entries`. As an illustrative example, `-Z patchable-function-entry=3,2` would produce: diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-reg.md b/src/doc/unstable-book/src/language-features/asm-experimental-reg.md index 5bbcb223b6026..095b541da6e46 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-reg.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-reg.md @@ -12,6 +12,8 @@ This tracks support for additional registers in architectures where inline assem | Architecture | Register class | Registers | LLVM constraint code | | ------------ | -------------- | --------- | -------------------- | +| LoongArch | `vreg` | `$vr[0-31]` | `f` | +| LoongArch | `xreg` | `$xr[0-31]` | `f` | ## Register class supported types @@ -20,11 +22,16 @@ This tracks support for additional registers in architectures where inline assem | x86 | `xmm_reg` | `sse` | `i128` | | x86 | `ymm_reg` | `avx` | `i128` | | x86 | `zmm_reg` | `avx512f` | `i128` | +| LoongArch | `vreg` | `lsx` | `f32`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +| LoongArch | `xreg` | `lasx` | `f32`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`,
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` | ## Register aliases | Architecture | Base register | Aliases | | ------------ | ------------- | ------- | +| LoongArch | `$f[0-7]` | `$fa[0-7]`, `$vr[0-7]`, `$xr[0-7]` | +| LoongArch | `$f[8-23]` | `$ft[0-15]`, `$vr[8-23]`, `$xr[8-23]` | +| LoongArch | `$f[24-31]` | `$fs[0-7]`, `$vr[24-31]`, `$xr[24-31]` | ## Unsupported registers @@ -35,3 +42,9 @@ This tracks support for additional registers in architectures where inline assem | Architecture | Register class | Modifier | Example output | LLVM modifier | | ------------ | -------------- | -------- | -------------- | ------------- | +| LoongArch | `freg` | `w` | `$vr0` | `w` | +| LoongArch | `freg` | `u` | `$xr0` | `u` | +| LoongArch | `vreg` | None | `$vr0` | `w` | +| LoongArch | `vreg` | `u` | `$xr0` | `u` | +| LoongArch | `xreg` | None | `$xr0` | `u` | +| LoongArch | `xreg` | `w` | `$vr0` | `w` | diff --git a/src/doc/unstable-book/src/language-features/strict-provenance-lints.md b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md index fec908fe3212e..3c7f29dc69f46 100644 --- a/src/doc/unstable-book/src/language-features/strict-provenance-lints.md +++ b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md @@ -5,17 +5,18 @@ The tracking issue for this feature is: [#130351] [#130351]: https://github.com/rust-lang/rust/issues/130351 ----- -The `strict_provenance_lints` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints. -These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model. +The `strict_provenance_lints` feature allows to enable the `implicit_provenance_casts` lint. ## Example ```rust #![feature(strict_provenance_lints)] -#![warn(fuzzy_provenance_casts)] +#![warn(implicit_provenance_casts)] fn main() { - let _dangling = 16_usize as *const u8; - //~^ WARNING: strict provenance disallows casting integer `usize` to pointer `*const u8` + let dangling = 16_usize as *const u8; + //~^ WARNING: cast from `usize` to `*const u8` implicitly relies on exposed provenance + let _addr = dangling as usize; + //~^ WARNING: cast from `*const u8` to `usize` implicitly exposes pointer provenance } ``` diff --git a/src/doc/unstable-book/src/library-features/tuple-trait.md b/src/doc/unstable-book/src/library-features/tuple-trait.md new file mode 100644 index 0000000000000..5521850f12bb6 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/tuple-trait.md @@ -0,0 +1,5 @@ +# `tuple_trait` + +The tracking issue for this feature is [#157987]. + +[#157987]: https://github.com/rust-lang/rust/issues/157987 diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index c06b4857e4903..45d084b2f9bf7 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -13,7 +13,7 @@ arrayvec = { version = "0.7", default-features = false } askama = { version = "0.16.0", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" indexmap = { version = "2", features = ["serde"] } -itertools = "0.12" +itertools = "0.15" minifier = { version = "0.3.5", default-features = false } proc-macro2 = "1.0.103" pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 170f1439ecc9c..74a04b4451040 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -8,11 +8,13 @@ use std::sync::Arc; use std::{fmt, mem, ops}; use itertools::Either; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_hir as hir; use rustc_hir::Attribute; -use rustc_hir::attrs::{self, AttributeKind, CfgEntry, CfgHideShow, HideOrShow}; +use rustc_hir::attrs::{ + AttributeKind, CfgEntry, CfgHideShow, DocCfgHideShow, DocCfgHideShowValue, HideOrShow, +}; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{DUMMY_SP, Span}; @@ -30,6 +32,87 @@ mod tests; #[cfg_attr(test, derive(PartialEq))] pub(crate) struct Cfg(CfgEntry); +// Similar to `hir::DocCfgHideShow` but allows to handle both `show` and `hide` as with the `except` +// field in `Any` variant. +#[derive(Clone, Debug)] +enum DocCfgHide { + Any { except: ThinVec }, + List(ThinVec), +} + +impl DocCfgHide { + fn new() -> Self { + Self::List([DocCfgHideShowValue::new_none(DUMMY_SP)].into()) + } + + fn contains(&self, value: Option) -> bool { + match self { + // Contains any values except the ones listed in `except`. + Self::Any { except } => !except.iter().any(|e| e.value == value), + Self::List(values) => values.iter().any(|v| v.value == value), + } + } + + fn merge_with(&mut self, other: &DocCfgHideShow) { + match (self, other) { + (Self::Any { except }, DocCfgHideShow::Any(_)) => { + except.clear(); + } + (s, DocCfgHideShow::Any(_)) => { + // We "upgrade" the list values to "all". + *s = Self::Any { except: ThinVec::new() }; + } + (Self::Any { except }, DocCfgHideShow::List(values)) => { + for other in values { + if let Some(index) = except.iter().position(|value| value.value == other.value) + { + except.remove(index); + } + } + } + (Self::List(values), DocCfgHideShow::List(other_values)) => { + for other in other_values { + if !values.iter().any(|value| value.value == other.value) { + values.push(*other); + } + } + } + } + } + + fn remove(&mut self, other: &DocCfgHideShow) { + match (self, other) { + (s, DocCfgHideShow::Any(_)) => { + *s = Self::List(ThinVec::new()); + } + (Self::Any { except }, DocCfgHideShow::List(other_values)) => { + for other in other_values { + if !except.iter().any(|value| value.value == other.value) { + except.push(*other); + } + } + } + (Self::List(values), DocCfgHideShow::List(other_values)) => { + for other in other_values { + if let Some(index) = values.iter().position(|value| value.value == other.value) + { + values.remove(index); + } + } + } + } + } +} + +impl From<&DocCfgHideShow> for DocCfgHide { + fn from(from: &DocCfgHideShow) -> Self { + match from { + DocCfgHideShow::Any(_) => Self::Any { except: ThinVec::new() }, + DocCfgHideShow::List(values) => Self::List(values.clone()), + } + } +} + /// Whether the configuration consists of just `Cfg` or `Not`. fn is_simple_cfg(cfg: &CfgEntry) -> bool { match cfg { @@ -53,14 +136,14 @@ fn is_any_cfg(cfg: &CfgEntry) -> bool { } } -fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashSet) -> Option { +fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashMap) -> Option { match cfg { CfgEntry::Bool(..) => Some(cfg.clone()), - CfgEntry::NameValue { .. } => { - if !hidden.contains(&NameValueCfg::from(cfg)) { - Some(cfg.clone()) - } else { + CfgEntry::NameValue { name, value, .. } => { + if hidden.get(name).is_some_and(|values| values.contains(*value)) { None + } else { + Some(cfg.clone()) } } CfgEntry::Not(cfg, _) => { @@ -555,11 +638,12 @@ fn human_readable_target_os(os: Symbol) -> Option<&'static str> { Motor => "Motor OS", NetBsd => "NetBSD", None => "bare-metal", - Nto => "QNX Neutrino", + Nto => "QNX SDP 7.x", NuttX => "NuttX", OpenBsd => "OpenBSD", Psp => "Play Station Portable", Psx => "Play Station 1", + Qnx => "QNX SDP 8.0+", Qurt => "QuRT", Redox => "Redox OS", Rtems => "RTEMS OS", @@ -635,10 +719,9 @@ fn human_readable_target_env(env: Symbol) -> Option<&'static str> { Msvc => "MSVC", Musl => "musl", Newlib => "Newlib", - Nto70 => "Neutrino 7.0", - Nto71 => "Neutrino 7.1", - Nto71IoSock => "Neutrino 7.1 with io-sock", - Nto80 => "Neutrino 8.0", + Nto70 => "QNX SDP 7.0", + Nto71 => "QNX SDP 7.1", + Nto71IoSock => "QNX SDP 7.1 with io-sock", Ohos => "OpenHarmony", P1 => "WASIp1", P2 => "WASIp2", @@ -653,39 +736,12 @@ fn human_readable_target_env(env: Symbol) -> Option<&'static str> { }) } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -struct NameValueCfg { - name: Symbol, - value: Option, -} - -impl NameValueCfg { - fn new(name: Symbol) -> Self { - Self { name, value: None } - } -} - -impl<'a> From<&'a CfgEntry> for NameValueCfg { - fn from(cfg: &'a CfgEntry) -> Self { - match cfg { - CfgEntry::NameValue { name, value, .. } => NameValueCfg { name: *name, value: *value }, - _ => NameValueCfg { name: sym::empty, value: None }, - } - } -} - -impl<'a> From<&'a attrs::CfgInfo> for NameValueCfg { - fn from(cfg: &'a attrs::CfgInfo) -> Self { - Self { name: cfg.name, value: cfg.value.map(|(value, _)| value) } - } -} - /// This type keeps track of (doc) cfg information as we go down the item tree. #[derive(Clone, Debug)] pub(crate) struct CfgInfo { /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active /// `doc(auto_cfg(show(...)))` cfgs. - hidden_cfg: FxHashSet, + hidden_cfg: FxHashMap, /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while /// taking into account the `hidden_cfg` information. current_cfg: Cfg, @@ -700,10 +756,10 @@ pub(crate) struct CfgInfo { impl Default for CfgInfo { fn default() -> Self { Self { - hidden_cfg: FxHashSet::from_iter([ - NameValueCfg::new(sym::test), - NameValueCfg::new(sym::doc), - NameValueCfg::new(sym::doctest), + hidden_cfg: FxHashMap::from_iter([ + (sym::test, DocCfgHide::new()), + (sym::doc, DocCfgHide::new()), + (sym::doctest, DocCfgHide::new()), ]), current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)), auto_cfg_active: true, @@ -712,51 +768,24 @@ impl Default for CfgInfo { } } -fn show_hide_show_conflict_error( - tcx: TyCtxt<'_>, - item_span: rustc_span::Span, - previous: rustc_span::Span, -) { - let mut diag = tcx.sess.dcx().struct_span_err( - item_span, - format!( - "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item" - ), - ); - diag.span_note(previous, "first change was here"); - diag.emit(); -} - /// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument. /// -/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and -/// `auto_cfg(show(...))` on the same item and emits an error if it's the case. -/// /// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs` /// and in `new_hide_attrs` arguments. -fn handle_auto_cfg_hide_show( - tcx: TyCtxt<'_>, - cfg_info: &mut CfgInfo, - attr: &CfgHideShow, - new_show_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, - new_hide_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, -) { - for value in &attr.values { - let simple = NameValueCfg::from(value); +fn handle_auto_cfg_hide_show(cfg_info: &mut CfgInfo, attr: &CfgHideShow) { + for (cfg_name, value) in &attr.values { if attr.kind == HideOrShow::Show { - if let Some(span) = new_hide_attrs.get(&(simple.name, simple.value)) { - show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span); - } else { - new_show_attrs.insert((simple.name, simple.value), value.span_for_name_and_value()); - } - cfg_info.hidden_cfg.remove(&simple); + cfg_info + .hidden_cfg + .entry(*cfg_name) + .and_modify(|entry| entry.remove(value)) + .or_insert_with(|| value.into()); } else { - if let Some(span) = new_show_attrs.get(&(simple.name, simple.value)) { - show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span); - } else { - new_hide_attrs.insert((simple.name, simple.value), value.span_for_name_and_value()); - } - cfg_info.hidden_cfg.insert(simple); + cfg_info + .hidden_cfg + .entry(*cfg_name) + .and_modify(|entry| entry.merge_with(value)) + .or_insert_with(|| value.into()); } } } @@ -791,9 +820,6 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator false } - let mut new_show_attrs = FxHashMap::default(); - let mut new_hide_attrs = FxHashMap::default(); - let mut doc_cfg = attrs .clone() .filter_map(|attr| match attr { @@ -844,13 +870,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator return None; } for (value, _) in &d.auto_cfg { - handle_auto_cfg_hide_show( - tcx, - cfg_info, - value, - &mut new_show_attrs, - &mut new_hide_attrs, - ); + handle_auto_cfg_hide_show(cfg_info, value); } } } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 08478a62b7d85..9469c806a26ad 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -142,7 +142,7 @@ pub(crate) fn try_inline( Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => return Some(Vec::new()), Res::Def(DefKind::Mod, did) => { record_extern_fqn(cx, did, ItemType::Module); - clean::ModuleItem(build_module(cx, did, visited)) + clean::ModuleItem(build_module(cx, did, name, visited)) } Res::Def(DefKind::Static { .. }, did) => { record_extern_fqn(cx, did, ItemType::Static); @@ -207,6 +207,7 @@ pub(crate) fn try_inline_glob( let mut items = build_module_items( cx, did, + cx.tcx.item_name(did), visited, inlined_names, Some(&reexports), @@ -665,16 +666,43 @@ pub(crate) fn build_impl( )); } -fn build_module(cx: &mut DocContext<'_>, did: DefId, visited: &mut DefIdSet) -> clean::Module { - let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None, None); +fn build_module( + cx: &mut DocContext<'_>, + did: DefId, + name: Symbol, + visited: &mut DefIdSet, +) -> clean::Module { + let items = build_module_items(cx, did, name, visited, &mut FxHashSet::default(), None, None); let span = clean::Span::new(cx.tcx.def_span(did)); clean::Module { items, span } } +// We are only interested into `Res::Def`. And in there, we only want "items" which get their own +// rustdoc page. So not `DefKind::Ctor` for example (which is returned by `tcx.module_children()`). +fn should_ignore_res(res: Res) -> bool { + !matches!( + res, + Res::Def(DefKind::Trait, _) + | Res::Def(DefKind::TraitAlias, _) + | Res::Def(DefKind::Fn, _) + | Res::Def(DefKind::Struct, _) + | Res::Def(DefKind::Union, _) + | Res::Def(DefKind::TyAlias, _) + | Res::Def(DefKind::Enum, _) + | Res::Def(DefKind::ForeignTy, _) + | Res::Def(DefKind::Variant, _) + | Res::Def(DefKind::Mod, _) + | Res::Def(DefKind::Static { .. }, _) + | Res::Def(DefKind::Const { .. }, _) + | Res::Def(DefKind::Macro(_), _) + ) +} + fn build_module_items( cx: &mut DocContext<'_>, - did: DefId, + module_def_id: DefId, + module_name: Symbol, visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, allowed_def_ids: Option<&DefIdSet>, @@ -685,61 +713,107 @@ fn build_module_items( // If we're re-exporting a re-export it may actually re-export something in // two namespaces, so the target may be listed twice. Make sure we only // visit each node at most once. - for item in cx.tcx.module_children(did).iter() { - if item.vis.is_public() { - let res = item.res.expect_non_local(); - if let Some(def_id) = res.opt_def_id() - && let Some(allowed_def_ids) = allowed_def_ids - && !allowed_def_ids.contains(&def_id) + for item in cx.tcx.module_children(module_def_id).iter() { + if !item.vis.is_public() { + continue; + } + let res = item.res.expect_non_local(); + if let Some(def_id) = res.opt_def_id() + && let Some(allowed_def_ids) = allowed_def_ids + && !allowed_def_ids.contains(&def_id) + { + continue; + } + if let Some(def_id) = res.mod_def_id() { + // If we're inlining a glob import, it's possible to have + // two distinct modules with the same name. We don't want to + // inline it, or mark any of its contents as visited. + if module_def_id == def_id + || inlined_names.contains(&(ItemType::Module, item.ident.name)) + || !visited.insert(def_id) { continue; } - if let Some(def_id) = res.mod_def_id() { - // If we're inlining a glob import, it's possible to have - // two distinct modules with the same name. We don't want to - // inline it, or mark any of its contents as visited. - if did == def_id - || inlined_names.contains(&(ItemType::Module, item.ident.name)) - || !visited.insert(def_id) - { - continue; - } - } - if let Res::PrimTy(p) = res { - // Primitive types can't be inlined so generate an import instead. - let prim_ty = clean::PrimitiveType::from(p); - items.push(clean::Item { - inner: Box::new(clean::ItemInner { - name: None, - // We can use the item's `DefId` directly since the only information ever - // used from it is `DefId.krate`. - item_id: ItemId::DefId(did), - attrs: Default::default(), - stability: None, - kind: clean::ImportItem(clean::Import::new_simple( - item.ident.name, - clean::ImportSource { - path: clean::Path { - res, - segments: thin_vec![clean::PathSegment { - name: prim_ty.as_sym(), - args: clean::GenericArgs::AngleBracketed { - args: Default::default(), - constraints: ThinVec::new(), - }, - }], - }, - did: None, + } + if let Res::PrimTy(p) = res { + // Primitive types can't be inlined so generate an import instead. + let prim_ty = clean::PrimitiveType::from(p); + items.push(clean::Item { + inner: Box::new(clean::ItemInner { + name: None, + // We can use the item's `DefId` directly since the only information ever + // used from it is `DefId.krate`. + item_id: ItemId::DefId(module_def_id), + attrs: Default::default(), + stability: None, + kind: clean::ImportItem(clean::Import::new_simple( + item.ident.name, + clean::ImportSource { + path: clean::Path { + res, + segments: thin_vec![clean::PathSegment { + name: prim_ty.as_sym(), + args: clean::GenericArgs::AngleBracketed { + args: Default::default(), + constraints: ThinVec::new(), + }, + }], }, - true, - )), - cfg: None, - inline_stmt_id: None, - }), - }); - } else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) { - items.extend(i) + did: None, + }, + true, + )), + cfg: None, + inline_stmt_id: None, + }), + }); + } else if let Some(def_id) = res.opt_def_id() + && let Some(reexport) = item.reexport_chain.first() + && let Some(reexport_def_id) = reexport.id() + && find_attr!( + load_attrs(cx.tcx, reexport_def_id), + Doc(d) + if d.inline.first().is_some_and(|(inline, _)| *inline == hir::attrs::DocInline::NoInline) + ) + { + if should_ignore_res(res) { + continue; } + // This item is reexported as `no_inline` so it shouldn't be inlined. + let item = Item::from_def_id_and_parts( + module_def_id, + None, + clean::ImportItem(clean::Import::new_simple( + item.ident.name, + clean::ImportSource { + path: clean::Path { + res, + segments: thin_vec![ + clean::PathSegment { + name: module_name, + args: clean::GenericArgs::AngleBracketed { + args: Default::default(), + constraints: ThinVec::new(), + }, + }, + clean::PathSegment { + name: cx.tcx.item_name(def_id), + args: clean::GenericArgs::AngleBracketed { + args: Default::default(), + constraints: ThinVec::new(), + }, + }, + ], + }, + did: None, + }, + true, + )), + cx.tcx, + ); + items.push(item); + } else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) { + items.extend(i) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 36cd2642e266a..3179bda868651 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1780,7 +1780,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type let self_type = clean_ty(qself, cx); let (trait_, should_fully_qualify) = match ty.kind() { - ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { .. }, .. }) => { + ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { .. }, .. }) => { let res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); register_res(cx, trait_.res); @@ -1790,7 +1790,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type (Some(trait_), should_fully_qualify) } - ty::Alias(ty::AliasTy { kind: ty::Inherent { .. }, .. }) => (None, false), + ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }) => (None, false), // Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s. ty::Error(_) => return Type::Infer, _ => bug!("clean: expected associated type, found `{ty:?}`"), @@ -2282,7 +2282,7 @@ pub(crate) fn clean_middle_ty<'tcx>( Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect()) } - ty::Alias(alias_ty @ ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) => { + ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) => { if cx.tcx.is_impl_trait_in_trait(def_id) { clean_middle_opaque_bounds(cx, def_id, args) } else { @@ -2294,7 +2294,7 @@ pub(crate) fn clean_middle_ty<'tcx>( } } - ty::Alias(alias_ty @ ty::AliasTy { kind: ty::Inherent { def_id }, .. }) => { + ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Inherent { def_id }, .. }) => { let alias_ty = bound_ty.rebind(alias_ty); let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None); @@ -2317,7 +2317,7 @@ pub(crate) fn clean_middle_ty<'tcx>( })) } - ty::Alias(ty::AliasTy { kind: ty::Free { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Free { def_id }, args, .. }) => { if cx.tcx.features().lazy_type_alias() { // Free type alias `data` represents the `type X` in `type X = Y`. If we need `Y`, // we need to use `type_of`. @@ -2345,7 +2345,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::BoundTyKind::Anon => panic!("unexpected anonymous bound type variable"), }, - ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { // If it's already in the same alias, don't get an infinite loop. if cx.current_type_aliases.contains_key(&def_id) { let path = diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 1d5142491a5ab..620951bb4974b 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1770,7 +1770,7 @@ impl PrimitiveType { ty::Tuple(elems) if elems.is_empty() => Some(Self::Unit), ty::Tuple(_) => Some(Self::Tuple), ty::Adt(..) - | ty::Alias(..) + | ty::Alias(_, ..) | ty::Bound(..) | ty::Closure(..) | ty::Coroutine(..) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index ad3d0e7802a54..f77e201805a2b 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -350,28 +350,21 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { match n.kind() { - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { kind, .. }) => match kind { - ty::UnevaluatedConstKind::Projection { def_id } => { - if let Some(local_def_id) = def_id.as_local() - && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) - { - rendered_const(tcx, body_id, local_def_id) - } else { - n.to_string() - } - } - ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { - if let Some(local_def_id) = def_id.as_local() - && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) - { - rendered_const(tcx, body_id, local_def_id) - } else { - inline::print_inlined_const(tcx, def_id) - } + ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => { + let def_id: DefId = match kind { + ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::Free { def_id } => def_id.into(), + ty::AliasConstKind::Anon { def_id } => def_id.into(), + }; + if let Some(local_def_id) = def_id.as_local() + && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) + { + rendered_const(tcx, body_id, local_def_id) + } else { + n.to_string() } - }, + } // array lengths are obviously usize ty::ConstKind::Value(cv) if *cv.ty.kind() == ty::Uint(ty::UintTy::Usize) => { cv.to_leaf().to_string() diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index fee783133133f..ccee062584e01 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -233,6 +233,30 @@ impl Cache { } } +impl CacheBuilder<'_, '_> { + /// Extends `dids` with ones that an impl should be associated with for a type appearing in its + /// `Self` type or trait generic arguments, accounting for references and `#[fundamental]` + /// wrappers. + /// + /// This ensures that impls like `impl Trait> for Foreign`, `impl Trait for + /// Box`, and other variations of these, are documented on `Local`'s page. + fn extend_with_fundamental_dids(&self, ty: &clean::Type, dids: &mut FxIndexSet) { + dids.extend(ty.def_id(self.cache)); + // without_borrowed_ref allows cases like `impl Trait<&Box> for Foreign` to be + // handled by this function. (This is rare in practice, but easy to handle here.) + if let clean::Type::Path { path } = ty.without_borrowed_ref() + && let Some(generics) = path.generics() + && let ty::Adt(adt, _) = + self.tcx.type_of(path.def_id()).instantiate_identity().skip_norm_wip().kind() + && adt.is_fundamental() + { + for inner in generics { + self.extend_with_fundamental_dids(inner, dids); + } + } + } +} + impl DocFolder for CacheBuilder<'_, '_> { fn fold_item(&mut self, item: clean::Item) -> Option { if item.item_id.is_local() { @@ -418,22 +442,9 @@ impl DocFolder for CacheBuilder<'_, '_> { // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxIndexSet::default(); match i.for_ { - clean::Type::Path { ref path } - | clean::BorrowedRef { type_: clean::Type::Path { ref path }, .. } => { - dids.insert(path.def_id()); - if let Some(generics) = path.generics() - && let ty::Adt(adt, _) = self - .tcx - .type_of(path.def_id()) - .instantiate_identity() - .skip_norm_wip() - .kind() - && adt.is_fundamental() - { - for ty in generics { - dids.extend(ty.def_id(self.cache)); - } - } + clean::Type::Path { .. } + | clean::BorrowedRef { type_: clean::Type::Path { .. }, .. } => { + self.extend_with_fundamental_dids(&i.for_, &mut dids); } clean::DynTrait(ref bounds, _) | clean::BorrowedRef { type_: clean::DynTrait(ref bounds, _), .. } => { @@ -452,7 +463,7 @@ impl DocFolder for CacheBuilder<'_, '_> { && let Some(generics) = trait_.generics() { for bound in generics { - dids.extend(bound.def_id(self.cache)); + self.extend_with_fundamental_dids(bound, &mut dids); } } let impl_item = Impl { impl_item: item }; diff --git a/src/librustdoc/html/render/sorted_template.rs b/src/librustdoc/html/render/sorted_template.rs index 659c5e6093bdd..8501421651ab6 100644 --- a/src/librustdoc/html/render/sorted_template.rs +++ b/src/librustdoc/html/render/sorted_template.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write as _}; use std::marker::PhantomData; use std::str::FromStr; -use itertools::{Itertools as _, Position}; +use itertools::Itertools as _; use serde::{Deserialize, Serialize}; /// Append-only templates for sorted, deduplicated lists of items. @@ -62,7 +62,7 @@ impl fmt::Display for SortedTemplate { write!(f, "{}", self.before)?; for (p, fragment) in self.fragments.iter().with_position() { let mut f = DeltaWriter { inner: &mut f, delta: 0 }; - let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR }; + let sep = if p.is_first() { "" } else { F::SEPARATOR }; f.write_str(sep)?; f.write_str(fragment)?; fragment_lengths.push(f.delta); @@ -95,7 +95,7 @@ impl FromStr for SortedTemplate { let (fragment, rest) = s.split_at_checked(index).ok_or(Error("invalid fragment length: out of bounds"))?; s = rest; - let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR }; + let sep = if p.is_first() { "" } else { F::SEPARATOR }; let fragment = fragment .strip_prefix(sep) .ok_or(Error("invalid fragment length: expected to find separator here"))?; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 117437f9b20bf..26cd2cd0b9698 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -5185,9 +5185,9 @@ function makeTab(tabNb, text, results, query, isTypeSearch, goToFirst) { errorReport.className = "error"; errorReport.innerHTML = `Query parser error: "${error.join("")}".`; search.insertBefore(errorReport, search.firstElementChild); - } else if (goToFirst || + } else if (tabNb === 0 && (goToFirst || (count === 1 && getSettingValue("go-to-only-result") === "true") - ) { + )) { // Needed to force re-execution of JS when coming back to a page. Let's take this // scenario as example: // diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index bfd2b240e4dac..7083d730de58c 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -7,10 +7,12 @@ use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::attrs::{self, DeprecatedSince, DocAttribute, DocInline, HideOrShow}; -use rustc_hir::def::CtorKind; +use rustc_hir::attrs::{ + self, DeprecatedSince, DocAttribute, DocCfgHideShow, DocInline, HideOrShow, +}; +use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; -use rustc_hir::{HeaderSafety, Safety}; +use rustc_hir::{HeaderSafety, Safety, find_attr}; use rustc_metadata::rendered_const; use rustc_middle::ty::TyCtxt; use rustc_middle::{bug, ty}; @@ -92,6 +94,9 @@ impl JsonRenderer<'_> { item.item_id.as_def_id() }; let stability = stability_def_id.and_then(|def_id| self.tcx.lookup_stability(def_id)); + let const_stability = item.item_id.as_def_id().and_then(|def_id| { + const_stability_for_def_id(self.tcx, def_id).map(|s| Box::new(s.into_json(self))) + }); Some(Item { id, @@ -100,6 +105,7 @@ impl JsonRenderer<'_> { span: span.and_then(|span| span.into_json(self)), visibility: visibility.into_json(self), stability: stability.map(|s| Box::new(s.into_json(self))), + const_stability, docs, attrs, deprecation: deprecation.into_json(self), @@ -246,6 +252,36 @@ impl FromClean for Stability { } } +impl FromClean for Stability { + fn from_clean(stab: &hir::ConstStability, _renderer: &JsonRenderer<'_>) -> Self { + let feature = stab.feature.to_string(); + let level = match stab.level { + hir::StabilityLevel::Stable { since, .. } => StabilityLevel::Stable { + since: match since { + hir::StableSince::Version(since) => Some(since.to_string()), + hir::StableSince::Current => Some(hir::RustcVersion::CURRENT.to_string()), + // Match rustdoc HTML: malformed stable-since values are omitted. + hir::StableSince::Err(_) => None, + }, + }, + hir::StabilityLevel::Unstable { .. } => StabilityLevel::Unstable, + }; + Stability { feature, level } + } +} + +impl FromClean for Box { + fn from_clean(stab: &hir::DefaultBodyStability, _renderer: &JsonRenderer<'_>) -> Self { + let hir::StabilityLevel::Unstable { .. } = stab.level else { + bug!( + "unexpected stable default-body stability, \ + there's no stable equivalent of `#[rustc_default_body_unstable]`" + ) + }; + Box::new(ProvidedDefaultUnstable { feature: stab.feature.to_string() }) + } +} + impl FromClean for Option> { fn from_clean(generic_args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArgs::*; @@ -329,18 +365,23 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)), VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)), FunctionItem(f) => { - ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, true, None, header.unwrap(), renderer)) } ForeignFunctionItem(f, _) => { - ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, false, None, header.unwrap(), renderer)) } TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)), - MethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) - } + MethodItem(m, _) => ItemEnum::Function(from_clean_function( + m, + true, + default_body_stability_for_def_id(renderer.tcx, item.item_id.expect_def_id()) + .map(|stab| stab.into_json(renderer)), + header.unwrap(), + renderer, + )), RequiredMethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(m, false, None, header.unwrap(), renderer)) } ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)), @@ -361,23 +402,41 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum }) } // FIXME(generic_const_items): Add support for generic associated consts. - RequiredAssocConstItem(_generics, ty) => { - ItemEnum::AssocConst { type_: ty.into_json(renderer), value: None } - } + RequiredAssocConstItem(_generics, ty) => ItemEnum::AssocConst { + type_: ty.into_json(renderer), + value: None, + default_unstable: None, + }, // FIXME(generic_const_items): Add support for generic associated consts. - ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst { + ProvidedAssocConstItem(ci) => ItemEnum::AssocConst { + type_: ci.type_.into_json(renderer), + value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), + }, + ImplAssocConstItem(ci) => ItemEnum::AssocConst { type_: ci.type_.into_json(renderer), value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: None, }, RequiredAssocTypeItem(g, b) => ItemEnum::AssocType { generics: g.into_json(renderer), bounds: b.into_json(renderer), type_: None, + default_unstable: None, }, AssocTypeItem(t, b) => ItemEnum::AssocType { generics: t.generics.into_json(renderer), bounds: b.into_json(renderer), type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), }, // `convert_item` early returns `None` for stripped items, keywords, attributes and // "special" macro rules. @@ -791,6 +850,7 @@ impl FromClean for Impl { pub(crate) fn from_clean_function( clean::Function { decl, generics }: &clean::Function, has_body: bool, + default_unstable: Option>, header: rustc_hir::FnHeader, renderer: &JsonRenderer<'_>, ) -> Function { @@ -799,6 +859,7 @@ pub(crate) fn from_clean_function( generics: generics.into_json(renderer), header: header.into_json(renderer), has_body, + default_unstable, } } @@ -948,6 +1009,66 @@ impl FromClean for ItemKind { } } +fn default_body_stability_for_def_id( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Option { + let stability = tcx.lookup_default_body_stability(def_id)?; + match stability.level { + hir::StabilityLevel::Unstable { .. } => Some(stability), + hir::StabilityLevel::Stable { .. } => None, + } +} + +fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + if !tcx.is_conditionally_const(def_id) { + // The item cannot be conditionally-const. No const stability here. + // + // This includes associated consts, which are an interesting exception + // to the general rule that items inside `const impl` and `const trait` carry + // the const-stability of that block. Associated consts are already const, always. + return None; + } + + let const_stability = tcx.lookup_const_stability(def_id)?; + if find_attr!(tcx, def_id, RustcConstStability { .. }) { + // Direct const-stability attribute on the item itself. Return it directly. + return Some(const_stability); + } + + if const_stability.is_const_stable() { + // Items that are const-stable without an explicit attribute on their own item + // must be associated items inside `const trait` or `const impl`. + // We don't want to duplicate their parent item's const-stability attribute. + return None; + } + + // We're dealing with an item that is const-unstable, + // but doesn't have an explicit const-stability attribute on it. + // + // Today, this means one of two cases: + // - The item is enclosed within a `#[rustc_const_unstable]` block, + // like a `const trait` or `const impl`, in which case our query propagated the parent's + // const-instability info. This const-instability is desirable to place into JSON + // because *only some* associated items inside such a block are const-unstable. + // Associated consts are the exception, and were handled earlier. + // - The item is `#[unstable]` which implies it's const-unstable under the same feature, + // in which case we don't want to duplicate the existing stability attribute + // which would already appear in an adjacent field in the JSON anyway. + if let Some(parent_def_id) = tcx.opt_parent(def_id) + && matches!(tcx.def_kind(parent_def_id), DefKind::Trait | DefKind::Impl { .. }) + && tcx.lookup_const_stability(parent_def_id) == Some(const_stability) + { + Some(const_stability) + } else { + std::debug_assert_matches!( + tcx.lookup_stability(def_id).map(|s| s.level), + Some(hir::StabilityLevel::Unstable { .. }) + ); + None + } +} + /// Maybe convert a attribute from hir to json. /// /// Returns `None` if the attribute shouldn't be in the output. @@ -966,6 +1087,8 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) vec![match kind { AK::Deprecated { .. } => return Vec::new(), // Handled separately into Item::deprecation. AK::Stability { .. } => return Vec::new(), // Handled separately into Item::stability + AK::RustcConstStability { .. } => return Vec::new(), // Handled separately into Item::const_stability. + AK::RustcBodyStability { .. } => return Vec::new(), // Handled separately by `default_unstable`. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), @@ -1050,24 +1173,41 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) for sub_cfg in cfg { ret.push(Attribute::Other(format!("#[doc(cfg({sub_cfg}))]"))); } - for (auto_cfg, _) in auto_cfg { - let kind = match auto_cfg.kind { - HideOrShow::Hide => "hide", - HideOrShow::Show => "show", - }; - let mut out = format!("#[doc(auto_cfg({kind}("); - for (pos, value) in auto_cfg.values.iter().enumerate() { - if pos > 0 { + if !auto_cfg.is_empty() { + let mut out = format!("#[doc(auto_cfg("); + for (index, (auto_cfg, _)) in auto_cfg.iter().enumerate() { + let kind = match auto_cfg.kind { + HideOrShow::Hide => "hide", + HideOrShow::Show => "show", + }; + if index > 0 { out.push_str(", "); } - out.push_str(value.name.as_str()); - if let Some((value, _)) = value.value { - // We use `as_str` and debug display to have characters escaped and `"` - // characters surrounding the string. - out.push_str(&format!(" = {:?}", value.as_str())); + out.push_str(&format!("{kind}(")); + for (name, cfgs) in &auto_cfg.values { + out.push_str(&format!("{name}, values(")); + match cfgs { + DocCfgHideShow::Any(_) => { + out.push_str("any()"); + } + DocCfgHideShow::List(values) => { + for (pos, value) in values.iter().enumerate() { + let separator = if pos > 0 { ", " } else { "" }; + if let Some(value) = &value.value { + // We use `as_str` and debug display to have characters escaped + // and `"` characters surrounding the string. + out.push_str(&format!("{separator}{:?}", value.as_str())); + } else { + out.push_str(&format!("{separator}none()")); + } + } + } + } + out.push_str(")"); } + out.push(')'); } - out.push_str(")))]"); + out.push_str("))]"); ret.push(Attribute::Other(out)); } for (change, _) in auto_cfg_change { diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 5a40b03dbd5e8..433e4487eb9d5 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -361,6 +361,6 @@ mod size_asserts { // tidy-alphabetical-end // These contains a `PathBuf`, which is different sizes on different OSes. - static_assert_size!(Item, 536 + size_of::()); + static_assert_size!(Item, 544 + size_of::()); static_assert_size!(ExternalCrate, 48 + size_of::()); } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 68dceb103037e..a12e95d210967 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -547,7 +547,7 @@ fn ty_to_res<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) | ty::Foreign(did) => { Res::from_def_id(tcx, did) } - ty::Alias(..) + ty::Alias(_, ..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index bf4e842ceec3f..4c6d62917ab24 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -287,12 +287,14 @@ impl DocFolder for ImportStripper<'_> { clean::ImportItem(imp) if !self.document_hidden && self.import_should_be_hidden(&i, imp) => { + debug!("ImportStripper: stripping {:?}", i.name); None } // clean::ImportItem(_) if !self.document_hidden && i.is_doc_hidden() => None, clean::ExternCrateItem { .. } | clean::ImportItem(..) if i.visibility(self.tcx) != Some(Visibility::Public) => { + debug!("ImportStripper: stripping {:?}", i.name); None } _ => Some(self.fold_item_recur(i)), diff --git a/src/llvm-project b/src/llvm-project index ec9ab9d68bf7a..3c3f13025bf9f 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit ec9ab9d68bf7a0e86b2ddf3c0e6e3c4620e02961 +Subproject commit 3c3f13025bf9f99bb2a757eb37dad4f09e7d6c36 diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml index 9e18691a9605a..ba6dc2d69e1ff 100644 --- a/src/rustdoc-json-types/Cargo.toml +++ b/src/rustdoc-json-types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustdoc-json-types" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] path = "lib.rs" diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index daaf30e25d15d..be4921d888278 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -114,8 +114,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Add `Item::stability`. -pub const FORMAT_VERSION: u32 = 58; +// Latest feature: Add default-body stability metadata. +pub const FORMAT_VERSION: u32 = 60; /// The root of the emitted JSON blob. /// @@ -286,6 +286,11 @@ pub struct Item { /// - `#[doc = "Doc Comment"]` or `/// Doc comment`: see [`Self::docs`] instead. /// - `#[deprecated]` attributes: see the [`Self::deprecation`] field instead. /// - `#[stable]` and `#[unstable]` attributes: see the [`Self::stability`] field instead. + /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes: + /// see the [`Self::const_stability`] field instead. + /// - `#[rustc_default_body_unstable]` attributes: instead see `default_unstable` fields on + /// item kinds that can have unstable default values, such as [`Function::default_unstable`], + /// [`ItemEnum::AssocConst::default_unstable`], and [`ItemEnum::AssocType::default_unstable`]. /// /// Attributes appear in pretty-printed Rust form, regardless of their formatting /// in the original source code. For example: @@ -319,20 +324,31 @@ pub struct Item { /// most ordinary third-party crates usually have no data here. pub stability: Option>, + /// Stability information for using this item in const contexts, if any. + /// + /// This is separate from [`Self::stability`]. An item can be stable as regular API while its + /// const use is unstable. An unstable item may have no separate const-stability value here. + /// + /// This field is only populated for item kinds whose const behavior can have separate + /// stability information, such as const functions, const traits, const trait impls, + /// and associated items whose const behavior is controlled by a const trait or const impl. + pub const_stability: Option>, + /// The type-specific fields describing this item. pub inner: ItemEnum, } /// Stability information for an item. /// -/// This only refers to regular item stability: whether the item is stable or unstable -/// as represented by the `#[stable]` or `#[unstable]` attributes. -/// Const stability and default-body stability are different things and not captured here. +/// In [`Item::stability`], this refers to regular item stability: whether the item is +/// stable or unstable as represented by the `#[stable]` or `#[unstable]` attributes. +/// In [`Item::const_stability`], this refers to using the item in const contexts, +/// as represented by `#[rustc_const_stable]` or `#[rustc_const_unstable]`. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] pub struct Stability { - /// The stability feature associated with this item. + /// The feature associated with this stability record. /// /// For unstable items, this is the feature gate associated with the item. /// For stable items, this is the historical label recorded when the item was stabilized. @@ -342,7 +358,6 @@ pub struct Stability { pub level: StabilityLevel, } -/// Whether an item is stable or unstable as regular public API. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] @@ -355,6 +370,19 @@ pub enum StabilityLevel { Unstable, } +/// Information about an unstable default provided by a trait item. +/// +/// Example unstable defaults include: +/// - a stable trait function or method whose body is not stable +/// - a stable trait associated type or const whose default value is not stable +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] +pub struct ProvidedDefaultUnstable { + /// The feature that must be enabled to use the provided default. + pub feature: String, +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] @@ -365,6 +393,11 @@ pub enum StabilityLevel { /// - `#[doc = "Doc Comment"]` or `/// Doc comment`. These are in [`Item::docs`] instead. /// - `#[deprecated]`. These are in [`Item::deprecation`] instead. /// - `#[stable]` and `#[unstable]`. These are in [`Item::stability`] instead. +/// - `#[rustc_const_stable]` and `#[rustc_const_unstable]`. These are in +/// [`Item::const_stability`] instead. +/// - `#[rustc_default_body_unstable]`. These are in the `default_unstable` field on the appropriate +/// item kinds: [`Function::default_unstable`], [`ItemEnum::AssocConst::default_unstable`], +/// and [`ItemEnum::AssocType::default_unstable`]. pub enum Attribute { /// `#[non_exhaustive]` NonExhaustive, @@ -861,6 +894,11 @@ pub enum ItemEnum { /// // ^^^^^^^^^^ /// ``` value: Option, + /// Metadata about an unstable default value provided for the associated constant, if any. + /// + /// Empty if the associated constant has no default (see [`ItemEnum::AssocConst::value`]), + /// or if the default value is stable. + default_unstable: Option>, }, /// An associated type of a trait or a type. AssocType { @@ -885,6 +923,11 @@ pub enum ItemEnum { /// ``` #[serde(rename = "type")] type_: Option, + /// Metadata about an unstable default value provided for the associated type, if any. + /// + /// Empty if the associated type has no default (see [`ItemEnum::AssocType::type_`]), + /// or if the default value is stable. + default_unstable: Option>, }, } @@ -1174,6 +1217,12 @@ pub struct Function { pub header: FunctionHeader, /// Whether the function has a body, i.e. an implementation. pub has_body: bool, + /// Metadata about a possible unstable provided default implementation for trait methods. + /// + /// Only populated for function items inside traits. Empty if the trait method + /// does not have a default implementation (see [`Function::has_body`]), + /// or if its default implementation is stable. + pub default_unstable: Option>, } /// Generic parameters accepted by an item and `where` clauses imposed on it and the parameters. diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index edcfc80890d3a..f5fdb662662d0 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -26,6 +26,7 @@ static DOCS_FALLBACK: &[(&str, &str)] = &[ ("-apple-", "aarch64-apple-darwin"), ("aarch64", "aarch64-unknown-linux-gnu"), ("arm-", "aarch64-unknown-linux-gnu"), + ("i686-pc-windows", "x86_64-pc-windows-msvc"), ("", "x86_64-unknown-linux-gnu"), ]; diff --git a/src/tools/cargo b/src/tools/cargo index a595d0da21f22..f40cc7a13a8eb 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit a595d0da21f228b7fdae64d3d5c0e527ea66bb59 +Subproject commit f40cc7a13a8ebe3dd0a35b1e0e37a1e26c8bb0e5 diff --git a/src/tools/clippy/.github/FUNDING.yml b/src/tools/clippy/.github/FUNDING.yml new file mode 100644 index 0000000000000..16ac7b6eb9bc9 --- /dev/null +++ b/src/tools/clippy/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: rustfoundation +custom: [ "rust-lang.org/funding" ] diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 3a99d65233d38..cd90bb0a25f5f 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index b612ea4611a9f..7d313ece05b80 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -34,7 +34,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -94,7 +94,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -112,7 +112,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -168,7 +168,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index f9e882d9757ce..5658e041e03cf 100644 --- a/src/tools/clippy/.github/workflows/clippy_pr.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -24,7 +24,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 872931160c35f..b95cbbddd4b32 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -25,13 +25,13 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 981fecd17c988..210f10b7b8804 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 2 # Unsetting this would make so that any malicious package could get our Github Token @@ -80,7 +80,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false @@ -113,7 +113,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index d4dc80efe79de..532af9dff83f2 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -14,7 +14,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 337c900aaf0c8..689d56d0fb7c6 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6670,6 +6670,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`by_ref_peekable_peek`]: https://rust-lang.github.io/rust-clippy/master/index.html#by_ref_peekable_peek [`byte_char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices [`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth @@ -6694,6 +6695,7 @@ Released 2018-09-13 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp [`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions +[`chunks_exact_to_as_chunks`]: https://rust-lang.github.io/rust-clippy/master/index.html#chunks_exact_to_as_chunks [`clear_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#clear_with_drain [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy @@ -7457,6 +7459,7 @@ Released 2018-09-13 [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_trailing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_trailing_comma [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap +[`unnecessary_unwrap_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap_unchecked [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern @@ -7571,6 +7574,7 @@ Released 2018-09-13 [`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api [`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types [`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish +[`check-grouped-late-init`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-grouped-late-init [`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests [`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items @@ -7608,7 +7612,6 @@ Released 2018-09-13 [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit -[`profiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#profiles [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline diff --git a/src/tools/clippy/book/src/continuous_integration/github_actions.md b/src/tools/clippy/book/src/continuous_integration/github_actions.md index bed0f66bab33a..e198a13b38d24 100644 --- a/src/tools/clippy/book/src/continuous_integration/github_actions.md +++ b/src/tools/clippy/book/src/continuous_integration/github_actions.md @@ -15,7 +15,7 @@ jobs: clippy_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Run Clippy run: cargo clippy --all-targets --all-features ``` diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index e123d7ba5704d..6c6ccd4747c1f 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -452,6 +452,37 @@ For internal testing only, ignores the current `publish` settings in the Cargo m * [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata) +## `check-grouped-late-init` +Whether to check for grouped late initializations from multiple `let` statements. + +#### Example +```rust +let a; +let b; +if true { + a = 1; + b = 2; +} else { + a = 3; + b = 4; +} +``` +Use instead: +```rust +let (a, b) = if true { + (1, 2) +} else { + (3, 4) +}; +``` + +**Default Value:** `true` + +--- +**Affected lints:** +* [`needless_late_init`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init) + + ## `check-incompatible-msrv-in-tests` Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. @@ -986,28 +1017,6 @@ The minimum size (in bytes) to consider a type for passing by reference instead * [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) -## `profiles` -Named profiles of disallowed items (unrelated to Cargo build profiles). - -#### Example - -```toml -[profiles.persistent] -disallowed-methods = [{ path = "std::env::temp_dir" }] -disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] - -[profiles.single_threaded] -disallowed-methods = [{ path = "std::thread::spawn" }] -``` - -**Default Value:** `{}` - ---- -**Affected lints:** -* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) -* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) - - ## `pub-underscore-fields-behavior` Lint "public" fields in a struct that are prefixed with an underscore based on their exported visibility, or whether they are marked as "pub". diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 47bc7b572c9d0..d910eb47752b5 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -1,13 +1,12 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, DisallowedProfile, InherentImplLintScope, MacroMatcher, - MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, + PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -223,74 +222,12 @@ macro_rules! deserialize { }}; } -macro_rules! parse_conf_value { - ( - $map:expr, - $ty:ty, - $errors:expr, - $file:expr, - $field_span:expr, - profiles @[$($profiles:expr)?], - disallowed @[$($disallowed:expr)?] - ) => { - parse_conf_value_impl!( - $map, - $ty, - $errors, - $file, - $field_span, - ($($profiles)?), - ($($disallowed)?) - ) - }; -} - -macro_rules! parse_conf_value_impl { - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ()) => {{ - let _ = &$field_span; - deserialize!($map, $ty, $errors, $file) - }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, ($profiles:expr), ()) => {{ - let raw_value = $map.next_value::()?; - let value_span = $field_span.clone(); - let toml::Value::Table(table) = raw_value else { - $errors.push(ConfError::spanned( - $file, - "expected table with named profiles", - None, - value_span.clone(), - )); - continue; - }; - - let map = parse_profiles(table, $file, value_span.clone(), &mut $errors); - - (map, value_span) - }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ($disallowed:expr)) => {{ - let _ = &$field_span; - deserialize!($map, $ty, $errors, $file, $disallowed) - }}; - ( - $map:expr, - $ty:ty, - $errors:expr, - $file:expr, - $field_span:expr, - ($profiles:expr), - ($disallowed:expr) - ) => { - compile_error!("field cannot specify both profiles and disallowed-paths attributes") - }; -} - macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? - $(#[profiles = $profiles:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -345,20 +282,10 @@ macro_rules! define_Conf { match field { $(Field::$name => { - let field_span = name.span(); // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let (value, value_span) = parse_conf_value!( - map, - $ty, - errors, - self.0, - field_span, - // Disallowed-profile table parsing is special-cased to preserve spans for - // diagnostics in disallowed-path entries. - profiles @[$($profiles)?], - disallowed @[$($replacements_allowed)?] - ); + let (value, value_span) = + deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); // Was this field set previously? if $name.is_some() { errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); @@ -415,121 +342,6 @@ fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { ) } -fn parse_profiles( - table: toml::value::Table, - file: &SourceFile, - value_span: Range, - errors: &mut Vec, -) -> FxHashMap { - let mut profiles = FxHashMap::default(); - let config_span = span_from_toml_range(file, value_span.clone()); - - for (profile_name, profile_value) in table { - let toml::Value::Table(mut profile_table) = profile_value else { - errors.push(ConfError::spanned( - file, - format!("invalid profile `{profile_name}`: expected table"), - None, - value_span.clone(), - )); - continue; - }; - - let disallowed_methods = match profile_table - .remove("disallowed-methods") - .or_else(|| profile_table.remove("disallowed_methods")) - { - Some(value) => parse_profile_list( - file, - &profile_name, - "disallowed-methods", - value, - value_span.clone(), - config_span, - errors, - ), - None => Vec::new(), - }; - - let disallowed_types = match profile_table - .remove("disallowed-types") - .or_else(|| profile_table.remove("disallowed_types")) - { - Some(value) => parse_profile_list( - file, - &profile_name, - "disallowed-types", - value, - value_span.clone(), - config_span, - errors, - ), - None => Vec::new(), - }; - - if !profile_table.is_empty() { - let keys = profile_table.keys().map(String::as_str).collect::>().join(", "); - errors.push(ConfError::spanned( - file, - format!("profile `{profile_name}` has unknown keys: {keys}"), - None, - value_span.clone(), - )); - } - - profiles.insert( - profile_name, - DisallowedProfile { - disallowed_methods, - disallowed_types, - }, - ); - } - - profiles -} - -fn parse_profile_list( - file: &SourceFile, - profile_name: &str, - key_name: &str, - value: toml::Value, - value_span: Range, - config_span: Span, - errors: &mut Vec, -) -> Vec { - let toml::Value::Array(entries) = value else { - errors.push(ConfError::spanned( - file, - format!("profile `{profile_name}`: `{key_name}` must be an array"), - None, - value_span, - )); - return Vec::new(); - }; - - let mut disallowed = Vec::with_capacity(entries.len()); - for entry in entries { - match DisallowedPath::deserialize(entry.clone()) { - Ok(mut path) => { - path.set_span(config_span); - disallowed.push(path); - }, - Err(err) => errors.push(ConfError::spanned( - file, - format!( - "profile `{profile_name}`: {}", - err.to_string().replace('\n', " ").trim() - ), - None, - value_span.clone(), - )), - } - } - - disallowed -} - define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -742,6 +554,30 @@ define_Conf! { /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. #[lints(cargo_common_metadata)] cargo_ignore_publish: bool = false, + /// Whether to check for grouped late initializations from multiple `let` statements. + /// + /// #### Example + /// ```rust + /// let a; + /// let b; + /// if true { + /// a = 1; + /// b = 2; + /// } else { + /// a = 3; + /// b = 4; + /// } + /// ``` + /// Use instead: + /// ```rust + /// let (a, b) = if true { + /// (1, 2) + /// } else { + /// (3, 4) + /// }; + /// ``` + #[lints(needless_late_init)] + check_grouped_late_init: bool = true, /// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. #[lints(incompatible_msrv)] check_incompatible_msrv_in_tests: bool = false, @@ -1029,21 +865,6 @@ define_Conf! { /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. #[lints(large_types_passed_by_value)] pass_by_value_size_limit: u64 = 256, - /// Named profiles of disallowed items (unrelated to Cargo build profiles). - /// - /// #### Example - /// - /// ```toml - /// [profiles.persistent] - /// disallowed-methods = [{ path = "std::env::temp_dir" }] - /// disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] - /// - /// [profiles.single_threaded] - /// disallowed-methods = [{ path = "std::thread::spawn" }] - /// ``` - #[profiles = true] - #[lints(disallowed_methods, disallowed_types)] - profiles: FxHashMap = FxHashMap::default(), /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index 5eaa44ddd51d7..8d9326a904b1e 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -57,15 +57,6 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, - #[serde(default, alias = "disallowed_types")] - pub disallowed_types: Vec, -} - // `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just // above. `DisallowedPathEnum` is not meant to be used outside of this file. #[derive(Debug, Deserialize, Serialize)] diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 49dc5c9f10a89..a11abc8d7cc4c 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -10,7 +10,7 @@ clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.8" -rustc-literal-escaper = "0.0.7" +rustc-literal-escaper = "0.0.8" walkdir = "2.3" [package.metadata.rust-analyzer] diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs index fd515939dfb8e..cf2059eecc079 100644 --- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs @@ -12,7 +12,7 @@ use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does - /// Checks for usage of items through absolute paths, like `std::env::current_dir`. + /// Checks for usage of items through absolute paths, like `std::f64::consts::PI`. /// /// ### Why restrict this? /// Many codebases have their own style when it comes to importing, but one that is seldom used diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index 6e57d0608bed8..118c3c23d727d 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -52,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { _ => return, } && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) - && is_const_evaluatable(cx, condition) + // Check if the whole expression can be moved into a const context. + // Note that const eval can evaluate things which cannot be moved (e.g. `false && x`). + && is_const_evaluatable(cx.tcx, cx.typeck_results(), condition) && let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt()) && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs index 7eff5eccfa138..91e2689a14731 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, NON_MINIMAL_CFG}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::{MetaItemInner, MetaItemKind}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; @@ -29,7 +29,7 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[MetaItemInner]) { meta.span, "unneeded sub `cfg` when there is only one condition", |diag| { - if let Some(snippet) = list[0].span().get_source_text(cx) { + if let Some(snippet) = list[0].span().get_text(cx) { diag.span_suggestion( meta.span, "try", diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 6efc931df2429..4c97c03471aa9 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, UNNECESSARY_CLIPPY_CFG}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::AttrStyle; use rustc_errors::Applicability; @@ -32,7 +32,7 @@ pub(super) fn check( return; } if nb_items == clippy_lints.len() { - if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) { + if let Some(snippet) = behind_cfg_attr.span.get_text(cx) { span_lint_and_sugg( cx, UNNECESSARY_CLIPPY_CFG, @@ -48,7 +48,7 @@ pub(super) fn check( ); } } else { - let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(","); + let snippet = clippy_lints.iter().filter_map(|sp| sp.get_text(cx)).join(","); span_lint_and_note( cx, UNNECESSARY_CLIPPY_CFG, diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 2d56086a96024..60cdcd4a85817 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -1,7 +1,7 @@ use super::USELESS_ATTRIBUTE; use super::utils::{is_lint_level, is_word, namespace_and_lint}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, first_line_of_span}; +use clippy_utils::source::{SpanExt, first_line_of_span}; use clippy_utils::sym; use rustc_ast::{Attribute, Item, ItemKind}; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { } let line_span = first_line_of_span(cx, attr.span); - if let Some(src) = line_span.get_source_text(cx) + if let Some(src) = line_span.get_text(cx) && src.contains("#[") { #[expect(clippy::collapsible_span_lint_calls)] 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 cdcb66566d951..2752430c92224 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -63,7 +63,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - ) }) .is_some_and(|assoc_item| { - let proj = Ty::new_projection(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, [])); + let proj = Ty::new_projection(cx.tcx, ty::IsRigid::No, assoc_item.def_id, cx.tcx.mk_args_trait(ty, [])); let nty = cx .tcx .normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(proj)); diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 2f2382d9a50ed..efd453b3276cc 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; @@ -157,30 +157,30 @@ fn check_inverted_bool_in_condition( let suggestion = match (left.kind, right.kind) { (ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = left_sub.span.get_source_text(cx) else { + let Some(left) = left_sub.span.get_text(cx) else { return; }; - let Some(right) = right_sub.span.get_source_text(cx) else { + let Some(right) = right_sub.span.get_text(cx) else { return; }; let Some(op) = bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (ExprKind::Unary(UnOp::Not, left_sub), _) => { - let Some(left) = left_sub.span.get_source_text(cx) else { + let Some(left) = left_sub.span.get_text(cx) else { return; }; - let Some(right) = right.span.get_source_text(cx) else { + let Some(right) = right.span.get_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (_, ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = left.span.get_source_text(cx) else { + let Some(left) = left.span.get_text(cx) else { return; }; - let Some(right) = right_sub.span.get_source_text(cx) else { + let Some(right) = right_sub.span.get_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; @@ -392,12 +392,8 @@ impl SuggestContext<'_, '_, '_> { } }, &Term(n) => { - self.output.push_str( - &self.terminals[n as usize] - .span - .source_callsite() - .get_source_text(self.cx)?, - ); + self.output + .push_str(&self.terminals[n as usize].span.source_callsite().get_text(self.cx)?); }, } Some(()) @@ -452,10 +448,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio .map(|arg| simplify_not(cx, curr_msrv, arg)) .collect::>>()? .join(", "); - Some(format!( - "{}.{neg_method}({negated_args})", - receiver.span.get_source_text(cx)? - )) + Some(format!("{}.{neg_method}({negated_args})", receiver.span.get_text(cx)?)) }) }, ExprKind::Closure(closure) => { @@ -463,13 +456,13 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio let params = body .params .iter() - .map(|param| param.span.get_source_text(cx).map(|t| t.to_string())) + .map(|param| param.span.get_text(cx).map(|t| t.to_string())) .collect::>>()? .join(", "); let negated = simplify_not(cx, curr_msrv, body.value)?; Some(format!("|{params}| {negated}")) }, - ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()), + ExprKind::Unary(UnOp::Not, expr) => expr.span.get_text(cx).map(|t| t.to_string()), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index 947b99696bb1f..13ae80868d9b5 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -1,6 +1,6 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use clippy_utils::{ get_enclosing_closure, get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable, @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { // If the new borrow might be itself borrowed mutably and the original reference is not a temporary // value, do not propose to use it directly. && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e)) - && let Some(deref_text) = deref_target.span.get_source_text(cx) + && let Some(deref_text) = deref_target.span.get_text(cx) { // `&*x` can be needed to shorten the borrow of `x`. Replacing it with `x` can be // incorrect when `x` is a closure-captured upvar (e.g. a closure returning another diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs index adc43e282fc5c..f23e789cf5d64 100644 --- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity().skip_norm_wip() && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() - && let Some(recv) = receiver.span.get_source_text(cx) + && let Some(recv) = receiver.span.get_text(cx) { // `as_mut_ptr` might not exist let applicability = Applicability::MaybeIncorrect; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index c924fba6b5c85..d2bf6db751320 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_const_context; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; @@ -40,7 +40,7 @@ pub(super) fn check( diag.help("an `as` cast can become silently lossy if the types change in the future"); let mut applicability = Applicability::MachineApplicable; let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "", &mut applicability); - let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else { + let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_text(cx) else { return; }; match cast_to_hir.kind { 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 55b0945f0962b..36d63ce63195b 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 @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{expr_or_init, std_or_core, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) { + } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs b/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs index 844d4c7acbe7f..5cfbffc6c3f66 100644 --- a/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs @@ -249,7 +249,7 @@ fn can_coerce_to_target_type(expr: &Expr<'_>) -> bool { fn check_binding_usages<'a>(cx: &LateContext<'a>, body: &Body<'a>, hir_id: HirId, binding_info: &BindingInfo<'a>) { let mut usages = Vec::new(); - for_each_expr(cx, body.value, |expr| { + for_each_expr(cx.tcx, body.value, |expr| { if let ExprKind::Path(ref qpath) = expr.kind && !expr.span.from_expansion() && let Res::Local(id) = cx.qpath_res(qpath, expr.hir_id) 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 333f31ba00eac..c14bd056de393 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::res::MaybeResPath as _; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, sym}; @@ -113,7 +113,7 @@ pub(super) fn check<'tcx>( let literal_str = &cast_str; if let LitKind::Int(n, _) = lit.node - && let Some(src) = cast_expr.span.get_source_text(cx) + && let Some(src) = cast_expr.span.get_text(cx) && cast_to.is_floating_point() && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && let from_nbits = 128 - n.get().leading_zeros() @@ -140,7 +140,7 @@ pub(super) fn check<'tcx>( | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => { - if let Some(src) = cast_expr.span.get_source_text(cx) + if let Some(src) = cast_expr.span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); @@ -232,7 +232,7 @@ fn lint_unnecessary_cast( // (-1).foo() instead of -1.foo()) let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(..) = parent_expr.kind - && literal_str.starts_with('-') + && literal_str.starts_with(['-', '!']) { format!("({literal_str}_{cast_to})") } else { @@ -253,7 +253,7 @@ fn lint_unnecessary_cast( fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option { match expr.kind { ExprKind::Lit(lit) => Some(lit), - ExprKind::Unary(UnOp::Neg, e) => { + ExprKind::Unary(UnOp::Neg | UnOp::Not, e) => { if let ExprKind::Lit(lit) = e.kind { Some(lit) } else { diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs index f4738e7b0d51d..90290caf10bf7 100644 --- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; @@ -21,7 +21,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_> let sugg = if let TyKind::Infer(()) = mut_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) { + } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs index 4f663f4aa9098..24955ac3c6258 100644 --- a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -64,15 +64,8 @@ impl<'a> ClonedRefToSliceRefs<'a> { impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if self.msrv.meets(cx, { - if is_in_const_context(cx) { - msrvs::CONST_SLICE_FROM_REF - } else { - msrvs::SLICE_FROM_REF - } - }) - // `&[foo.clone()]` expressions - && let ExprKind::AddrOf(_, mutability, arr) = &expr.kind + // `&[foo.clone()]` expressions + if let ExprKind::AddrOf(_, mutability, arr) = &expr.kind // mutable references would have a different meaning && mutability.is_not() @@ -81,10 +74,18 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { // check for clones && let ExprKind::MethodCall(path, recv, _, _) = item.kind + + && self.msrv.meets(cx, { + if is_in_const_context(cx) { + msrvs::CONST_SLICE_FROM_REF + } else { + msrvs::SLICE_FROM_REF + } + }) && let Some(adjustment) = is_needless_clone_or_equivalent(cx, recv, path.ident.name, item.hir_id) // check for immutability or purity - && (!is_mutable(cx, recv) || is_const_evaluatable(cx, recv)) + && (!is_mutable(cx, recv) || is_const_evaluatable(cx.tcx, cx.typeck_results(), recv)) // get appropriate crate for `slice::from_ref` && let Some(builtin_crate) = clippy_utils::std_or_core(cx) diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 95b99c1d2435f..c330ed3d5a0a4 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index baa13b2133277..6def41b775d1d 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::{HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; +use clippy_utils::source::{IntoSpan as _, SpanExt, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, span_contains_cfg, span_contains_non_whitespace, sym, tokenize_with_text}; use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; @@ -319,7 +319,7 @@ pub(super) fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { } } -fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Option { +fn span_extract_keyword(cx: &LateContext<'_>, span: Span, keyword: &str) -> Option { span.with_source_text(cx, |snippet| { tokenize_with_text(snippet) .filter(|(t, s, _)| matches!(t, TokenKind::Ident if *s == keyword)) @@ -335,7 +335,7 @@ fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Opti } /// Peel the parentheses from an `if` expression, e.g. `((if true {} else {}))`. -pub(super) fn peel_parens(cx: &impl HasSession, mut span: Span) -> (Span, Span, Span) { +pub(super) fn peel_parens(cx: &LateContext<'_>, mut span: Span) -> (Span, Span, Span) { use crate::rustc_span::Pos; let start = span.shrink_to_lo(); diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index ddd3e8b805d19..4ddd396c50ce4 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -79,7 +79,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI let mut has_read_access = false; // Inspect all expressions and sub-expressions in the block. - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { // Ignore expressions that are not simply `id`. if expr.res_local_id() != Some(id) { return ControlFlow::Continue(()); diff --git a/src/tools/clippy/clippy_lints/src/combined_early_pass.rs b/src/tools/clippy/clippy_lints/src/combined_early_pass.rs index 85de0b00a1b04..7494711c1f240 100644 --- a/src/tools/clippy/clippy_lints/src/combined_early_pass.rs +++ b/src/tools/clippy/clippy_lints/src/combined_early_pass.rs @@ -11,7 +11,7 @@ //! inlined calls. No vtable, no per-node dynamic dispatch. //! //! Unlike the late combine there is no `active` gate. rustc drops fully-disabled -//! late passes via `lints_that_dont_need_to_run`, but the early pass runner has +//! late passes via `skippable_lints`, but the early pass runner has //! no such filtering, so a plain forward is equivalent and loses nothing. //! //! [`combined_late_pass`]: crate::combined_late_pass diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 6daa70f390aaa..51a848d022d80 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -361,11 +361,13 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO, crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO, crate::methods::BIND_INSTEAD_OF_MAP_INFO, + crate::methods::BY_REF_PEEKABLE_PEEK_INFO, crate::methods::BYTES_COUNT_TO_LEN_INFO, crate::methods::BYTES_NTH_INFO, crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO, crate::methods::CHARS_LAST_CMP_INFO, crate::methods::CHARS_NEXT_CMP_INFO, + crate::methods::CHUNKS_EXACT_TO_AS_CHUNKS_INFO, crate::methods::CLEAR_WITH_DRAIN_INFO, crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, @@ -507,6 +509,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, + crate::methods::UNNECESSARY_UNWRAP_UNCHECKED_INFO, crate::methods::UNWRAP_OR_DEFAULT_INFO, crate::methods::UNWRAP_USED_INFO, crate::methods::USELESS_ASREF_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index c831f96443c60..9e141bd945db0 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_ty_alias; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use hir::ExprKind; use hir::def::Res; use rustc_errors::Applicability; @@ -78,7 +78,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && !base.is_suggestable_infer_ty() { let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; - if expr.span.check_source_text(cx, |s| s.starts_with('<')) { + if expr.span.check_text(cx, |s| s.starts_with('<')) { // Remove `<`, '>` has already been removed by the existing removal expression. removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 26a06992d1a6f..fbff41fe00914 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -925,18 +925,18 @@ impl TyCoercionStability { continue; }, ty::Param(_) if for_return => Self::Deref, - ty::Alias(ty::AliasTy { + ty::Alias(_, ty::AliasTy { kind: ty::Free { .. } | ty::Inherent { .. }, .. }) => unreachable!("should have been normalized away above"), - ty::Alias(ty::AliasTy { + ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }) if !for_return && ty.has_non_region_param() => Self::Reborrow, ty::Infer(_) | ty::Error(_) | ty::Bound(..) - | ty::Alias(ty::AliasTy { + | ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) @@ -970,7 +970,7 @@ impl TyCoercionStability { | ty::CoroutineClosure(..) | ty::Never | ty::Tuple(_) - | ty::Alias(ty::AliasTy { + | ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }) diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 58cc318d57ccd..e2fd71b7d990f 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -1,18 +1,13 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; -use clippy_utils::sym; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::smallvec::SmallVec; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -60,19 +55,6 @@ declare_clippy_lint! { /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. /// xs.push(123); // Vec::push is _not_ disallowed in the config. /// ``` - /// - /// Disallowed profiles allow scoping different disallow lists: - /// ```toml - /// [profiles.forward_pass] - /// disallowed-methods = [{ path = "crate::devices::Buffer::copy_to_host", reason = "Forward code must not touch host buffers" }] - /// ``` - /// - /// ```rust,ignore - /// #[clippy::disallowed_profile("forward_pass")] - /// fn evaluate() { - /// // Method calls in this function use the `forward_pass` profile. - /// } - /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, style, @@ -82,22 +64,12 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); pub struct DisallowedMethods { - default: DefIdMap<(&'static str, &'static DisallowedPath)>, - /// Lookup per profile that declares a non-empty `disallowed_methods` list. Profiles - /// declared in `[profiles.*]` but without `disallowed_methods` entries are absent here. - profiles: FxHashMap>, - /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes - /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist - /// in config but only define entries for other lints (e.g. `disallowed_types`). - known_profiles: FxHashSet, - profile_cache: ProfileResolver, - warned_unknown_profiles: FxHashSet, + disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, } impl DisallowedMethods { - #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (default, _) = create_disallowed_map( + let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_methods, PathNS::Value, @@ -110,62 +82,7 @@ impl DisallowedMethods { "function", false, ); - - let mut profiles = FxHashMap::default(); - let mut known_profiles = FxHashSet::default(); - let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); - profile_entries.sort_by_key(|(a, _)| *a); - for (name, profile) in profile_entries { - let symbol = Symbol::intern(name.as_str()); - known_profiles.insert(symbol); - - let paths = profile.disallowed_methods.as_slice(); - if paths.is_empty() { - continue; - } - - let (map, _) = create_disallowed_map( - tcx, - paths, - PathNS::Value, - |def_kind| { - matches!( - def_kind, - DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn - ) - }, - "function", - false, - ); - profiles.insert(symbol, map); - } - - Self { - default, - profiles, - known_profiles, - profile_cache: ProfileResolver::default(), - warned_unknown_profiles: FxHashSet::default(), - } - } - - fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { - if self.warned_unknown_profiles.insert(entry.span) { - let attr_name = if entry.attr_name == sym::disallowed_profiles { - "clippy::disallowed_profiles" - } else { - "clippy::disallowed_profile" - }; - span_lint( - cx, - DISALLOWED_METHODS, - entry.span, - format!( - "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_methods`", - entry.name - ), - ); - } + Self { disallowed } } } @@ -181,43 +98,13 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); - // Copy entries out of the cache before iterating: `warn_unknown_profile` takes - // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. - let entries: SmallVec<[ProfileEntry; 2]> = self - .profile_cache - .active_profiles(cx, expr.hir_id) - .map(|selection| selection.iter().copied().collect()) - .unwrap_or_default(); - for entry in &entries { - if self.profiles.contains_key(&entry.name) { - active_profiles.push(entry.name); - } else if !self.known_profiles.contains(&entry.name) { - self.warn_unknown_profile(cx, entry); - } - } - - if let Some((profile, &(path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { - self.profiles - .get(symbol) - .and_then(|map| map.get(&id).map(|info| (*symbol, info))) - }) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_METHODS, - span, - format!("use of a disallowed method `{path}` (profile: {profile})"), - |diag| diag_amendment(diag), - ); - } else if let Some(&(path, disallowed_path)) = self.default.get(&id) { - let diag_amendment = disallowed_path.diag_amendment(span); + if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { span_lint_and_then( cx, DISALLOWED_METHODS, span, format!("use of a disallowed method `{path}`"), - |diag| diag_amendment(diag), + disallowed_path.diag_amendment(span), ); } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 37dd605617a7a..2c520d053f439 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -1,18 +1,15 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; -use clippy_utils::sym; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::smallvec::SmallVec; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -54,17 +51,6 @@ declare_clippy_lint! { /// // A similar type that is allowed by the config /// use std::collections::HashMap; /// ``` - /// - /// Disallowed profiles can scope lists to specific modules: - /// ```toml - /// [profiles.forward_pass] - /// disallowed-types = [{ path = "crate::buffers::HostBuffer", reason = "Prefer device buffers in forward computations" }] - /// ``` - /// - /// ```rust,ignore - /// #[clippy::disallowed_profile("forward_pass")] - /// fn forward_step(buffer: crate::buffers::DeviceBuffer) { /* ... */ } - /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, style, @@ -73,127 +59,37 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); -struct TypeLookup { +pub struct DisallowedTypes { def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>, prim_tys: FxHashMap, } -impl TypeLookup { - fn from_config(tcx: TyCtxt<'_>, paths: &'static [DisallowedPath]) -> Self { - let (def_ids, prim_tys) = create_disallowed_map(tcx, paths, PathNS::Type, def_kind_predicate, "type", true); - Self { def_ids, prim_tys } - } - - fn find(&self, res: &Res) -> Option<(&'static str, &'static DisallowedPath)> { - match res { - Res::Def(_, did) => self.def_ids.get(did).copied(), - Res::PrimTy(prim) => self.prim_tys.get(prim).copied(), - _ => None, - } - } -} - -pub struct DisallowedTypes { - default: TypeLookup, - /// Lookup per profile that declares a non-empty `disallowed_types` list. Profiles - /// declared in `[profiles.*]` but without `disallowed_types` entries are absent here. - profiles: FxHashMap, - /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes - /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist - /// in config but only define entries for other lints (e.g. `disallowed_methods`). - known_profiles: FxHashSet, - profile_cache: ProfileResolver, - warned_unknown_profiles: FxHashSet, -} - impl DisallowedTypes { - #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let default = TypeLookup::from_config(tcx, &conf.disallowed_types); - - let mut profiles = FxHashMap::default(); - let mut known_profiles = FxHashSet::default(); - let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); - profile_entries.sort_by_key(|(a, _)| *a); - for (name, profile) in profile_entries { - let symbol = Symbol::intern(name.as_str()); - known_profiles.insert(symbol); - - let paths = profile.disallowed_types.as_slice(); - if paths.is_empty() { - continue; - } - profiles.insert(symbol, TypeLookup::from_config(tcx, paths)); - } - - Self { - default, - profiles, - known_profiles, - profile_cache: ProfileResolver::default(), - warned_unknown_profiles: FxHashSet::default(), - } - } - - fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { - if self.warned_unknown_profiles.insert(entry.span) { - let attr_name = if entry.attr_name == sym::disallowed_profiles { - "clippy::disallowed_profiles" - } else { - "clippy::disallowed_profile" - }; - span_lint( - cx, - DISALLOWED_TYPES, - entry.span, - format!( - "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_types`", - entry.name - ), - ); - } + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + PathNS::Type, + def_kind_predicate, + "type", + true, + ); + Self { def_ids, prim_tys } } - fn check_res_emit(&mut self, cx: &LateContext<'_>, hir_id: rustc_hir::HirId, res: &Res, span: Span) { - let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); - // Copy entries out of the cache before iterating: `warn_unknown_profile` takes - // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. - let entries: SmallVec<[ProfileEntry; 2]> = self - .profile_cache - .active_profiles(cx, hir_id) - .map(|selection| selection.iter().copied().collect()) - .unwrap_or_default(); - for entry in &entries { - if self.profiles.contains_key(&entry.name) { - active_profiles.push(entry.name); - } else if !self.known_profiles.contains(&entry.name) { - self.warn_unknown_profile(cx, entry); - } - } - - if let Some((profile, (path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { - self.profiles - .get(symbol) - .and_then(|lookup| lookup.find(res).map(|info| (*symbol, info))) - }) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}` (profile: {profile})"), - |diag| diag_amendment(diag), - ); - } else if let Some((path, disallowed_path)) = self.default.find(res) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}`"), - |diag| diag_amendment(diag), - ); - } + fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { + let (path, disallowed_path) = match res { + Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, + Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, + _ => return, + }; + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}`"), + disallowed_path.diag_amendment(span), + ); } } @@ -215,22 +111,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind && let Some(res) = path.res.type_ns { - self.check_res_emit(cx, item.hir_id(), &res, item.span); + self.check_res_emit(cx, &res, item.span); } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Path(path) = &ty.kind { - self.check_res_emit(cx, ty.hir_id, &cx.qpath_res(path, ty.hir_id), ty.span); + self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span); } } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - self.check_res_emit( - cx, - poly.trait_ref.hir_ref_id, - &poly.trait_ref.path.res, - poly.trait_ref.path.span, - ); + self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span); } } diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index b164a9a99782b..4139df9cd7997 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -99,7 +99,7 @@ pub fn check( fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { let mut panic_span = None; let typeck = cx.tcx.typeck_body(body_id); - for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + for_each_expr(cx.tcx, cx.tcx.hir_body(body_id), |expr| { if is_inside_always_const_context(cx.tcx, expr.hir_id) { return ControlFlow::::Continue(()); } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 81da571bdd601..c5816cb8b2d63 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1124,6 +1124,9 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range Gap<'a> { let prev_stop = prev_chunk.last()?; let next_stop = next_chunk.first()?; let gap_span = prev_stop.span.between(next_stop.span); - let gap_snippet = gap_span.get_source_text(cx)?; + let gap_snippet = gap_span.get_text(cx)?; let mut has_comment = false; let mut empty_lines = Vec::new(); diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index ee1be48417222..b01745008510c 100644 --- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -1,6 +1,6 @@ use clippy_utils::attrs::span_contains_cfg; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::span_contains_non_whitespace; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_errors::Applicability; @@ -192,7 +192,7 @@ impl LateLintPass<'_> for EmptyWithBrackets { // Span of the parentheses in variant definition let span = variant.span.with_lo(variant.ident.span.hi()); let span_inner = span - .with_lo(SpanRangeExt::trim_start(span, cx).start + BytePos(1)) + .with_lo(SpanExt::trim_start(span, cx).start + BytePos(1)) .with_hi(span.hi() - BytePos(1)); if span_contains_non_whitespace(cx, span_inner, false) { continue; diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index a24cc957df070..cba40abf04e17 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -604,7 +604,7 @@ fn is_any_expr_in_map_used<'tcx>( map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>, ) -> bool { - for_each_expr(cx, map, |e| { + for_each_expr(cx.tcx, map, |e| { if spanless_eq.eq_expr(ctxt, e, expr) { return ControlFlow::Break(()); } diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 93a6dc59993a6..a6f80da5dab9c 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -102,7 +102,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { fn emit_sugg(&self, spans: Vec, msg: String, help: &'static str) { let suggestions: Vec<(Span, String)> = spans.iter().copied().zip(std::iter::repeat(String::new())).collect(); span_lint_and_then(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, |diag| { - diag.multipart_suggestion(help, suggestions, Applicability::MachineApplicable); + diag.multipart_suggestion(help, suggestions, Applicability::MaybeIncorrect); }); } diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs index 888d5b7b762a0..eb96e65638445 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs @@ -91,7 +91,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, SUBOPTIMAL_FLOPS, expr.span, - "multiply and add expressions can be calculated more efficiently and accurately", + "multiply and add expressions may be calculated more efficiently and accurately", |diag| { let maybe_neg_sugg = |expr, app: &mut _| { let sugg = Sugg::hir_with_applicability(cx, expr, "_", app); @@ -120,6 +120,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { }, app, ); + diag.note_once("the performance gain from `mul_add` may vary depending on the target architecture"); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 2691fb4766397..f13278d91ce7f 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, first_node_in_macro, matching_root_macro_call}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; use rustc_errors::Applicability; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), ([], [_]) => { // Simulate macro expansion, converting {{ and }} to { and }. - let Some(snippet) = format_args.span.get_source_text(cx) else { + let Some(snippet) = format_args.span.get_text(cx) else { return; }; let s_expand = snippet.replace("{{", "{").replace("}}", "}"); diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 05145456071c0..9014302fb8ea6 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -10,7 +10,7 @@ use clippy_utils::macros::{ }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_opt}; +use clippy_utils::source::{SpanExt, snippet, snippet_opt}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_from_proc_macro, is_in_test, peel_hir_expr_while, sym, trait_ref_of_method}; use itertools::Itertools; @@ -380,7 +380,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { /// Check if there is a comma after the last format macro arg. fn check_trailing_comma(&self) { let span = self.macro_call.span; - if let Some(src) = span.get_source_text(self.cx) + if let Some(src) = span.get_text(self.cx) && let Some(src) = src.strip_suffix([')', ']', '}']) && let src = src.trim_end_matches(|c: char| c.is_whitespace() && c != '\n') && let Some(src) = src.strip_suffix(',') @@ -694,7 +694,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) && implements_trait(cx, target, display_trait_id, &[]) && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() - && let Some(receiver_snippet) = receiver.span.source_callsite().get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.source_callsite().get_text(cx) { let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); if n_needed_derefs == 0 && !needs_ref { diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs index 57c1dd2eb72b0..c64e742592080 100644 --- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs +++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::Item; @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { // If the comment contains a bare CR (not followed by a LF), do not propose an auto-fix // as bare CR are not allowed in doc comments. - if span.check_source_text(cx, contains_bare_cr) { + if span.check_text(cx, contains_bare_cr) { diag.help(msg) .note("bare CR characters are not allowed in doc comments"); return; diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 433d591418798..c2836741b969e 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && span_is_local(item.span) && let middle_trait_ref = cx.tcx.impl_trait_ref(item.owner_id).instantiate_identity().skip_norm_wip() && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) - && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::AliasTy { kind: ty::Opaque{..} , .. })) + && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(_, ty::AliasTy { kind: ty::Opaque{..} , .. })) && self.msrv.meets(cx, msrvs::RE_REBALANCING_COHERENCE) // skip if there's a blanket From impl, the suggested impl would conflict && !has_blanket_from_impl(cx, middle_trait_ref.self_ty()) @@ -197,8 +197,8 @@ fn convert_to_from( return None; }; - let from = self_ty.span.get_source_text(cx)?; - let into = target_ty.span.get_source_text(cx)?; + let from = self_ty.span.get_text(cx)?; + let into = target_ty.span.get_text(cx)?; let mut suggestions = vec![ // impl Into for U -> impl From for U diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 39b53e02831c4..1dc23df6706ca 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -163,12 +163,11 @@ fn check_needless_must_use( ); } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { // Ignore async functions unless Future::Output type is a must_use type - if sig.header.is_async() { - if let Some(future_ty) = cx.tcx.get_impl_future_output_ty(return_ty(cx, item_id)) - && !is_must_use_ty(cx, future_ty) - { - return; - } + if sig.header.is_async() + && let Some(future_ty) = cx.tcx.get_impl_future_output_ty(return_ty(cx, item_id)) + && !is_must_use_ty(cx, future_ty) + { + return; } span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index e49dee4164b88..f3952b2687a56 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -50,7 +50,7 @@ fn check_raw_ptr<'tcx>( if !raw_ptrs.is_empty() { let typeck = cx.tcx.typeck_body(body.id()); - let _: Option = for_each_expr(cx, body.value, |e| { + let _: Option = for_each_expr(cx.tcx, body.value, |e| { match e.kind { hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => { for arg in args { diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs index 33eede8e65ac9..a5fed9f03a620 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; @@ -23,7 +23,7 @@ pub(super) fn check_fn( } let mut line_count: u64 = 0; - let too_many = body.value.span.check_source_text(cx, |src| { + let too_many = body.value.span.check_text(cx, |src| { let mut in_comment = false; let mut code_in_line; diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index fcdc12e3438cf..9af3d72bcffa9 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = return_ty(cx, cx.tcx.local_def_id_to_hir_id(fn_def_id).expect_owner()); - if let ty::Alias(AliasTy { kind: ty::Opaque{def_id}, args, .. }) = *ret_ty.kind() + if let ty::Alias(_, AliasTy { kind: ty::Opaque{def_id}, args, .. }) = *ret_ty.kind() && let Some(future_trait) = cx.tcx.lang_items().future_trait() && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) && let preds = cx.tcx.explicit_item_self_bounds(def_id) @@ -149,6 +149,7 @@ impl<'tcx> TypeVisitor> for TyParamAtTopLevelVisitor { match ty.kind() { ty::Param(_) => ControlFlow::Break(true), ty::Alias( + _, ty @ AliasTy { kind: ty::Projection { .. }, .. diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index ff22ba4fcd0d6..ea1992d471312 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::is_zero_integer_const; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_else_clause; -use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_context}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -104,28 +104,27 @@ impl LateLintPass<'_> for IfNotElse { } } -fn make_sugg<'a>( - sess: &impl HasSession, +fn make_sugg( + cx: &LateContext<'_>, expr_span: Span, - cond_kind: &'a ExprKind<'a>, + cond_kind: &ExprKind<'_>, cond_inner: Span, els_span: Span, - default: &'a str, + default: &str, applicability: &mut Applicability, ) -> String { - let (cond_inner_snip, _) = snippet_with_context(sess, cond_inner, expr_span.ctxt(), default, applicability); - let (els_snip, _) = snippet_with_context(sess, els_span, expr_span.ctxt(), default, applicability); - let indent = indent_of(sess, expr_span); - + let (cond_inner_snip, _) = snippet_with_context(cx, cond_inner, expr_span.ctxt(), default, applicability); + let (els_snip, _) = snippet_with_context(cx, els_span, expr_span.ctxt(), default, applicability); + let indent = indent_of(cx, expr_span); let suggestion = match cond_kind { ExprKind::Unary(UnOp::Not, cond_rest) => { let (cond_rest_snip, _) = - snippet_with_context(sess, cond_rest.span, expr_span.ctxt(), default, applicability); + snippet_with_context(cx, cond_rest.span, expr_span.ctxt(), default, applicability); format!("if {cond_rest_snip} {els_snip} else {cond_inner_snip}") }, ExprKind::Binary(_, lhs, rhs) => { - let (lhs_snip, _) = snippet_with_context(sess, lhs.span, expr_span.ctxt(), default, applicability); - let (rhs_snip, _) = snippet_with_context(sess, rhs.span, expr_span.ctxt(), default, applicability); + let (lhs_snip, _) = snippet_with_context(cx, lhs.span, expr_span.ctxt(), default, applicability); + let (rhs_snip, _) = snippet_with_context(cx, rhs.span, expr_span.ctxt(), default, applicability); format!("if {lhs_snip} == {rhs_snip} {els_snip} else {cond_inner_snip}") }, diff --git a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs index 06ebd3ac7f5fd..2701cdaa394da 100644 --- a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; +use clippy_utils::source::{IntoSpan, SpanExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 70176c62772b7..bf2c7a007644c 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet, snippet_with_context}; +use clippy_utils::source::{IntoSpan, SpanExt, snippet, snippet_with_context}; use clippy_utils::sym; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 1ae8198a432dd..99e7d202fe46f 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -193,10 +193,11 @@ impl IncompatibleMsrv { } } - if (self.check_in_tests || !is_in_test(cx.tcx, node)) - && let Some(current) = self.msrv.current(cx) + // Check `is_in_test` last as it walks the HIR parent chain. + if let Some(current) = self.msrv.current(cx) && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const) && version > current + && (self.check_in_tests || !is_in_test(cx.tcx, node)) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 0d35e7e7dfc36..e19e02901caef 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -280,7 +280,7 @@ fn ty_has_applicable_get_function<'tcx>( && let generic_ty = option_generic_param.expect_ty().peel_refs() // FIXME: ideally this would handle type params and projections properly, for now just assume it's the same type && (cx.typeck_results().expr_ty(index_expr).peel_refs() == generic_ty.peel_refs() - || matches!(generic_ty.peel_refs().kind(), ty::Param(_) | ty::Alias(_))) + || matches!(generic_ty.peel_refs().kind(), ty::Param(_) | ty::Alias(_, _))) { true } else { diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs index bc57d9e85478a..aae847031bf64 100644 --- a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs +++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs index 3ffa23dcd6046..87e63e8732fe8 100644 --- a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, snippet}; +use clippy_utils::source::snippet; use clippy_utils::sym; use rustc_ast::ast::{Fn, FnRetTy, GenericParam, GenericParamKind}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{HasAttrs as _, NodeId}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs index dd63de288b87f..f83bcb3cf4076 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro}; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::{HirId, Item, ItemKind, Mod}; @@ -99,7 +99,7 @@ impl LateLintPass<'_> for ItemsAfterTestModule { if let Some(prev) = mod_pos.checked_sub(1) && let prev = cx.tcx.hir_item(module.item_ids[prev]) && let items_span = last.span.with_lo(test_mod.span.hi()) - && let Some(items) = items_span.get_source_text(cx) + && let Some(items) = items_span.get_text(cx) { diag.multipart_suggestion_with_style( "move the items to before the test module was defined", diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs index 7cf594384b5a2..dec3f3bcae895 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs @@ -2,7 +2,7 @@ use std::{fmt, ops}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, SpanRangeExt}; +use clippy_utils::source::SpanExt; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_in_test}; use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; @@ -197,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { // TODO: Is there a cleaner, robust way to ask this question? // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", // and that doesn't get us the true name in scope rather than the span text either. - if let Some(name) = local_span.get_source_text(cx) + if let Some(name) = local_span.get_text(cx) && is_ident(&name) { // If the local is an ordinary named variable, @@ -228,11 +228,11 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { if fn_span.from_expansion() { // Don't lint on the main function generated by `--test` target - if cx.sess().is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { + if cx.tcx.sess.is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { return; } - let is_from_external_macro = fn_span.in_external_macro(cx.sess().source_map()); + let is_from_external_macro = fn_span.in_external_macro(cx.tcx.sess.source_map()); span_lint_and_then( cx, LARGE_STACK_FRAMES, diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 838abc81ce449..48e1414defadf 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_from_proc_macro, sym}; use hir::def_id::DefId; use rustc_errors::Applicability; @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && let QPath::TypeRelative(ty, last_segment) = qpath && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && is_integer_method(cx, def_id) - && let Some(mod_name) = ty.span.get_source_text(cx) + && let Some(mod_name) = ty.span.get_text(cx) && ty.span.eq_ctxt(last_segment.ident.span) { let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); diff --git a/src/tools/clippy/clippy_lints/src/len_without_is_empty.rs b/src/tools/clippy/clippy_lints/src/len_without_is_empty.rs index 34730dbb098c4..4453c4622caed 100644 --- a/src/tools/clippy/clippy_lints/src/len_without_is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/len_without_is_empty.rs @@ -149,7 +149,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Iden } fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { - if let ty::Alias(ty::AliasTy { + if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = *ty.kind() diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 53ee157dd6d70..9b54da5b020e7 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; use clippy_utils::{parent_item_name, peel_ref_operators, sym}; @@ -261,7 +261,7 @@ impl LenZero { } fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { - let Some(snippet) = span.get_source_text(cx) else { + let Some(snippet) = span.get_text(cx) else { return span; }; if has_enclosing_paren(snippet) { @@ -336,7 +336,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) -> bool { .filter_by_name_unhygienic(sym::is_empty) .any(|item| is_is_empty_and_stable(cx, item, msrv)) }), - &ty::Alias(ty::AliasTy { + &ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, .. }) => has_is_empty_impl(cx, def_id, msrv), diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 984574c221fb6..3f8bd2223876f 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -162,7 +162,8 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { ); }, ); - } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + } else if local.ty.is_none() + && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 7f2687664c686..3f4b59382ca15 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use rustc_ast::{Local, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 12d53e742f19a..a65b27f550995 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -414,7 +414,7 @@ mod zombie_processes; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::Lint; +use rustc_lint::{Lint, is_lint_pass_required}; use rustc_middle::ty::TyCtxt; use utils::attr_collector::AttrStorage; @@ -454,7 +454,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. // Due to the architecture of the compiler, currently `cfg_attr` attributes on crate // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. - store.register_pre_expansion_pass( + store.register_pre_expansion_lint_pass( Box::new(move || Box::new(attrs::EarlyAttributes::new(conf))) ); @@ -464,19 +464,14 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co { let format_args = format_args_storage.clone(); let attrs = attr_storage.clone(); - store.early_passes.push(Box::new(move || { + store.register_early_lint_pass(Box::new(move || { Box::new(CombinedEarlyLintPass::new(conf, format_args.clone(), attrs.clone())) })); } - store.late_passes.push(Box::new(move |tcx: TyCtxt<'_>| { - let dont_need = tcx.lints_that_dont_need_to_run(()); - let is_active = |lints: &rustc_lint::LintVec| { - lints.is_empty() - || !lints - .iter() - .all(|lint| dont_need.contains(&rustc_lint::LintId::of(lint))) - }; + store.register_late_lint_pass(Box::new(move |tcx: TyCtxt<'_>| { + let skippable_lints = tcx.skippable_lints(()); + let is_active = |lints: &rustc_lint::LintVec| is_lint_pass_required(skippable_lints, lints); Box::new(CombinedLateLintPass::new( tcx, conf, @@ -536,7 +531,6 @@ rustc_lint::early_lint_methods!( PartialPubFields: partial_pub_fields::PartialPubFields = partial_pub_fields::PartialPubFields, UnderscoreTyped: let_with_type_underscore::UnderscoreTyped = let_with_type_underscore::UnderscoreTyped, ExcessiveNesting: excessive_nesting::ExcessiveNesting = excessive_nesting::ExcessiveNesting::new(conf), - RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, NeedlessElse: needless_else::NeedlessElse = needless_else::NeedlessElse, RawStrings: raw_strings::RawStrings = raw_strings::RawStrings::new(conf), Visibility: visibility::Visibility = visibility::Visibility, @@ -734,7 +728,7 @@ rustc_lint::late_lint_methods!( UndocumentedUnsafeBlocks: undocumented_unsafe_blocks::UndocumentedUnsafeBlocks = undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf), FormatArgs: format_args::FormatArgs<'tcx> = format_args::FormatArgs::new(tcx, conf, format_args.clone()), TrailingEmptyArray: trailing_empty_array::TrailingEmptyArray = trailing_empty_array::TrailingEmptyArray, - NeedlessLateInit: needless_late_init::NeedlessLateInit = needless_late_init::NeedlessLateInit, + NeedlessLateInit: needless_late_init::NeedlessLateInit<'tcx> = needless_late_init::NeedlessLateInit::new(conf), ReturnSelfNotMustUse: return_self_not_must_use::ReturnSelfNotMustUse = return_self_not_must_use::ReturnSelfNotMustUse, NumberedFields: init_numbered_fields::NumberedFields = init_numbered_fields::NumberedFields, ManualBits: manual_bits::ManualBits = manual_bits::ManualBits::new(conf), @@ -864,6 +858,7 @@ rustc_lint::late_lint_methods!( ByteCharSlice: byte_char_slices::ByteCharSlice = byte_char_slices::ByteCharSlice, ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq, WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero, + RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, // add late passes here, used by `cargo dev new_lint` ]] ); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 2b4828872b202..3e58448af5266 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -161,7 +161,9 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly_trait_ref: &'tcx PolyTraitRef<'tcx>) { - report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + if !poly_trait_ref.span.from_expansion() { + report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 2ec046c8d6ba4..4e7ff879ae590 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; @@ -207,7 +207,9 @@ pub struct LiteralDigitGrouping { impl EarlyLintPass for LiteralDigitGrouping { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + // `NumericLiteral::from_lit_kind` only accepts integer and float literals. if let ExprKind::Lit(lit) = expr.kind + && matches!(lit.kind, token::LitKind::Integer | token::LitKind::Float) && !expr.span.in_external_macro(cx.sess().source_map()) { self.check_lit(cx, lit, expr.span); @@ -226,7 +228,7 @@ impl LiteralDigitGrouping { } fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { - if let Some(src) = span.get_source_text(cx) + if let Some(src) = span.get_text(cx) && let Ok(lit_kind) = LitKind::from_token_lit(lit) && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) { @@ -418,7 +420,9 @@ pub struct DecimalLiteralRepresentation { impl EarlyLintPass for DecimalLiteralRepresentation { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + // Only integer tokens can produce `LitKind::Int`. if let ExprKind::Lit(lit) = expr.kind + && lit.kind == token::LitKind::Integer && !expr.span.in_external_macro(cx.sess().source_map()) { self.check_lit(cx, lit, expr.span); @@ -436,10 +440,10 @@ impl DecimalLiteralRepresentation { // Lint integral literals. if let Ok(lit_kind) = LitKind::from_token_lit(lit) && let LitKind::Int(val, _) = lit_kind - && let Some(src) = span.get_source_text(cx) + && val >= u128::from(self.threshold) + && let Some(src) = span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) && num_lit.radix == Radix::Decimal - && val >= u128::from(self.threshold) { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); diff --git a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 41022e6a5321b..f4d71d0524b22 100644 --- a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr && let PatKind::Binding(_, binding_id, ..) = pat.kind { // Destructured iterator element `(idx, _)`, look for uses of the binding - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if expr.res_local_id() == Some(binding_id) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); } @@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr }); } else if let PatKind::Binding(_, binding_id, ..) = pat.kind { // Bound as a tuple, look for `tup.0` - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Field(e, field) = expr.kind && e.res_local_id() == Some(binding_id) && field.name == sym::integer(0) diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index 2cb98fdfe4484..a189bd7b717e9 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -139,7 +139,7 @@ fn is_ref_iterable<'tcx>( } let res_ty = cx.tcx.erase_and_anonymize_regions( - EarlyBinder::bind(req_res_ty) + EarlyBinder::bind(cx.tcx, req_res_ty) .instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)) .skip_norm_wip(), ); diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs index ffc6f7186922a..ceaf681088399 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -13,6 +13,7 @@ use rustc_hir::QPath::Resolved; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, Pat}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::{Spanned, sym}; use super::MANUAL_SLICE_FILL; @@ -84,6 +85,24 @@ pub(super) fn check<'tcx>( { sugg(cx, body, expr, recv_path.span, assignval.span); } + // `for slot in s { *slot = value; }` where `s` is already `&mut [T; N]` + else if let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind + && let ExprKind::Unary(UnOp::Deref, slice_iter) = assignee.kind + && let ExprKind::Path(Resolved(_, slice_path)) = slice_iter.kind + && let Res::Local(local) = slice_path.res + && local == pat.hir_id + && !assignval.span.from_expansion() + && switch_to_eager_eval(cx, assignval) + && !is_local_used(cx, assignval, local) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && let ty::Ref(_, inner_ty, rustc_ast::Mutability::Mut) = arg_ty.kind() + && is_slice_like(cx, *inner_ty) + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, *inner_ty, clone_trait, &[]) + && msrv.meets(cx, msrvs::SLICE_FILL) + { + sugg(cx, body, expr, arg.span, assignval.span); + } } fn sugg<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs index 4ecfadbf9d494..b80efdae9294c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use clippy_utils::{expr_or_init, pat_is_wild, sym}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Pat, PatKind, TyKind}; diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index bee3b19b597c0..161834914b309 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, position_before_rarrow, snippet_block}; +use clippy_utils::source::{SpanExt, position_before_rarrow, snippet_block}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -76,8 +76,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { "this function can be simplified using the `async fn` syntax", |diag| { if let Some(vis_span) = vis_span_opt - && let Some(vis_snip) = vis_span.get_source_text(cx) - && let Some(header_snip) = header_span.get_source_text(cx) + && let Some(vis_snip) = vis_span.get_text(cx) + && let Some(header_snip) = header_span.get_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some((_, ret_snip)) = suggested_ret(cx, output) { @@ -185,6 +185,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, Some((sugg, String::new())) } else { let sugg = "return the output of the future directly"; - output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}"))) + output.span.get_text(cx).map(|src| (sugg, format!(" -> {src}"))) } } diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 6848af7748f3f..c81fa2e2b3b27 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -140,7 +140,13 @@ struct InputMinMax<'tcx> { impl<'tcx> LateLintPass<'tcx> for ManualClamp { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.from_expansion() && !is_in_const_context(cx) { + // Cheap kind check before the costlier const context query. + if matches!( + expr.kind, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::MethodCall(..) | ExprKind::Call(..) + ) && !expr.span.from_expansion() + && !is_in_const_context(cx) + { let suggestion = is_if_elseif_else_pattern(cx, expr) .or_else(|| is_max_min_pattern(cx, expr)) .or_else(|| is_call_max_min_pattern(cx, expr)) @@ -155,6 +161,15 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + // Cheap `if`-statement check before the costlier const context query. + if !block + .stmts + .iter() + .any(|stmt| matches!(stmt.kind, StmtKind::Expr(e) if matches!(e.kind, ExprKind::If(..)))) + { + return; + } + if is_in_const_context(cx) || !self.msrv.meets(cx, msrvs::CLAMP) { return; } @@ -293,18 +308,19 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx /// ``` fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = expr.kind + && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind + // Match method names before the costlier type queries. + && let Some((min, max)) = match (seg_first.ident.name, seg_second.ident.name) { + (sym::min, sym::max) => Some((arg_second, arg_first)), + (sym::max, sym::min) => Some((arg_first, arg_second)), + _ => None, + } && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || cx.ty_based_def(expr).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) - && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || cx.ty_based_def(receiver).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) { let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); - let (min, max) = match (seg_first.ident.name, seg_second.ident.name) { - (sym::min, sym::max) => (arg_second, arg_first), - (sym::max, sym::min) => (arg_first, arg_second), - _ => return None, - }; Some(ClampSuggestion { params: InputMinMax { input, @@ -367,12 +383,16 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) && let Some(inner_seg) = segment(cx, inner_fn) && let Some(outer_seg) = segment(cx, outer_fn) { - let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) { + let typeck = cx.typeck_results(); + let (input, inner_arg) = match ( + is_const_evaluatable(cx.tcx, typeck, first), + is_const_evaluatable(cx.tcx, typeck, second), + ) { (true, false) => (second, first), (false, true) => (first, second), _ => return None, }; - let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let is_float = typeck.expr_ty_adjusted(input).is_floating_point(); let (min, max) = match (inner_seg, outer_seg) { (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg), (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg), diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 4a2784caf4c74..acd47fcd279db 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -157,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // case somebody does that for some reason && (const_1.is_pos_infinity() && const_2.is_neg_infinity() || const_1.is_neg_infinity() && const_2.is_pos_infinity()) - && let Some(local_snippet) = first.span.get_source_text(cx) + && let Some(local_snippet) = first.span.get_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite, diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index 96d69003934fc..33370194d4e7c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use clippy_utils::visitors::{is_local_used, local_used_once}; use rustc_errors::Applicability; @@ -105,8 +105,8 @@ impl LateLintPass<'_> for ManualHashOne { finish_expr.span, "manual implementation of `BuildHasher::hash_one`", |diag| { - if let Some(build_hasher) = build_hasher.span.get_source_text(cx) - && let Some(hashed_value) = hashed_value.span.get_source_text(cx) + if let Some(build_hasher) = build_hasher.span.get_text(cx) + && let Some(hashed_value) = hashed_value.span.get_text(cx) { diag.multipart_suggestion( "try", diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index f60b2a1a6729f..94a3f2b1f15f9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -143,8 +143,8 @@ impl LateLintPass<'_> for ManualRangePatterns { pat.span, "this OR pattern can be rewritten using a range", |diag| { - if let Some(min) = min.span.get_source_text(cx) - && let Some(max) = max.span.get_source_text(cx) + if let Some(min) = min.span.get_text(cx) + && let Some(max) = max.span.get_text(cx) { diag.span_suggestion( pat.span, diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 67685f803841c..2eb15928f25aa 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::{If, IfLetOrMatch}; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::source::{IntoSpan, SpanExt, snippet}; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::is_local_used; use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index e3a112d1f7802..e317056e29809 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -160,7 +160,7 @@ fn handle( ); } else if let Some(ty_name) = find_type_name(cx, cx.typeck_results().expr_ty(condition)) && cx.typeck_results().expr_adjustments(body_some).is_empty() - && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) + && let Some(or_body_snippet) = peel_blocks(body_none).span.get_text(cx) && let Some(indent) = indent_of(cx, expr.span) && ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some() { 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 caef6a0b36377..f500f21246bdb 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 @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, search_same}; use core::cmp::Ordering; use core::{iter, slice}; @@ -152,7 +152,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { if let Some(((_, dest), src)) = split && let Some(pat_snippets) = group .iter() - .map(|(_, arm)| arm.pat.span.get_source_text(cx)) + .map(|(_, arm)| arm.pat.span.get_text(cx)) .collect::>>() { let suggs = src diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 6b46791eb117a..98530b7808076 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -119,7 +119,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { wildcard_ident.map_or(String::new(), |ident| { ident .span - .get_source_text(cx) + .get_text(cx) .map_or_else(|| format!("{} @ ", ident.name), |s| format!("{s} @ ")) }), if let CommonPrefixSearcher::Path(path_prefix) = path_prefix { diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index ae0f3ffa0edc9..459c2bf1fef73 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -26,14 +26,15 @@ mod wild_in_or_pats; use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::walk_span_to_context; +use clippy_utils::source::SpanExt; use clippy_utils::{ - higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, sym, + higher, is_direct_expn_of, is_in_const_context, is_lint_allowed, is_span_match, sym, tokenize_with_text, }; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; +use rustc_lexer::{TokenKind, is_whitespace}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{SpanData, SyntaxContext}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -1084,34 +1085,38 @@ impl<'tcx> LateLintPass<'tcx> for Matches { try_err::check(cx, expr, ex); } - if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { + if !from_expansion + && let mut has_cfg = false + && let mut has_comments = false + && walk_intra_arm_text(cx, expr.span, ex.span, arms, |s| { + let mut iter = tokenize_with_text(s).filter(|(t, ..)| match t { + TokenKind::Whitespace => false, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } => { + has_comments = true; + false + }, + _ => true, + }); + while let Some((t, ..)) = iter.next() { + if matches!(t, TokenKind::Pound) + && matches!(iter.next(), Some((TokenKind::OpenBracket, ..))) + && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _))) + { + has_cfg = true; + } + } + }) + && !has_cfg + { if source == MatchSource::Normal { - if !(self.msrv.meets(cx, msrvs::MATCHES_MACRO) - && match_like_matches::check_match(cx, expr, ex, arms)) - { + let is_match_like_matches = self.msrv.meets(cx, msrvs::MATCHES_MACRO) + && match_like_matches::check_match(cx, expr, ex, arms); + if !(is_match_like_matches || is_lint_allowed(cx, MATCH_SAME_ARMS, expr.hir_id)) { match_same_arms::check(cx, arms); } redundant_pattern_match::check_match(cx, expr, ex, arms); - let mut match_comments = span_extract_comments(cx, expr.span); - // We remove comments from inside arms block. - if !match_comments.is_empty() { - for arm in arms { - for comment in span_extract_comments(cx, arm.body.span) { - if let Some(index) = match_comments - .iter() - .enumerate() - .find(|(_, cm)| **cm == comment) - .map(|(index, _)| index) - { - match_comments.remove(index); - } - } - } - } - // If there are still comments, it means they are outside of the arms. Tell the lint - // code about it. - single_match::check(cx, ex, arms, expr, !match_comments.is_empty()); + single_match::check(cx, ex, arms, expr, has_comments); match_bool::check(cx, ex, arms, expr); overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); @@ -1216,64 +1221,51 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } -/// Checks if there are any arms with a `#[cfg(..)]` attribute. -fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool { - let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else { - // Shouldn't happen, but treat this as though a `cfg` attribute were found - return true; - }; - - let start = scrutinee_span.hi(); - let mut arm_spans = arms.iter().map(|arm| { - let data = arm.span.data(); - (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi)) - }); - let end = e.span.hi(); - - // Walk through all the non-code space before each match arm. The space trailing the final arm is - // handled after the `try_fold` e.g. - // - // match foo { - // _________^- everything between the scrutinee and arm1 - //| arm1 => (), - //|---^___________^ everything before arm2 - //| #[cfg(feature = "enabled")] - //| arm2 => some_code(), - //|---^____________________^ everything before arm3 - //| // some comment about arm3 - //| arm3 => some_code(), - //|---^____________________^ everything after arm3 - //| #[cfg(feature = "disabled")] - //| arm4 = some_code(), - //|}; - //|^ - let found = arm_spans.try_fold(start, |start, range| { - let Some((end, next_start)) = range else { - // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute - // were found. - return Err(()); - }; - let span = SpanData { - lo: start, - hi: end, - ctxt: SyntaxContext::root(), - parent: None, - } - .span(); - (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(()) - }); - match found { - Ok(start) => { - let span = SpanData { - lo: start, - hi: end, - ctxt: SyntaxContext::root(), - parent: None, +/// Calls the given function for each segment of the source text within the +/// match block which is not part of any arm. For the purposes of this function +/// attributes on an arm are not considered part of the arm. +/// +/// This will return whether all the relevant source text could be retrieved. If +/// all the source text cannot be retrieved it should be assumed that the match +/// originates from a macro. +#[must_use] +fn walk_intra_arm_text( + cx: &LateContext<'_>, + match_sp: Span, + scrutinee_sp: Span, + arms: &[Arm<'_>], + mut f: impl FnMut(&str), +) -> bool { + if let Some(src) = match_sp.get_source_range(cx) + && let scrutinee_sp = scrutinee_sp.source_callsite().data() + && let block_start = (scrutinee_sp.hi.0 - src.sf.start_pos.0) as usize + && let Some(src_text) = src.sf.src.as_ref().map(|x| &***x) + && let Some(block_text) = src_text.get(block_start..src.range.end) + && let Some(stripped_text) = block_text.trim_start_matches(is_whitespace).strip_prefix('{') + && let arms_start = block_start + (block_text.len() - stripped_text.len()) + && let Some(arms_end) = stripped_text + .trim_end_matches(|c| is_whitespace(c) || c == ')') + .strip_suffix('}') + .map(|s| src.range.end - (stripped_text.len() - s.len())) + && let Some(range) = arms.iter().try_fold(arms_start..arms_end, |range, arm| { + let arm_sp: rustc_span::SpanData = arm.span.source_callsite().data(); + let arm_range = (arm_sp.lo.0 - src.sf.start_pos.0) as usize..(arm_sp.hi.0 - src.sf.start_pos.0) as usize; + if range.start <= arm_range.start + && arm_range.end <= range.end + && let Some(src) = src_text.get(range.start..arm_range.start) + { + f(src); + Some(arm_range.end..range.end) + } else { + None } - .span(); - span_contains_cfg(cx, span) - }, - Err(()) => true, + }) + && let Some(src) = src_text.get(range) + { + f(src); + true + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index b96c869b5151a..7c740ababb060 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{ - SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, + SpanExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, }; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, sym}; @@ -22,7 +22,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - span.check_source_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) + span.check_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) } pub(crate) fn check<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs b/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs new file mode 100644 index 0000000000000..1faaf2c9c8e43 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs @@ -0,0 +1,84 @@ +use crate::clippy_utils::res::MaybeTypeckRes; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath as _}; +use clippy_utils::sugg::Sugg; +use clippy_utils::sym; +use clippy_utils::ty::implements_trait; +use rustc_ast::BindingMode; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LetStmt, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::BY_REF_PEEKABLE_PEEK; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { + if let ExprKind::MethodCall(maybe_peekable, peekable_recv, [], _) = recv.kind + && maybe_peekable.ident.name == sym::peekable + && !peekable_recv.span.from_expansion() + && let ExprKind::MethodCall(maybe_by_ref, by_ref_recv, [], _) = peekable_recv.kind + && maybe_by_ref.ident.name == sym::by_ref + && !by_ref_recv.span.from_expansion() + && [peekable_recv, recv] + .into_iter() + .all(|e| cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Iterator)) + { + span_lint_and_then( + cx, + BY_REF_PEEKABLE_PEEK, + expr.span, + "calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output", + |diag| { + let span = by_ref_recv.span.shrink_to_hi().with_hi(expr.span.hi()); + if let ty::Ref(_, iter_ty, _) = cx.typeck_results().expr_ty_adjusted(by_ref_recv).kind() + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, *iter_ty, clone_trait, &[]) + { + diag.span_suggestion_verbose( + span, + "to peek the first item without advancing the underlying iterator, use", + ".clone().next().as_ref()", + Applicability::MaybeIncorrect, + ); + } + diag.span_suggestion_verbose( + span, + "to advance the underlying iterator, use", + ".next().as_ref()", + Applicability::MaybeIncorrect, + ); + // If the iterator is a local variable, initialized through a simple binding with an inferred + // initialization expression, suggest making the initialization expression peekable. + if let Some(iter_local_id) = by_ref_recv.res_local_id() + && let Node::LetStmt(LetStmt { + pat: let_pat, + ty: None, + init: Some(init_expr), + els: None, + span: let_stmt_span, + .. + }) = cx.tcx.parent_hir_node(iter_local_id) + && let PatKind::Binding(BindingMode::MUT, _, _, None) = let_pat.kind + && !let_stmt_span.from_expansion() + // Changing the type of the iterator may prevent the code from compiling + && let mut app = Applicability::MaybeIncorrect + && let sugg = + Sugg::hir_with_context(cx, init_expr, let_stmt_span.ctxt(), "_", &mut app).maybe_paren() + { + diag.multipart_suggestion( + "to make the iterator peekable, use", + vec![ + (init_expr.span.source_callsite(), format!("{sugg}.peekable()")), + (recv.span.with_lo(by_ref_recv.span.hi()), String::new()), + ], + app, + ); + } else { + diag.help( + "you might want to transform the iterator itself using `.peekable()` without using `.by_ref()`", + ); + } + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 15f4d91e4bd9e..b6694ad5148ba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use clippy_utils::sym; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( "case-sensitive file extension comparison", |diag| { diag.help("consider using a case-insensitive comparison instead"); - if let Some(recv_source) = recv.span.get_source_text(cx) { + if let Some(recv_source) = recv.span.get_text(cx) { let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() { recv_source.to_owned() } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs b/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs new file mode 100644 index 0000000000000..e151cf43a5044 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs @@ -0,0 +1,104 @@ +use super::CHUNKS_EXACT_TO_AS_CHUNKS; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::{get_expr_use_site, sym}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{DesugaringKind, ExpnKind, Span, Symbol}; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, + expr: &'tcx Expr<'tcx>, + call_span: Span, + method_name: Symbol, + msrv: Msrv, +) { + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); + if !matches!(recv_ty.kind(), ty::Ref(_, inner, _) if inner.is_slice()) { + return; + } + + if is_const_evaluatable(cx.tcx, cx.typeck_results(), arg) { + if !msrv.meets(cx, msrvs::AS_CHUNKS) { + return; + } + + let use_ctxt = get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr); + + if use_ctxt.is_ty_unified { + return; + } + + let suggestion_method = if method_name == sym::chunks_exact_mut { + "as_chunks_mut" + } else { + "as_chunks" + }; + + let mut applicability = Applicability::MachineApplicable; + let arg_str = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut applicability).0; + + let as_chunks = format_args!("{suggestion_method}::<{arg_str}>()"); + + span_lint_and_then( + cx, + CHUNKS_EXACT_TO_AS_CHUNKS, + call_span, + format!("using `{method_name}` with a constant chunk size"), + |diag| { + if let Node::Expr(use_expr) = use_ctxt.node { + match use_expr.kind { + ExprKind::Call(_, [recv]) | ExprKind::MethodCall(_, recv, [], _) + if recv.hir_id == use_ctxt.child_id + && matches!( + use_expr.span.ctxt().outer_expn_data().kind, + ExpnKind::Desugaring(DesugaringKind::ForLoop), + ) => + { + diag.span_suggestion( + call_span, + "consider using `as_chunks` instead", + format!("{as_chunks}.0"), + applicability, + ); + return; + }, + ExprKind::MethodCall(_, recv, ..) + if recv.hir_id == use_ctxt.child_id + && cx + .ty_based_def(use_expr) + .assoc_fn_parent(cx) + .is_diag_item(cx, sym::Iterator) => + { + diag.span_suggestion( + call_span, + "consider using `as_chunks` instead", + format!("{as_chunks}.0.iter()"), + applicability, + ); + return; + }, + _ => {}, + } + } + + diag.span_help(call_span, format!("consider using `{as_chunks}` instead")); + + if let Node::LetStmt(let_stmt) = use_ctxt.node + && let PatKind::Binding(_, _, ident, _) = let_stmt.pat.kind + { + diag.note(format!( + "you can access the chunks using `{ident}.0.iter()`, and the remainder using `{ident}.1`" + )); + } + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs index cd6b39b15a1b9..0b6ead0747a80 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; -use clippy_utils::{is_range_full, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{is_full_collection_range, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; @@ -16,8 +16,7 @@ const ACCEPTABLE_TYPES_WITHOUT_ARG: [rustc_span::Symbol; 3] = [sym::BinaryHeap, pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: Option<&Expr<'_>>) { if let Some(arg) = arg { if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITH_ARG) - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind - && is_range_full(cx, arg, Some(container_path)) + && is_full_collection_range(cx, recv.res_local_id(), arg) { suggest(cx, expr, recv, span); } diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index 6c222640e2601..2b4b200904b2e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -1,67 +1,31 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_range_full, std_or_core, sym}; +use clippy_utils::{is_full_collection_range, std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_middle::ty::Ty; -use rustc_span::Symbol; -/// Checks if both types match the given diagnostic item, e.g.: -/// -/// `vec![1,2].drain(..).collect::>()` -/// ^^^^^^^^^ ^^^^^^ true -/// `vec![1,2].drain(..).collect::>()` -/// ^^^^^^^^^ ^^^^^^^^^^ false -fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool { - if let Some(expr_adt) = expr.ty_adt_def() - && let Some(recv_adt) = recv.ty_adt_def() - { - cx.tcx.is_diagnostic_item(sym, expr_adt.did()) && cx.tcx.is_diagnostic_item(sym, recv_adt.did()) - } else { - false - } -} - -/// Checks `std::{vec::Vec, collections::VecDeque}`. -fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - (types_match_diagnostic_item(cx, expr, recv, sym::Vec) - || types_match_diagnostic_item(cx, expr, recv, sym::VecDeque)) - && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) -} - -/// Checks `std::string::String` -fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - expr.is_lang_item(cx, LangItem::String) - && recv.is_lang_item(cx, LangItem::String) - && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) -} - -/// Checks `std::collections::{HashSet, HashMap, BinaryHeap}`. -fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option<&'static str> { - types_match_diagnostic_item(cx, expr, recv, sym::HashSet) - .then_some("HashSet") - .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::HashMap).then_some("HashMap")) - .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::BinaryHeap).then_some("BinaryHeap")) -} - -pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, recv: &Expr<'_>) { - let expr_ty = cx.typeck_results().expr_ty(expr); - let recv_ty = cx.typeck_results().expr_ty(recv); - let recv_ty_no_refs = recv_ty.peel_refs(); - - if let ExprKind::Path(QPath::Resolved(_, recv_path)) = recv.kind - && let Some(typename) = check_vec(cx, args, expr_ty, recv_ty_no_refs, recv_path) - .then_some("Vec") - .or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String")) - .or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs)) +pub(super) fn check(cx: &LateContext<'_>, arg: Option<&Expr<'_>>, expr: &Expr<'_>, recv: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); + let (is_ref, ty) = match *ty.kind() { + ty::Ref(_, ty, _) => (true, ty), + _ => (false, ty), + }; + if cx.typeck_results().expr_ty(expr) == ty + && let Some(did) = ty.opt_def_id() + && (cx.tcx.lang_items().string() == Some(did) + || matches!( + ty.opt_diag_name(cx), + Some(sym::HashMap | sym::HashSet | sym::BinaryHeap | sym::Vec | sym::VecDeque) + )) + && arg.is_none_or(|arg| is_full_collection_range(cx, recv.res_local_id(), arg)) && let Some(exec_context) = std_or_core(cx) { let recv = snippet(cx, recv.span, ""); - let sugg = if let ty::Ref(..) = recv_ty.kind() { + let sugg = if is_ref { format!("{exec_context}::mem::take({recv})") } else { format!("{exec_context}::mem::take(&mut {recv})") @@ -71,8 +35,8 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re cx, DRAIN_COLLECT, expr.span, - format!("you seem to be trying to move all elements into a new `{typename}`"), - "consider using `mem::take`", + "draining all elements of a collection into a new collection of the same type", + "use `mem::take` to avoid creating a new allocation", sugg, Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 89b37d507b702..97a03f9868b1d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -100,7 +100,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex } fn contains_call<'a>(cx: &LateContext<'a>, arg: &'a hir::Expr<'a>) -> bool { - for_each_expr(cx, arg, |expr| { + for_each_expr(cx.tcx, arg, |expr| { if matches!(expr.kind, hir::ExprKind::MethodCall { .. } | hir::ExprKind::Call { .. }) && !is_inside_always_const_context(cx.tcx, expr.hir_id) { diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs index 8e5b271212e67..419a7e0ed877d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,7 +1,7 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::ty::is_copy; use clippy_utils::{CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, peel_blocks, sym}; use rustc_ast::Mutability; @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & .iter() .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) .count() - && let Some(param_snippet) = param.span.get_source_text(cx) + && let Some(param_snippet) = param.span.get_text(cx) { let mut applicability = Applicability::MachineApplicable; let (filter, _) = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs index 9c6b16b88bfd1..3584833bc61cd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs @@ -1,15 +1,15 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::source::snippet; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{path_to_local_with_projections, sym}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; use super::FILTER_NEXT; -#[derive(Copy, Clone)] +#[derive(Clone, Copy)] pub(super) enum Direction { Forward, Backward, @@ -17,15 +17,13 @@ pub(super) enum Direction { /// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for /// `DoubleEndedIterator` -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - recv: &'tcx hir::Expr<'_>, - filter_arg: &'tcx hir::Expr<'_>, +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + filter_arg: &Expr<'_>, direction: Direction, ) { - // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a - // DoubleEndedIterator let (required_trait, next_method, find_method) = match direction { Direction::Forward => (sym::Iterator, "next", "find"), Direction::Backward => (sym::DoubleEndedIterator, "next_back", "rfind"), @@ -37,30 +35,31 @@ pub(super) fn check<'tcx>( { return; } - let msg = format!( - "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \ - `.{find_method}(..)` instead", - required_trait.as_str() - ); - let filter_snippet = snippet(cx, filter_arg.span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - // add note if not multi-line - span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) - && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + span_lint_and_then( + cx, + FILTER_NEXT, + expr.span, + format!("called `filter(..).{next_method}()` on an `{required_trait}`"), + |diag| { + let mut app = Applicability::MachineApplicable; + let filter_snippet = snippet_with_applicability(cx, filter_arg.span, "..", &mut app); + let iter_snippet = snippet_with_applicability(cx, recv.span, "..", &mut app); + + let pat = if let Some(id) = path_to_local_with_projections(recv) + && let Node::Pat(pat) = cx.tcx.hir_node(id) + && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind { - (Applicability::Unspecified, Some((pat.span, ident))) + app = Applicability::Unspecified; + Some((pat.span, ident)) } else { - (Applicability::MachineApplicable, None) + None }; - diag.span_suggestion( + diag.span_suggestion_verbose( expr.span, - "try", + format!("use `.{find_method}(..)` instead"), format!("{iter_snippet}.{find_method}({filter_snippet})"), - applicability, + app, ); if let Some((pat_span, ident)) = pat { @@ -69,8 +68,6 @@ pub(super) fn check<'tcx>( format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), ); } - }); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } + }, + ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs index 8d1585baac451..2ef09fa3f8d2c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_range_full, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{is_full_collection_range, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -12,8 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did()) && matches!(ty_name, sym::Vec | sym::VecDeque) - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind - && is_range_full(cx, arg, Some(container_path)) + && is_full_collection_range(cx, recv.res_local_id(), arg) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index a89a656a6bc7e..5ef8450da93bc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; use clippy_utils::{ExprUseNode, get_expr_use_site, sym}; @@ -47,7 +47,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: let can_lint = for_each_expr_without_closures(block.stmts, |e| { if let ExprKind::Closure(c) = e.kind { // Nested closures don't need to treat returns specially. - let _: Option = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| { + let _: Option = for_each_expr(cx.tcx, cx.tcx.hir_body(c.body).value, |e| { if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, ctxt, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs index 48f2c10f97cc6..050b28b3babd7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -27,8 +27,8 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && is_ok_wrapping(cx, map_expr) - && let Some(recv_snippet) = recv.span.get_source_text(cx) - && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) + && let Some(recv_snippet) = recv.span.get_text(cx) + && let Some(err_arg_snippet) = err_arg.span.get_text(cx) && let Some(indent) = indent_of(cx, expr.span) { let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str(), true, Some(indent + 4)); diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs b/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs index aa23b9deff45b..538f8a2fe93dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::peel_blocks; use clippy_utils::res::{MaybeDef, MaybeResPath}; @@ -29,6 +30,8 @@ pub(super) fn check<'tcx>( && let ExprKind::MethodCall(method_path, map_recv, [map_arg], _) = peel_blocks(outer_value).kind && method_path.ident.name == sym::map && cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option) + // `b` is not lazy evaluated + && switch_to_eager_eval(cx, map_recv) // `b` does not reference the outer closure parameter `a`. && !local_used_in(cx, outer_param_id, map_recv) // `|b| (a, b)` diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs index 5f5944d5d4230..6ef7abffb0b2f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -31,14 +31,12 @@ pub(super) fn check<'tcx>( && let ExprKind::Closure(closure) = acc.kind && msrv.meets(cx, msrvs::ITERATOR_TRY_FOLD) && !is_from_proc_macro(cx, expr) - && let Some(args_snip) = closure - .fn_arg_span - .and_then(|fn_arg_span| fn_arg_span.get_source_text(cx)) + && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| fn_arg_span.get_text(cx)) { let init_snip = rest .is_empty() .then_some(first.span) - .and_then(|span| span.get_source_text(cx)) + .and_then(|span| span.get_text(cx)) .map_or_else(|| "...".to_owned(), |src| src.to_owned()); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs index ad950f75f8130..14a9752d3bded 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_expr_identity_function; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -24,7 +24,7 @@ pub(super) fn check( && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) && is_expr_identity_function(cx, any_arg) && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) - && let Some(map_arg) = map_arg.span.get_source_text(cx) + && let Some(map_arg) = map_arg.span.get_text(cx) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs index ac2f991804865..ec6dccd4fe543 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs @@ -128,10 +128,10 @@ pub(super) fn check<'tcx>( (SuggestedKind::AndThen, _) => "and_then", (SuggestedKind::IsVariantAnd, sym::Result) => "is_ok_and", (SuggestedKind::IsVariantAnd, sym::Option) => "is_some_and", - (SuggestedKind::Other, _) - if unwrap_arg_ty.peel_refs().is_array() - && cx.typeck_results().expr_ty_adjusted(unwrap_arg).peel_refs().is_slice() => - { + (SuggestedKind::Other, _) if unwrap_arg_ty != cx.typeck_results().expr_ty_adjusted(unwrap_arg) => { + // If the `unwrap_or` argument needs an adjustment, moving it into `map_or`'s + // first argument can make type inference pick the unadjusted type and reject + // the closure return type. Keep the lint, but don't emit a rustfix. return; }, _ => "map_or", diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index b01cd49739431..38a3b1705ad17 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1,4 +1,5 @@ mod bind_instead_of_map; +mod by_ref_peekable_peek; mod bytecount; mod bytes_count_to_len; mod bytes_nth; @@ -9,6 +10,7 @@ mod chars_last_cmp; mod chars_last_cmp_with_unwrap; mod chars_next_cmp; mod chars_next_cmp_with_unwrap; +mod chunks_exact_to_as_chunks; mod clear_with_drain; mod clone_on_copy; mod clone_on_ref_ptr; @@ -141,6 +143,7 @@ mod unnecessary_map_or_else; mod unnecessary_min_or_max; mod unnecessary_sort_by; mod unnecessary_to_owned; +mod unnecessary_unwrap_unchecked; mod unwrap_expect_used; mod useless_asref; mod useless_nonzero_new_unchecked; @@ -201,6 +204,39 @@ declare_clippy_lint! { "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `Iterator::by_ref().peekable().peek()`. + /// + /// ### Why is this bad? + /// While it might look like this will allow peeking on the first + /// element of an iterator without consuming it and without consuming + /// the iterator itself, it will in practice consume the first element. + /// + /// The implementation of `Peekable::peek()` produces the first element + /// of the underlying iterator, and stores it internally so that it can + /// be later produced. As a consequence, it advances the underlying + /// iterator, whose `.next()` method will now produce its second element. + /// + /// ### Example + /// ```no_run + /// let mut iter = [1, 2, 3].into_iter(); + /// let x = iter.by_ref().peekable().peek(); // 1 + /// let y = iter.by_ref().peekable().peek(); // 2 + /// ``` + /// If this does what you intended, use the following instead, which is + /// shorter and clearer: + /// ```no_run + /// let mut iter = [1, 2, 3].into_iter(); + /// let x = iter.next().as_ref(); // 1 + /// let y = iter.next().as_ref(); // 2 + /// ``` + #[clippy::version = "1.98.0"] + pub BY_REF_PEEKABLE_PEEK, + suspicious, + "Using `.by_ref().peekable().peek()` on an iterator" +} + declare_clippy_lint! { /// ### What it does /// It checks for `str::bytes().count()` and suggests replacing it with @@ -329,6 +365,32 @@ declare_clippy_lint! { "using `.chars().next()` to check if a string starts with a char" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `chunks_exact` or `chunks_exact_mut` with a constant chunk size. + /// + /// ### Why is this bad? + /// `as_chunks` provides better ergonomics and type safety by returning arrays instead of slices. + /// It was stabilized in Rust 1.88. + /// + /// ### Example + /// ```no_run + /// let slice = [1, 2, 3, 4, 5, 6]; + /// let mut it = slice.chunks_exact(2); + /// for chunk in it {} + /// ``` + /// Use instead: + /// ```no_run + /// let slice = [1, 2, 3, 4, 5, 6]; + /// let (chunks, remainder) = slice.as_chunks::<2>(); + /// for chunk in chunks {} + /// ``` + #[clippy::version = "1.93.0"] + pub CHUNKS_EXACT_TO_AS_CHUNKS, + style, + "using `chunks_exact` with constant when `as_chunks` is more ergonomic" +} + declare_clippy_lint! { /// ### What it does /// Checks for usage of `.drain(..)` for the sole purpose of clearing a container. @@ -4541,6 +4603,34 @@ declare_clippy_lint! { "unnecessary calls to `to_owned`-like functions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `unwrap_unchecked` when an `_unchecked` variant of the function exists. + /// + /// ### Why is this bad? + /// Calling the non-unchecked variant may result in checking that is then discarded + /// if `unwrap_unchecked` is called directly afterwards, whereas the unchecked + /// variant most likely avoids performing the check completely. + /// + /// ### Known problems + /// + /// The unchecked variant is only suggested if it's defined in the same `impl` block + /// as the non-unchecked one + /// + /// ### Example + /// ```rust + /// let s = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + /// ``` + /// Use instead: + /// ```rust + /// let s = unsafe { std::str::from_utf8_unchecked(&[]) }; + /// ``` + #[clippy::version = "1.98.0"] + pub UNNECESSARY_UNWRAP_UNCHECKED, + complexity, + "calling `unwrap_unchecked` on a function which has an `_unchecked` variant" +} + declare_clippy_lint! { /// ### What it does /// Checks for usages of the following functions with an argument that constructs a default value @@ -4835,9 +4925,11 @@ impl_lint_pass!(Methods => [ BIND_INSTEAD_OF_MAP, BYTES_COUNT_TO_LEN, BYTES_NTH, + BY_REF_PEEKABLE_PEEK, CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, CHARS_LAST_CMP, CHARS_NEXT_CMP, + CHUNKS_EXACT_TO_AS_CHUNKS, CLEAR_WITH_DRAIN, CLONED_INSTEAD_OF_COPIED, CLONE_ON_COPY, @@ -4979,6 +5071,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_RESULT_MAP_OR_ELSE, UNNECESSARY_SORT_BY, UNNECESSARY_TO_OWNED, + UNNECESSARY_UNWRAP_UNCHECKED, UNWRAP_OR_DEFAULT, UNWRAP_USED, USELESS_ASREF, @@ -5198,12 +5291,15 @@ impl Methods { _ => {}, } }, + (name @ (sym::chunks_exact | sym::chunks_exact_mut), [arg]) => { + chunks_exact_to_as_chunks::check(cx, recv, arg, expr, call_span, name, self.msrv); + }, (sym::and_then, [arg]) => { manual_option_zip::check(cx, expr, recv, arg, self.msrv); let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { - let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and", true); if !ule_and_linted { return_and_then::check(cx, expr, recv, arg); } @@ -5277,8 +5373,10 @@ impl Methods { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, - Some((sym::drain, recv, args, ..)) => { - drain_collect::check(cx, args, expr, recv); + Some((sym::drain, recv, args, ..)) => match args { + [arg] => drain_collect::check(cx, Some(arg), expr, recv), + [] => drain_collect::check(cx, None, expr, recv), + _ => {}, }, _ => {}, } @@ -5333,7 +5431,11 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_unchecked | sym::unwrap_err_unchecked, []) => { + (sym::unwrap_unchecked, []) => { + unnecessary_unwrap_unchecked::check(cx, expr, recv, call_span); + unnecessary_literal_unwrap::check(cx, expr, recv, name, args); + }, + (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_err_unchecked, []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, (sym::extend, [arg]) => { @@ -5439,7 +5541,7 @@ impl Methods { get_last_with_len::check(cx, expr, recv, arg); }, (sym::get_or_insert_with, [arg]) => { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert", false); }, (sym::hash, [arg]) => { unit_hash::check(cx, expr, recv, arg); @@ -5594,16 +5696,19 @@ impl Methods { ptr_offset_by_literal::check(cx, expr, self.msrv); }, (sym::ok_or_else, [arg]) => { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or", true); }, (sym::open, [_]) => { open_options::check(cx, expr, recv); }, (sym::or_else, [arg]) => { if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or", false); } }, + (sym::peek, []) => { + by_ref_peekable_peek::check(cx, expr, recv); + }, (sym::push, [arg]) => { path_buf_push_overwrite::check(cx, expr, arg); }, @@ -5699,7 +5804,7 @@ impl Methods { if !self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { return; } - unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some", true); }, (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); @@ -5789,7 +5894,7 @@ impl Methods { ); }, _ => { - unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); + unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or", false); }, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 948ed8a25746e..20468784aae7a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { @@ -36,7 +36,7 @@ fn handle_expr( && receiver.res_local_id() == Some(first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char - && let Some(snippet) = before_chars.get_source_text(cx) + && let Some(snippet) = before_chars.get_text(cx) { span_lint_and_sugg( cx, @@ -78,7 +78,7 @@ fn handle_expr( if revert != is_all && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) && peels_expr_ref(arg).res_local_id() == Some(first_param) - && let Some(snippet) = before_chars.get_source_text(cx) + && let Some(snippet) = before_chars.get_text(cx) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 62a192b7f770f..335915625a8ce 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -247,7 +247,7 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, sym::Item, [collect_ty]) && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( cx.typing_env(), - Unnormalized::new_wip(Ty::new_alias(cx.tcx, into_iter_item_proj)), + Unnormalized::new_wip(Ty::new_alias(cx.tcx, ty::IsRigid::No, into_iter_item_proj)), ) { iter_item_ty == into_iter_item_ty @@ -276,13 +276,13 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - iter_trait, ) && let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))]) - && let proj_ty = Ty::new_projection_from_args(cx.tcx, iter_item.def_id, args) + && let proj_ty = Ty::new_projection_from_args(cx.tcx, ty::IsRigid::No, iter_item.def_id, args) && let Ok(item_ty) = cx .tcx .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(proj_ty)) { item_ty - == EarlyBinder::bind(search_ty) + == EarlyBinder::bind(cx.tcx, search_ty) .instantiate(cx.tcx, cx.typeck_results().node_args(call_id)) .skip_norm_wip() } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs index 06e6a3c70b87d..7626cdb5a5fe8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use clippy_utils::usage::local_used_after_expr; use rustc_errors::Applicability; @@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name expr.span, "derefed type is same as origin", "try", - recv.span.get_source_text(cx).unwrap().to_owned(), + recv.span.get_text(cx).unwrap().to_owned(), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index a07cd5a8925ae..6f9e817490c12 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -30,9 +30,26 @@ pub(super) fn check<'tcx>( args: &'tcx [hir::Expr<'_>], msrv: Msrv, ) { + // Bail out early unless the method is one that `check_unwrap_or_default` or + // `check_or_fn_call` can lint, to avoid walking the argument of every method call. + if !matches!( + name, + sym::unwrap_or + | sym::unwrap_or_else + | sym::or_insert + | sym::or_insert_with + | sym::get_or_insert + | sym::map_or + | sym::ok_or + | sym::or + | sym::and + ) { + return; + } + if let [arg] = args { let inner_arg = peel_blocks(arg); - for_each_expr(cx, inner_arg, |ex| { + for_each_expr(cx.tcx, inner_arg, |ex| { // `or_fun_call` lint needs to take nested expr into account, // but `unwrap_or_default` lint doesn't, we don't want something like: // `opt.unwrap_or(Foo { inner: String::default(), other: 1 })` to get replaced by @@ -72,7 +89,7 @@ pub(super) fn check<'tcx>( // `map_or` takes two arguments if let [arg, lambda] = args { let inner_arg = peel_blocks(arg); - for_each_expr(cx, inner_arg, |ex| { + for_each_expr(cx.tcx, inner_arg, |ex| { let is_top_most_expr = ex.hir_id == inner_arg.hir_id; match ex.kind { hir::ExprKind::Call(fun, fun_args) => { @@ -112,6 +129,12 @@ fn check_unwrap_or_default( method_span: Span, msrv: Msrv, ) -> bool { + let sugg = match (name, call_expr.is_some()) { + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, + _ => return false, + }; + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); // Check MSRV, but only for `Result::unwrap_or_default` @@ -150,12 +173,6 @@ fn check_unwrap_or_default( } }; - let sugg = match (name, call_expr.is_some()) { - (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, - (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, - _ => return false, - }; - let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) diff --git a/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs b/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs index b5d2add65cf19..17f81f547aeb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs +++ b/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -92,7 +92,7 @@ fn expr_as_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti } fn format_isize_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { - let text = expr.span.get_source_text(cx)?; + let text = expr.span.get_text(cx)?; let text = peel_parens_str(&text); Some(text.trim_end_matches("isize").trim_end_matches('_').to_string()) } diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs index 5fec0c5f2cf7d..b01adbf4d58af 100644 --- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; +use clippy_utils::source::{SpanExt as _, snippet_with_applicability}; use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; @@ -49,8 +49,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' ), )]; if let Some((left, right)) = invert_bindings - && let Some(snip_left) = left.get_source_text(cx) - && let Some(snip_right) = right.get_source_text(cx) + && let Some(snip_left) = left.get_text(cx) + && let Some(snip_right) = right.get_text(cx) { suggestions.extend([(left, snip_right.to_string()), (right, snip_left.to_string())]); } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs index 8641f7c0abec3..fbb3dd41c7a4e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_split.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -22,8 +22,9 @@ pub(super) fn check<'a>( // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. if let ExprKind::MethodCall(trim_method_name, trim_recv, [], trim_span) = split_recv.kind && trim_method_name.ident.name == sym::trim - && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() - && !is_const_evaluatable(cx, trim_recv) + && let typeck = cx.typeck_results() + && typeck.expr_ty_adjusted(trim_recv).peel_refs().is_str() + && !is_const_evaluatable(cx.tcx, typeck, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind && matches!( split_lit.node, diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index fff203296bce9..13782a492cbc5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -214,7 +214,7 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - let _: Option = for_each_expr(cx, init_expr, |e| { + let _: Option = for_each_expr(cx.tcx, init_expr, |e| { if e.res_local_id() == Some(binding) { path_to_binding = Some(e); } diff --git a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs index 48e89c2998efa..53c695e378476 100644 --- a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( } && msrv.meets(cx, msrvs::MATCHES_MACRO) && !is_from_proc_macro(cx, expr) - && let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx) + && let Some(scrutinee_snip) = scrutinee.span.get_text(cx) { // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then // something like `r"\"` will become `'\'`, which is of course invalid diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs index d322909bef359..43dac249f7440 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -29,8 +29,8 @@ pub(super) fn check( }; let both_calls_span = first_call_span.with_hi(call_span.hi()); - if let Some(both_calls_snippet) = both_calls_span.get_source_text(cx) - && let Some(first_caller_snippet) = first_caller.span.get_source_text(cx) + if let Some(both_calls_snippet) = both_calls_span.get_text(cx) + && let Some(first_caller_snippet) = first_caller.span.get_text(cx) { let (sugg_span, suggestion) = if is_some { ( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs index b2413bb77aa73..b622a219f35dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_errors::Applicability; @@ -39,11 +39,11 @@ pub(super) fn check( return; }; let both_calls_span = get_call_span.with_hi(call_span.hi()); - if let Some(snippet) = both_calls_span.get_source_text(cx) - && let Some(arg_snippet) = arg.span.get_source_text(cx) + if let Some(snippet) = both_calls_span.get_text(cx) + && let Some(arg_snippet) = arg.span.get_text(cx) { let generics_snippet = if let Some(generics) = path.args - && let Some(generics_snippet) = generics.span_ext.get_source_text(cx) + && let Some(generics_snippet) = generics.span_ext.get_text(cx) { format!("::{generics_snippet}") } else { @@ -64,7 +64,7 @@ pub(super) fn check( suggestion, Applicability::MaybeIncorrect, ); - } else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) { + } else if let Some(caller_snippet) = get_caller.span.get_text(cx) { let full_span = get_caller.span.with_hi(call_span.hi()); span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 444d0a1d72422..5b19c43f1b32f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr}; @@ -42,7 +42,7 @@ pub fn check_for_loop_iter( && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent) && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body) && !clone_or_copy_needed - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) { // Issue 12098 // https://github.com/rust-lang/rust-clippy/issues/12098 @@ -102,7 +102,7 @@ pub fn check_for_loop_iter( && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, sym::Item) && iter_item_ty == into_iter_item_ty - && let Some(collection_snippet) = collection.span.get_source_text(cx) + && let Some(collection_snippet) = collection.span.get_text(cx) { collection_snippet } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 2869547650f31..0b3c8146b9f76 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use hir::FnRetTy; use rustc_errors::Applicability; @@ -18,6 +18,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, simplify_using: &str, + use_turbofish: bool, ) -> bool { let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); @@ -46,23 +47,43 @@ pub(super) fn check<'tcx>( } else { "unnecessary closure used with `bool::then`" }; - let applicability = if body + + let mut applicability = Applicability::MachineApplicable; + if body .params .iter() // bindings are checked to be unused above - .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) - && matches!( - fn_decl.output, - FnRetTy::DefaultReturn(_) - | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer(()), - .. - }) - ) { - Applicability::MachineApplicable - } else { - // replacing the lambda may break type inference - Applicability::MaybeIncorrect + .any(|param| !matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) + { + // If the closure parameters have a pattern, + // it might be required for type inferrence. + applicability = Applicability::MaybeIncorrect; + } + let (ascription, turbofish) = match fn_decl.output { + FnRetTy::DefaultReturn(_) + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer(()), + .. + }) => { + // if the closure has no explicit return type, + // then there's nothing to preserve + (String::new(), String::new()) + }, + FnRetTy::Return(ty) => { + // explicit return type was given on the closure + // + // we can preserve this information using `as`, but `as` is + // a somewhat dangerous feature, because it can be used to + // truncate integers + // + // if possible, use turbofish to preserve the type information + let ty = snippet_with_applicability(cx, ty.span, "_", &mut applicability); + if use_turbofish { + (String::new(), format!("::<{ty}>")) + } else { + (format!(" as {ty}"), String::new()) + } + }, }; // This is a duplicate of what's happening in clippy_lints::methods::method_call, @@ -73,7 +94,10 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( span, format!("use `{simplify_using}` instead"), - format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), + format!( + "{simplify_using}{turbofish}({}{ascription})", + snippet(cx, body_expr.span, "..") + ), applicability, ); }); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index 391209d8c3657..60bc4de69237a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -262,7 +262,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) -> Option< if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Left) { (left_expr, l_pat.span, false) } else if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Right) { - (left_expr, r_pat.span, true) + // Use the right-hand expr (the `a` side) as the key body, peeling any `&` + // introduced by the `.cmp(&rhs)` call so the suggestion doesn't contain a + // spurious borrow. + let right_body = if let ExprKind::AddrOf(_, _, inner) = right_expr.kind { + inner + } else { + right_expr + }; + (right_body, l_pat.span, true) } else { return None; }; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index a56dcd894b6aa..35ae44591c188 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,7 +3,7 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_context}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; @@ -217,7 +217,7 @@ fn check_into_iter_call_arg( && let parent_ty = cx.typeck_results().expr_ty(parent) && implements_trait(cx, parent_ty, iterator_trait_id, &[]) && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. && !cx.typeck_results().expr_ty(receiver).is_diag_item(cx, sym::Cow) // Calling `iter()` on a temporary object can lead to false positives. #14242 @@ -313,8 +313,8 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) && let Some((sym::split, argument_expr)) = get_fn_name_and_arg(cx, parent) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) - && let Some(arg_snippet) = argument_expr.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) + && let Some(arg_snippet) = argument_expr.span.get_text(cx) { // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef` but does not implement `Deref`. In this case, we have to @@ -712,7 +712,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx && let arg_ty = arg_ty.peel_refs() // For now we limit this lint to `String` and `Vec`. && (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty)) - && let Some(snippet) = caller.span.get_source_text(cx) + && let Some(snippet) = caller.span.get_text(cx) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs new file mode 100644 index 0000000000000..3f7f146446361 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs @@ -0,0 +1,276 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeQPath; +use clippy_utils::ty::{option_or_result_arg_ty, same_type_modulo_regions}; +use clippy_utils::{is_from_proc_macro, last_path_segment, over}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Body, Expr, ExprKind, PatKind, Safety}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; +use rustc_span::symbol::Ident; + +use super::UNNECESSARY_UNWRAP_UNCHECKED; + +#[derive(Clone, Copy, Debug)] +enum Variant { + /// Free `fn` in a module + Fn, + /// Associated item from an `impl` + Assoc(AssocKind), +} + +impl Variant { + fn msg(self) -> &'static str { + // Don't use `format!` instead -- it won't be optimized out. + match self { + Variant::Fn => "usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists", + Variant::Assoc(AssocKind::Fn) => { + "usage of `unwrap_unchecked` when an `_unchecked` variant of the associated function exists" + }, + Variant::Assoc(AssocKind::Method) => { + "usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists" + }, + } + } +} + +/// This only exists so the help message shows `associated function` or `method`, depending on +/// whether it has a `self` parameter. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum AssocKind { + /// No `self`: `fn new() -> Self` + Fn, + /// Has `self`: `fn ty<'tcx>(&self) -> Ty<'tcx>` + Method, +} + +impl AssocKind { + fn new(fn_has_self_parameter: bool) -> Self { + if fn_has_self_parameter { Self::Method } else { Self::Fn } + } +} + +fn unchecked_ident(checked_ident: Ident) -> Option { + let checked_ident = checked_ident.to_string(); + // Only add `_unchecked` if it doesn't already end with `_` + (!checked_ident.ends_with('_')).then(|| Ident::from_str(&(checked_ident + "_unchecked"))) +} + +/// Find a function called the same as `checked`, but with added `_unchecked`. +/// +/// This doesn't check if the methods are actually "similar" -- for that, see +/// [`same_functions_modulo_safety`] +fn find_unchecked_sibling_fn( + cx: &LateContext<'_>, + checked_def_id: DefId, + checked_ident: Ident, +) -> Option<(DefId, Ident)> { + // Don't use `parent_module`. We only want to lint if its first parent is a `Mod`, + // i.e. if this is a free-standing function + let parent = cx.tcx.parent(checked_def_id); + if cx.tcx.def_kind(parent) == DefKind::Mod + && let children = parent.as_local().map_or_else( + || cx.tcx.module_children(parent), + // We must use a !query for local modules to prevent an ICE. + |parent| cx.tcx.module_children_local(parent), + ) + // Make sure that there are other functions in this module + // (otherwise there couldn't be an unchecked version) + && children.len() > 1 + && let Some(unchecked_ident) = unchecked_ident(checked_ident) + && let Some(unchecked_def_id) = children.iter().find_map(|child| { + if child.ident == unchecked_ident + && let Res::Def(DefKind::Fn, def_id) = child.res + { + Some(def_id) + } else { + None + } + }) + { + Some((unchecked_def_id, unchecked_ident)) + } else { + None + } +} + +/// Find a method called the same as `checked`, but with added `_unchecked`. +/// +/// This doesn't check if the methods are actually "similar" -- for that, see +/// [`same_functions_modulo_safety`] +fn find_unchecked_sibling_method<'tcx>( + cx: &LateContext<'tcx>, + checked_def_id: DefId, + checked_ident: Ident, +) -> Option<(&'tcx ty::AssocItem, Ident)> { + // Don't use `parent_impl`. We only want to lint if its first parent is an `Impl` + let parent = cx.tcx.parent(checked_def_id); + if matches!(cx.tcx.def_kind(parent), DefKind::Impl { .. }) + && let Some(unchecked_ident) = unchecked_ident(checked_ident) + // Only look in the same impl (to avoid dealing with generics etc.) + && let Some(unchecked) = cx.tcx.associated_items(parent).find_by_ident_and_namespace( + cx.tcx, + unchecked_ident, + Namespace::ValueNS, + parent, + ) + { + Some((unchecked, unchecked_ident)) + } else { + None + } +} + +/// Checks that `checked_def_id` and `unchecked_def_id` refer to functions with: +/// - same visibility +/// - identical signatures, apart from unsafety +/// - "matching" return types: the checked version returns `Option`/`Result`, while the +/// unchecked one returns `T` +fn same_functions_modulo_safety<'tcx>( + cx: &LateContext<'tcx>, + checked_def_id: DefId, + unchecked_def_id: DefId, + unwrapped_ret_ty: Ty<'tcx>, +) -> bool { + let hir_body = |def_id: DefId| -> Option<&'tcx Body<'tcx>> { cx.tcx.hir_maybe_body_owned_by(def_id.as_local()?) }; + let fn_sig = |def_id| cx.tcx.fn_sig(def_id).skip_binder().skip_binder(); + + if match (hir_body(checked_def_id), hir_body(unchecked_def_id)) { + // For local functions, we can get the parameter names. In that case, we want to make sure + // that the latter are equal between the checked and unchecked versions. + (Some(checked_body), Some(unchecked_body)) => { + over(checked_body.params, unchecked_body.params, |p1, p2| { + // We only allow simple params (plain bindings) for now, to stay on the safer side. + if let PatKind::Binding(bm1, _, ident1, None) = p1.pat.kind + && let PatKind::Binding(bm2, _, ident2, None) = p2.pat.kind + { + bm1 == bm2 && ident1 == ident2 + } else { + false + } + }) + }, + // For non-local functions, parameter names are not accessible. Oh well, we'll let it slip + (None, None) => true, + // If only one of the versions is non-local, then something weird happened. Bail just in case + _ => false, + } { + // Check that the functions have identical signatures, apart from safety, and return type (see + // below) + let checked_fn_sig = fn_sig(checked_def_id); + let unchecked_fn_sig = fn_sig(unchecked_def_id); + + (checked_fn_sig.safety() == Safety::Safe && unchecked_fn_sig.safety() == Safety::Unsafe) + && checked_fn_sig.c_variadic() == unchecked_fn_sig.c_variadic() + && checked_fn_sig.abi() == unchecked_fn_sig.abi() + // NOTE: the reason we use `same_type_modulo_regions` all over the place here is that + // the regions of different functions will be distinct, even if they are called the same + && over(checked_fn_sig.inputs(), unchecked_fn_sig.inputs(), |ty1, ty2| { + same_type_modulo_regions(*ty1, *ty2) + }) + // The checked version should return `Option` or `Result`, + // and the unchecked version should return just `T` + && same_type_modulo_regions(unchecked_fn_sig.output(), unwrapped_ret_ty) + && option_or_result_arg_ty(cx, checked_fn_sig.output()) + .is_some_and(|wrapped_ty| same_type_modulo_regions(wrapped_ty, unwrapped_ret_ty)) + // Check that the visibilities are the same (for the purposes of replacing, it would be enough to have + // the former _at least as_ visible as the latter, but we don't bother) + && cx.tcx.visibility(unchecked_def_id) == cx.tcx.visibility(checked_def_id) + } else { + false + } +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, recv: &Expr<'_>, call_span: Span) { + if expr.span.from_expansion() { + return; + } + let expected_ret_ty = cx.typeck_results().expr_ty(expr); + let (variant, checked_span, unchecked_sugg, unchecked_full_path) = match recv.kind { + // Construct `Variant::Fn(_)`, if applicable. This is necessary for us to handle + // functions like `std::str::from_utf8_unchecked`. + ExprKind::Call(path, _) + if let ExprKind::Path(qpath) = path.kind + && let checked_ident = last_path_segment(&qpath).ident + && let checked_def_id = path.res(cx).def_id() + && let Some((unchecked_def_id, unchecked_ident)) = + find_unchecked_sibling_fn(cx, checked_def_id, checked_ident) + && same_functions_modulo_safety(cx, checked_def_id, unchecked_def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked_def_id); + ( + Variant::Fn, + checked_ident.span, + if checked_ident.span == path.span { + // replacing `bar(x)` with `bar_unchecked(x)` + // `bar_unchecked` might not be in scope, so suggest the full path + unchecked_full_path.clone() + } else { + // replacing `foo::bar(x)` with `foo::bar_unchecked(x)` + // since the path is qualified, we can just replace the final segment + unchecked_ident.to_string() + }, + unchecked_full_path, + ) + }, + // We unfortunately must handle `A::a(&a)` and `a.a()` separately, this handles the + // former + ExprKind::Call(path, _) + if let ExprKind::Path(qpath) = path.kind + && let checked_ident = last_path_segment(&qpath).ident + && let checked_def_id = path.res(cx).def_id() + && let Some((unchecked, unchecked_ident)) = + find_unchecked_sibling_method(cx, checked_def_id, checked_ident) + && let ty::AssocKind::Fn { has_self, .. } = unchecked.kind + && same_functions_modulo_safety(cx, checked_def_id, unchecked.def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked.def_id); + ( + Variant::Assoc(AssocKind::new(has_self)), + // since this is basically a method call, we only need to replace the method ident + checked_ident.span, + unchecked_ident.to_string(), + unchecked_full_path, + ) + }, + // ... And now the latter ^^ + ExprKind::MethodCall(segment, _, _, _) + if let checked_ident = segment.ident + && let Some(checked_def_id) = cx.typeck_results().type_dependent_def_id(recv.hir_id) + && let Some((unchecked, unchecked_ident)) = + find_unchecked_sibling_method(cx, checked_def_id, checked_ident) + && same_functions_modulo_safety(cx, checked_def_id, unchecked.def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked.def_id); + ( + Variant::Assoc(AssocKind::Method), + // since this is a method call, we only need to replace the method ident + checked_ident.span, + unchecked_ident.to_string(), + unchecked_full_path, + ) + }, + _ => return, + }; + + if !is_from_proc_macro(cx, expr) { + span_lint_and_then(cx, UNNECESSARY_UNWRAP_UNCHECKED, expr.span, variant.msg(), |diag| { + let sugg = vec![ + // replace the function with the unchecked version + (checked_span, unchecked_sugg), + // remove the call to `.unwrap_unchecked()` + (call_span.with_lo(recv.span.hi()), String::new()), + ]; + diag.multipart_suggestion( + format!("use `{unchecked_full_path}` instead, and remove the call to `.unwrap_unchecked()`"), + sugg, + // TODO: make this `MachineApplicable` when the function comes from std/alloc/core + // The reasoning is that, if the function comes from std/alloc/core, its checked and unchecked are + // pretty likely to have their semantics match. + Applicability::MaybeIncorrect, + ); + }); + } +} diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index ba62853c74571..74da699e3c006 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -79,14 +79,15 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { + let m = match path.ident.name { + sym::max => MinMax::Max, + sym::min => MinMax::Min, + _ => return None, + }; if cx.typeck_results().expr_ty(receiver).is_floating_point() || cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Ord) { - match path.ident.name { - sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), - sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), - _ => None, - } + fetch_const(cx, expr.span.ctxt(), Some(receiver), args, m) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs index 314004621ce05..79e56b7de41f8 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs @@ -333,11 +333,15 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.in_external_macro(cx.sess().source_map()) { - return; - } - - if let ExprKind::Lit(lit) = expr.kind { + // `check_lit` only lints integer literals and suffixed float literals. + if let ExprKind::Lit(lit) = expr.kind + && match lit.kind { + token::LitKind::Integer => true, + token::LitKind::Float => lit.suffix.is_some(), + _ => false, + } + && !expr.span.in_external_macro(cx.sess().source_map()) + { MiscEarlyLints::check_lit(cx, lit, expr.span); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 33ab94e00a5c6..5bc437cf24f4c 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::ast::{Pat, PatKind}; use rustc_lint::EarlyContext; @@ -59,7 +59,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { .iter() .filter_map(|f| match f.pat.kind { PatKind::Wild => None, - _ => f.span.get_source_text(cx), + _ => f.span.get_text(cx), }) .format(", "), )); diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 066094394e379..95c02751f8619 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -213,7 +213,7 @@ fn fn_inputs_has_impl_trait_ty(cx: &LateContext<'_>, def_id: LocalDefId) -> bool inputs.iter().any(|input| { matches!( input.kind(), - &ty::Alias(ty::AliasTy { kind: ty::Free{def_id} , ..}) if cx.tcx.type_of(def_id).skip_binder().is_opaque() + &ty::Alias(_, ty::AliasTy { kind: ty::Free{def_id} , ..}) if cx.tcx.type_of(def_id).skip_binder().is_opaque() ) }) } diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index 7dbe39bb099d6..36a2a7f4b406c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::{PathNS, lookup_path_str}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; @@ -78,7 +78,7 @@ impl LateLintPass<'_> for ImportRename { && let Some(name) = self.renames.get(&id) // Remove semicolon since it is not present for nested imports && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';') - && let Some(snip) = span_without_semi.get_source_text(cx) + && let Some(snip) = span_without_semi.get_text(cx) && let Some(import) = match snip.split_once(" as ") { None => Some(snip.as_str()), Some((import, rename)) => { diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 634745422204f..657da09f5ca2b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -109,7 +109,7 @@ fn should_lint<'tcx>( // Is there a call to `DebugStruct::debug_struct`? Do lint if there is. let mut has_debug_struct = false; - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); @@ -166,7 +166,7 @@ fn check_struct<'tcx>( let mut has_direct_field_access = false; let mut field_accesses = FxHashSet::default(); - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { if let ExprKind::Field(target, ident) = expr.kind && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs() && target_ty == self_ty diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 93cfed38c43ed..32a86db67c194 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -1,8 +1,6 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, Attribute, find_attr}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::AssocContainer; +use clippy_utils::diagnostics::span_lint; +use rustc_hir::{ImplItem, ImplItemKind, Item, ItemKind, OwnerId, TraitFn, TraitItem, TraitItemKind, find_attr}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::config::CrateType; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -66,134 +64,41 @@ declare_clippy_lint! { declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); -fn check_missing_inline_attrs( - cx: &LateContext<'_>, - attrs: &[Attribute], - sp: Span, - desc: &'static str, - hir_id: Option, -) { - if !find_attr!(attrs, Inline(..)) { - let msg = format!("missing `#[inline]` for {desc}"); - if let Some(hir_id) = hir_id { - span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg); - } else { - span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg); - } +fn check(cx: &LateContext<'_>, item: OwnerId, sp: Span) { + if cx.effective_visibilities.is_exported(item.def_id) + && !find_attr!(cx.tcx.hir_attrs(item.into()), Inline(..)) + // Rust `inline` doesn't mean anything with external linkage. + && !cx.tcx.codegen_fn_attrs(item.def_id).contains_extern_indicator() + && !cx.tcx.crate_types().iter().any(|&t| matches!(t, CrateType::ProcMacro)) + && !sp.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint( + cx, + MISSING_INLINE_IN_PUBLIC_ITEMS, + sp, + "missing `#[inline]` on a publicly callable function", + ); } } impl<'tcx> LateLintPass<'tcx> for MissingInline { - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - if it.span.in_external_macro(cx.sess().source_map()) { - return; - } - - if cx - .tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) - { - return; - } - - if !cx.effective_visibilities.is_exported(it.owner_id.def_id) { - return; - } - match it.kind { - hir::ItemKind::Fn { .. } => { - if fn_is_externally_exported(cx, it.owner_id.to_def_id()) { - return; - } - - let desc = "a function"; - let attrs = cx.tcx.hir_attrs(it.hir_id()); - check_missing_inline_attrs(cx, attrs, it.span, desc, None); - }, - hir::ItemKind::Trait { items: trait_items, .. } => { - // note: we need to check if the trait is exported so we can't use - // `LateLintPass::check_trait_item` here. - for &tit in trait_items { - let tit_ = cx.tcx.hir_trait_item(tit); - match tit_.kind { - hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, - hir::TraitItemKind::Fn(..) => { - if cx.tcx.defaultness(tit.owner_id).has_value() { - // trait method with default body needs inline in case - // an impl is not provided - let desc = "a default trait method"; - let item = cx.tcx.hir_trait_item(tit); - let attrs = cx.tcx.hir_attrs(item.hir_id()); - check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id())); - } - }, - } - } - }, - hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::GlobalAsm { .. } - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::ExternCrate(..) - | hir::ItemKind::ForeignMod { .. } - | hir::ItemKind::Impl { .. } - | hir::ItemKind::Use(..) => {}, + fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { + if let ItemKind::Fn { .. } = it.kind { + check(cx, it.owner_id, it.span); } } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if impl_item.span.in_external_macro(cx.sess().source_map()) - || cx - .tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(_, f) = item.kind + && let TraitFn::Provided(_) = f { - return; + check(cx, item.owner_id, item.span); } + } - // If the item being implemented is not exported, then we don't need #[inline] - if !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - return; - } - - let desc = match impl_item.kind { - hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => return, - }; - - let assoc_item = cx.tcx.associated_item(impl_item.owner_id); - let container_id = assoc_item.container_id(cx.tcx); - let trait_def_id = match assoc_item.container { - AssocContainer::Trait => Some(container_id), - AssocContainer::TraitImpl(_) => Some(cx.tcx.impl_trait_id(container_id)), - AssocContainer::InherentImpl => None, - }; - - if let Some(trait_def_id) = trait_def_id - && trait_def_id.is_local() - && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) - { - // If a trait is being implemented for an item, and the - // trait is not exported, we don't need #[inline] - return; + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(..) = item.kind { + check(cx, item.owner_id, item.span); } - - let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); - check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None); } } - -/// Checks if this function is externally exported, where #[inline] wouldn't have the desired effect -/// and a rustc warning would be triggered, see #15301 -fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool { - let attrs = cx.tcx.codegen_fn_attrs(def_id); - attrs.contains_extern_indicator() -} diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index 8b02c4865d138..a78f53188ba8b 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -6,7 +6,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index ad44d65b4d663..6d9950579fb24 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::{Applicability, Diag}; diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index f8fe6aa15d324..c16d4fbe1e899 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -285,7 +285,7 @@ fn needless_borrow_count<'tcx>( return false; } - let predicate = EarlyBinder::bind(predicate) + let predicate = EarlyBinder::bind(cx.tcx, predicate) .instantiate(cx.tcx, &args_with_referent_ty[..]) .skip_norm_wip(); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); @@ -341,6 +341,7 @@ fn is_mixed_projection_predicate<'tcx>( loop { match *projection_term.self_ty().kind() { ty::Alias( + _, inner_projection_ty @ ty::AliasTy { kind: ty::Projection { .. }, .. @@ -434,7 +435,7 @@ fn replace_types<'tcx>( .projection_term .with_replaced_self_ty(cx.tcx, new_ty) .expect_ty() - .to_ty(cx.tcx); + .to_ty(cx.tcx, ty::IsRigid::No); if let Ok(projected_ty) = cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/needless_else.rs b/src/tools/clippy/clippy_lints/src/needless_else.rs index 807c4cf5da8e1..299860ac1e037 100644 --- a/src/tools/clippy/clippy_lints/src/needless_else.rs +++ b/src/tools/clippy/clippy_lints/src/needless_else.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -43,7 +43,7 @@ impl EarlyLintPass for NeedlessElse { && !else_clause.span.from_expansion() && block.stmts.is_empty() && let range = (then_block.span.hi()..expr.span.hi()).trim_start(cx) - && range.clone().check_source_text(cx, |src| { + && range.clone().check_text(cx, |src| { // Ignore else blocks that contain comments or #[cfg]s !src.contains(['/', '#']) }) diff --git a/src/tools/clippy/clippy_lints/src/needless_ifs.rs b/src/tools/clippy/clippy_lints/src/needless_ifs.rs index cdf9bd91339c6..48d15056f7291 100644 --- a/src/tools/clippy/clippy_lints/src/needless_ifs.rs +++ b/src/tools/clippy/clippy_lints/src/needless_ifs.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::If; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -48,7 +48,7 @@ impl LateLintPass<'_> for NeedlessIfs { && block.stmts.is_empty() && block.expr.is_none() && !expr.span.in_external_macro(cx.sess().source_map()) - && then.span.check_source_text(cx, |src| { + && then.span.check_text(cx, |src| { // Ignore // - empty macro expansions // - empty reptitions in macro expansions @@ -58,7 +58,7 @@ impl LateLintPass<'_> for NeedlessIfs { .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace() || ch == b'\x0b') }) && let Some(cond_span) = walk_span_to_context(cond.span, expr.span.ctxt()) - && let Some(cond_snippet) = cond_span.get_source_text(cx) + && let Some(cond_snippet) = cond_span.get_text(cx) && !is_from_proc_macro(cx, expr) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index bc4f6ca401710..69184d1894025 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -1,16 +1,19 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, + BindingMode, Block, Expr, ExprKind, HirId, HirIdMap, HirIdSet, LetStmt, LocalSource, MatchSource, Node, Pat, + PatKind, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -61,17 +64,126 @@ declare_clippy_lint! { "late initializations that can be replaced by a `let` statement with an initializer" } -declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); +impl_lint_pass!(NeedlessLateInit<'_> => [NEEDLESS_LATE_INIT]); -fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - for_each_expr(cx, stmt, |e| { - if matches!(e.kind, ExprKind::Assign(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) +pub struct NeedlessLateInit<'tcx> { + check_grouped_late_init: bool, + grouped_late_inits: Vec<(HirId, HirIdMap>)>, +} + +impl<'tcx> NeedlessLateInit<'tcx> { + pub fn new(conf: &'static Conf) -> Self { + Self { + check_grouped_late_init: conf.check_grouped_late_init, + grouped_late_inits: Vec::default(), } - }) - .is_some() + } + + fn check_if_or_match( + &mut self, + cx: &LateContext<'tcx>, + local_stmt: &'tcx LetStmt<'tcx>, + block: &'tcx Block<'tcx>, + binding_id: HirId, + usage: Usage<'tcx>, + exprs: impl IntoIterator>, + ) { + let mut assigns: Vec> = Vec::new(); + for expr in exprs { + let ty = cx.typeck_results().expr_ty(expr); + if ty.is_never() { + continue; + } + if !ty.is_unit() { + return; + } + + let Some(assign_group) = LocalAssignGroup::new(cx, expr) else { + return; + }; + + if let Some(last_group) = assigns.last() + && !assign_group.is_parallel(last_group) + { + return; + } + + assigns.push(assign_group); + } + + let Some(first_group) = assigns.first() else { + return; + }; + // If there are multiple assignments grouped together, lazyly check them after processing the block. + if first_group.0.len() > 1 { + if self.check_grouped_late_init { + let late_inits = if let Some((hir_id, late_inits)) = self.grouped_late_inits.last_mut() + && *hir_id == block.hir_id + { + late_inits + } else { + &mut self.grouped_late_inits.push_mut((block.hir_id, HirIdMap::default())).1 + }; + + let mut decls = HirIdMap::default(); + decls.insert(binding_id, local_stmt); + late_inits.insert(usage.expr.hir_id, GroupedLateInit { usage, assigns, decls }); + } + + return; + } + + if first_group.0[0].lhs_id == binding_id { + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + local_stmt.span, + "unneeded late initialization", + |diag| { + let mut suggestions = vec![]; + for group in assigns { + suggestions.extend( + group + .0 + .iter() + .flat_map(|assign| { + let rhs_span = assign.rhs.span.source_callsite(); + let mut spans = vec![assign.span.until(rhs_span)]; + + if rhs_span.hi() != assign.span.hi() { + spans.push(rhs_span.shrink_to_hi().with_hi(assign.span.hi())); + } + + spans + }) + .map(|span| (span, String::new())), + ); + } + + suggestions.push((local_stmt.span, String::new())); + let mut applicability = Applicability::MachineApplicable; + let let_snippet = local_snippet_without_semicolon(cx, local_stmt, &mut applicability); + suggestions.push((usage.span.shrink_to_lo(), format!("{let_snippet} = "))); + if usage.needs_semi { + suggestions.push((usage.span.shrink_to_hi(), ";".to_owned())); + } + let binding_name = cx.tcx.hir_name(binding_id); + let descriptor = if matches!(usage.expr.kind, ExprKind::If(..)) { + "branches" + } else { + "`match` arms" + }; + diag.multipart_suggestion( + format!( + "move the declaration `{binding_name}` here and remove the assignments from the {descriptor}", + ), + suggestions, + applicability, + ); + }, + ); + } + } } fn contains_let(cond: &Expr<'_>) -> bool { @@ -99,112 +211,35 @@ fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { } #[derive(Debug)] -struct LocalAssign { +struct LocalAssign<'tcx> { lhs_id: HirId, - rhs_span: Span, + rhs: &'tcx Expr<'tcx>, span: Span, } -impl LocalAssign { - fn from_expr(expr: &Expr<'_>, span: Span) -> Option { +impl<'tcx> LocalAssign<'tcx> { + fn new(expr: &'tcx Expr<'tcx>, span: Span) -> Option { if expr.span.from_expansion() { return None; } - if let ExprKind::Assign(lhs, rhs, _) = expr.kind { - if lhs.span.from_expansion() { - return None; - } - - Some(Self { + if let ExprKind::Assign(lhs, rhs, _) = expr.kind + && !lhs.span.from_expansion() + { + return Some(Self { lhs_id: lhs.res_local_id()?, - rhs_span: rhs.span.source_callsite(), + rhs, span, - }) - } else { - None - } - } - - fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option { - let assign = match expr.kind { - ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span), - ExprKind::Block(block, _) => { - if let Some((last, other_stmts)) = block.stmts.split_last() - && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind - - && let assign = Self::from_expr(expr, last.span)? - - // avoid visiting if not needed - && assign.lhs_id == binding_id - && other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt)) - { - Some(assign) - } else { - None - } - }, - ExprKind::Assign(..) => Self::from_expr(expr, expr.span), - _ => None, - }?; - - if assign.lhs_id == binding_id { - Some(assign) - } else { - None - } - } -} - -fn assignment_suggestions<'tcx>( - cx: &LateContext<'tcx>, - binding_id: HirId, - exprs: impl IntoIterator>, -) -> Option<(Applicability, Vec<(Span, String)>)> { - let mut assignments = Vec::new(); - - for expr in exprs { - let ty = cx.typeck_results().expr_ty(expr); - - if ty.is_never() { - continue; - } - if !ty.is_unit() { - return None; + }); } - let assign = LocalAssign::new(cx, expr, binding_id)?; - - assignments.push(assign); - } - - let suggestions = assignments - .iter() - .flat_map(|assignment| { - let mut spans = vec![assignment.span.until(assignment.rhs_span)]; - - if assignment.rhs_span.hi() != assignment.span.hi() { - spans.push(assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())); - } - - spans - }) - .map(|span| (span, String::new())) - .collect::>(); - - match suggestions.len() { - // All of `exprs` are never types - // https://github.com/rust-lang/rust-clippy/issues/8911 - 0 => None, - 1 => Some((Applicability::MachineApplicable, suggestions)), - // multiple suggestions don't work with rustfix in multipart_suggest - // https://github.com/rust-lang/rustfix/issues/141 - _ => Some((Applicability::Unspecified, suggestions)), + None } } +#[derive(Debug)] struct Usage<'tcx> { - stmt: &'tcx Stmt<'tcx>, + span: Span, expr: &'tcx Expr<'tcx>, needs_semi: bool, } @@ -226,20 +261,34 @@ fn first_usage<'tcx>( .find(|&stmt| is_local_used(cx, stmt, binding_id)) .and_then(|stmt| match stmt.kind { StmtKind::Expr(expr) => Some(Usage { - stmt, + span: stmt.span, expr, needs_semi: true, }), StmtKind::Semi(expr) => Some(Usage { - stmt, + span: stmt.span, expr, needs_semi: false, }), _ => None, }) + .or_else(|| { + block + .expr + .filter(|expr| is_local_used(cx, *expr, binding_id)) + .map(|expr| Usage { + span: expr.span, + expr, + needs_semi: true, + }) + }) } -fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option { +fn local_snippet_without_semicolon<'a>( + cx: &LateContext<'_>, + local: &LetStmt<'_>, + applicability: &mut Applicability, +) -> Cow<'a, str> { let span = local.span.with_hi(match local.ty { // let : ; // ~~~~~~~~~~~~~~~ @@ -249,105 +298,70 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> None => local.pat.span.hi(), }); - span.get_source_text(cx) + snippet_with_applicability(cx, span, "..", applicability) } -fn check<'tcx>( - cx: &LateContext<'tcx>, - local: &'tcx LetStmt<'tcx>, - local_stmt: &'tcx Stmt<'tcx>, - block: &'tcx Block<'tcx>, - binding_id: HirId, -) -> Option<()> { - let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?; - let binding_name = cx.tcx.hir_opt_name(binding_id)?; - let let_snippet = local_snippet_without_semicolon(cx, local)?; - - match usage.expr.kind { - ExprKind::Assign(..) => { - let assign = LocalAssign::new(cx, usage.expr, binding_id)?; - let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); - msg_span.push_span_label(local_stmt.span, "created here"); - msg_span.push_span_label(assign.span, "initialised here"); - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - msg_span, - "unneeded late initialization", - |diag| { - diag.multipart_suggestion( - format!("move the declaration `{binding_name}` here"), - vec![ - (local_stmt.span, String::new()), - ( - assign.span, - let_snippet.to_owned() + " = " + &snippet(cx, assign.rhs_span, ".."), - ), - ], - Applicability::MachineApplicable, - ); - }, - ); - }, - ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { - let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initialization", - |diag| { - suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - - if usage.needs_semi { - suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); +#[derive(Debug)] +struct LocalAssignGroup<'tcx>(Vec>); + +impl<'tcx> LocalAssignGroup<'tcx> { + fn new(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + match expr.kind { + ExprKind::Block(Block { expr: Some(expr), .. }, _) + if let Some(assign) = LocalAssign::new(expr, expr.span) => + { + Some(LocalAssignGroup(vec![assign])) + }, + ExprKind::Block(Block { expr: None, stmts, .. }, _) => { + let mut assign_group = Vec::new(); + // Avoid cases when the assignee is used or reassigned in the subsequent assignments + let mut used_locals = HirIdSet::default(); + for stmt in stmts.iter().rev() { + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind + && let Some(assign) = LocalAssign::new(expr, stmt.span) + && !used_locals.contains(&assign.lhs_id) + { + used_locals.insert(assign.lhs_id); + for_each_expr(cx.tcx, assign.rhs, |e| { + if let Some(id) = e.res_local_id() { + used_locals.insert(id); + } + ControlFlow::<()>::Continue(()) + }); + assign_group.push(assign); + continue; } - diag.multipart_suggestion( - format!( - "move the declaration `{binding_name}` here and remove the assignments from the branches" - ), - suggestions, - applicability, - ); - }, - ); - }, - ExprKind::Match(_, arms, MatchSource::Normal) => { - let (applicability, mut suggestions) = - assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initialization", - |diag| { - suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - - if usage.needs_semi { - suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); - } + break; + } + if assign_group.is_empty() { + None + } else { + Some(LocalAssignGroup(assign_group)) + } + }, + ExprKind::Assign(..) if let Some(assign) = LocalAssign::new(expr, expr.span) => { + Some(LocalAssignGroup(vec![assign])) + }, + _ => None, + } + } - diag.multipart_suggestion( - format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"), - suggestions, - applicability, - ); - }, - ); - }, - _ => {}, + /// Checks if the assignments in `self` and `other` are parallel, i.e. they have the same number + /// of assignments and the same assignees in the same order. + fn is_parallel(&self, other: &Self) -> bool { + self.0.len() == other.0.len() && self.0.iter().zip(other.0.iter()).all(|(a, b)| a.lhs_id == b.lhs_id) } +} - Some(()) +#[derive(Debug)] +struct GroupedLateInit<'tcx> { + usage: Usage<'tcx>, + assigns: Vec>, + decls: HirIdMap<&'tcx LetStmt<'tcx>>, } -impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit<'tcx> { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { let mut parents = cx.tcx.hir_parent_iter(local.hir_id); if let LetStmt { @@ -362,8 +376,147 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { } = local && let Some((_, Node::Stmt(local_stmt))) = parents.next() && let Some((_, Node::Block(block))) = parents.next() + && let Some(usage) = first_usage(cx, *binding_id, local_stmt.hir_id, block) { - check(cx, local, local_stmt, block, *binding_id); + if self.check_grouped_late_init + && let Some((hir_id, late_inits)) = self.grouped_late_inits.last_mut() + && *hir_id == block.hir_id + && let Some(late_init) = late_inits.get_mut(&usage.expr.hir_id) + { + late_init.decls.insert(*binding_id, local); + return; + } + + match usage.expr.kind { + ExprKind::Assign(..) + if let Some(assign) = LocalAssign::new(usage.expr, usage.expr.span) + && assign.lhs_id == *binding_id => + { + let mut applicability = Applicability::MachineApplicable; + let let_snippet = local_snippet_without_semicolon(cx, local, &mut applicability); + let binding_name = cx.tcx.hir_name(*binding_id); + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); + + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + msg_span, + "unneeded late initialization", + |diag| { + let mut applicability = Applicability::MachineApplicable; + let rhs_snippet = snippet_with_applicability( + cx, + assign.rhs.span.source_callsite(), + "..", + &mut applicability, + ); + diag.multipart_suggestion( + format!("move the declaration `{binding_name}` here"), + vec![ + (local_stmt.span, String::new()), + (assign.span, format!("{let_snippet} = {rhs_snippet}")), + ], + applicability, + ); + }, + ); + }, + ExprKind::If(cond, then_expr, Some(mut else_expr)) if !contains_let(cond) => { + // Flatten multiple if branches + let mut exprs = vec![then_expr]; + while let ExprKind::If(cond, then, Some(else_)) = else_expr.kind { + if contains_let(cond) { + return; + } + exprs.push(then); + else_expr = else_; + } + exprs.push(else_expr); + self.check_if_or_match(cx, local, block, *binding_id, usage, exprs); + }, + ExprKind::Match(_, arms, MatchSource::Normal) => { + self.check_if_or_match(cx, local, block, *binding_id, usage, arms.iter().map(|arm| arm.body)); + }, + _ => {}, + } + } + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if self.check_grouped_late_init + && let Some((_, late_inits)) = self.grouped_late_inits.pop_if(|(hir_id, _)| *hir_id == block.hir_id) + { + 'outer: for (_, late_init) in late_inits { + if late_init.decls.len() < late_init.assigns[0].0.len() { + continue; + } + + let mut suggestions = vec![]; + for assign in &late_init.assigns[0].0 { + if let Some(local) = late_init.decls.get(&assign.lhs_id) + // If the local has a type annotation, skip it since removing the annotation might cause type + // inference issues while annotating the tuple makes the suggestion harder to read. + && local.ty.is_none() + { + suggestions.push((local.span, String::new())); + } else { + continue 'outer; + } + } + + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + late_init.usage.span, + "unneeded late initialization", + |diag| { + let mut applicability = Applicability::MachineApplicable; + for group in &late_init.assigns { + let rhs_snippet = group + .0 + .iter() + .rev() + .map(|assign| { + snippet_with_applicability( + cx, + assign.rhs.span.source_callsite(), + "..", + &mut applicability, + ) + }) + .intersperse(", ".into()) + .collect::(); + suggestions.push(( + group.0.last().unwrap().span.to(group.0[0].span), + format!("({rhs_snippet})"), + )); + } + let let_snippet = late_init.assigns[0] + .0 + .iter() + .rev() + .map(|assign| cx.tcx.hir_name(assign.lhs_id).to_string()) + .intersperse(", ".to_owned()) + .collect::(); + suggestions.push((late_init.usage.span.shrink_to_lo(), format!("let ({let_snippet}) = "))); + if late_init.usage.needs_semi { + suggestions.push((late_init.usage.span.shrink_to_hi(), ";".to_owned())); + } + let descriptor = if matches!(late_init.usage.expr.kind, ExprKind::If(..)) { + "branches" + } else { + "`match` arms" + }; + diag.multipart_suggestion( + format!("move the declarations here and remove the assignments from the {descriptor}"), + suggestions, + applicability, + ); + }, + ); + } } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 91358ef77fa1e..0d5b2ee6ba29e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,7 +1,6 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::HasSession as _; use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; use core::ops::ControlFlow; @@ -207,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We retrieve all the closures declared in the function because they will not be found // by `euv::Delegate`. let mut closures: FxIndexSet = FxIndexSet::default(); - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Closure(closure) = expr.kind { closures.insert(closure.def_id); } @@ -269,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // If the argument is never used mutably, we emit the warning. let sp = input.span; if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind { - let Some(after_mut_span) = cx.sess().source_map().span_extend_to_prev_str( + let Some(after_mut_span) = cx.tcx.sess.source_map().span_extend_to_prev_str( inner_ty.ty.span.shrink_to_lo(), "mut", true, diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 4ff5b0b0b3c39..a2ad531751288 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanExt, snippet}; use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_self, peel_hir_ty_options, strip_pat_refs, sym}; @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - span.get_source_text(cx).map_or_else( + span.get_text(cx).map_or_else( || "change the call to".to_owned(), |src| format!("change `{src}` to"), ), @@ -275,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - span.get_source_text(cx).map_or_else( + span.get_text(cx).map_or_else( || "change the call to".to_owned(), |src| format!("change `{src}` to"), ), diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 9e6fc68116d4f..933ad716446f2 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{in_automatically_derived, is_inside_always_const_context, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; @@ -278,8 +278,8 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let ExprKind::Index(..) = &expr.kind { if !is_inside_always_const_context(cx.tcx, expr.hir_id) && let [arr, func] = &*reduced - && let Some(arr) = arr.span.get_source_text(cx) - && let Some(func) = func.span.get_source_text(cx) + && let Some(arr) = arr.span.get_text(cx) + && let Some(func) = func.span.get_text(cx) { span_lint_hir_and_then( cx, @@ -300,7 +300,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } else { let mut snippet = String::new(); for e in reduced { - if let Some(snip) = e.span.get_source_text(cx) { + if let Some(snip) = e.span.get_text(cx) { snippet.push_str(&snip); snippet.push_str("; "); } else { diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 22d7d83d730d9..1a4dbf6c9c3d8 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -323,7 +323,7 @@ impl<'tcx> NonCopyConst<'tcx> { IsFreeze::from_fields(tys.iter().map(|ty| self.is_ty_freeze(tcx, typing_env, ty))) }, // Treat type parameters as though they were `Freeze`. - ty::Param(_) | ty::Alias(..) => return IsFreeze::Yes, + ty::Param(_) | ty::Alias(_, ..) => return IsFreeze::Yes, // TODO: check other types. _ => { *e = IsFreeze::No; @@ -384,7 +384,7 @@ impl<'tcx> NonCopyConst<'tcx> { e: &'tcx Expr<'tcx>, ) -> bool { // Make sure to instantiate all types coming from `typeck` with `gen_args`. - let ty = EarlyBinder::bind(typeck.expr_ty(e)).instantiate(tcx, gen_args); + let ty = EarlyBinder::bind(tcx, typeck.expr_ty(e)).instantiate(tcx, gen_args); let ty = tcx .try_normalize_erasing_regions(typing_env, ty) .unwrap_or(ty.skip_norm_wip()); @@ -401,7 +401,7 @@ impl<'tcx> NonCopyConst<'tcx> { }, ExprKind::Path(ref p) => { let res = typeck.qpath_res(p, e.hir_id); - let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)) + let gen_args = EarlyBinder::bind(tcx, typeck.node_args(e.hir_id)) .instantiate(tcx, gen_args) .skip_norm_wip(); match res { @@ -599,7 +599,7 @@ impl<'tcx> NonCopyConst<'tcx> { init_expr = next_init; }, ExprKind::Path(ref init_path) => { - let next_init_args = EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)) + let next_init_args = EarlyBinder::bind(tcx, init_typeck.node_args(init_expr.hir_id)) .instantiate(tcx, init_args) .skip_norm_wip(); match init_typeck.qpath_res(init_path, init_expr.hir_id) { @@ -904,6 +904,7 @@ impl<'tcx> TypeFolder> for ReplaceAssocFolder<'tcx> { fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if let ty::Alias( + _, ty @ ty::AliasTy { kind: ty::Projection { .. }, .. diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index cb934466bd890..cf19cd9fc81d5 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet_with_applicability}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && param.span.eq_ctxt(expr.span) && param .span - .check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) + .check_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) { show_error(cx, param); } @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && param.span.eq_ctxt(expr.span) && param .span - .check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) + .check_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) { show_error(cx, param); } diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs index 487eaecf18890..2755e3add760e 100644 --- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -198,7 +198,7 @@ impl LazyInfo { // visit body to collect `Lazy::new` calls let mut new_fn_calls = FxIndexMap::default(); - for_each_expr::<(), ()>(cx, body, |ex| { + for_each_expr::<(), ()>(cx.tcx, body, |ex| { if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did) { diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index b449681ae2e14..e601d54cc4eda 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::MacroMatcher; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanExt}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -129,7 +129,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace if let ExpnKind::Macro(MacroKind::Bang, mac_name) = expn_data.kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = expn_data.call_site.get_source_text(cx) + && let Some(snip) = expn_data.call_site.get_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index 6e7ee727965db..5735c23a62ac8 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::token::LitKind; use rustc_ast::{Expr, ExprKind}; use rustc_errors::Applicability; @@ -86,7 +86,7 @@ impl EarlyLintPass for OctalEscapes { // Last check to make sure the source text matches what we read from the string. // Macros are involved somehow if this doesn't match. - if span.check_source_text(cx, |src| match *src.as_bytes() { + if span.check_text(cx, |src| match *src.as_bytes() { [b'\\', b'0', lo] => lo == c_lo, [b'\\', b'0', hi, lo] => hi == c_hi && lo == c_lo, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 5695779425f4a..fa391fad589fe 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::qualify_min_const_fn::is_stable_const_fn; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, eq_expr_value, is_in_const_context, trait_ref_of_method}; @@ -74,8 +74,8 @@ pub(super) fn check<'tcx>( expr.span, "manual implementation of an assign operation", |diag| { - if let Some(snip_a) = assignee.span.get_source_text(cx) - && let Some(snip_r) = rhs.span.get_source_text(cx) + if let Some(snip_a) = assignee.span.get_text(cx) + && let Some(snip_r) = rhs.span.get_text(cx) { diag.span_suggestion( expr.span, diff --git a/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs b/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs index 8511f2151342d..08e56e4516c52 100644 --- a/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs +++ b/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::numeric_literal; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -40,7 +40,7 @@ fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { if let LitKind::Int(Pu128(val), _) = lit.node && !is_single_digit(val) && !is_power_of_twoish(val) - && let Some(src) = lit.span.get_source_text(cx) + && let Some(src) = lit.span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && num_lit.is_decimal() { diff --git a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs index f0b6407a141bd..8a2cdf430927b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{eq_expr_value, sugg}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -43,8 +43,8 @@ fn lint_misrefactored_assign_op( expr.span, "variable appears on both sides of an assignment operation", |diag| { - if let Some(snip_a) = assignee.span.get_source_text(cx) - && let Some(snip_r) = rhs_other.span.get_source_text(cx) + if let Some(snip_a) = assignee.span.get_text(cx) + && let Some(snip_r) = rhs_other.span.get_text(cx) { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index cc147a4a84bdc..fa7a5d8feb380 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - let _: Option = for_each_expr(cx, body.value, |e| { + let _: Option = for_each_expr(cx.tcx, body.value, |e| { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return ControlFlow::Continue(Descend::Yes); }; diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index a5e57d97301e3..473718c5419f1 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanExt, snippet}; use clippy_utils::sym; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; @@ -73,7 +73,7 @@ impl PathbufPushSearcher<'_> { && let Some(arg) = self.arg && let ExprKind::Lit(x) = arg.kind && let LitKind::Str(_, StrStyle::Cooked) = x.node - && let Some(s) = arg.span.get_source_text(cx) + && let Some(s) = arg.span.get_text(cx) { Some(format!(" = PathBuf::from({s});")) } else { @@ -83,8 +83,8 @@ impl PathbufPushSearcher<'_> { fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option { let arg = self.arg?; - let arg_str = arg.span.get_source_text(cx)?; - let init_val = self.init_val.span.get_source_text(cx)?; + let arg_str = arg.span.get_text(cx)?; + let init_val = self.init_val.span.get_text(cx)?; Some(format!(" = {init_val}.join({arg_str});")) } diff --git a/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs index 40bc42dcdcb36..200ab8f8418f1 100644 --- a/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs +++ b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs @@ -1,7 +1,7 @@ use super::PTR_ARG; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_expr_use_or_unification_node, is_lint_allowed, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; @@ -55,7 +55,7 @@ pub(super) fn check_body<'tcx>( .chain(result.replacements.iter().map(|r| { ( r.expr_span, - format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement), + format!("{}{}", r.self_span.get_text(cx).unwrap(), r.replacement), ) })) .collect(), @@ -156,7 +156,7 @@ impl fmt::Display for DerefTyDisplay<'_, '_> { DerefTy::Path => f.write_str("Path"), DerefTy::Slice(hir_ty, ty) => { f.write_char('[')?; - match hir_ty.and_then(|s| s.get_source_text(self.0)) { + match hir_ty.and_then(|s| s.get_text(self.0)) { Some(s) => f.write_str(&s)?, None => ty.fmt(f)?, } @@ -279,7 +279,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( diag.span_suggestion( hir_ty.span, "change this to", - match ty.span().get_source_text(cx) { + match ty.span().get_text(cx) { Some(s) => format!("&{}{s}", mutability.prefix_str()), None => format!("&{}{}", mutability.prefix_str(), args.type_at(1)), }, diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index cbfd1af1907b7..7f95adba870d7 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -629,6 +629,11 @@ fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + // Cheap `let` check before the costlier lint level and const context queries. + if !matches!(stmt.kind, StmtKind::Let(..)) { + return; + } + if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) || !self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) { return; @@ -646,7 +651,9 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() + // Cheap `if`/`match` check before the costlier lint level and const context queries. + if matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..)) + && !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) && self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 71965ee1e29f9..2fe368c0639ab 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{ @@ -191,14 +191,21 @@ impl Ranges { impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind + && matches!( + op.node, + BinOpKind::And | BinOpKind::BitAnd | BinOpKind::Or | BinOpKind::BitOr + ) && self.msrv.meets(cx, msrvs::RANGE_CONTAINS) + && !is_in_const_context(cx) { check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } - check_exclusive_range_plus_one(cx, expr); - check_inclusive_range_minus_one(cx, expr); - check_reversed_empty_range(cx, expr); + if let Some(range) = higher::Range::hir(cx, expr) { + check_exclusive_range_plus_one(cx, expr, &range); + check_inclusive_range_minus_one(cx, expr, &range); + check_reversed_empty_range(cx, expr, &range); + } } } @@ -210,10 +217,6 @@ fn check_possible_range_contains( expr: &Expr<'_>, span: Span, ) { - if is_in_const_context(cx) { - return; - } - let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, @@ -300,7 +303,7 @@ fn check_possible_range_contains( if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind && op == lhs_op.node && let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()) - && new_span.check_source_text(cx, |src| { + && new_span.check_text(cx, |src| { // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong src.matches('(').count() == src.matches(')').count() }) @@ -481,54 +484,58 @@ fn can_switch_ranges<'tcx>( } // exclusive range plus one: `x..(y+1)` -fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, range: &higher::Range<'tcx>) { check_range_switch( cx, expr, + range, RangeLimits::HalfOpen, y_plus_one, RANGE_PLUS_ONE, "an inclusive range would be more readable", - "..=", ); } // inclusive range minus one: `x..=(y-1)` -fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, range: &higher::Range<'tcx>) { check_range_switch( cx, expr, + range, RangeLimits::Closed, y_minus_one, RANGE_MINUS_ONE, "an exclusive range would be more readable", - "..", ); } /// Check for a `kind` of range in `expr`, check for `predicate` on the end, -/// and emit the `lint` with `msg` and the `operator`. +/// and emit the `lint` with `msg`, suggesting the opposite range limits. fn check_range_switch<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, + range: &higher::Range<'tcx>, kind: RangeLimits, predicate: impl for<'hir> FnOnce(&Expr<'hir>) -> Option<&'hir Expr<'hir>>, lint: &'static Lint, msg: &'static str, - operator: &str, ) { - if let Some(range) = higher::Range::hir(cx, expr) - && let higher::Range { - start, - end: Some(end), - limits, - span, - } = range + if let higher::Range { + start, + end: Some(end), + limits, + span, + } = *range && span.can_be_used_for_suggestions() && limits == kind && let Some(y) = predicate(end) && can_switch_ranges(cx, span.ctxt(), expr, kind, cx.typeck_results().expr_ty(y)) { + // Suggest the opposite range limits to the ones being checked. + let operator = match kind { + RangeLimits::HalfOpen => "..=", + RangeLimits::Closed => "..", + }; span_lint_and_then(cx, lint, span, msg, |diag| { let mut app = Applicability::MachineApplicable; let start = start.map_or(String::new(), |x| { @@ -550,7 +557,7 @@ fn check_range_switch<'tcx>( } } -fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>, range: &higher::Range<'_>) { fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!( get_parent_expr(cx, expr), @@ -580,12 +587,12 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { } } - if let Some(higher::Range { + if let higher::Range { start: Some(start), end: Some(end), limits, span, - }) = higher::Range::hir(cx, expr) + } = *range && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() && let ecx = ConstEvalCtxt::new(cx) diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index d8aa88d48b09d..31c90c591b7c2 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, snippet_opt}; +use clippy_utils::source::{SpanExt, snippet_opt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(format_args) = &expr.kind && !format_args.span.in_external_macro(cx.sess().source_map()) - && format_args.span.check_source_text(cx, |src| src.starts_with('r')) + && format_args.span.check_text(cx, |src| src.starts_with('r')) && let Some(str) = snippet_opt(cx.sess(), format_args.span) && let count_hash = str.bytes().skip(1).take_while(|b| *b == b'#').count() && let Some(str) = str.get(count_hash + 2..str.len() - count_hash - 1) @@ -97,7 +97,7 @@ impl EarlyLintPass for RawStrings { _ => return, } && !expr.span.in_external_macro(cx.sess().source_map()) - && expr.span.check_source_text(cx, |src| src.starts_with(prefix)) + && expr.span.check_text(cx, |src| src.starts_with(prefix)) { self.check_raw_string(cx, lit.symbol.as_str(), expr.span, prefix, max, lit.kind.descr()); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index bfb704dd21719..db3be9e64f147 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{has_drop, is_copy, peel_and_count_ty_refs}; use clippy_utils::{fn_has_unsatisfiable_preds, sym}; use rustc_errors::Applicability; @@ -214,7 +214,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .unwrap_crate_local() .lint_root; - if let Some(snip) = span.get_source_text(cx) + if let Some(snip) = span.get_text(cx) && let Some(dot) = snip.rfind('.') { let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap())); diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 7ebf7ccb7571e..c7f7b47403376 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::HasSession; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, UseKind}; @@ -49,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false) && !is_ignorable_export(item) - && !item.span.in_external_macro(cx.sess().source_map()) + && !item.span.in_external_macro(cx.tcx.sess.source_map()) { let span = item .kind diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index be5e4190e1284..dbad96fa9be49 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{GenericArg, Ty, Unnormalized}; +use rustc_middle::ty::{self, GenericArg, Ty, Unnormalized}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -144,6 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { cx.typing_env(), Unnormalized::new_wip(Ty::new_projection_from_args( cx.tcx, + ty::IsRigid::No, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)]), )), diff --git a/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs index 602093259eaec..2dc03328fef86 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs @@ -144,7 +144,7 @@ fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: S // Also check that within the body of the function there is also no function call // with the same name (since it will result in recursion) - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Path(qpath) = &expr.kind && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id() && let Some(name) = tcx.opt_item_name(def_id) diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs index b6a28571230a6..22d1c21aa43ba 100644 --- a/src/tools/clippy/clippy_lints/src/ref_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast::{BindingMode, Pat, PatKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::in_automatically_derived; +use rustc_hir::{BindingMode, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -29,10 +30,11 @@ declare_clippy_lint! { declare_lint_pass!(RefPatterns => [REF_PATTERNS]); -impl EarlyLintPass for RefPatterns { - fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Ident(BindingMode::REF, _, _) = pat.kind +impl<'tcx> LateLintPass<'tcx> for RefPatterns { + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'tcx>) { + if let PatKind::Binding(BindingMode::REF, _, _, _) = pat.kind && !pat.span.from_expansion() + && !in_automatically_derived(cx.tcx, pat.hir_id) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, REF_PATTERNS, pat.span, "usage of ref pattern", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 80696f0a81b15..837c29c08851b 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::paths; use clippy_utils::paths::PathLookup; use clippy_utils::res::MaybeQPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -190,7 +190,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape }; if let Some((primary, auxiliary, kind)) = parts - && let Some(literal_snippet) = base.get_source_text(cx) + && let Some(literal_snippet) = base.get_text(cx) && let Some(inner) = literal_snippet.get(offset as usize..) // Only convert to native rustc spans if the parsed regex matches the // source snippet exactly, to ensure the span offsets are correct diff --git a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs index 2ec921ed21c7d..c1a909fdde4e1 100644 --- a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs @@ -67,7 +67,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) } } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(cx, expr, |e| { + for_each_expr(cx.tcx, expr, |e| { if let Some(def_id) = fn_def_id(cx, e) && cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index e806123596b85..7fdc03891a889 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -94,7 +94,7 @@ impl SemicolonBlock { // ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call // (5); // } - if remove_span.check_source_text(cx, |src| src.contains(')')) { + if remove_span.check_text(cx, |src| src.contains(')')) { return; } diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs index 4aba49071ee56..03acfb22c6c82 100644 --- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs +++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs @@ -127,7 +127,7 @@ fn find_insert_calls<'tcx>( contains_expr: &OpExpr<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> { - for_each_expr(cx, expr, |e| { + for_each_expr(cx.tcx, expr, |e| { if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert) && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.value, insert_expr.value) diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 4ddf82773d872..5072e39afb8c1 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -199,7 +199,7 @@ pub fn is_local_used_except<'tcx>( id: HirId, except: Option, ) -> bool { - for_each_expr(cx, visitable, |e| { + for_each_expr(cx.tcx, visitable, |e| { if except.is_some_and(|it| it == e.hir_id) { ControlFlow::Continue(Descend::No) } else if e.res_local_id() == Some(id) { diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index aba51114c533a..67c8710b80ec8 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -146,16 +146,24 @@ impl SingleComponentPathImports { // ``` let mut macros = Vec::new(); - let mut import_usage_visitor = ImportUsageVisitor::default(); for item in items { self.track_uses(item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros); + } + + // Only walk the module's AST in search of `self::xxx` paths when there are single + // component imports left to lint, as the visitor recurses into every nested item. + single_use_usages.retain(|usage| !imports_reused_with_self.contains(&usage.name)); + if single_use_usages.is_empty() { + return; + } + + let mut import_usage_visitor = ImportUsageVisitor::default(); + for item in items { import_usage_visitor.visit_item(item); } for usage in single_use_usages { - if !imports_reused_with_self.contains(&usage.name) - && !import_usage_visitor.imports_referenced_with_self.contains(&usage.name) - { + if !import_usage_visitor.imports_referenced_with_self.contains(&usage.name) { self.found.entry(usage.item_id).or_default().push(usage); } } diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index fc5702a358bbc..3d5de785b790c 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::{Range, VecArgs}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_no_std_crate, sym}; use rustc_ast::{LitIntType, LitKind, RangeLimits, UintTy}; @@ -91,7 +91,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let Some(snippet) = span.get_source_text(cx) else { + let Some(snippet) = span.get_text(cx) else { return; }; // `is_from_proc_macro` will skip any `vec![]`. Let's not! diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index d519afba61b53..0f7cbd503bcd8 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -238,20 +238,21 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> /// Does not catch individually moved items fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: Msrv) -> bool { loop { - if let Some(stability) = cx.tcx.lookup_stability(def_id) - && let StabilityLevel::Stable { - since, - allowed_through_unstable_modules: None, - } = stability.level - { - let stable = match since { - StableSince::Version(v) => msrv.meets(cx, v), - StableSince::Current => msrv.current(cx).is_none(), - StableSince::Err(_) => false, - }; - - if !stable { - return false; + if let Some(stability) = cx.tcx.lookup_stability(def_id) { + match stability.level { + // Workaround for items from `core::intrinsics` with a stable export in a different module. + // Not that we ignore the `since` field as we are already accessing the item in question. + StabilityLevel::Stable { + allowed_through_unstable_modules: Some(_), + .. + } => return true, + StabilityLevel::Stable { since, .. } => match since { + StableSince::Version(v) if !msrv.meets(cx, v) => return false, + StableSince::Current if msrv.current(cx).is_none() => return false, + StableSince::Err(_) => return false, + StableSince::Version(_) | StableSince::Current => {}, + }, + StabilityLevel::Unstable { .. } => return false, } } diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index b4eb8977bf0fb..986429ec0829e 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -147,7 +147,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< // We want to retrieve all the comparisons done. // They are ordered in a nested way and so we need to traverse the AST to collect them all. - if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { + if for_each_expr(cx.tcx, body.value, |sub_expr| -> ControlFlow<(), Descend> { match sub_expr.kind { ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { if left.res_local_id() == Some(binding) @@ -230,13 +230,13 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind - && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() - && ty.is_str() && let method_name = method.ident.name && let Some(&(_, pos)) = PATTERN_METHODS .iter() .find(|(array_method_name, _)| *array_method_name == method_name) && let Some(arg) = args.get(pos) + && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() + && ty.is_str() { check_single_char_pattern_lint(cx, arg); diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 7b690b7eb136c..16cf0ecbc0bc7 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro}; use core::hash::{Hash, Hasher}; use itertools::Itertools; @@ -212,10 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { bounds_span = bounds_span.to(bound.span); } - let fixed_trait_snippet = unique_traits - .iter() - .filter_map(|b| b.span.get_source_text(cx)) - .join(" + "); + let fixed_trait_snippet = unique_traits.iter().filter_map(|b| b.span.get_text(cx)).join(" + "); span_lint_and_sugg( cx, @@ -451,7 +448,7 @@ fn rollup_traits<'cx, 'tcx>( let traits = comparable_bounds .iter() - .filter_map(|&(_, span)| span.get_source_text(cx)) + .filter_map(|&(_, span)| span.get_text(cx)) .join(" + "); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs index 42f3e06b7d6f5..89627eded65f5 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, SpanRangeExt as _}; +use clippy_utils::source::SpanExt as _; use rustc_errors::Applicability; use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind}; use rustc_lint::LateContext; @@ -107,15 +107,15 @@ pub(super) fn check<'tcx>( fn ty_cannot_be_named(ty: Ty<'_>) -> bool { matches!( ty.kind(), - ty::Alias(ty::AliasTy { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. } | ty::Inherent { .. }, .. }) ) } -fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - span.with_source_text(sess, |name| { +fn maybe_name_by_expr<'a>(cx: &LateContext<'_>, span: Span, default: &'a str) -> Cow<'a, str> { + span.with_source_text(cx, |name| { (name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into()) }) .flatten() diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 3df16bf71ce7e..e63a524ff5dc0 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -101,6 +101,7 @@ fn get_hir_ty_def_id<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: rustc_hir::Ty<'tcx>) -> Op match ty.kind() { ty::Alias( + _, proj @ ty::AliasTy { kind: ty::Projection { .. }, .. diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 2bf1d8be46536..d35139f2f6b9b 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -393,7 +393,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( } // this should roughly be the reverse of `block_parents_have_safety_comment` - if for_each_expr(cx, expr, |expr| match expr.kind { + if for_each_expr(cx.tcx, expr, |expr| match expr.kind { hir::ExprKind::Block( Block { rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index 735225a9e2cee..13baa378910d9 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -180,32 +180,21 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { let args = std::iter::once(receiver).chain(args.iter()).collect::>(); let arg_indices = get_args_to_check(cx, expr, args.len(), fn_mut_trait, ord_trait, partial_ord_trait); for (i, trait_name) in arg_indices { - match check_arg(cx, args[i]) { - Some((span, None)) => { - span_lint( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ + if let Some((span, last_semi)) = check_arg(cx, args[i]) { + span_lint_and_then( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + format!( + "this closure returns \ the unit type which also implements {trait_name}" - ), - ); - }, - Some((span, Some(last_semi))) => { - span_lint_and_help( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ - the unit type which also implements {trait_name}" - ), - Some(last_semi), - "probably caused by this trailing semicolon", - ); - }, - None => {}, + ), + |diag| { + if let Some(last_semi) = last_semi { + diag.span_help(last_semi, "probably caused by this trailing semicolon"); + } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 6df3e7baa1bca..b9d6c81a152b3 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -1,7 +1,7 @@ use std::iter; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::expr_type_is_certain; use clippy_utils::{is_empty_block, is_expr_default, is_from_proc_macro}; @@ -85,7 +85,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ && block.expr.is_none() && let Some(last_stmt) = block.stmts.iter().last() && let StmtKind::Semi(last_expr) = last_stmt.kind - && let Some(snip) = last_expr.span.get_source_text(cx) + && let Some(snip) = last_expr.span.get_text(cx) { Some((last_stmt.span, snip)) } else { @@ -117,7 +117,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ .filter_map(|arg| get_expr_snippet_with_type_certainty(cx, arg)) .collect(); - if let Some(call_snippet) = expr.span.get_source_text(cx) { + if let Some(call_snippet) = expr.span.get_text(cx) { if arg_snippets_without_redundant_exprs.is_empty() && let suggestions = args_to_recover .iter() diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs index 8d4f1a153def8..4112e1b6ca63f 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::approx_ty_size; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; use rustc_span::Symbol; @@ -81,8 +81,13 @@ impl UnnecessaryBoxReturns { // It's sometimes useful to return Box if T is unsized, so don't lint those. // Also, don't lint if we know that T is very large, in which case returning - // a Box may be beneficial. - if boxed_ty.is_sized(cx.tcx, cx.typing_env()) && approx_ty_size(cx, boxed_ty) <= self.maximum_size { + // a Box may be beneficial. When the size depends on generic parameters + // (e.g. `[T; N]`) it cannot be determined here, so don't lint that either, as + // the `Box` may be a deliberate choice to avoid copying a large value. + if boxed_ty.is_sized(cx.tcx, cx.typing_env()) + && let Ok(layout) = cx.layout_of(boxed_ty) + && layout.size.bytes() <= self.maximum_size + { span_lint_and_then( cx, UNNECESSARY_BOX_RETURNS, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs index 75b0b7b10687a..60a6688927ab5 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index bfbfd0903bbe8..0a6f3f5992ece 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_def_id_trait_method; -use clippy_utils::source::{HasSession, snippet_with_applicability, walk_span_to_context}; +use clippy_utils::source::{snippet_with_applicability, walk_span_to_context}; use clippy_utils::usage::is_todo_unimplemented_stub; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -303,7 +303,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // evaluate the expression, to immediately evaluate the expression. let mut app = Applicability::MaybeIncorrect; - let async_span = cx.sess().source_map().span_extend_while_whitespace(async_span); + let async_span = cx.tcx.sess.source_map().span_extend_while_whitespace(async_span); let signature_snippet = snippet_with_applicability(cx, signature_span, "_", &mut app); let tail_snippet = snippet_with_applicability(cx, tail_span, "_", &mut app).to_string(); diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index d503bd3379b08..bb2ad9f92157c 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; +use clippy_utils::source::{SpanExt, position_before_rarrow}; use clippy_utils::{is_never_expr, is_unit_expr}; use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index d6db088ba76c0..f487b5777b77a 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -112,7 +112,7 @@ fn into_iter_bound<'tcx>( } })); - let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args).skip_norm_wip(); + let predicate = EarlyBinder::bind(cx.tcx, tr).instantiate(cx.tcx, args).skip_norm_wip(); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); if !cx .tcx @@ -132,8 +132,8 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { if let ExprKind::MethodCall(name, recv, [], _) = expr.kind - && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && name.ident.name == sym::into_iter + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) { Some(recv) } else { @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) && name.ident.name == sym::into { + if name.ident.name == sym::into && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_modulo_regions(a, b) { @@ -393,8 +393,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) - && name.ident.name == sym::try_into + if name.ident.name == sym::try_into + && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) && a.is_diag_item(cx, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/useless_vec.rs b/src/tools/clippy/clippy_lints/src/useless_vec.rs index 7b6122213d608..cdb757ed89e23 100644 --- a/src/tools/clippy/clippy_lints/src/useless_vec.rs +++ b/src/tools/clippy/clippy_lints/src/useless_vec.rs @@ -7,7 +7,7 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_parent_expr, higher, is_in_test, span_contains_comment}; @@ -285,10 +285,10 @@ impl SuggestedType { assert!(args_span.is_none_or(|s| !s.from_expansion())); assert!(len_span.is_none_or(|s| !s.from_expansion())); - let maybe_args = args_span.map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")); + let maybe_args = args_span.map(|sp| sp.get_text(cx).expect("spans are always crate-local")); let maybe_args = maybe_args.as_deref().unwrap_or_default(); let maybe_len = len_span - .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) + .map(|sp| sp.get_text(cx).expect("spans are always crate-local")) .map(|st| format!("; {st}")) .unwrap_or_default(); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index c60d84785a229..2bcf369fb3334 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -6,11 +6,11 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_hir::{ - self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, + self as hir, BindingMode, Body, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{FloatTy, IntTy, UintTy}; +use rustc_middle::ty::{FloatTy, IntTy, TypeckResults, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; @@ -137,15 +137,31 @@ impl<'tcx> LateLintPass<'tcx> for Author { fn check_item(cx: &LateContext<'_>, hir_id: HirId) { if let Some(body) = cx.tcx.hir_maybe_body_owned_by(hir_id.expect_owner().def_id) { - check_node(cx, hir_id, |v| { - v.expr(&v.bind("expr", body.value)); - }); + check_node_with_body( + cx, + hir_id, + |v| { + v.expr(&v.bind("expr", body.value)); + }, + Some(body), + ); } } fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { + check_node_with_body(cx, hir_id, f, None); +} + +/// Check the node at `hir_id`, in the context of `body` or the default from `cx` if none is given. +fn check_node_with_body( + cx: &LateContext<'_>, + hir_id: HirId, + f: impl Fn(&PrintVisitor<'_, '_>), + body: Option<&Body<'_>>, +) { if has_attr(cx, hir_id) { - f(&PrintVisitor::new(cx)); + let typeck_results = body.map_or_else(|| cx.typeck_results(), |body| cx.tcx.typeck_body(body.id())); + f(&PrintVisitor::new(cx, typeck_results)); println!("{{"); println!(" // report your lint here"); println!("}}"); @@ -199,6 +215,7 @@ impl Display for OptionPat { struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, + typeck_results: &'tcx TypeckResults<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names ids: Cell>, @@ -207,9 +224,10 @@ struct PrintVisitor<'a, 'tcx> { } impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>) -> Self { Self { cx, + typeck_results, ids: Cell::default(), first: Cell::new(true), } @@ -291,7 +309,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn maybe_path<'p>(&self, path: &Binding>) { - if let Some(id) = path.value.res(self.cx).opt_def_id() + if let Some(id) = path.value.res(self.typeck_results).opt_def_id() && !id.is_local() { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs index 6629a67f78bd4..6401b48f70b05 100644 --- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs @@ -1,18 +1,18 @@ use clippy_utils::macros::FormatArgsStorage; -use clippy_utils::source::SpanRangeExt; -use itertools::Itertools; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; use rustc_data_structures::fx::FxHashMap; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, hygiene}; -use std::iter::once; +use rustc_span::source_map::SourceMap; +use rustc_span::{Span, SpanData}; use std::mem; /// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes pub struct FormatArgsCollector { format_args: FxHashMap, + parent_spans: Vec, storage: FormatArgsStorage, } @@ -20,6 +20,7 @@ impl FormatArgsCollector { pub fn new(storage: FormatArgsStorage) -> Self { Self { format_args: FxHashMap::default(), + parent_spans: Vec::new(), storage, } } @@ -30,7 +31,7 @@ impl_lint_pass!(FormatArgsCollector => []); impl EarlyLintPass for FormatArgsCollector { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(args) = &expr.kind { - if has_span_from_proc_macro(cx, args) { + if self.has_span_from_external_macro(cx.sess().source_map(), expr.span, args) { return; } @@ -43,53 +44,99 @@ impl EarlyLintPass for FormatArgsCollector { } } -/// Detects if the format string or an argument has its span set by a proc macro to something inside -/// a macro callsite, e.g. -/// -/// ```ignore -/// println!(some_proc_macro!("input {}"), a); -/// ``` -/// -/// Where `some_proc_macro` expands to -/// -/// ```ignore -/// println!("output {}", a); -/// ``` -/// -/// But with the span of `"output {}"` set to the macro input -/// -/// ```ignore -/// println!(some_proc_macro!("input {}"), a); -/// // ^^^^^^^^^^ -/// ``` -fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool { - let ctxt = args.span.ctxt(); +impl FormatArgsCollector { + /// Detects if the format string or an argument has its span set by a proc macro to something + /// inside a macro callsite, e.g. + /// + /// ```ignore + /// println!(some_proc_macro!("input {}"), a); + /// ``` + /// + /// Where `some_proc_macro` expands to + /// + /// ```ignore + /// println!("output {}", a); + /// ``` + /// + /// But with the span of `"output {}"` set to the macro input + /// + /// ```ignore + /// println!(some_proc_macro!("input {}"), a); + /// // ^^^^^^^^^^ + /// ``` + fn has_span_from_external_macro(&mut self, sm: &SourceMap, fmt_sp: Span, args: &FormatArgs) -> bool { + let mut fmt_sp = fmt_sp.data(); + + // Find the first macro call that contains the format string. + let arg_sp = if let Some(arg_sp) = walk_span_to_context(args.span, fmt_sp.ctxt) { + arg_sp.data() + } else { + // Try to find a common parent for the format call and the format string. + self.parent_spans.clear(); + // `fmt_sp.ctxt` isn't a parent of the format string so don't add it to the + // search. The first iteration will always run since it can't be the root. + while !fmt_sp.ctxt.is_root() { + fmt_sp = fmt_sp.ctxt.outer_expn_data().call_site.data(); + self.parent_spans.push(fmt_sp); + } + let mut arg_sp = args.span.data(); + // Note: A parent span will always eventually be found since the root context + // is an ancestor of all contexts. + loop { + match self.parent_spans.iter().find(|s| s.ctxt == arg_sp.ctxt) { + Some(call_sp) if call_sp.lo <= arg_sp.lo && arg_sp.hi <= call_sp.hi => { + fmt_sp = *call_sp; + break arg_sp; + }, + // If the string isn't within the call span we some macro stuff we can't + // easily interpret. + Some(_) => return true, + None => arg_sp = arg_sp.ctxt.outer_expn_data().call_site.data(), + } + } + }; + if fmt_sp.ctxt.in_external_macro(sm) { + return true; + } + let Some(src) = arg_sp.get_source_range(sm) else { + return true; + }; + let Some(src_text) = src.sf.src.as_ref().map(|x| &***x) else { + return true; + }; - // `format!("{} {} {c}", "one", "two", c = "three")` - // ^^^^^ ^^^^^ ^^^^^^^ - let argument_span = args - .arguments - .explicit_args() - .iter() - .map(|argument| hygiene::walk_chain(argument.expr.span, ctxt)); + // Check the spans between the format string and the arguments and between each argument. + args.arguments + .explicit_args() + .iter() + .try_fold(src.range.end, |start, arg| { + let expr_sp = walk_span_to_context(arg.expr.span, fmt_sp.ctxt)?.data(); + let expr_start = (expr_sp.lo.0 - src.sf.start_pos.0) as usize; + let expr_end = (expr_sp.hi.0 - src.sf.start_pos.0) as usize; + let mut tks = tokenize(src_text.get(start..expr_start)?, FrontmatterAllowed::No) + .map(|x| x.kind) + .filter(|x| { + !matches!( + x, + TokenKind::LineComment { doc_style: None } + | TokenKind::BlockComment { + doc_style: None, + terminated: true + } + | TokenKind::Whitespace + ) + }); - // `format!("{} {} {c}", "one", "two", c = "three")` - // ^^ ^^ ^^^^^^ - !once(args.span) - .chain(argument_span) - .tuple_windows() - .map(|(start, end)| start.between(end)) - .all(|sp| { - sp.check_source_text(cx, |src| { - // text should be either `, name` or `, name =` - let mut iter = tokenize(src, FrontmatterAllowed::No).filter(|t| { - !matches!( - t.kind, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }); - iter.next().is_some_and(|t| matches!(t.kind, TokenKind::Comma)) - && iter.all(|t| matches!(t.kind, TokenKind::Ident | TokenKind::Eq)) + // `,` or `, ident =` + let matches = matches!(tks.next(), Some(TokenKind::Comma)) + && match tks.next() { + Some(TokenKind::Ident) => matches!(tks.next(), Some(TokenKind::Eq)), + Some(_) => false, + None => true, + } + && tks.next().is_none(); + matches.then_some(expr_end) }) - }) + .is_none() + } } diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs index 74ffeb629d2be..9cefc54598b78 100644 --- a/src/tools/clippy/clippy_lints/src/visibility.rs +++ b/src/tools/clippy/clippy_lints/src/visibility.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{Item, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -152,5 +152,5 @@ impl EarlyLintPass for Visibility { } fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> bool { - !span.check_source_text(cx, |src| src.starts_with("pub")) + !span.check_text(cx, |src| src.starts_with("pub")) } diff --git a/src/tools/clippy/clippy_lints/src/write/literal.rs b/src/tools/clippy/clippy_lints/src/write/literal.rs index 699ac7ea7a5cd..df31fad8615ed 100644 --- a/src/tools/clippy/clippy_lints/src/write/literal.rs +++ b/src/tools/clippy/clippy_lints/src/write/literal.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::format_arg_removal_span; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_ast::token::LitKind; use rustc_ast::{ @@ -48,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) && let Some(arg) = format_args.arguments.by_index(index) && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind && !arg.expr.span.from_expansion() - && let Some(value_string) = arg.expr.span.get_source_text(cx) + && let Some(value_string) = arg.expr.span.get_text(cx) { let (replacement, replace_raw) = match lit.kind { LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { @@ -71,7 +71,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) _ => continue, }; - let Some(format_string_snippet) = format_args.span.get_source_text(cx) else { + let Some(format_string_snippet) = format_args.span.get_text(cx) else { continue; }; let format_string_is_raw = format_string_snippet.starts_with('r'); diff --git a/src/tools/clippy/clippy_lints/src/write/with_newline.rs b/src/tools/clippy/clippy_lints/src/write/with_newline.rs index e4b51da3cadcf..15cc29818aec4 100644 --- a/src/tools/clippy/clippy_lints/src/write/with_newline.rs +++ b/src/tools/clippy/clippy_lints/src/write/with_newline.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::MacroCall; -use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::source::{SpanExt, expand_past_previous_comma}; use clippy_utils::sym; use rustc_ast::{FormatArgs, FormatArgsPiece}; use rustc_errors::Applicability; @@ -48,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: format!("using `{name}!()` with a format string that ends in a single newline"), |diag| { let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); - let Some(format_snippet) = format_string_span.get_source_text(cx) else { + let Some(format_snippet) = format_string_span.get_text(cx) else { return; }; diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs index 8e2166858fec8..f75a9f67d0563 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lib.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -63,15 +63,23 @@ static LINTS: &[&Lint] = &[ pub fn register_lints(store: &mut LintStore) { store.register_lints(LINTS); - store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); - store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); - store.register_late_pass(|_| Box::new(collapsible_span_lint_calls::CollapsibleCalls)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath)); - store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); - store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); - store.register_late_pass(|_| Box::new(unusual_names::UnusualNames)); - store.register_late_pass(|_| Box::new(repeated_is_diagnostic_item::RepeatedIsDiagnosticItem)); + store.register_early_pass(Box::new(|| { + Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths) + })); + store.register_early_pass(Box::new(|| Box::new(produce_ice::ProduceIce))); + store.register_late_pass(Box::new(|_| Box::new(collapsible_span_lint_calls::CollapsibleCalls))); + store.register_late_pass(Box::new(|_| Box::::default())); + store.register_late_pass(Box::new(|_| { + Box::::default() + })); + store.register_late_pass(Box::new(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath))); + store.register_late_pass(Box::new(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass))); + store.register_late_pass(Box::new(|_| Box::new(msrv_attr_impl::MsrvAttrImpl))); + store.register_late_pass(Box::new(|_| { + Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new()) + })); + store.register_late_pass(Box::new(|_| Box::new(unusual_names::UnusualNames))); + store.register_late_pass(Box::new(|_| { + Box::new(repeated_is_diagnostic_item::RepeatedIsDiagnosticItem) + })); } diff --git a/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs b/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs index b300dfa27b0eb..e0148c79477c0 100644 --- a/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs +++ b/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs @@ -541,7 +541,7 @@ fn extract_nested_is_diag_item<'tcx>( cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, ) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { - for_each_expr(cx, cond, |cond_part| { + for_each_expr(cx.tcx, cond, |cond_part| { if let Some(res) = extract_is_diag_item(cx, cond_part) { ControlFlow::Break((cond_part.span, res)) } else { @@ -554,7 +554,7 @@ fn extract_nested_is_diagnostic_item<'tcx>( cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, ) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { - for_each_expr(cx, cond, |cond_part| { + for_each_expr(cx.tcx, cond, |cond_part| { if let Some(res) = extract_is_diagnostic_item(cx, cond_part) { ControlFlow::Break((cond_part.span, res)) } else { diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 95b8bdde5cd91..f322d03f18795 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2026-06-11 +nightly-2026-06-25 ``` diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 8df82489d6a3f..7718afc5f0222 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,6 +1,6 @@ //! Utility functions for attributes, including Clippy's built-in ones -use crate::source::SpanRangeExt; +use crate::source::SpanExt; use crate::{sym, tokenize_with_text}; use rustc_ast::attr::AttributeExt; use rustc_errors::Applicability; @@ -37,8 +37,6 @@ pub fn check_clippy_attr(sess: &Session, attr: &A) { | sym::version | sym::cognitive_complexity | sym::dump - | sym::disallowed_profile - | sym::disallowed_profiles | sym::msrv | sym::has_significant_drop | sym::format_args => {}, @@ -107,7 +105,12 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { /// Checks whether the given span contains a `#[cfg(..)]` attribute pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { - s.check_source_text(cx, |src| { + s.check_text(cx, |src| { + // PERF: A `#[cfg]` needs a literal `#`, so skip the lexer when the source has none. + if !src.contains('#') { + return false; + } + let mut iter = tokenize_with_text(src); // Search for the token sequence [`#`, `[`, `cfg`] diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 098be98f3672c..11e6e32ba6c56 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -5,7 +5,7 @@ #![expect(clippy::float_cmp)] use crate::res::MaybeDef; -use crate::source::{SpanRangeExt, walk_span_to_context}; +use crate::source::{SpanExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, sym, unsext}; use rustc_abi::Size; @@ -522,9 +522,9 @@ pub fn eval_int(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { /// /// See the module level documentation for some context. pub struct ConstEvalCtxt<'tcx> { - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, + pub tcx: TyCtxt<'tcx>, + pub typing_env: ty::TypingEnv<'tcx>, + pub typeck: &'tcx TypeckResults<'tcx>, source: Cell, ctxt: Cell, } @@ -885,12 +885,22 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => return None, }; + let args = self.typeck.node_args(id); + + if !args.is_empty() { + let owner_def_id = self.typeck.hir_owner.def_id.to_def_id(); + let identity_args = ty::GenericArgs::identity_for_item(self.tcx, owner_def_id); + // Don't try to fully evaluate consts inside code whose bounds can't be satisfied. + if self + .tcx + .instantiate_and_check_impossible_predicates((owner_def_id, identity_args)) + { + return None; + } + } + self.tcx - .const_eval_resolve( - self.typing_env, - mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), - qpath.span(), - ) + .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(did, args), qpath.span()) .ok() } @@ -934,7 +944,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) && let expr_lo = expr_span.lo() && expr_lo >= span.lo - && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx) + && let Some(src) = (span.lo..expr_lo).get_source_range(self.tcx) && let Some(src) = src.as_str() { use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; diff --git a/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs b/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs deleted file mode 100644 index 98a498981b3b5..0000000000000 --- a/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::sym; -use rustc_ast::ast::{LitKind, MetaItemInner}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::smallvec::SmallVec; -use rustc_hir::{Attribute, HirId}; -use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; - -/// One profile name referenced by a `#[clippy::disallowed_profile(...)]` or -/// `#[clippy::disallowed_profiles(...)]` attribute on an item. -/// -/// A single attribute produces one `ProfileEntry` per string argument. The entry records which -/// attribute variant introduced it, the profile name, and the span of that string literal so -/// diagnostics (e.g. "unknown profile") can point at the exact argument. -#[derive(Copy, Clone)] -pub struct ProfileEntry { - pub attr_name: Symbol, - pub name: Symbol, - pub span: Span, -} - -/// The set of profiles active at some `HirId`, obtained by walking up the HIR from that id and -/// collecting the first ancestor that carries a `#[clippy::disallowed_profile(s)]` attribute. -/// -/// An empty selection is represented by `None` at the call site; a `ProfileSelection` is always -/// non-empty. -#[derive(Clone)] -pub struct ProfileSelection { - entries: SmallVec<[ProfileEntry; 2]>, -} - -impl ProfileSelection { - pub fn new(entries: SmallVec<[ProfileEntry; 2]>) -> Self { - Self { entries } - } - - pub fn is_empty(&self) -> bool { - self.entries.is_empty() - } - - pub fn iter(&self) -> impl Iterator { - self.entries.iter() - } -} - -#[derive(Default)] -pub struct ProfileResolver { - cache: FxHashMap>, -} - -impl ProfileResolver { - pub fn active_profiles(&mut self, cx: &LateContext<'_>, hir_id: HirId) -> Option<&ProfileSelection> { - // NOTE: The `contains_key`+`get` dance is intentional: using only `get` here triggers borrowck - // errors because we need to mutate `self.cache` on cache misses. - if self.cache.contains_key(&hir_id) { - return self.cache.get(&hir_id).and_then(|selection| selection.as_ref()); - } - - let (resolved, visited) = self.resolve(cx, hir_id); - - for id in visited { - self.cache.entry(id).or_insert_with(|| resolved.clone()); - } - self.cache.insert(hir_id, resolved); - - self.cache.get(&hir_id).and_then(|selection| selection.as_ref()) - } - - fn resolve(&self, cx: &LateContext<'_>, start: HirId) -> (Option, SmallVec<[HirId; 8]>) { - let mut visited = SmallVec::<[HirId; 8]>::new(); - let mut current = Some(start); - - while let Some(id) = current { - if let Some(cached) = self.cache.get(&id) { - return (cached.clone(), visited); - } - - visited.push(id); - - if let Some(selection) = profiles_from_attrs(cx, cx.tcx.hir_attrs(id)) { - return (Some(selection), visited); - } - - if id == rustc_hir::CRATE_HIR_ID { - current = None; - } else { - current = Some(cx.tcx.parent_hir_id(id)); - } - } - - (None, visited) - } -} - -fn profiles_from_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) -> Option { - let mut entries = SmallVec::<[ProfileEntry; 2]>::new(); - - for attr in attrs { - let path = attr.path(); - if path.len() != 2 || path[0] != sym::clippy { - continue; - } - - let name = path[1]; - if name != sym::disallowed_profile && name != sym::disallowed_profiles { - continue; - } - - let attr_label = if name == sym::disallowed_profiles { - "`clippy::disallowed_profiles`" - } else { - "`clippy::disallowed_profile`" - }; - - let Some(items) = attr.meta_item_list() else { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), format!("{attr_label} expects string arguments")) - .emit(); - continue; - }; - - if items.is_empty() { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), format!("{attr_label} expects at least one profile name")) - .emit(); - continue; - } - - if name == sym::disallowed_profile && items.len() != 1 { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), "use `clippy::disallowed_profiles` for multiple profiles") - .emit(); - } - - for item in items { - match literal_symbol(&item) { - Some((symbol, span)) => entries.push(ProfileEntry { - attr_name: name, - name: symbol, - span, - }), - None => emit_string_error(cx, &item), - } - } - } - - if entries.is_empty() { - None - } else { - Some(ProfileSelection::new(entries)) - } -} - -fn literal_symbol(item: &MetaItemInner) -> Option<(Symbol, Span)> { - match item { - MetaItemInner::Lit(lit) => { - let LitKind::Str(symbol, _) = lit.kind else { return None }; - Some((symbol, lit.span)) - }, - MetaItemInner::MetaItem(_) => None, - } -} - -fn emit_string_error(cx: &LateContext<'_>, item: &MetaItemInner) { - let span = match item { - MetaItemInner::Lit(lit) => lit.span, - MetaItemInner::MetaItem(meta) => meta.span, - }; - cx.tcx - .sess - .dcx() - .struct_span_err(span, "expected string literal profile name") - .emit(); -} diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index cd609980b149b..1c133fccdb308 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -11,15 +11,15 @@ use crate::consts::{ConstEvalCtxt, FullInt}; use crate::sym; -use crate::ty::{all_predicates_of, is_copy}; +use crate::ty::all_predicates_of; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; use std::{cmp, ops}; @@ -48,17 +48,17 @@ impl ops::BitOrAssign for EagernessSuggestion { } /// Determine the eagerness of the given function call. -fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { +fn fn_eagerness(tcx: TyCtxt<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; - let ty = match cx.tcx.impl_of_assoc(fn_id) { - Some(id) => cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(), + let ty = match tcx.impl_of_assoc(fn_id) { + Some(id) => tcx.type_of(id).instantiate_identity().skip_norm_wip(), None => return Lazy, }; if (matches!(name, sym::is_empty | sym::len) || name.as_str().starts_with("as_")) && have_one_arg { if matches!( - cx.tcx.crate_name(fn_id.krate), + tcx.crate_name(fn_id.krate), sym::std | sym::core | sym::alloc | sym::proc_macro ) { Eager @@ -71,22 +71,20 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: // Due to the limited operations on these types functions should be fairly cheap. if def.variants().iter().flat_map(|v| v.fields.iter()).any(|x| { matches!( - cx.tcx - .type_of(x.did) + tcx.type_of(x.did) .instantiate_identity() .skip_norm_wip() .peel_refs() .kind(), ty::Param(_) ) - }) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, + }) && all_predicates_of(tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { + ty::ClauseKind::Trait(pred) => tcx.trait_def(pred.trait_ref.def_id).is_marker, _ => true, }) && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) { // Limit the function to either `(self) -> bool` or `(&self) -> bool` - match &**cx - .tcx + match &**tcx .fn_sig(fn_id) .instantiate_identity() .skip_norm_wip() @@ -104,14 +102,12 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: } } -fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool { +fn res_has_significant_drop(res: Res, ecx: &ConstEvalCtxt<'_>, e: &Expr<'_>) -> bool { if let Res::Def(DefKind::Ctor(..) | DefKind::Variant | DefKind::Enum | DefKind::Struct, _) | Res::SelfCtor(_) | Res::SelfTyAlias { .. } = res { - cx.typeck_results() - .expr_ty(e) - .has_significant_drop(cx.tcx, cx.typing_env()) + ecx.typeck.expr_ty(e).has_significant_drop(ecx.tcx, ecx.typing_env) } else { false } @@ -119,12 +115,12 @@ fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> boo #[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { - struct V<'cx, 'tcx> { - cx: &'cx LateContext<'tcx>, + struct V<'tcx> { + ecx: ConstEvalCtxt<'tcx>, eagerness: EagernessSuggestion, } - impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { use EagernessSuggestion::{ForceNoChange, Lazy, NoChange}; if self.eagerness == ForceNoChange { @@ -134,8 +130,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Autoderef through a user-defined `Deref` impl can have side-effects, // so don't suggest changing it. if self - .cx - .typeck_results() + .ecx + .typeck .expr_adjustments(e) .iter() .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) @@ -152,58 +148,62 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .. }, args, - ) => match self.cx.qpath_res(path, hir_id) { + ) => match self.ecx.typeck.qpath_res(path, hir_id) { res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => { - if res_has_significant_drop(res, self.cx, e) { + if res_has_significant_drop(res, &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, - Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (), + Res::Def(_, id) if self.ecx.tcx.is_promotable_const_fn(id) => (), // No need to walk the arguments here, `is_const_evaluatable` already did - Res::Def(..) if is_const_evaluatable(self.cx, e) => { + Res::Def(..) if is_const_evaluatable(self.ecx.tcx, self.ecx.typeck, e) => { self.eagerness |= NoChange; return; }, Res::Def(_, id) => match path { QPath::Resolved(_, p) => { - self.eagerness |= - fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, !args.is_empty()); + self.eagerness |= fn_eagerness( + self.ecx.tcx, + id, + p.segments.last().unwrap().ident.name, + !args.is_empty(), + ); }, QPath::TypeRelative(_, name) => { - self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty()); + self.eagerness |= fn_eagerness(self.ecx.tcx, id, name.ident.name, !args.is_empty()); }, }, _ => self.eagerness = Lazy, }, // No need to walk the arguments here, `is_const_evaluatable` already did - ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => { + ExprKind::MethodCall(..) if is_const_evaluatable(self.ecx.tcx, self.ecx.typeck, e) => { self.eagerness |= NoChange; return; }, #[expect(clippy::match_same_arms)] // arm pattern can't be merged due to `ref`, see rust#105778 ExprKind::Struct(path, ..) => { - if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + if res_has_significant_drop(self.ecx.typeck.qpath_res(path, e.hir_id), &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, ExprKind::Path(ref path) => { - if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + if res_has_significant_drop(self.ecx.typeck.qpath_res(path, e.hir_id), &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, ExprKind::MethodCall(name, ..) => { self.eagerness |= self - .cx - .typeck_results() + .ecx + .typeck .type_dependent_def_id(e.hir_id) - .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, true)); + .map_or(Lazy, |id| fn_eagerness(self.ecx.tcx, id, name.ident.name, true)); }, ExprKind::Index(_, e, _) => { - let ty = self.cx.typeck_results().expr_ty_adjusted(e); - if is_copy(self.cx, ty) && !ty.is_ref() { + let ty = self.ecx.typeck.expr_ty_adjusted(e); + if self.ecx.tcx.type_is_copy_modulo_regions(self.ecx.typing_env, ty) && !ty.is_ref() { self.eagerness |= NoChange; } else { self.eagerness = Lazy; @@ -211,24 +211,19 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, // `-i32::MIN` panics with overflow checks - ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => { + ExprKind::Unary(UnOp::Neg, right) if self.ecx.eval(right).is_none() => { self.eagerness |= NoChange; }, // Custom `Deref` impl might have side effects - ExprKind::Unary(UnOp::Deref, e) - if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => - { + ExprKind::Unary(UnOp::Deref, e) if self.ecx.typeck.expr_ty(e).builtin_deref(true).is_none() => { self.eagerness |= NoChange; }, // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe. - ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(UnOp::Deref, e) if !self.ecx.typeck.expr_ty(e).is_raw_ptr() => (), ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange, ExprKind::Unary(_, e) - if matches!( - self.cx.typeck_results().expr_ty(e).kind(), - ty::Bool | ty::Int(_) | ty::Uint(_), - ) => {}, + if matches!(self.ecx.typeck.expr_ty(e).kind(), ty::Bool | ty::Int(_) | ty::Uint(_),) => {}, // `>>` and `<<` panic when the right-hand side is greater than or equal to the number of bits in the // type of the left-hand side, or is negative. @@ -236,18 +231,16 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // overflow with constants, the compiler emits an error for it and the programmer will have to fix it. // Thus, we would realistically only delay the lint. ExprKind::Binary(op, _, right) - if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) - && ConstEvalCtxt::new(self.cx).eval(right).is_none() => + if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) && self.ecx.eval(right).is_none() => { self.eagerness |= NoChange; }, ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Div | BinOpKind::Rem) - && let right_ty = self.cx.typeck_results().expr_ty(right) - && let ecx = ConstEvalCtxt::new(self.cx) - && let left = ecx.eval(left) - && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty)) + && let right_ty = self.ecx.typeck.expr_ty(right) + && let left = self.ecx.eval(left) + && let right = self.ecx.eval(right).and_then(|c| c.int_value(self.ecx.tcx, right_ty)) && matches!( (left, right), // `1 / x`: x might be zero @@ -265,16 +258,14 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // error and it's good to have the eagerness warning up front when the user fixes the logic error. ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul) - && !self.cx.typeck_results().expr_ty(e).is_floating_point() - && let ecx = ConstEvalCtxt::new(self.cx) - && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) => + && !self.ecx.typeck.expr_ty(e).is_floating_point() + && (self.ecx.eval(left).is_none() || self.ecx.eval(right).is_none()) => { self.eagerness |= NoChange; }, ExprKind::Binary(_, lhs, rhs) - if self.cx.typeck_results().expr_ty(lhs).is_primitive() - && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {}, + if self.ecx.typeck.expr_ty(lhs).is_primitive() && self.ecx.typeck.expr_ty(rhs).is_primitive() => {}, // Can't be moved into a closure ExprKind::Break(..) @@ -322,7 +313,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS } let mut v = V { - cx, + ecx: ConstEvalCtxt::new(cx), eagerness: EagernessSuggestion::Eager, }; v.visit_expr(e); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 38724a1053f65..050e43344d0c4 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1,6 +1,6 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; -use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; +use crate::source::{SpanExt, SpanRange, walk_span_to_context}; use crate::{sym, tokenize_with_text}; use core::mem; use rustc_ast::ast; @@ -1036,7 +1036,7 @@ fn reduce_exprkind<'hir>( // `{}` => `()` ([], None) if block.span.ctxt() != eval_ctxt - || block.span.check_source_text(cx, |src| { + || block.span.check_text(cx, |src| { tokenize(src, FrontmatterAllowed::No) .map(|t| t.kind) .filter(|t| { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index c208b8b97506f..0264c68776085 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -46,7 +46,6 @@ mod check_proc_macro; pub mod comparisons; pub mod consts; pub mod diagnostics; -pub mod disallowed_profiles; pub mod eager_or_lazy; pub mod higher; mod hir_utils; @@ -116,14 +115,14 @@ use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span, SyntaxContext}; -use source::{SpanRangeExt, walk_span_to_context}; +use source::{SpanExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; -use crate::consts::ConstEvalCtxt; use crate::higher::Range; use crate::msrvs::Msrv; use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use crate::source::HasSourceMap; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -1329,59 +1328,23 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { }) } -/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. -/// -/// For the lower bound, this means that: -/// - either there is none -/// - or it is the smallest value that can be represented by the range's integer type -/// -/// For the upper bound, this means that: -/// - either there is none -/// - or it is the largest value that can be represented by the range's integer type and is -/// inclusive -/// - or it is a call to some container's `len` method and is exclusive, and the range is passed to -/// a method call on that same container (e.g. `v.drain(..v.len())`) -/// -/// If the given `Expr` is not some kind of range, the function returns `false`. -pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); +/// Checks whether the given `Expr` is a range over the entire container. +pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option, expr: &Expr<'_>) -> bool { if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) { - let start_is_none_or_min = start.is_none_or(|start| { - if let rustc_ty::Adt(_, subst) = ty.kind() - && let bnd_ty = subst.type_at(0) - && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) - { - start_const.is_numeric_min(cx.tcx, bnd_ty) - } else { - false - } - }); - let end_is_none_or_max = end.is_none_or(|end| match limits { - RangeLimits::Closed => { - if let rustc_ty::Adt(_, subst) = ty.kind() - && let bnd_ty = subst.type_at(0) - && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) - { - end_const.is_numeric_max(cx.tcx, bnd_ty) - } else { - false - } - }, - RangeLimits::HalfOpen => { - if let Some(container_path) = container_path - && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind - && name.ident.name == sym::len - && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind + start.is_none_or(|start| is_integer_literal(start, 0)) + && end.is_none_or(|end| { + if limits == RangeLimits::HalfOpen + && let Some(container) = container + && let ExprKind::MethodCall(seg, recv, [], _) = end.kind { - container_path.res == path.res + seg.ident.name == sym::len && recv.res_local_id() == Some(container) } else { false } - }, - }); - return start_is_none_or_min && end_is_none_or_max; + }) + } else { + false } - false } /// Checks whether the given expression is a constant literal of the given value. @@ -2880,8 +2843,8 @@ pub fn tokenize_with_text(s: &str) -> impl Iterator bool { - span.check_source_text(cx, |snippet| { +pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool { + span.check_text(sm, |snippet| { tokenize(snippet, FrontmatterAllowed::No).any(|token| { matches!( token.kind, @@ -2895,8 +2858,8 @@ pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool { /// token, including comments unless `skip_comments` is set. /// This is useful to determine if there are any actual code tokens in the span that are omitted in /// the late pass, such as platform-specific code. -pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool { - span.check_source_text(cx, |snippet| { +pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool { + span.check_text(sm, |snippet| { tokenize_with_text(snippet).any(|(token, _, _)| match token { TokenKind::Whitespace => false, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments, @@ -2904,18 +2867,19 @@ pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, sk }) }) } + /// Returns all the comments a given span contains /// /// Comments are returned wrapped with their relevant delimiters -pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String { - span_extract_comments(cx, span).join("\n") +pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String { + span_extract_comments(sm, span).join("\n") } /// Returns all the comments a given span contains. /// /// Comments are returned wrapped with their relevant delimiters. -pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec { - span.with_source_text(cx, |snippet| { +pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec { + span.with_source_text(sm, |snippet| { tokenize_with_text(snippet) .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) .map(|(_, s, _)| s.to_string()) @@ -3293,7 +3257,7 @@ fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: | rustc_ty::RawPtr(_, _) | rustc_ty::Ref(..) | rustc_ty::Slice(_) - | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args).skip_norm_wip()), + | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(tcx, ty).instantiate(tcx, args).skip_norm_wip()), _ => ty.to_string(), } } diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 5151351ed2ade..d9b34ebfe21e4 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -3,8 +3,9 @@ use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; use rustc_attr_parsing::parse_version; use rustc_data_structures::smallvec::SmallVec; -use rustc_hir::RustcVersion; +use rustc_hir::{HirId, RustcVersion}; use rustc_lint::LateContext; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::Symbol; use serde::Deserialize; @@ -26,7 +27,7 @@ msrv_aliases! { 1,97,0 { ISOLATE_LOWEST_ONE } 1,93,0 { VEC_DEQUE_POP_BACK_IF, VEC_DEQUE_POP_FRONT_IF } 1,91,0 { DURATION_FROM_MINUTES_HOURS } - 1,88,0 { LET_CHAINS } + 1,88,0 { LET_CHAINS, AS_CHUNKS } 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,86,0 { VEC_POP_IF } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL, WAKER_NOOP } @@ -118,16 +119,29 @@ impl Msrv { /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations pub fn current(self, cx: &LateContext<'_>) -> Option { if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { - let start = cx.last_node_with_lint_attrs; - if let Some(msrv_attr) = once(start) - .chain(cx.tcx.hir_parent_id_iter(start)) - .find_map(|id| parse_attrs(cx.tcx.sess, cx.tcx.hir_attrs(id))) - { - return Some(msrv_attr); - } + self.for_attrs(cx.tcx, cx.last_node_with_lint_attrs) + } else { + self.0 } + } - self.0 + /// Returns the MSRV at the specified node + /// + /// If the crate being linted uses an `#[clippy::msrv]` attribute this will search the parent + /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations + pub fn at(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { + self.for_attrs(tcx, node) + } else { + self.0 + } + } + + fn for_attrs(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + once(node) + .chain(tcx.hir_parent_id_iter(node)) + .find_map(|id| parse_attrs(tcx.sess, tcx.hir_attrs(id))) + .or(self.0) } /// Checks if a required version from [this module](self) is met at the current node @@ -138,6 +152,14 @@ impl Msrv { self.current(cx).is_none_or(|msrv| msrv >= required) } + /// Checks if a required version from [this module](self) is met at the specified node + /// + /// If the crate being linted uses an `#[clippy::msrv]` attribute this will search the parent + /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations + pub fn meets_at(self, tcx: TyCtxt<'_>, node: HirId, required: RustcVersion) -> bool { + self.at(tcx, node).is_none_or(|msrv| msrv >= required) + } + pub fn read_cargo(&mut self, sess: &Session) { let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") .ok() diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index ddb3c2d6cb4a5..5c61f424adf1a 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -6,9 +6,8 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; use rustc_const_eval::check_consts::ConstCx; -use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::{RustcVersion, StableSince}; +use rustc_hir::{self as hir, HirId, RustcVersion, StableSince}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::LateContext; @@ -87,7 +86,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, msrv: Msrv) ty::Ref(_, _, hir::Mutability::Mut) if !msrv.meets(cx, msrvs::CONST_MUT_REFS) => { return Err((span, "mutable references in const fn are unstable".into())); }, - ty::Alias(ty::AliasTy { + ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => return Err((span, "`impl Trait` in const fn is unstable".into())), @@ -420,14 +419,17 @@ fn check_terminator<'tcx>( /// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { - cx.tcx.is_const_fn(def_id) - && cx - .tcx + is_stable_const_fn_at(cx.tcx, cx.last_node_with_lint_attrs, def_id, msrv) +} + +/// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. +pub fn is_stable_const_fn_at(tcx: TyCtxt<'_>, node: HirId, def_id: DefId, msrv: Msrv) -> bool { + tcx.is_const_fn(def_id) + && tcx .lookup_const_stability(def_id) .or_else(|| { - cx.tcx - .trait_of_assoc(def_id) - .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) + tcx.trait_of_assoc(def_id) + .and_then(|trait_def_id| tcx.lookup_const_stability(trait_def_id)) }) .is_none_or(|const_stab| { if let rustc_hir::StabilityLevel::Stable { since, .. } = const_stab.level { @@ -441,10 +443,10 @@ pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bo StableSince::Err(_) => return false, }; - msrv.meets(cx, const_stab_rust_version) + msrv.meets_at(tcx, node, const_stab_rust_version) } else { // Unstable const fn, check if the feature is enabled. - cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() + tcx.features().enabled(const_stab.feature) && msrv.at(tcx, node).is_none() } }) } diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 02d502f8b67f8..171276f997d39 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -20,27 +20,38 @@ use std::borrow::Cow; use std::fmt; use std::ops::{Deref, Index, Range}; -pub trait HasSession { - fn sess(&self) -> &Session; +pub trait HasSourceMap<'sm>: Copy { + #[must_use] + fn source_map(self) -> &'sm SourceMap; } -impl HasSession for Session { - fn sess(&self) -> &Session { +impl<'sm> HasSourceMap<'sm> for &'sm SourceMap { + #[inline] + fn source_map(self) -> &'sm SourceMap { self } } -impl HasSession for TyCtxt<'_> { - fn sess(&self) -> &Session { - self.sess +impl<'sm> HasSourceMap<'sm> for &'sm Session { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.source_map() } } -impl HasSession for EarlyContext<'_> { - fn sess(&self) -> &Session { - ::rustc_lint::LintContext::sess(self) +impl<'sm> HasSourceMap<'sm> for TyCtxt<'sm> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.sess.source_map() } } -impl HasSession for LateContext<'_> { - fn sess(&self) -> &Session { - self.tcx.sess() +impl<'sm> HasSourceMap<'sm> for &'sm EarlyContext<'_> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + ::rustc_lint::LintContext::sess(self).source_map() + } +} +impl<'sm> HasSourceMap<'sm> for &LateContext<'sm> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.tcx.sess.source_map() } } @@ -95,39 +106,39 @@ impl IntoSpan for Range { } } -pub trait SpanRangeExt: SpanRange { +pub trait SpanExt: SpanRange { /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed, /// or the source text is not accessible. - fn get_source_text(self, cx: &impl HasSession) -> Option { - get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new) + fn get_text<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + get_source_range(sm.source_map(), self.into_range()).and_then(SourceText::new) } /// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// extends through multiple files, or is malformed. - fn get_source_range(self, cx: &impl HasSession) -> Option { - get_source_range(cx.sess().source_map(), self.into_range()) + fn get_source_range<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + get_source_range(sm.source_map(), self.into_range()) } /// Calls the given function with the source text referenced and returns the value. Returns /// `None` if the source text cannot be retrieved. - fn with_source_text(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { - with_source_text(cx.sess().source_map(), self.into_range(), f) + fn with_source_text<'sm, T>(self, sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { + with_source_text(sm.source_map(), self.into_range(), f) } /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// source text cannot be retrieved. - fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { - self.with_source_text(cx, pred).unwrap_or(false) + fn check_text<'sm>(self, sm: impl HasSourceMap<'sm>, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { + self.with_source_text(sm, pred).unwrap_or(false) } /// Calls the given function with the both the text of the source file and the referenced range, /// and returns the value. Returns `None` if the source text cannot be retrieved. - fn with_source_text_and_range( + fn with_source_text_and_range<'sm, T>( self, - cx: &impl HasSession, + sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a str, Range) -> T, ) -> Option { - with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) + with_source_text_and_range(sm.source_map(), self.into_range(), f) } /// Calls the given function with the both the text of the source file and the referenced range, @@ -135,12 +146,12 @@ pub trait SpanRangeExt: SpanRange { /// retrieved, or no result is returned. /// /// The new range must reside within the same source file. - fn map_range( + fn map_range<'sm>( self, - cx: &impl HasSession, + sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { - map_range(cx.sess().source_map(), self.into_range(), f) + map_range(sm.source_map(), self.into_range(), f) } /// Extends the range to include all preceding whitespace characters. @@ -156,16 +167,16 @@ pub trait SpanRangeExt: SpanRange { /// /// When the range points to `foo`, suggesting to remove the range after it's been extended will /// cause the `)` to be placed inside the line comment as `( // Some comment)`. - fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { - with_leading_whitespace(cx.sess().source_map(), self.into_range()) + fn with_leading_whitespace<'sm>(self, sm: impl HasSourceMap<'sm>) -> Range { + with_leading_whitespace(sm.source_map(), self.into_range()) } /// Trims the leading whitespace from the range. - fn trim_start(self, cx: &impl HasSession) -> Range { - trim_start(cx.sess().source_map(), self.into_range()) + fn trim_start<'sm>(self, sm: impl HasSourceMap<'sm>) -> Range { + trim_start(sm.source_map(), self.into_range()) } } -impl SpanRangeExt for T {} +impl SpanExt for T {} /// Handle to a range of text in a source file. pub struct SourceText(SourceFileRange); @@ -353,15 +364,15 @@ impl SourceFileRange { } /// Like [`snippet_block`], but add braces if the expr is not an `ExprKind::Block` with no label. -pub fn expr_block( - sess: &impl HasSession, +pub fn expr_block<'sm>( + sm: impl HasSourceMap<'sm>, expr: &Expr<'_>, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> String { - let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app); + let (code, from_macro) = snippet_block_with_context(sm, expr.span, outer, default, indent_relative_to, app); if !from_macro && let ExprKind::Block(block, None) = expr.kind && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) @@ -386,13 +397,13 @@ pub fn expr_block( /// let x = (); /// // ^^^^^^^^^^ /// ``` -pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span { - first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) +pub fn first_line_of_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { + first_char_in_first_line(sm, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) } -fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option { - let line_span = line_span(sess, span); - snippet_opt(sess, line_span).and_then(|snip| { +fn first_char_in_first_line<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + let line_span = line_span(sm, span); + snippet_opt(sm, line_span).and_then(|snip| { snip.find(|c: char| !c.is_whitespace()) .map(|pos| line_span.lo() + BytePos::from_usize(pos)) }) @@ -407,9 +418,9 @@ fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option Span { +fn line_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); - let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap(); + let SourceFileAndLine { sf, line } = sm.source_map().lookup_line(span.lo()).unwrap(); let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) @@ -423,13 +434,13 @@ fn line_span(sess: &impl HasSession, span: Span) -> Span { /// let x = (); /// // ^^ -- will return 4 /// ``` -pub fn indent_of(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +pub fn indent_of<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + snippet_opt(sm, line_span(sm, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } /// Gets a snippet of the indentation of the line of a span -pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).map(|mut s| { +pub fn snippet_indent<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + snippet_opt(sm, line_span(sm, span)).map(|mut s| { let len = s.len() - s.trim_start().len(); s.truncate(len); s @@ -441,8 +452,8 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { // sources that the user has no control over. // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. -pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { - if let Some(snippet) = snippet_opt(sess, span) +pub fn is_present_in_source<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool { + if let Some(snippet) = snippet_opt(sm, span) && snippet.is_empty() { return false; @@ -534,8 +545,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, /// snippet(cx, span1, "..") // -> "value" /// snippet(cx, span2, "..") // -> "Vec::new()" /// ``` -pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from) +pub fn snippet<'a, 'sm>(sm: impl HasSourceMap<'sm>, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(sm, span).map_or_else(|| Cow::Borrowed(default), From::from) } /// Same as [`snippet`], but it adapts the applicability level by following rules: @@ -547,17 +558,17 @@ pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow< /// /// If the span might realistically contain a macro call (e.g. `vec![]`), consider using /// [`snippet_with_context`] instead. -pub fn snippet_with_applicability<'a>( - sess: &impl HasSession, +pub fn snippet_with_applicability<'a, 'sm>( + sm: impl HasSourceMap<'sm>, span: Span, default: &'a str, applicability: &mut Applicability, ) -> Cow<'a, str> { - snippet_with_applicability_sess(sess.sess(), span, default, applicability) + snippet_with_applicability_sm(sm.source_map(), span, default, applicability) } -fn snippet_with_applicability_sess<'a>( - sess: &Session, +fn snippet_with_applicability_sm<'a>( + sm: &SourceMap, span: Span, default: &'a str, applicability: &mut Applicability, @@ -565,7 +576,7 @@ fn snippet_with_applicability_sess<'a>( if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - if let Some(t) = snippet_opt(sess, span) { + if let Some(t) = snippet_opt(sm, span) { Cow::Owned(t) } else { if *applicability == Applicability::MachineApplicable { @@ -576,8 +587,8 @@ fn snippet_with_applicability_sess<'a>( } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { - sess.sess().source_map().span_to_snippet(span).ok() +pub fn snippet_opt<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + sm.source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -614,37 +625,42 @@ pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { /// } // aligned with `if` /// ``` /// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block(sess: &impl HasSession, span: Span, default: &str, indent_relative_to: Option) -> String { - let snip = snippet(sess, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); +pub fn snippet_block<'sm>( + sm: impl HasSourceMap<'sm>, + span: Span, + default: &str, + indent_relative_to: Option, +) -> String { + let snip = snippet(sm, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); reindent_multiline(&snip, true, indent) } /// Same as [`snippet_block`], but adapts the applicability level by the rules of /// [`snippet_with_applicability`]. -pub fn snippet_block_with_applicability( - sess: &impl HasSession, +pub fn snippet_block_with_applicability<'sm>( + sm: impl HasSourceMap<'sm>, span: Span, default: &str, indent_relative_to: Option, applicability: &mut Applicability, ) -> String { - let snip = snippet_with_applicability(sess, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + let snip = snippet_with_applicability(sm, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); reindent_multiline(&snip, true, indent) } /// Combination of [`snippet_block`] and [`snippet_with_context`]. -pub fn snippet_block_with_context( - sess: &impl HasSession, +pub fn snippet_block_with_context<'sm>( + sm: impl HasSourceMap<'sm>, span: Span, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> (String, bool) { - let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + let (snip, from_macro) = snippet_with_context(sm, span, outer, default, app); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); (reindent_multiline(&snip, true, indent), from_macro) } @@ -658,18 +674,18 @@ pub fn snippet_block_with_context( /// correctly get a snippet of `vec![]`. /// /// This will also return whether or not the snippet is a macro call. -pub fn snippet_with_context<'a>( - sess: &impl HasSession, +pub fn snippet_with_context<'a, 'sm>( + sm: impl HasSourceMap<'sm>, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - snippet_with_context_sess(sess.sess(), span, outer, default, applicability) + snippet_with_context_sm(sm.source_map(), span, outer, default, applicability) } -fn snippet_with_context_sess<'a>( - sess: &Session, +fn snippet_with_context_sm<'a>( + sm: &SourceMap, span: Span, outer: SyntaxContext, default: &'a str, @@ -677,10 +693,7 @@ fn snippet_with_context_sess<'a>( ) -> (Cow<'a, str>, bool) { // If it is just range desugaring, use the desugaring span since it may include parenthesis. if span.desugaring_kind() == Some(DesugaringKind::RangeExpr) && span.parent_callsite().unwrap().ctxt() == outer { - return ( - snippet_with_applicability_sess(sess, span, default, applicability), - false, - ); + return (snippet_with_applicability_sm(sm, span, default, applicability), false); } let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( @@ -696,7 +709,7 @@ fn snippet_with_context_sess<'a>( ); ( - snippet_with_applicability_sess(sess, span, default, applicability), + snippet_with_applicability_sm(sm, span, default, applicability), is_macro_call, ) } @@ -759,15 +772,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { /// writeln!(o, "") -> writeln!(o, "") /// ^^ ^^^^ /// ``` -pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span { - let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true); +pub fn expand_past_previous_comma<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { + let extended = sm.source_map().span_extend_to_prev_char(span, ',', true); extended.with_lo(extended.lo() - BytePos(1)) } /// Converts `expr` to a `char` literal if it's a `str` literal containing a single /// character (or a single byte with `ascii_only`) -pub fn str_literal_to_char_literal( - sess: &impl HasSession, +pub fn str_literal_to_char_literal<'sm>( + sm: impl HasSourceMap<'sm>, expr: &Expr<'_>, applicability: &mut Applicability, ascii_only: bool, @@ -782,7 +795,7 @@ pub fn str_literal_to_char_literal( } && len == 1 { - let snip = snippet_with_applicability(sess, expr.span, string, applicability); + let snip = snippet_with_applicability(sm, expr.span, string, applicability); let ch = if let StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 59e98cdc7bef8..6053788c82b13 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -176,6 +176,8 @@ generate! { checked_sub, child_id, child_kill, + chunks_exact, + chunks_exact_mut, clamp, clippy_utils, clone_into, @@ -209,8 +211,6 @@ generate! { deprecated_in_future, deref_mut_method, diagnostics, - disallowed_profile, - disallowed_profiles, disallowed_types, drain, dump, diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 8cded245fac22..ae348651c0d62 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -3,7 +3,7 @@ #![allow(clippy::module_name_repetitions)] use core::ops::ControlFlow; -use rustc_abi::VariantIdx; +use rustc_abi::{BackendRepr, FieldsShape, VariantIdx, Variants}; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -17,7 +17,7 @@ use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; -use rustc_middle::ty::layout::ValidityRequirement; +use rustc_middle::ty::layout::{LayoutError, LayoutOf, TyAndLayout}; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, IntTy, ProjectionAliasTy, Region, RegionKind, TraitRef, Ty, TyCtxt, @@ -34,7 +34,7 @@ use std::{debug_assert_matches, iter, mem}; use crate::paths::{PathNS, lookup_path_str}; use crate::res::{MaybeDef, MaybeQPath}; -use crate::sym; +use crate::{over, sym}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -104,7 +104,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return true; } - if let ty::Alias(AliasTy { + if let ty::Alias(_, AliasTy { kind: ty::Opaque { def_id }, .. }) = *inner_ty.kind() @@ -337,7 +337,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { is_must_use_ty(cx, *ty) }, ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Alias(AliasTy { + ty::Alias(_, AliasTy { kind: ty::Opaque { def_id }, .. }) => { @@ -486,8 +486,8 @@ pub fn peel_n_ty_refs(mut ty: Ty<'_>, n: usize) -> (Ty<'_>, Option) /// and `false` for: /// - `Result` and `Result` pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind(), &b.kind()) { - (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { + match (a.kind(), b.kind()) { + (ty::Adt(did_a, args_a), ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } @@ -500,35 +500,112 @@ pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { _ => true, }) }, + (ty::Ref(_, a, mut_a), ty::Ref(_, b, mut_b)) => mut_a == mut_b && same_type_modulo_regions(*a, *b), + (ty::Tuple(as_), ty::Tuple(bs)) => over(as_, bs, |a, b| same_type_modulo_regions(*a, *b)), + (ty::Array(a, na), ty::Array(b, nb)) => na == nb && same_type_modulo_regions(*a, *b), _ => a == b, } } /// Checks if a given type looks safe to be uninitialized. pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); - cx.tcx - .check_validity_requirement((ValidityRequirement::Uninit, typing_env.as_query_input(ty))) - .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty)) + match cx.layout_of(ty) { + Ok(layout) => is_uninit_value_valid_for_layout(cx, layout), + // The type layout is either not concrete enough yet or too large, fall back to structural check instead + Err(LayoutError::TooGeneric(_) | LayoutError::SizeOverflow(_)) => is_uninit_value_valid_for_ty_fallback(cx, ty), + Err(_) => false, + } +} + +fn is_uninit_value_valid_for_layout<'tcx>(cx: &LateContext<'tcx>, layout: TyAndLayout<'tcx>) -> bool { + // ZSTs contribute no bytes to the vector buffer + if layout.layout.is_zst() { + return true; + } + + match layout.layout.backend_repr { + BackendRepr::Scalar(s) => s.is_uninit_valid(), + BackendRepr::ScalarPair(a, b) => a.is_uninit_valid() && b.is_uninit_valid(), + BackendRepr::SimdVector { element, count } => count == 0 || element.is_uninit_valid(), + BackendRepr::SimdScalableVector { element, .. } => element.is_uninit_valid(), + // Here validity is determined by the structural fields instead. + BackendRepr::Memory { .. } => match &layout.layout.variants { + Variants::Single { .. } => match &layout.layout.fields { + FieldsShape::Primitive => { + debug_assert!(false, "Both Scalar primitives and ! should be handled above."); + false + }, + // Arrays are valid if empty, or if their elements are valid. + FieldsShape::Array { count, .. } => { + if *count == 0 { + true + } else { + is_uninit_value_valid_for_layout(cx, layout.field(cx, 0)) + } + }, + // Structs like types are valid only if all fields are valid. + FieldsShape::Arbitrary { offsets, .. } => { + (0..offsets.len()).all(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i))) + }, + // Unions are valid if at least one field is valid. + FieldsShape::Union(count) => { + (0..count.get()).any(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i))) + }, + }, + // Types with no valid variants must be uninhabited + Variants::Empty => true, + // Enum like with multiple inhabited variants have a discriminant, they cannot be uninitialized. + Variants::Multiple { .. } => false, + }, + } } -/// A fallback for polymorphic types, which are not supported by `check_validity_requirement`. +/// Fallback for polymorphic types where `layout_of` fails fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); + match *ty.kind() { // The array length may be polymorphic, let's try the inner type. - ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component), + ty::Array(component, len) => { + // Zero-length arrays are always valid + if len.try_to_target_usize(cx.tcx) == Some(0) { + return true; + } + is_uninit_value_valid_for_ty(cx, component) + }, // Peek through tuples and try their fallbacks. ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), - // Unions are always fine right now. - // This includes MaybeUninit, the main way people use uninitialized memory. - ty::Adt(adt, _) if adt.is_union() => true, + // For Unions, check if any field is uninit + ty::Adt(adt, args) if adt.is_union() => adt.all_fields().any(|field| { + let unnormalized_field_ty = field.ty(cx.tcx, args); + let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else { + debug_assert!( + false, + "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly." + ); + return false; + }; + is_uninit_value_valid_for_ty(cx, field_ty) + }), // Types (e.g. `UnsafeCell>`) that recursively contain only types that can be uninit // can themselves be uninit too. - // This purposefully ignores enums as they may have a discriminant that can't be uninit. - ty::Adt(adt, args) if adt.is_struct() => adt - .all_fields() - .all(|field| is_uninit_value_valid_for_ty(cx, field.ty(cx.tcx, args).skip_norm_wip())), - // For the rest, conservatively assume that they cannot be uninit. + // This also applies for single variant enums, whose validity is determined by their fields. + ty::Adt(adt, args) if adt.is_struct() || adt.variants().len() == 1 => adt.all_fields().all(|field| { + let unnormalized_field_ty = field.ty(cx.tcx, args); + let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else { + debug_assert!( + false, + "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly." + ); + return false; + }; + + is_uninit_value_valid_for_ty(cx, field_ty) + }), + // Without a usable whole type layout, + // conservatively reject remaining enum cases + ty::Adt(adt, _) if adt.is_enum() => false, + // Conservatively reject remaining types _ => false, } } @@ -639,7 +716,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, } }, - ty::Alias(alias) if let Some(proj) = alias.try_to_projection() => match cx + ty::Alias(_, alias) if let Some(proj) = alias.try_to_projection() => match cx .tcx .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) { @@ -1049,9 +1126,13 @@ pub fn make_projection<'tcx>( assert_generic_args_match(tcx, assoc_item.def_id, args); let kind = if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(assoc_item.def_id)) { - ty::AliasTyKind::Inherent { def_id: assoc_item.def_id } + ty::AliasTyKind::Inherent { + def_id: assoc_item.def_id, + } } else { - ty::AliasTyKind::Projection { def_id: assoc_item.def_id } + ty::AliasTyKind::Projection { + def_id: assoc_item.def_id, + } }; Some(AliasTy::new_from_args(tcx, kind, args)) @@ -1093,7 +1174,7 @@ pub fn make_normalized_projection<'tcx>( ); return None; } - match tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(Ty::new_alias(tcx, ty))) { + match tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(Ty::new_alias(tcx, ty::IsRigid::No, ty))) { Ok(ty) => Some(ty), Err(e) => { debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); @@ -1195,7 +1276,7 @@ impl<'tcx> InteriorMut<'tcx> { .find_map(|f| self.interior_mut_ty_chain_inner(cx, f.ty(cx.tcx, args).skip_norm_wip(), depth)) } }, - ty::Alias(AliasTy { + ty::Alias(_, AliasTy { kind: ty::Projection { .. }, .. }) => match cx @@ -1247,7 +1328,7 @@ pub fn make_normalized_projection_with_regions<'tcx>( } let cause = ObligationCause::dummy(); let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); - match infcx.at(&cause, param_env).query_normalize(Ty::new_alias(tcx, ty)) { + match infcx.at(&cause, param_env).query_normalize(Ty::new_alias(tcx, ty::IsRigid::No, ty)) { Ok(ty) => Some(ty.value), Err(e) => { debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); @@ -1335,6 +1416,14 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option` or a `Result` and return its argument type (`T`) if it is. +pub fn option_or_result_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) if matches!(adt.opt_diag_name(cx), Some(sym::Option | sym::Result)) => Some(args.type_at(0)), + _ => None, + } +} + /// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if /// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze` /// types, or `PhantomData` types containing any of the previous. This can be used to check whether diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 4a1d24024915b..d644cd5a4aace 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -195,7 +195,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { } pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { - for_each_expr(cx, v, |e| { + for_each_expr(cx.tcx, v, |e| { if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { @@ -220,7 +220,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id); let mut past_expr = false; - for_each_expr(cx, block, |e| { + for_each_expr(cx.tcx, block, |e| { if past_expr { if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 28449a75a8fc5..ca1fa57cffc10 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,6 +1,6 @@ use crate::get_enclosing_block; use crate::msrvs::Msrv; -use crate::qualify_min_const_fn::is_stable_const_fn; +use crate::qualify_min_const_fn::is_stable_const_fn_at; use crate::res::MaybeResPath; use crate::ty::needs_ordered_drop; use core::ops::ControlFlow; @@ -8,8 +8,8 @@ use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ - self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, - ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, + self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, CRATE_HIR_ID, Expr, ExprKind, HirId, + ItemId, ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -145,7 +145,7 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( /// Calls the given function once for each expression contained. This will enter bodies, but not /// nested items. pub fn for_each_expr<'tcx, B, C: Continue>( - cx: &LateContext<'tcx>, + tcx: TyCtxt<'tcx>, node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, ) -> Option { @@ -188,7 +188,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>( ControlFlow::Continue(()) } } - let mut v = V { tcx: cx.tcx, f }; + let mut v = V { tcx, f }; node.visit(&mut v).break_value() } @@ -299,7 +299,7 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - for_each_expr(cx, cx.tcx.hir_body(body).value, |e| { + for_each_expr(cx.tcx, cx.tcx.hir_body(body).value, |e| { if let ExprKind::Path(p) = &e.kind && cx.qpath_res(p, e.hir_id) == res { @@ -312,7 +312,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - for_each_expr(cx, visitable, |e| { + for_each_expr(cx.tcx, visitable, |e| { if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { @@ -322,89 +322,69 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc .is_some() } -/// Checks if the given expression is a constant. -pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { - struct V<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - } - - impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = intravisit::nested_filter::None; - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { - match e.kind { - ExprKind::ConstBlock(_) => return ControlFlow::Continue(()), - ExprKind::Call( - &Expr { - kind: ExprKind::Path(ref p), - hir_id, - .. - }, - _, - ) if self - .cx - .qpath_res(p, hir_id) - .opt_def_id() - .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, - ExprKind::MethodCall(..) - if self - .cx - .typeck_results() - .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, - ExprKind::Binary(_, lhs, rhs) - if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() - && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, - ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), - ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), - ExprKind::Index(base, _, _) - if matches!( - self.cx.typeck_results().expr_ty(base).peel_refs().kind(), - ty::Slice(_) | ty::Array(..) - ) => {}, - ExprKind::Path(ref p) - if matches!( - self.cx.qpath_res(p, e.hir_id), - Res::Def( - DefKind::Const { .. } - | DefKind::AssocConst { .. } - | DefKind::AnonConst - | DefKind::ConstParam - | DefKind::Ctor(..) - | DefKind::Fn - | DefKind::AssocFn, - _ - ) | Res::SelfCtor(_) - ) => {}, - - ExprKind::AddrOf(..) - | ExprKind::Array(_) - | ExprKind::Block(..) - | ExprKind::Cast(..) - | ExprKind::DropTemps(_) - | ExprKind::Field(..) - | ExprKind::If(..) - | ExprKind::Let(..) - | ExprKind::Lit(_) - | ExprKind::Match(..) - | ExprKind::Repeat(..) - | ExprKind::Struct(..) - | ExprKind::Tup(_) - | ExprKind::Type(..) - | ExprKind::UnsafeBinderCast(..) => (), - - _ => { - return ControlFlow::Break(()); +/// Checks if the given expression can be evaluated as a constant at the specified node +pub fn is_const_evaluatable<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool { + for_each_expr(tcx, e, move |e| { + match e.kind { + ExprKind::ConstBlock(_) => return ControlFlow::Continue(Descend::No), + ExprKind::Call( + &Expr { + kind: ExprKind::Path(ref p), + hir_id, + .. }, - } + _, + ) if typeck + .qpath_res(p, hir_id) + .opt_def_id() + .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, + ExprKind::MethodCall(..) + if typeck + .type_dependent_def_id(e.hir_id) + .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, + ExprKind::Binary(_, lhs, rhs) + if typeck.expr_ty(lhs).peel_refs().is_primitive_ty() + && typeck.expr_ty(rhs).peel_refs().is_primitive_ty() => {}, + ExprKind::Unary(UnOp::Deref, e) if typeck.expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(_, e) if typeck.expr_ty(e).peel_refs().is_primitive_ty() => (), + ExprKind::Index(base, _, _) + if matches!(typeck.expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..)) => {}, + ExprKind::Path(ref p) + if matches!( + typeck.qpath_res(p, e.hir_id), + Res::Def( + DefKind::Const { .. } + | DefKind::AssocConst { .. } + | DefKind::AnonConst + | DefKind::ConstParam + | DefKind::Ctor(..) + | DefKind::Fn + | DefKind::AssocFn, + _ + ) | Res::SelfCtor(_) + ) => {}, + + ExprKind::AddrOf(..) + | ExprKind::Array(_) + | ExprKind::Block(..) + | ExprKind::Cast(..) + | ExprKind::DropTemps(_) + | ExprKind::Field(..) + | ExprKind::If(..) + | ExprKind::Let(..) + | ExprKind::Lit(_) + | ExprKind::Match(..) + | ExprKind::Repeat(..) + | ExprKind::Struct(..) + | ExprKind::Tup(_) + | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) => {}, - walk_expr(self, e) + _ => return ControlFlow::Break(()), } - } - - let mut v = V { cx }; - v.visit_expr(e).is_continue() + ControlFlow::Continue(Descend::Yes) + }) + .is_none() } /// Checks if the given expression performs an unsafe operation outside of an unsafe block. @@ -785,7 +765,7 @@ pub fn local_used_once<'tcx>( ) -> Option<&'tcx Expr<'tcx>> { let mut expr = None; - let cf = for_each_expr(cx, visitable, |e| { + let cf = for_each_expr(cx.tcx, visitable, |e| { if e.res_local_id() == Some(id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 3afa9da1f947d..ac044327aafe9 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2026-06-11" +channel = "nightly-2026-06-25" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml deleted file mode 100644 index c77f5658ae69c..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml +++ /dev/null @@ -1,13 +0,0 @@ -disallowed-methods = [ - { path = "std::mem::drop" } -] - -[profiles.forward_pass] -disallowed-methods = [ - { path = "alloc::vec::Vec::push", reason = "push is forbidden in forward profile" } -] - -[profiles.export] -disallowed-methods = [ - { path = "core::option::Option::unwrap" } -] diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs deleted file mode 100644 index 07bb22e05d6f7..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -#![warn(clippy::disallowed_methods)] -#![allow( - unused, - clippy::no_effect, - clippy::needless_borrow, - clippy::vec_init_then_push, - clippy::unnecessary_literal_unwrap -)] - -fn default_violation() { - let value = String::from("test"); - std::mem::drop(value); //~ ERROR: use of a disallowed method `std::mem::drop` -} - -#[expect(clippy::disallowed_methods)] -fn expected_violation() { - let value = String::from("test"); - std::mem::drop(value); -} - -#[clippy::disallowed_profile("forward_pass")] -fn forward_profile() { - let mut values = Vec::new(); - values.push(1); //~ ERROR: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) -} - -#[clippy::disallowed_profile("export")] -fn export_profile() { - let value = Some(1); - value.unwrap(); //~ ERROR: use of a disallowed method `core::option::Option::unwrap` (profile: export) -} - -#[clippy::disallowed_profile("unknown_profile")] -//~^ ERROR: unknown profile `unknown_profile` for -//~| ERROR: unknown profile `unknown_profile` for -fn unknown_profile() { - let mut values = Vec::new(); - values.push(1); - // unknown profile falls back to the default list - std::mem::drop(values); //~ ERROR: use of a disallowed method `std::mem::drop` -} - -#[clippy::disallowed_profiles("forward_pass", "export")] -fn merged_profiles() { - let mut values = Vec::new(); - values.push(1); //~ ERROR: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - let value = Some(1); - value.unwrap(); //~ ERROR: use of a disallowed method `core::option::Option::unwrap` (profile: export) -} - -// `#[expect(clippy::disallowed_methods)]` silences the body warning and the unknown-profile -// warning tagged under `DISALLOWED_METHODS`, but not the one tagged under `DISALLOWED_TYPES`. -#[expect(clippy::disallowed_methods)] -#[clippy::disallowed_profile("unknown_profile_expect_before")] -//~^ ERROR: unknown profile `unknown_profile_expect_before` for `clippy::disallowed_types` -fn expect_before_unknown_profile() { - let value = String::from("test"); - std::mem::drop(value); -} - -#[clippy::disallowed_profile("unknown_profile_expect_after")] -//~^ ERROR: unknown profile `unknown_profile_expect_after` for `clippy::disallowed_types` -#[expect(clippy::disallowed_methods)] -fn expect_after_unknown_profile() { - let value = String::from("test"); - std::mem::drop(value); -} - -fn main() { - default_violation(); - expected_violation(); - forward_profile(); - export_profile(); - unknown_profile(); - merged_profiles(); - expect_before_unknown_profile(); - expect_after_unknown_profile(); -} diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr deleted file mode 100644 index 5f64b1bf4499b..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr +++ /dev/null @@ -1,72 +0,0 @@ -error: use of a disallowed method `std::mem::drop` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:12:5 - | -LL | std::mem::drop(value); - | ^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` - -error: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:24:12 - | -LL | values.push(1); - | ^^^^ - | - = note: push is forbidden in forward profile - -error: use of a disallowed method `core::option::Option::unwrap` (profile: export) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:30:11 - | -LL | value.unwrap(); - | ^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:33:30 - | -LL | #[clippy::disallowed_profile("unknown_profile")] - | ^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:33:30 - | -LL | #[clippy::disallowed_profile("unknown_profile")] - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-types` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` - -error: use of a disallowed method `std::mem::drop` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:40:5 - | -LL | std::mem::drop(values); - | ^^^^^^^^^^^^^^ - -error: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:46:12 - | -LL | values.push(1); - | ^^^^ - | - = note: push is forbidden in forward profile - -error: use of a disallowed method `core::option::Option::unwrap` (profile: export) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:48:11 - | -LL | value.unwrap(); - | ^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile_expect_before` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:54:30 - | -LL | #[clippy::disallowed_profile("unknown_profile_expect_before")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile_expect_after` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:61:30 - | -LL | #[clippy::disallowed_profile("unknown_profile_expect_after")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 10 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml deleted file mode 100644 index 3c47b2afe26fe..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml +++ /dev/null @@ -1,13 +0,0 @@ -disallowed-types = [ - { path = "std::rc::Rc" } -] - -[profiles.forward_pass] -disallowed-types = [ - { path = "std::cell::RefCell", reason = "Prefer shared references" } -] - -[profiles.export] -disallowed-types = [ - { path = "std::sync::Mutex" } -] diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs deleted file mode 100644 index 0161417dd6022..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![warn(clippy::disallowed_types)] -#![allow(dead_code)] - -use std::rc::Rc; //~ ERROR: use of a disallowed type `std::rc::Rc` -use std::sync::Mutex; - -struct Wrapper; - -fn default_type() { - let _value: Rc = todo!(); //~ ERROR: use of a disallowed type `std::rc::Rc` -} - -#[clippy::disallowed_profile("forward_pass")] -fn forward_profile() { - let _value: std::cell::RefCell = todo!(); //~ ERROR: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) -} - -#[clippy::disallowed_profile("export")] -fn export_profile() { - let _value: Mutex = todo!(); //~ ERROR: use of a disallowed type `std::sync::Mutex` (profile: export) -} - -#[clippy::disallowed_profile("unknown_type_profile")] -//~^ ERROR: unknown profile `unknown_type_profile` for -//~| ERROR: unknown profile `unknown_type_profile` for -fn unknown_profile() { - let _other = 1u32; - let _fallback: Rc = todo!(); //~ ERROR: use of a disallowed type `std::rc::Rc` -} - -#[clippy::disallowed_profiles("forward_pass", "export")] -fn merged_profiles() { - let _value: std::cell::RefCell = todo!(); //~ ERROR: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - let _other: Mutex = todo!(); //~ ERROR: use of a disallowed type `std::sync::Mutex` (profile: export) -} - -// `#[expect(clippy::disallowed_types)]` silences the body warning and the unknown-profile -// warning tagged under `DISALLOWED_TYPES`, but not one tagged under `DISALLOWED_METHODS`. -#[expect(clippy::disallowed_types)] -#[clippy::disallowed_profile("unknown_type_profile_expect_before")] -//~^ ERROR: unknown profile `unknown_type_profile_expect_before` for `clippy::disallowed_methods` -fn expect_before_unknown_profile() { - let _value: Rc = todo!(); -} - -#[clippy::disallowed_profile("unknown_type_profile_expect_after")] -//~^ ERROR: unknown profile `unknown_type_profile_expect_after` for `clippy::disallowed_methods` -#[expect(clippy::disallowed_types)] -fn expect_after_unknown_profile() { - let _value: Rc = todo!(); -} - -fn main() { - default_type(); - forward_profile(); - export_profile(); - unknown_profile(); - merged_profiles(); - expect_before_unknown_profile(); - expect_after_unknown_profile(); -} diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr deleted file mode 100644 index 68d63e218c0de..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr +++ /dev/null @@ -1,78 +0,0 @@ -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:4:1 - | -LL | use std::rc::Rc; - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-types` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` - -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:10:17 - | -LL | let _value: Rc = todo!(); - | ^^^^^^^ - -error: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_types/main.rs:15:17 - | -LL | let _value: std::cell::RefCell = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: Prefer shared references - -error: use of a disallowed type `std::sync::Mutex` (profile: export) - --> tests/ui-toml/disallowed_profiles_types/main.rs:20:17 - | -LL | let _value: Mutex = todo!(); - | ^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:23:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile")] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_types/main.rs:23:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile")] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:28:20 - | -LL | let _fallback: Rc = todo!(); - | ^^^^^^^ - -error: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_types/main.rs:33:17 - | -LL | let _value: std::cell::RefCell = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: Prefer shared references - -error: use of a disallowed type `std::sync::Mutex` (profile: export) - --> tests/ui-toml/disallowed_profiles_types/main.rs:34:17 - | -LL | let _other: Mutex = todo!(); - | ^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile_expect_before` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:40:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile_expect_before")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile_expect_after` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:46:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile_expect_after")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 11 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml b/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml new file mode 100644 index 0000000000000..2ff5a13e893be --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml @@ -0,0 +1 @@ +check-grouped-late-init = false diff --git a/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs b/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs new file mode 100644 index 0000000000000..5a5e657b295c3 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs @@ -0,0 +1,64 @@ +//@check-pass + +fn main() {} + +fn issue16330() { + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 3; + b = 4; + } + + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + let a; + let b; + let c; + match 1 { + 1 => { + a = 1; + b = 2; + c = 3; + }, + _ if false => { + a = 4; + b = 5; + c = 6; + }, + _ => { + a = 7; + b = 8; + c = 9; + }, + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index ba930d094fc19..528e2f1089c04 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -33,6 +33,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -70,7 +71,6 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline @@ -135,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -172,7 +173,6 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline @@ -237,6 +237,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -274,7 +275,6 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline diff --git a/src/tools/clippy/tests/ui/author/issue_17240.rs b/src/tools/clippy/tests/ui/author/issue_17240.rs new file mode 100644 index 0000000000000..591a19907720a --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_17240.rs @@ -0,0 +1,8 @@ +//@ check-pass +// Ensure that a proper body is used when printing paths (`x`) +// if the attribute is placed on an item. +#[clippy::author] +fn main() { + let x = 42i32; + _ = -x; +} diff --git a/src/tools/clippy/tests/ui/author/issue_17240.stdout b/src/tools/clippy/tests/ui/author/issue_17240.stdout new file mode 100644 index 0000000000000..81c927180ce1c --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_17240.stdout @@ -0,0 +1,20 @@ +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 2 + && let StmtKind::Let(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Semi(e) = block.stmts[1].kind + && let ExprKind::Block(block1, None) = e.kind + && block1.stmts.len() == 1 + && let StmtKind::Let(local1) = block1.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Unary(UnOp::Neg, inner) = init1.kind + && let PatKind::Wild = local1.pat.kind + && block1.expr.is_none() + && block.expr.is_none() +{ + // report your lint here +} diff --git a/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs b/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs new file mode 100644 index 0000000000000..4cc9ac80692d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs @@ -0,0 +1,17 @@ +#![allow(unused, clippy::missing_safety_doc)] + +pub fn lol() -> Option { + Some(0) +} + +pub unsafe fn lol_unchecked() -> u32 { + 0 +} + +pub fn kek() -> Option { + Some(0) +} + +unsafe fn kek_unchecked() -> u32 { + 0 +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed new file mode 100644 index 0000000000000..e40b745245568 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.clone().next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.clone().next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.clone().next().as_ref(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed new file mode 100644 index 0000000000000..b9b7cdeaf5922 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.next().as_ref(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed new file mode 100644 index 0000000000000..67256fc86887c --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter().peekable(); + let _: Option<&i32> = i.peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!().peekable(); + let _: Option<&i32> = i.peek(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter().peekable(); + let _: Option<&i32> = iter.peek(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs b/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs new file mode 100644 index 0000000000000..d8ccb8b74fb64 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr b/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr new file mode 100644 index 0000000000000..5a284775e04f3 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr @@ -0,0 +1,101 @@ +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:19:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you might want to transform the iterator itself using `.peekable()` without using `.by_ref()` + = note: `-D clippy::by-ref-peekable-peek` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::by_ref_peekable_peek)]` +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:25:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut i = a.into_iter().peekable(); +LL ~ let _: Option<&i32> = i.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:36:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut i = mac!().peekable(); +LL ~ let _: Option<&i32> = i.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:42:27 + | +LL | let _: Option<&i32> = iter.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = iter.by_ref().peekable().peek(); +LL + let _: Option<&i32> = iter.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = iter.by_ref().peekable().peek(); +LL + let _: Option<&i32> = iter.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut iter = [1, 2, 3].into_iter().peekable(); +LL ~ let _: Option<&i32> = iter.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:46:27 + | +LL | let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you might want to transform the iterator itself using `.peekable()` without using `.by_ref()` +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); +LL + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); +LL + let _: Option<&i32> = (&mut iter).next().as_ref(); + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs new file mode 100644 index 0000000000000..0d5f4d06043d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs @@ -0,0 +1,43 @@ +#![warn(clippy::chunks_exact_to_as_chunks)] +#![allow(unused)] + +fn main() { + let slice = [1, 2, 3, 4, 5, 6, 7, 8]; + + // Should trigger lint - literal constant + let mut it = slice.chunks_exact(4); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should trigger lint - const value + const CHUNK_SIZE: usize = 4; + let mut it = slice.chunks_exact(CHUNK_SIZE); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should NOT trigger - runtime value + let size = 4; + let mut it = slice.chunks_exact(size); + for chunk in it {} + + // Should trigger lint - with remainder + let mut it = slice.chunks_exact(3); + //~^ chunks_exact_to_as_chunks + for chunk in &mut it {} + for e in it.remainder() {} + + // Should trigger - mutable variant + let mut arr = [1, 2, 3, 4, 5, 6, 7, 8]; + let mut it = arr.chunks_exact_mut(4); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should NOT trigger - type must unify with another branch + let condition = true; + let y = 3; + let _ = if condition { + slice.chunks_exact(5) + } else { + slice.chunks_exact(y) + }; +} diff --git a/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr new file mode 100644 index 0000000000000..628752f9bae71 --- /dev/null +++ b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr @@ -0,0 +1,56 @@ +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:8:24 + | +LL | let mut it = slice.chunks_exact(4); + | ^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::<4>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:8:24 + | +LL | let mut it = slice.chunks_exact(4); + | ^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]` + +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:14:24 + | +LL | let mut it = slice.chunks_exact(CHUNK_SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:14:24 + | +LL | let mut it = slice.chunks_exact(CHUNK_SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:24:24 + | +LL | let mut it = slice.chunks_exact(3); + | ^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::<3>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:24:24 + | +LL | let mut it = slice.chunks_exact(3); + | ^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: using `chunks_exact_mut` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:31:22 + | +LL | let mut it = arr.chunks_exact_mut(4); + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks_mut::<4>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:31:22 + | +LL | let mut it = arr.chunks_exact_mut(4); + | ^^^^^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/clear_with_drain.fixed b/src/tools/clippy/tests/ui/clear_with_drain.fixed index aaaec700cc9d2..5f8441c0470dc 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.fixed +++ b/src/tools/clippy/tests/ui/clear_with_drain.fixed @@ -20,11 +20,6 @@ fn vec_range() { let mut v = vec![1, 2, 3]; v.clear(); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.clear(); - //~^ clear_with_drain } fn vec_range_from() { @@ -45,11 +40,6 @@ fn vec_range_from() { let mut v = vec![1, 2, 3]; v.clear(); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.clear(); - //~^ clear_with_drain } fn vec_range_full() { @@ -124,11 +114,6 @@ fn vec_deque_range() { let mut deque = VecDeque::from([1, 2, 3]); deque.clear(); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.clear(); - //~^ clear_with_drain } fn vec_deque_range_from() { @@ -149,11 +134,6 @@ fn vec_deque_range_from() { let mut deque = VecDeque::from([1, 2, 3]); deque.clear(); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.clear(); - //~^ clear_with_drain } fn vec_deque_range_full() { @@ -228,11 +208,6 @@ fn string_range() { let mut s = String::from("Hello, world!"); s.clear(); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.clear(); - //~^ clear_with_drain } fn string_range_from() { @@ -253,11 +228,6 @@ fn string_range_from() { let mut s = String::from("Hello, world!"); s.clear(); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.clear(); - //~^ clear_with_drain } fn string_range_full() { diff --git a/src/tools/clippy/tests/ui/clear_with_drain.rs b/src/tools/clippy/tests/ui/clear_with_drain.rs index e36384e09c693..59024dcfe93d8 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.rs +++ b/src/tools/clippy/tests/ui/clear_with_drain.rs @@ -20,11 +20,6 @@ fn vec_range() { let mut v = vec![1, 2, 3]; v.drain(0..v.len()); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.drain(usize::MIN..v.len()); - //~^ clear_with_drain } fn vec_range_from() { @@ -45,11 +40,6 @@ fn vec_range_from() { let mut v = vec![1, 2, 3]; v.drain(0..); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.drain(usize::MIN..); - //~^ clear_with_drain } fn vec_range_full() { @@ -124,11 +114,6 @@ fn vec_deque_range() { let mut deque = VecDeque::from([1, 2, 3]); deque.drain(0..deque.len()); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.drain(usize::MIN..deque.len()); - //~^ clear_with_drain } fn vec_deque_range_from() { @@ -149,11 +134,6 @@ fn vec_deque_range_from() { let mut deque = VecDeque::from([1, 2, 3]); deque.drain(0..); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.drain(usize::MIN..); - //~^ clear_with_drain } fn vec_deque_range_full() { @@ -228,11 +208,6 @@ fn string_range() { let mut s = String::from("Hello, world!"); s.drain(0..s.len()); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.drain(usize::MIN..s.len()); - //~^ clear_with_drain } fn string_range_from() { @@ -253,11 +228,6 @@ fn string_range_from() { let mut s = String::from("Hello, world!"); s.drain(0..); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.drain(usize::MIN..); - //~^ clear_with_drain } fn string_range_full() { diff --git a/src/tools/clippy/tests/ui/clear_with_drain.stderr b/src/tools/clippy/tests/ui/clear_with_drain.stderr index 8b09c26e46af2..b4c71bf60be7f 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.stderr +++ b/src/tools/clippy/tests/ui/clear_with_drain.stderr @@ -8,124 +8,88 @@ LL | v.drain(0..v.len()); = help: to override `-D warnings` add `#[allow(clippy::clear_with_drain)]` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:26:7 - | -LL | v.drain(usize::MIN..v.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:46:7 + --> tests/ui/clear_with_drain.rs:41:7 | LL | v.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:51:7 - | -LL | v.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:68:7 + --> tests/ui/clear_with_drain.rs:58:7 | LL | v.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:86:7 + --> tests/ui/clear_with_drain.rs:76:7 | LL | v.drain(..v.len()); | ^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:125:11 + --> tests/ui/clear_with_drain.rs:115:11 | LL | deque.drain(0..deque.len()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:130:11 - | -LL | deque.drain(usize::MIN..deque.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:150:11 + --> tests/ui/clear_with_drain.rs:135:11 | LL | deque.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:155:11 - | -LL | deque.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:172:11 + --> tests/ui/clear_with_drain.rs:152:11 | LL | deque.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:190:11 + --> tests/ui/clear_with_drain.rs:170:11 | LL | deque.drain(..deque.len()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:229:7 + --> tests/ui/clear_with_drain.rs:209:7 | LL | s.drain(0..s.len()); | ^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:234:7 - | -LL | s.drain(usize::MIN..s.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:254:7 + --> tests/ui/clear_with_drain.rs:229:7 | LL | s.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:259:7 - | -LL | s.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:276:7 + --> tests/ui/clear_with_drain.rs:246:7 | LL | s.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:294:7 + --> tests/ui/clear_with_drain.rs:264:7 | LL | s.drain(..s.len()); | ^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `HashSet` - --> tests/ui/clear_with_drain.rs:333:9 + --> tests/ui/clear_with_drain.rs:303:9 | LL | set.drain(); | ^^^^^^^ help: try: `clear()` error: `drain` used to clear a `HashMap` - --> tests/ui/clear_with_drain.rs:353:9 + --> tests/ui/clear_with_drain.rs:323:9 | LL | map.drain(); | ^^^^^^^ help: try: `clear()` error: `drain` used to clear a `BinaryHeap` - --> tests/ui/clear_with_drain.rs:373:10 + --> tests/ui/clear_with_drain.rs:343:10 | LL | heap.drain(); | ^^^^^^^ help: try: `clear()` -error: aborting due to 21 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-16950.rs b/src/tools/clippy/tests/ui/crashes/ice-16950.rs new file mode 100644 index 0000000000000..414b34ebed348 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-16950.rs @@ -0,0 +1,27 @@ +//@check-pass +#![feature(trivial_bounds)] + +struct Helper(T); + +trait Unsized { + const SIZE: usize = usize::MAX; +} + +impl Unsized for T {} + +impl Helper { + const SIZE: usize = size_of::(); +} + +struct TrickClippy(str); + +impl TrickClippy { + fn trick_clippy() -> bool + where + Self: Sized, + { + Helper::::SIZE == str::SIZE + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/drain_collect.stderr b/src/tools/clippy/tests/ui/drain_collect.stderr index 46da4e799d2e8..7c1008aebe58a 100644 --- a/src/tools/clippy/tests/ui/drain_collect.stderr +++ b/src/tools/clippy/tests/ui/drain_collect.stderr @@ -1,8 +1,8 @@ -error: you seem to be trying to move all elements into a new `BinaryHeap` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:6:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` | note: the lint level is defined here --> tests/ui/drain_collect.rs:1:9 @@ -10,59 +10,59 @@ note: the lint level is defined here LL | #![deny(clippy::drain_collect)] | ^^^^^^^^^^^^^^^^^^^^^ -error: you seem to be trying to move all elements into a new `HashMap` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:15:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `HashSet` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:24:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:33:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:42:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:47:5 | LL | b.drain(0..).collect() - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:52:5 | LL | b.drain(..b.len()).collect() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:57:5 | LL | b.drain(0..b.len()).collect() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:63:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(&mut b)` -error: you seem to be trying to move all elements into a new `String` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:72:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.stderr b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr index 91b38932fee79..77fcae84d570e 100644 --- a/src/tools/clippy/tests/ui/drain_collect_nostd.stderr +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr @@ -1,8 +1,8 @@ -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect_nostd.rs:7:5 | LL | v.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `core::mem::take(v)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `core::mem::take(v)` | = note: `-D clippy::drain-collect` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::drain_collect)]` diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs index 2e062208935a2..fa2ebddfde3f2 100644 --- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs @@ -211,4 +211,18 @@ mod proc_macro_generated { } } +mod issue17255 { + + trait AnotherSimpleTrait<'a> {} + + macro_rules! mac { + ($lt:lifetime, $t:ident, $tr:path) => { + impl<$t: for<'lt> $tr> AnotherSimpleTrait<'_> for $t {} + }; + } + + // Do not lint code expanded from macros + mac!('a, T, super::SimplerTrait); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs index 87ce517a0a451..6a20967b7fc50 100644 --- a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs @@ -1,3 +1,5 @@ +//@no-rustfix + #![warn(clippy::extra_unused_type_parameters)] fn unused_where_clause(x: U) @@ -24,4 +26,17 @@ where unimplemented!(); } +// The fix just removes the type parameter from the definition of `unused_ty`, but it doesn't adjust +// its callsites, leading to compilation errors. +mod issue15884 { + fn unused_ty(x: u8) { + //~^ extra_unused_type_parameters + unimplemented!() + } + + fn main() { + unused_ty::(0); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr index 0765c41609368..2c7051886bf45 100644 --- a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr @@ -1,5 +1,5 @@ error: type parameter `T` goes unused in function definition - --> tests/ui/extra_unused_type_parameters_unfixable.rs:3:24 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:5:24 | LL | fn unused_where_clause(x: U) | ^ @@ -9,7 +9,7 @@ LL | fn unused_where_clause(x: U) = help: to override `-D warnings` add `#[allow(clippy::extra_unused_type_parameters)]` error: type parameters go unused in function definition: T, V - --> tests/ui/extra_unused_type_parameters_unfixable.rs:11:30 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:13:30 | LL | fn unused_multi_where_clause(x: U) | ^ ^^^^^^^^^^ @@ -17,12 +17,24 @@ LL | fn unused_multi_where_clause(x: U) = help: consider removing the parameters error: type parameters go unused in function definition: T, U, V - --> tests/ui/extra_unused_type_parameters_unfixable.rs:19:28 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:21:28 | LL | fn unused_all_where_clause() | ^ ^^^^^^^^^^ ^^^^^^^^^^ | = help: consider removing the parameters -error: aborting due to 3 previous errors +error: type parameter `T` goes unused in function definition + --> tests/ui/extra_unused_type_parameters_unfixable.rs:32:17 + | +LL | fn unused_ty(x: u8) { + | ^^^ + | +help: consider removing the parameter + | +LL - fn unused_ty(x: u8) { +LL + fn unused_ty(x: u8) { + | + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/filter_next.fixed b/src/tools/clippy/tests/ui/filter_next.fixed new file mode 100644 index 0000000000000..5502fa1fb2733 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.fixed @@ -0,0 +1,63 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::filter_next)] +#![expect(clippy::disallowed_names)] +#![allow(clippy::useless_vec)] + +extern crate option_helpers; + +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; + +fn main() {} + +fn filter_next() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().find(|&x| *x < 0); + //~^ filter_next + + let _ = v.iter().rfind(|&x| *x < 0); + //~^ filter_next + + // Multi-line case. + #[rustfmt::skip] + let _ = v.iter().find(|&x| { + //~^ filter_next + *x < 0 + }); + + #[rustfmt::skip] + let _ = v.iter().rfind(|&x| { + //~^ filter_next + *x < 0 + }); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); +} + +fn filter_next_back() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().rfind(|&x| x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +} diff --git a/src/tools/clippy/tests/ui/filter_next.rs b/src/tools/clippy/tests/ui/filter_next.rs new file mode 100644 index 0000000000000..c8e9f060cd609 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.rs @@ -0,0 +1,65 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::filter_next)] +#![expect(clippy::disallowed_names)] +#![allow(clippy::useless_vec)] + +extern crate option_helpers; + +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; + +fn main() {} + +fn filter_next() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().filter(|&x| *x < 0).next(); + //~^ filter_next + + let _ = v.iter().filter(|&x| *x < 0).next_back(); + //~^ filter_next + + // Multi-line case. + #[rustfmt::skip] + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next(); + + #[rustfmt::skip] + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); +} + +fn filter_next_back() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +} diff --git a/src/tools/clippy/tests/ui/filter_next.stderr b/src/tools/clippy/tests/ui/filter_next.stderr new file mode 100644 index 0000000000000..19c2428c3d17d --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.stderr @@ -0,0 +1,78 @@ +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next.rs:16:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-next` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` +help: use `.find(..)` instead + | +LL - let _ = v.iter().filter(|&x| *x < 0).next(); +LL + let _ = v.iter().find(|&x| *x < 0); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:19:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.rfind(..)` instead + | +LL - let _ = v.iter().filter(|&x| *x < 0).next_back(); +LL + let _ = v.iter().rfind(|&x| *x < 0); + | + +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next.rs:24:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + | +help: use `.find(..)` instead + | +LL ~ let _ = v.iter().find(|&x| { +LL + +LL + *x < 0 +LL ~ }); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:31:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + | +help: use `.rfind(..)` instead + | +LL ~ let _ = v.iter().rfind(|&x| { +LL + +LL + *x < 0 +LL ~ }); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:58:13 + | +LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.rfind(..)` instead + | +LL - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +LL + let _ = vec![1].into_iter().rfind(|&x| x < 0); + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_next_unfixable.rs b/src/tools/clippy/tests/ui/filter_next_unfixable.rs new file mode 100644 index 0000000000000..07a0db1353500 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next_unfixable.rs @@ -0,0 +1,19 @@ +//@no-rustfix +#![warn(clippy::filter_next)] + +fn main() {} + +// The fixed version doesn't compile, as `iter` isn't `mut`. +// We do emit a note suggesting adding it, but not an autofix +pub fn issue10029() { + { + let iter = (0..10); + let _ = iter.filter(|_| true).next(); + //~^ filter_next + } + { + let iter = (0..10); + let _ = iter.filter(|_| true).next_back(); + //~^ filter_next + } +} diff --git a/src/tools/clippy/tests/ui/filter_next_unfixable.stderr b/src/tools/clippy/tests/ui/filter_next_unfixable.stderr new file mode 100644 index 0000000000000..2c35badec4853 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next_unfixable.stderr @@ -0,0 +1,38 @@ +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next_unfixable.rs:11:17 + | +LL | let _ = iter.filter(|_| true).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you will also need to make `iter` mutable, because `find` takes `&mut self` + --> tests/ui/filter_next_unfixable.rs:10:13 + | +LL | let iter = (0..10); + | ^^^^ + = note: `-D clippy::filter-next` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` +help: use `.find(..)` instead + | +LL - let _ = iter.filter(|_| true).next(); +LL + let _ = iter.find(|_| true); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next_unfixable.rs:16:17 + | +LL | let _ = iter.filter(|_| true).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you will also need to make `iter` mutable, because `rfind` takes `&mut self` + --> tests/ui/filter_next_unfixable.rs:15:13 + | +LL | let iter = (0..10); + | ^^^^ +help: use `.rfind(..)` instead + | +LL - let _ = iter.filter(|_| true).next_back(); +LL + let _ = iter.rfind(|_| true); + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr index 3f2ca76826f45..b14d0585f2c96 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr @@ -1,121 +1,122 @@ -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:19:13 | LL | let _ = a * b + c; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` | + = note: the performance gain from `mul_add` may vary depending on the target architecture = note: `-D clippy::suboptimal-flops` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:21:13 | LL | let _ = a * b - c; | ^^^^^^^^^ help: consider using: `a.mul_add(b, -c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:23:13 | LL | let _ = c + a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:25:13 | LL | let _ = c - a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(-b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:27:13 | LL | let _ = a + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:29:13 | LL | let _ = a + 2. * 4.; | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:32:13 | LL | let _ = (a * b) + c; | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:34:13 | LL | let _ = c + (a * b); | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:36:13 | LL | let _ = a * b * c + d; | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:39:13 | LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:41:13 | LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:44:13 | LL | let _ = (a * a + b).sqrt(); | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:48:13 | LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:102:13 | LL | let _ = 0.5 + 2.0 * x; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:104:13 | LL | let _ = 2.0 * x + 0.5; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:107:13 | LL | let _ = x + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:111:13 | LL | let _ = y * 2.0 + 0.5; | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:113:13 | LL | let _ = 1.0 * 2.0 + 0.5; | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:122:5 | LL | a += b * c; | ^^^^^^^^^^ help: consider using: `a = b.mul_add(c, a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:125:5 | LL | a -= b * c; diff --git a/src/tools/clippy/tests/ui/let_underscore_future.rs b/src/tools/clippy/tests/ui/let_underscore_future.rs index eb1a985a91172..17c73876302dd 100644 --- a/src/tools/clippy/tests/ui/let_underscore_future.rs +++ b/src/tools/clippy/tests/ui/let_underscore_future.rs @@ -1,4 +1,5 @@ use std::future::Future; +use std::pin::Pin; async fn some_async_fn() {} @@ -10,6 +11,10 @@ fn custom() -> impl Future { fn do_something_to_future(future: &mut impl Future) {} +fn boxed() -> Pin>> { + Box::pin(async {}) +} + fn main() { let _ = some_async_fn(); //~^ let_underscore_future @@ -21,4 +26,7 @@ fn main() { do_something_to_future(&mut future); let _ = future; //~^ let_underscore_future + + // Typed bindings are an intentional discard, see also `let_underscore_untyped`. + let _: Pin>> = boxed(); } diff --git a/src/tools/clippy/tests/ui/let_underscore_future.stderr b/src/tools/clippy/tests/ui/let_underscore_future.stderr index baa489551d4a3..12cbfff133e9c 100644 --- a/src/tools/clippy/tests/ui/let_underscore_future.stderr +++ b/src/tools/clippy/tests/ui/let_underscore_future.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:14:5 + --> tests/ui/let_underscore_future.rs:19:5 | LL | let _ = some_async_fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = some_async_fn(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_future)]` error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:17:5 + --> tests/ui/let_underscore_future.rs:22:5 | LL | let _ = custom(); | ^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = custom(); = help: consider awaiting the future or dropping explicitly with `std::mem::drop` error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:22:5 + --> tests/ui/let_underscore_future.rs:27:5 | LL | let _ = future; | ^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.fixed b/src/tools/clippy/tests/ui/manual_abs_diff.fixed index 2766942140ce4..3b5cac5dfc986 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.fixed +++ b/src/tools/clippy/tests/ui/manual_abs_diff.fixed @@ -51,6 +51,7 @@ fn main() { } // FIXME: bunch of patterns that should be linted +#[expect(clippy::needless_late_init)] fn fixme() { let a: usize = 5; let b: usize = 3; diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.rs b/src/tools/clippy/tests/ui/manual_abs_diff.rs index 2c408f2be3754..f845dd9b149a9 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.rs +++ b/src/tools/clippy/tests/ui/manual_abs_diff.rs @@ -61,6 +61,7 @@ fn main() { } // FIXME: bunch of patterns that should be linted +#[expect(clippy::needless_late_init)] fn fixme() { let a: usize = 5; let b: usize = 3; diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.stderr b/src/tools/clippy/tests/ui/manual_abs_diff.stderr index bb6d312b435f8..b441ac9751f05 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.stderr +++ b/src/tools/clippy/tests/ui/manual_abs_diff.stderr @@ -80,7 +80,7 @@ LL | let _ = if a > b { (a - b) as u32 } else { (b - a) as u32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` error: manual absolute difference pattern without using `abs_diff` - --> tests/ui/manual_abs_diff.rs:119:5 + --> tests/ui/manual_abs_diff.rs:120:5 | LL | / if a < b { LL | | diff --git a/src/tools/clippy/tests/ui/manual_option_zip.fixed b/src/tools/clippy/tests/ui/manual_option_zip.fixed index 0a903fb784075..8b821f8737cbb 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.fixed +++ b/src/tools/clippy/tests/ui/manual_option_zip.fixed @@ -21,11 +21,6 @@ fn should_lint() { let _ = None::.zip(b); //~^ manual_option_zip - // with function call as map receiver - let a: Option = Some(1); - let _ = a.zip(get_option()); - //~^ manual_option_zip - // tuple order reversed: (inner, outer) instead of (outer, inner) let a: Option = Some(1); let b: Option = Some(2); @@ -123,3 +118,26 @@ fn issue16968() { let opts = [1, 2]; let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b))); } + +fn issue17253() { + // don't trigger the lint if map receiver is a lazy evaluated expression + // because `a.zip(b_func())` requires eager evaluation of the argument, + // preventing the otherwise conditional execution of `b_func()` + + use std::hint::black_box; + let a: Option = Some(1); + + // conditional function call + let _ = a.and_then(|a| black_box(get_option()).map(|b| (a, b))); + + let mut b = 2; + + // conditional side effects + let _ = a.and_then(|a| { + { + b /= 2; + Some(b) + } + .map(|b| (a, b)) + }); +} diff --git a/src/tools/clippy/tests/ui/manual_option_zip.rs b/src/tools/clippy/tests/ui/manual_option_zip.rs index 942d8aea3e835..5d059a98ee12c 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.rs +++ b/src/tools/clippy/tests/ui/manual_option_zip.rs @@ -21,11 +21,6 @@ fn should_lint() { let _ = None::.and_then(|a| b.map(|b| (a, b))); //~^ manual_option_zip - // with function call as map receiver - let a: Option = Some(1); - let _ = a.and_then(|a| get_option().map(|b| (a, b))); - //~^ manual_option_zip - // tuple order reversed: (inner, outer) instead of (outer, inner) let a: Option = Some(1); let b: Option = Some(2); @@ -123,3 +118,26 @@ fn issue16968() { let opts = [1, 2]; let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b))); } + +fn issue17253() { + // don't trigger the lint if map receiver is a lazy evaluated expression + // because `a.zip(b_func())` requires eager evaluation of the argument, + // preventing the otherwise conditional execution of `b_func()` + + use std::hint::black_box; + let a: Option = Some(1); + + // conditional function call + let _ = a.and_then(|a| black_box(get_option()).map(|b| (a, b))); + + let mut b = 2; + + // conditional side effects + let _ = a.and_then(|a| { + { + b /= 2; + Some(b) + } + .map(|b| (a, b)) + }); +} diff --git a/src/tools/clippy/tests/ui/manual_option_zip.stderr b/src/tools/clippy/tests/ui/manual_option_zip.stderr index 473f21702654c..55175660b7a26 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.stderr +++ b/src/tools/clippy/tests/ui/manual_option_zip.stderr @@ -20,34 +20,28 @@ LL | let _ = None::.and_then(|a| b.map(|b| (a, b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `None::.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:26:13 - | -LL | let _ = a.and_then(|a| get_option().map(|b| (a, b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(get_option())` - -error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:32:13 + --> tests/ui/manual_option_zip.rs:27:13 | LL | let _ = a.and_then(|a| b.map(|b| (b, a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.zip(a)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:39:13 + --> tests/ui/manual_option_zip.rs:34:13 | LL | let _ = a.and_then(|a| { b.map(|b| (a, b)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:42:13 + --> tests/ui/manual_option_zip.rs:37:13 | LL | let _ = a.and_then(|a| b.map(|b| { (a, b) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:45:13 + --> tests/ui/manual_option_zip.rs:40:13 | LL | let _ = a.and_then(|a| { b.map(|b| { (a, b) }) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.fixed b/src/tools/clippy/tests/ui/manual_slice_fill.fixed index 38ab10b539c18..aa8037af771c2 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_fill.fixed @@ -34,6 +34,38 @@ fn should_lint() { some_slice.fill(0); } +fn should_lint_direct_mutref_array(s: &mut [u8; 1]) { + s.fill(0); +} + +fn should_lint_direct_mutref_array_non_zero(s: &mut [u8; 4]) { + s.fill(42); +} + +fn should_lint_direct_mutref_array_variable(s: &mut [i32; 3]) { + let x = 7; + s.fill(x); +} + +fn should_not_lint_direct_mutref_array_fn(s: &mut [usize; 2]) { + for slot in s { + *slot = num(); + } +} + +fn should_not_lint_direct_mutref_array_iter_used(s: &mut [u8; 3]) { + for slot in s { + *slot = !*slot; + } +} + +fn should_not_lint_direct_mutref_array_extra_stmt(s: &mut [u8; 2]) { + for slot in s { + *slot = 0; + println!("foo"); + } +} + fn should_not_lint() { let mut some_slice = [1, 2, 3, 4, 5]; diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.rs b/src/tools/clippy/tests/ui/manual_slice_fill.rs index d80b23ce985d1..6ca200a046295 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.rs +++ b/src/tools/clippy/tests/ui/manual_slice_fill.rs @@ -47,6 +47,47 @@ fn should_lint() { } } +fn should_lint_direct_mutref_array(s: &mut [u8; 1]) { + for slot in s { + //~^ manual_slice_fill + *slot = 0; + } +} + +fn should_lint_direct_mutref_array_non_zero(s: &mut [u8; 4]) { + for slot in s { + //~^ manual_slice_fill + *slot = 42; + } +} + +fn should_lint_direct_mutref_array_variable(s: &mut [i32; 3]) { + let x = 7; + for slot in s { + //~^ manual_slice_fill + *slot = x; + } +} + +fn should_not_lint_direct_mutref_array_fn(s: &mut [usize; 2]) { + for slot in s { + *slot = num(); + } +} + +fn should_not_lint_direct_mutref_array_iter_used(s: &mut [u8; 3]) { + for slot in s { + *slot = !*slot; + } +} + +fn should_not_lint_direct_mutref_array_extra_stmt(s: &mut [u8; 2]) { + for slot in s { + *slot = 0; + println!("foo"); + } +} + fn should_not_lint() { let mut some_slice = [1, 2, 3, 4, 5]; diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.stderr b/src/tools/clippy/tests/ui/manual_slice_fill.stderr index 38e43d5b4e06e..cd1f36f4291f8 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.stderr +++ b/src/tools/clippy/tests/ui/manual_slice_fill.stderr @@ -38,5 +38,32 @@ LL | | // foo LL | | } | |_____^ help: try: `some_slice.fill(0);` -error: aborting due to 4 previous errors +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:51:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = 0; +LL | | } + | |_____^ help: try: `s.fill(0);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:58:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = 42; +LL | | } + | |_____^ help: try: `s.fill(42);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:66:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = x; +LL | | } + | |_____^ help: try: `s.fill(x);` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.rs b/src/tools/clippy/tests/ui/map_unwrap_or.rs index bcbb14d5c2e1b..cfad8efc23979 100644 --- a/src/tools/clippy/tests/ui/map_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/map_unwrap_or.rs @@ -161,3 +161,10 @@ fn issue15752() { x.map(|y| y.0).unwrap_or(&[]); //~^ map_unwrap_or } + +fn issue16901() { + let raw = String::from("scope:value"); + let after_scope = raw.split_once(':').map(|(_, v)| v).unwrap_or(&raw); + //~^ map_unwrap_or + let _: &str = after_scope; +} diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.stderr b/src/tools/clippy/tests/ui/map_unwrap_or.stderr index a90da4a97e0c8..cd62c17848583 100644 --- a/src/tools/clippy/tests/ui/map_unwrap_or.stderr +++ b/src/tools/clippy/tests/ui/map_unwrap_or.stderr @@ -238,5 +238,11 @@ error: called `map().unwrap_or(
)` on an `Option` value LL | x.map(|y| y.0).unwrap_or(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: called `map().unwrap_or()` on an `Option` value + --> tests/ui/map_unwrap_or.rs:167:23 + | +LL | let after_scope = raw.split_once(':').map(|(_, v)| v).unwrap_or(&raw); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index f73ec563dceb1..10f0934bede74 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -1,36 +1,10 @@ -//@aux-build:option_helpers.rs - -#![allow( - clippy::disallowed_names, - clippy::default_trait_access, - clippy::let_underscore_untyped, - clippy::missing_docs_in_private_items, - clippy::missing_safety_doc, - clippy::non_ascii_literal, - clippy::new_without_default, - clippy::needless_pass_by_value, - clippy::needless_lifetimes, - clippy::elidable_lifetime_names, - clippy::print_stdout, - clippy::must_use_candidate, - clippy::use_self, - clippy::useless_format, - clippy::wrong_self_convention, - clippy::unused_async, - clippy::unused_self, - clippy::useless_vec -)] - -#[macro_use] -extern crate option_helpers; +#![warn(clippy::filter_next, clippy::new_ret_no_self)] use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; -use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; - struct Lt<'a> { foo: &'a u32, } @@ -115,43 +89,4 @@ impl Mul for T { } } -/// Checks implementation of `FILTER_NEXT` lint. -#[rustfmt::skip] -fn filter_next() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case. - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next(); - - // Check that we don't lint if the caller is not an `Iterator`. - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.filter().next(); - - let foo = IteratorMethodFalsePositives {}; - let _ = foo.filter(42).next(); -} - -#[rustfmt::skip] -fn filter_next_back() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case. - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next_back(); - - // Check that we don't lint if the caller is not an `Iterator`. - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.filter().next_back(); - - let foo = IteratorMethodFalsePositives {}; - let _ = foo.filter(42).next_back(); -} - fn main() {} diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index 45efea4ee01cd..1136b33ea9104 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> tests/ui/methods.rs:102:5 + --> tests/ui/methods.rs:76:5 | LL | / fn new() -> i32 { LL | | @@ -10,30 +10,5 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::new_ret_no_self)]` -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods.rs:124:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next(); - | |___________________________^ - | - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods.rs:143:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next_back(); - | |________________________________^ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/methods_fixable.fixed b/src/tools/clippy/tests/ui/methods_fixable.fixed deleted file mode 100644 index 287d8d881ec28..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.fixed +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::filter_next)] -#![allow(clippy::useless_vec)] - -/// Checks implementation of `FILTER_NEXT` lint. -fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Single-line case. - let _ = v.iter().find(|&x| *x < 0); - //~^ filter_next - - let _ = v.iter().rfind(|&x| *x < 0); - //~^ filter_next -} - -#[clippy::msrv = "1.27"] -fn msrv_1_27() { - let _ = vec![1].into_iter().rfind(|&x| x < 0); - //~^ filter_next -} - -#[clippy::msrv = "1.26"] -fn msrv_1_26() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); -} diff --git a/src/tools/clippy/tests/ui/methods_fixable.rs b/src/tools/clippy/tests/ui/methods_fixable.rs deleted file mode 100644 index 11ce1b63560db..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::filter_next)] -#![allow(clippy::useless_vec)] - -/// Checks implementation of `FILTER_NEXT` lint. -fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Single-line case. - let _ = v.iter().filter(|&x| *x < 0).next(); - //~^ filter_next - - let _ = v.iter().filter(|&x| *x < 0).next_back(); - //~^ filter_next -} - -#[clippy::msrv = "1.27"] -fn msrv_1_27() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); - //~^ filter_next -} - -#[clippy::msrv = "1.26"] -fn msrv_1_26() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); -} diff --git a/src/tools/clippy/tests/ui/methods_fixable.stderr b/src/tools/clippy/tests/ui/methods_fixable.stderr deleted file mode 100644 index d26b5e9ac271e..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_fixable.rs:9:13 - | -LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().find(|&x| *x < 0)` - | - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods_fixable.rs:12:13 - | -LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods_fixable.rs:18:13 - | -LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/methods_unfixable.rs b/src/tools/clippy/tests/ui/methods_unfixable.rs deleted file mode 100644 index 46a5d95eb1b72..0000000000000 --- a/src/tools/clippy/tests/ui/methods_unfixable.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![warn(clippy::filter_next)] -//@no-rustfix -fn main() {} - -pub fn issue10029() { - let iter = (0..10); - let _ = iter.filter(|_| true).next(); - //~^ filter_next -} diff --git a/src/tools/clippy/tests/ui/methods_unfixable.stderr b/src/tools/clippy/tests/ui/methods_unfixable.stderr deleted file mode 100644 index 137112ae41795..0000000000000 --- a/src/tools/clippy/tests/ui/methods_unfixable.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_unfixable.rs:7:13 - | -LL | let _ = iter.filter(|_| true).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)` - | -help: you will also need to make `iter` mutable, because `find` takes `&mut self` - --> tests/ui/methods_unfixable.rs:6:9 - | -LL | let iter = (0..10); - | ^^^^ - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui/min_ident_chars.rs b/src/tools/clippy/tests/ui/min_ident_chars.rs index e2f82e2a182f6..298798be7a1e6 100644 --- a/src/tools/clippy/tests/ui/min_ident_chars.rs +++ b/src/tools/clippy/tests/ui/min_ident_chars.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs -#![allow(irrefutable_let_patterns, nonstandard_style, unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::min_ident_chars)] +#![expect(irrefutable_let_patterns, nonstandard_style)] +#![allow(clippy::struct_field_names)] extern crate proc_macros; use proc_macros::{external, with_span}; diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs index ee19d3ff71421..cc97e496514bc 100644 --- a/src/tools/clippy/tests/ui/min_max.rs +++ b/src/tools/clippy/tests/ui/min_max.rs @@ -1,4 +1,5 @@ -#![allow(clippy::manual_clamp)] +#![warn(clippy::min_max)] +#![expect(clippy::manual_clamp)] use std::cmp::{max as my_max, max, min as my_min, min}; diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr index 87510a465a08b..84b4d37545529 100644 --- a/src/tools/clippy/tests/ui/min_max.stderr +++ b/src/tools/clippy/tests/ui/min_max.stderr @@ -1,79 +1,80 @@ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:21:5 + --> tests/ui/min_max.rs:22:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ | - = note: `#[deny(clippy::min_max)]` on by default + = note: `-D clippy::min-max` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::min_max)]` error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:24:5 + --> tests/ui/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:27:5 + --> tests/ui/min_max.rs:28:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:30:5 + --> tests/ui/min_max.rs:31:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:33:5 + --> tests/ui/min_max.rs:34:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:44:5 + --> tests/ui/min_max.rs:45:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:47:5 + --> tests/ui/min_max.rs:48:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:53:5 + --> tests/ui/min_max.rs:54:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:56:5 + --> tests/ui/min_max.rs:57:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:59:5 + --> tests/ui/min_max.rs:60:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:66:5 + --> tests/ui/min_max.rs:67:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:71:5 + --> tests/ui/min_max.rs:72:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:74:5 + --> tests/ui/min_max.rs:75:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs index 4627bef28a29b..c624774a8a9cf 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs @@ -1,4 +1,3 @@ -#![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] fn main() {} diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr index 809b1cfe73bf0..842bd5c238618 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr @@ -1,5 +1,5 @@ error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:13:19 + --> tests/ui/min_rust_version_attr.rs:12:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let log2_10 = 3.321928094887362; = note: `#[deny(clippy::approx_constant)]` on by default error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:19:19 + --> tests/ui/min_rust_version_attr.rs:18:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:30:19 + --> tests/ui/min_rust_version_attr.rs:29:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:41:19 + --> tests/ui/min_rust_version_attr.rs:40:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:52:19 + --> tests/ui/min_rust_version_attr.rs:51:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:60:27 + --> tests/ui/min_rust_version_attr.rs:59:27 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/mismatching_type_param_order.rs b/src/tools/clippy/tests/ui/mismatching_type_param_order.rs index 11ff865b583bf..49d7031441066 100644 --- a/src/tools/clippy/tests/ui/mismatching_type_param_order.rs +++ b/src/tools/clippy/tests/ui/mismatching_type_param_order.rs @@ -1,5 +1,4 @@ #![warn(clippy::mismatching_type_param_order)] -#![allow(clippy::disallowed_names, clippy::needless_lifetimes)] fn main() { struct Foo { diff --git a/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr b/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr index 214d9d734d1bb..a1b5ca7ec26c7 100644 --- a/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr +++ b/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr @@ -1,5 +1,5 @@ error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:11:20 + --> tests/ui/mismatching_type_param_order.rs:10:20 | LL | impl Foo {} | ^ @@ -9,7 +9,7 @@ LL | impl Foo {} = help: to override `-D warnings` add `#[allow(clippy::mismatching_type_param_order)]` error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:11:23 + --> tests/ui/mismatching_type_param_order.rs:10:23 | LL | impl Foo {} | ^ @@ -17,7 +17,7 @@ LL | impl Foo {} = help: try `B`, or a name that does not conflict with `Foo`'s generic params error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:16:23 + --> tests/ui/mismatching_type_param_order.rs:15:23 | LL | impl Foo {} | ^ @@ -25,7 +25,7 @@ LL | impl Foo {} = help: try `B`, or a name that does not conflict with `Foo`'s generic params error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:28:44 + --> tests/ui/mismatching_type_param_order.rs:27:44 | LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} | ^ @@ -33,7 +33,7 @@ LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:28:47 + --> tests/ui/mismatching_type_param_order.rs:27:47 | LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} | ^ @@ -41,7 +41,7 @@ LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:27 + --> tests/ui/mismatching_type_param_order.rs:45:27 | LL | impl FooEnum {} | ^ @@ -49,7 +49,7 @@ LL | impl FooEnum {} = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:30 + --> tests/ui/mismatching_type_param_order.rs:45:30 | LL | impl FooEnum {} | ^ @@ -57,7 +57,7 @@ LL | impl FooEnum {} = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:33 + --> tests/ui/mismatching_type_param_order.rs:45:33 | LL | impl FooEnum {} | ^ @@ -65,7 +65,7 @@ LL | impl FooEnum {} = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:60:31 + --> tests/ui/mismatching_type_param_order.rs:59:31 | LL | impl FooUnion where A: Copy {} | ^ @@ -73,7 +73,7 @@ LL | impl FooUnion where A: Copy {} = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:60:34 + --> tests/ui/mismatching_type_param_order.rs:59:34 | LL | impl FooUnion where A: Copy {} | ^ diff --git a/src/tools/clippy/tests/ui/misnamed_getters.fixed b/src/tools/clippy/tests/ui/misnamed_getters.fixed index bc123d1a40ba2..479d5f91492d5 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters.fixed @@ -1,6 +1,5 @@ -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] +#![expect(clippy::struct_field_names)] struct A { a: u8, diff --git a/src/tools/clippy/tests/ui/misnamed_getters.rs b/src/tools/clippy/tests/ui/misnamed_getters.rs index 6590101157c3f..985c273c59ed7 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters.rs @@ -1,6 +1,5 @@ -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] +#![expect(clippy::struct_field_names)] struct A { a: u8, diff --git a/src/tools/clippy/tests/ui/misnamed_getters.stderr b/src/tools/clippy/tests/ui/misnamed_getters.stderr index aaf21cecb9255..37f57d7f79e55 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:12:5 + --> tests/ui/misnamed_getters.rs:11:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]` error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:17:5 + --> tests/ui/misnamed_getters.rs:16:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -24,7 +24,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:23:5 + --> tests/ui/misnamed_getters.rs:22:5 | LL | / fn b(self) -> u8 { LL | | @@ -35,7 +35,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:29:5 + --> tests/ui/misnamed_getters.rs:28:5 | LL | / fn b_mut(&mut self) -> &mut u8 { LL | | @@ -46,7 +46,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:35:5 + --> tests/ui/misnamed_getters.rs:34:5 | LL | / fn c(&self) -> &u8 { LL | | @@ -57,7 +57,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:41:5 + --> tests/ui/misnamed_getters.rs:40:5 | LL | / fn c_mut(&mut self) -> &mut u8 { LL | | @@ -68,7 +68,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:54:5 + --> tests/ui/misnamed_getters.rs:53:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | @@ -79,7 +79,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:59:5 + --> tests/ui/misnamed_getters.rs:58:5 | LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | @@ -90,7 +90,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:65:5 + --> tests/ui/misnamed_getters.rs:64:5 | LL | / unsafe fn b(self) -> u8 { LL | | @@ -101,7 +101,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:71:5 + --> tests/ui/misnamed_getters.rs:70:5 | LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | @@ -112,7 +112,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:85:5 + --> tests/ui/misnamed_getters.rs:84:5 | LL | / unsafe fn a_unchecked(&self) -> &u8 { LL | | @@ -123,7 +123,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:90:5 + --> tests/ui/misnamed_getters.rs:89:5 | LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -134,7 +134,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:96:5 + --> tests/ui/misnamed_getters.rs:95:5 | LL | / unsafe fn b_unchecked(self) -> u8 { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:102:5 + --> tests/ui/misnamed_getters.rs:101:5 | LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -156,7 +156,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:136:5 + --> tests/ui/misnamed_getters.rs:135:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -167,7 +167,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:141:5 + --> tests/ui/misnamed_getters.rs:140:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -178,7 +178,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:147:5 + --> tests/ui/misnamed_getters.rs:146:5 | LL | / fn d(&self) -> &u8 { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:152:5 + --> tests/ui/misnamed_getters.rs:151:5 | LL | / fn d_mut(&mut self) -> &mut u8 { LL | | diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed index 7112719a9f284..a923da1962fbe 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed @@ -1,6 +1,4 @@ //@edition: 2021 -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] // Edition 2021 specific check, where `unsafe` blocks are not required diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.rs b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs index 19b5d086041f4..bdb011939da64 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs @@ -1,6 +1,4 @@ //@edition: 2021 -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] // Edition 2021 specific check, where `unsafe` blocks are not required diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr index 5495e2e3733f0..9771d2d8b119f 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters_2021.rs:15:5 + --> tests/ui/misnamed_getters_2021.rs:13:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed index 882ff6bf8944a..c5628f2486b0a 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed index de3a0f1710d24..02c4383f45ab7 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs index 62d83d1619c1e..dcb284574ce43 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr index 63f3a3e28f12c..50547a91dedc1 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:6:5 + --> tests/ui/misrefactored_assign_op.rs:7:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -18,7 +18,7 @@ LL + a = a + a + 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:9:5 + --> tests/ui/misrefactored_assign_op.rs:10:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + a = a + 1 + a; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:12:5 + --> tests/ui/misrefactored_assign_op.rs:13:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + a = a - (a - 1); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:15:5 + --> tests/ui/misrefactored_assign_op.rs:16:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL + a = a * a * 99; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:18:5 + --> tests/ui/misrefactored_assign_op.rs:19:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + a = a * 42 * a; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:21:5 + --> tests/ui/misrefactored_assign_op.rs:22:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + a = a / (a / 2); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:24:5 + --> tests/ui/misrefactored_assign_op.rs:25:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + a = a % (a % 5); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:27:5 + --> tests/ui/misrefactored_assign_op.rs:28:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -137,7 +137,7 @@ LL + a = a & a & 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:30:5 + --> tests/ui/misrefactored_assign_op.rs:31:5 | LL | a *= a * a; | ^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs index eb98969efa47f..b8cdb71bd30a9 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::missing_asserts_for_indexing)] fn sum(v: &[u8]) -> u8 { diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr index 2929646494a41..fe929aa36dbad 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -1,5 +1,5 @@ error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:4:5 | LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ @@ -10,7 +10,7 @@ LL | v[0] + v[1] + v[2] + v[3] + v[4] = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:9:13 | LL | let _ = v[0]; | ^^^^ @@ -21,7 +21,7 @@ LL | let _ = v[1..4]; = help: consider asserting the length before indexing: `assert!(v.len() > 3);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:16:13 | LL | let a = v[0]; | ^^^^ @@ -34,7 +34,7 @@ LL | let c = v[2]; = help: consider asserting the length before indexing: `assert!(v.len() > 2);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:25:13 | LL | let _ = v1[0] + v1[12]; | ^^^^^ ^^^^^^ @@ -42,7 +42,7 @@ LL | let _ = v1[0] + v1[12]; = help: consider asserting the length before indexing: `assert!(v1.len() > 12);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:27:13 | LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ @@ -50,7 +50,7 @@ LL | let _ = v2[5] + v2[15]; = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:34:13 | LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ @@ -58,7 +58,7 @@ LL | let _ = v2[5] + v2[15]; = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:44:13 | LL | let _ = f.v[0] + f.v[1]; | ^^^^^^ ^^^^^^ @@ -66,7 +66,7 @@ LL | let _ = f.v[0] + f.v[1]; = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:58:13 | LL | let _ = x[0] + x[1]; | ^^^^ ^^^^ @@ -74,7 +74,7 @@ LL | let _ = x[0] + x[1]; = help: consider asserting the length before indexing: `assert!(x.len() > 1);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:76:13 | LL | let _ = v1[1] + v1[2]; | ^^^^^ ^^^^^ @@ -82,7 +82,7 @@ LL | let _ = v1[1] + v1[2]; = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:84:13 | LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ ^^^^^ ^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index 46a7f79ac16ce..bc2d5b9d1c7b4 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] +#![expect(clippy::let_and_return)] #![feature(const_trait_impl)] use std::mem::transmute; diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 78e1939a85973..26baebfc535e2 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] +#![expect(clippy::let_and_return)] #![feature(const_trait_impl)] use std::mem::transmute; diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs index b206f4d8f8ce3..86c97ce32b687 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::missing_fields_in_debug)] use std::fmt; diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr index 75b551e1f5c7e..4ea837e47836d 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr @@ -1,5 +1,5 @@ error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:14:1 + --> tests/ui/missing_fields_in_debug.rs:13:1 | LL | / impl fmt::Debug for NamedStruct1Ignored { ... | @@ -7,7 +7,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:11:5 + --> tests/ui/missing_fields_in_debug.rs:10:5 | LL | hidden: u32, | ^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | hidden: u32, = help: to override `-D warnings` add `#[allow(clippy::missing_fields_in_debug)]` error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:34:1 + --> tests/ui/missing_fields_in_debug.rs:33:1 | LL | / impl fmt::Debug for NamedStructMultipleIgnored { ... | @@ -25,17 +25,17 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:28:5 + --> tests/ui/missing_fields_in_debug.rs:27:5 | LL | hidden: u32, | ^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:29:5 + --> tests/ui/missing_fields_in_debug.rs:28:5 | LL | hidden2: String, | ^^^^^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:31:5 + --> tests/ui/missing_fields_in_debug.rs:30:5 | LL | hidden4: ((((u8), u16), u32), u64), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +43,7 @@ LL | hidden4: ((((u8), u16), u32), u64), = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:97:1 + --> tests/ui/missing_fields_in_debug.rs:96:1 | LL | / impl fmt::Debug for MultiExprDebugImpl { LL | | @@ -54,7 +54,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:93:5 + --> tests/ui/missing_fields_in_debug.rs:92:5 | LL | b: String, | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs index 8e937d609512a..0adaeaa9cab8a 100644 --- a/src/tools/clippy/tests/ui/missing_inline.rs +++ b/src/tools/clippy/tests/ui/missing_inline.rs @@ -2,7 +2,7 @@ #![crate_type = "dylib"] // When denying at the crate level, be sure to not get random warnings from the // injected intrinsics by the compiler. -#![allow(dead_code, non_snake_case)] +#![expect(non_snake_case)] type Typedef = String; pub type PubTypedef = String; diff --git a/src/tools/clippy/tests/ui/missing_inline.stderr b/src/tools/clippy/tests/ui/missing_inline.stderr index 99ee828e805ca..9f5987fb8e79b 100644 --- a/src/tools/clippy/tests/ui/missing_inline.stderr +++ b/src/tools/clippy/tests/ui/missing_inline.stderr @@ -1,4 +1,4 @@ -error: missing `#[inline]` for a function +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:20:1 | LL | pub fn pub_foo() {} @@ -7,31 +7,31 @@ LL | pub fn pub_foo() {} = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_inline_in_public_items)]` -error: missing `#[inline]` for a default trait method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:39:5 | LL | fn PubBar_b() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:56:5 | LL | fn PubBar_a() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:60:5 | LL | fn PubBar_b() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:64:5 | LL | fn PubBar_c() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:76:5 | LL | pub fn PubFooImpl() {} diff --git a/src/tools/clippy/tests/ui/missing_inline_executable.stderr b/src/tools/clippy/tests/ui/missing_inline_executable.stderr index 3108e4e490659..d083cb2b54915 100644 --- a/src/tools/clippy/tests/ui/missing_inline_executable.stderr +++ b/src/tools/clippy/tests/ui/missing_inline_executable.stderr @@ -1,4 +1,4 @@ -error: missing `#[inline]` for a function +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline_executable.rs:3:1 | LL | pub fn foo() {} diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index d016e099e303b..63e84ffaab634 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -1,6 +1,6 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_panics_doc)] -#![allow(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)] +#![expect(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.fixed b/src/tools/clippy/tests/ui/missing_spin_loop.fixed index 03fbf8ccc8a37..7876a0c47d5e7 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.fixed +++ b/src/tools/clippy/tests/ui/missing_spin_loop.fixed @@ -1,6 +1,5 @@ #![warn(clippy::missing_spin_loop)] -#![allow(clippy::bool_comparison)] -#![allow(unused_braces)] +#![expect(clippy::bool_comparison)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.rs b/src/tools/clippy/tests/ui/missing_spin_loop.rs index bf18590b9408e..06bfe8eeaab80 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.rs +++ b/src/tools/clippy/tests/ui/missing_spin_loop.rs @@ -1,6 +1,5 @@ #![warn(clippy::missing_spin_loop)] -#![allow(clippy::bool_comparison)] -#![allow(unused_braces)] +#![expect(clippy::bool_comparison)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.stderr b/src/tools/clippy/tests/ui/missing_spin_loop.stderr index 98c32d0423469..d4a93bb2d75b7 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.stderr +++ b/src/tools/clippy/tests/ui/missing_spin_loop.stderr @@ -1,5 +1,5 @@ error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:10:37 + --> tests/ui/missing_spin_loop.rs:9:37 | LL | while b.load(Ordering::Acquire) {} | ^^ help: try: `{ std::hint::spin_loop() }` @@ -8,31 +8,31 @@ LL | while b.load(Ordering::Acquire) {} = help: to override `-D warnings` add `#[allow(clippy::missing_spin_loop)]` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:13:37 + --> tests/ui/missing_spin_loop.rs:12:37 | LL | while !b.load(Ordering::SeqCst) {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:16:46 + --> tests/ui/missing_spin_loop.rs:15:46 | LL | while b.load(Ordering::Acquire) == false {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:19:49 + --> tests/ui/missing_spin_loop.rs:18:49 | LL | while { true == b.load(Ordering::Acquire) } {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:22:93 + --> tests/ui/missing_spin_loop.rs:21:93 | LL | while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:25:94 + --> tests/ui/missing_spin_loop.rs:24:94 | LL | while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {} | ^^ help: try: `{ std::hint::spin_loop() }` diff --git a/src/tools/clippy/tests/ui/missing_trait_methods.rs b/src/tools/clippy/tests/ui/missing_trait_methods.rs index 67070a2999510..90349a0de101f 100644 --- a/src/tools/clippy/tests/ui/missing_trait_methods.rs +++ b/src/tools/clippy/tests/ui/missing_trait_methods.rs @@ -1,5 +1,5 @@ -#![allow(unused, clippy::needless_lifetimes)] #![warn(clippy::missing_trait_methods)] +#![expect(clippy::needless_lifetimes)] trait A { fn provided() {} diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed index 58faeaee09d46..2602a2fda5135 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_transmute_annotations)] -#![allow(clippy::let_with_type_underscore)] +#![expect(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs index c9a4c5fa83b2b..fe7b7238d33fe 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_transmute_annotations)] -#![allow(clippy::let_with_type_underscore)] +#![expect(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed index a0190acc5d4b3..3a7d714375a77 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed @@ -1,10 +1,8 @@ //@aux-build: proc_macros.rs -#![allow( - dead_code, - unused_variables, +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( overflowing_literals, - clippy::excessive_precision, clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings )] diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs index 12a26204e7557..02e76a468d210 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs @@ -1,10 +1,8 @@ //@aux-build: proc_macros.rs -#![allow( - dead_code, - unused_variables, +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( overflowing_literals, - clippy::excessive_precision, clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings )] diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr index 9c6b2fab21c31..a986bc0e69ec0 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr @@ -1,97 +1,98 @@ error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:16:18 + --> tests/ui/mistyped_literal_suffix.rs:14:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` | - = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default + = note: `-D clippy::mistyped-literal-suffixes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mistyped_literal_suffixes)]` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:18:18 + --> tests/ui/mistyped_literal_suffix.rs:16:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:20:18 + --> tests/ui/mistyped_literal_suffix.rs:18:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:23:18 + --> tests/ui/mistyped_literal_suffix.rs:21:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:28:18 + --> tests/ui/mistyped_literal_suffix.rs:26:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:31:18 + --> tests/ui/mistyped_literal_suffix.rs:29:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:36:18 + --> tests/ui/mistyped_literal_suffix.rs:34:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:38:18 + --> tests/ui/mistyped_literal_suffix.rs:36:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:40:18 + --> tests/ui/mistyped_literal_suffix.rs:38:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:42:18 + --> tests/ui/mistyped_literal_suffix.rs:40:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:47:18 + --> tests/ui/mistyped_literal_suffix.rs:45:18 | LL | let fail30 = 127_8; // should be i8 | ^^^^^ help: did you mean to write: `127_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:50:18 + --> tests/ui/mistyped_literal_suffix.rs:48:18 | LL | let fail31 = 240_8; // should be u8 | ^^^^^ help: did you mean to write: `240_u8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:54:18 + --> tests/ui/mistyped_literal_suffix.rs:52:18 | LL | let fail33 = 0x1234_16; | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:56:18 + --> tests/ui/mistyped_literal_suffix.rs:54:18 | LL | let fail34 = 0xABCD_16; | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:59:18 + --> tests/ui/mistyped_literal_suffix.rs:57:18 | LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:67:13 + --> tests/ui/mistyped_literal_suffix.rs:65:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs index 5d16858bf85ec..8fdce71d05528 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.rs +++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs @@ -1,7 +1,6 @@ //@compile-flags: --test #![warn(clippy::module_name_repetitions)] -#![allow(dead_code)] pub mod foo { pub fn foo() {} diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr index 9000c44a3902e..e4c3daacdbead 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.stderr +++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr @@ -1,5 +1,5 @@ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:8:12 + --> tests/ui/module_name_repetitions.rs:7:12 | LL | pub fn foo_bar() {} | ^^^^^^^ @@ -8,31 +8,31 @@ LL | pub fn foo_bar() {} = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]` error: item name ends with its containing module's name - --> tests/ui/module_name_repetitions.rs:11:12 + --> tests/ui/module_name_repetitions.rs:10:12 | LL | pub fn bar_foo() {} | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:14:16 + --> tests/ui/module_name_repetitions.rs:13:16 | LL | pub struct FooCake; | ^^^^^^^ error: item name ends with its containing module's name - --> tests/ui/module_name_repetitions.rs:17:14 + --> tests/ui/module_name_repetitions.rs:16:14 | LL | pub enum CakeFoo {} | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:20:16 + --> tests/ui/module_name_repetitions.rs:19:16 | LL | pub struct Foo7Bar; | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:33:20 + --> tests/ui/module_name_repetitions.rs:32:20 | LL | pub use error::FooError; | ^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs index 0e08174f686eb..46d554616f8df 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs @@ -1,7 +1,7 @@ #![feature(f128)] #![feature(f16)] #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![expect(clippy::no_effect)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs index d7cd910e43cb7..583637dbc2855 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs @@ -1,5 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![expect(clippy::no_effect)] fn main() { // Lint on signed integral numbers diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs index 9f5fb697df0ea..160bc86629247 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs @@ -1,10 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::modulo_one, - clippy::identity_op -)] +#![expect(clippy::identity_op, clippy::modulo_one, clippy::no_effect)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr index 12edee8cf4255..e09673213f79a 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:11:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:6:5 | LL | -1 % 2; | ^^^^^^ @@ -10,7 +10,7 @@ LL | -1 % 2; = help: to override `-D warnings` add `#[allow(clippy::modulo_arithmetic)]` error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:14:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:9:5 | LL | 1 % -2; | ^^^^^^ @@ -19,7 +19,7 @@ LL | 1 % -2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 3` - --> tests/ui/modulo_arithmetic_integral_const.rs:17:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:12:5 | LL | (1 - 2) % (1 + 2); | ^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | (1 - 2) % (1 + 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `3 % -1` - --> tests/ui/modulo_arithmetic_integral_const.rs:20:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:15:5 | LL | (1 + 2) % (1 - 2); | ^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | (1 + 2) % (1 - 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-35 % 300000` - --> tests/ui/modulo_arithmetic_integral_const.rs:23:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:18:5 | LL | 35 * (7 - 4 * 2) % (-500 * -600); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | 35 * (7 - 4 * 2) % (-500 * -600); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:26:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:21:5 | LL | -1i8 % 2i8; | ^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | -1i8 % 2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:29:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:24:5 | LL | 1i8 % -2i8; | ^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | 1i8 % -2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:32:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:27:5 | LL | -1i16 % 2i16; | ^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | -1i16 % 2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:35:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:30:5 | LL | 1i16 % -2i16; | ^^^^^^^^^^^^ @@ -82,7 +82,7 @@ LL | 1i16 % -2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:38:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:33:5 | LL | -1i32 % 2i32; | ^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | -1i32 % 2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:41:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:36:5 | LL | 1i32 % -2i32; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | 1i32 % -2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:44:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:39:5 | LL | -1i64 % 2i64; | ^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | -1i64 % 2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:47:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:42:5 | LL | 1i64 % -2i64; | ^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | 1i64 % -2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:50:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:45:5 | LL | -1i128 % 2i128; | ^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | -1i128 % 2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:53:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:48:5 | LL | 1i128 % -2i128; | ^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | 1i128 % -2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:56:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:51:5 | LL | -1isize % 2isize; | ^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | -1isize % 2isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:59:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:54:5 | LL | 1isize % -2isize; | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/modulo_one.rs b/src/tools/clippy/tests/ui/modulo_one.rs index 63ae61059cb86..730c37d0e368e 100644 --- a/src/tools/clippy/tests/ui/modulo_one.rs +++ b/src/tools/clippy/tests/ui/modulo_one.rs @@ -1,6 +1,5 @@ #![warn(clippy::modulo_one)] -#![allow(unconditional_panic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)] +#![expect(clippy::identity_op, clippy::no_effect)] static STATIC_ONE: usize = 2 - 1; static STATIC_NEG_ONE: i64 = 1 - 2; diff --git a/src/tools/clippy/tests/ui/modulo_one.stderr b/src/tools/clippy/tests/ui/modulo_one.stderr index 1c9c79d1c6214..f841ac18bbc3d 100644 --- a/src/tools/clippy/tests/ui/modulo_one.stderr +++ b/src/tools/clippy/tests/ui/modulo_one.stderr @@ -1,5 +1,5 @@ error: any number modulo 1 will be 0 - --> tests/ui/modulo_one.rs:9:5 + --> tests/ui/modulo_one.rs:8:5 | LL | 10 % 1; | ^^^^^^ @@ -8,31 +8,31 @@ LL | 10 % 1; = help: to override `-D warnings` add `#[allow(clippy::modulo_one)]` error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:12:5 + --> tests/ui/modulo_one.rs:11:5 | LL | 10 % -1; | ^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:17:5 + --> tests/ui/modulo_one.rs:16:5 | LL | i32::MIN % (-1); | ^^^^^^^^^^^^^^^ error: any number modulo 1 will be 0 - --> tests/ui/modulo_one.rs:24:5 + --> tests/ui/modulo_one.rs:23:5 | LL | 2 % ONE; | ^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:29:5 + --> tests/ui/modulo_one.rs:28:5 | LL | 2 % NEG_ONE; | ^^^^^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:35:5 + --> tests/ui/modulo_one.rs:34:5 | LL | INT_MIN % NEG_ONE; | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs b/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs index dcef1a485fceb..fe83023aaeb79 100644 --- a/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs +++ b/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs @@ -1,6 +1,5 @@ //@check-pass -#![allow(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)] #![forbid(clippy::ptr_as_ptr)] /// MSRV checking in late passes skips checking the parent nodes if no early pass sees a diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs index 0ff881472cbb9..5d51c47eeed91 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,11 +1,11 @@ //@needs-asm-support //@aux-build:proc_macros.rs +#![warn(clippy::multiple_unsafe_ops_per_block)] #![expect( dropping_copy_types, - clippy::unnecessary_operation, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::unnecessary_operation )] -#![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index 17569884658b1..226fafccddced 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -1,10 +1,4 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut, - static_mut_refs -)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::Arc; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index 8b898533d01b7..f9b6c2986504c 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -1,10 +1,4 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut, - static_mut_refs -)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::Arc; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.stderr b/src/tools/clippy/tests/ui/must_use_candidates.stderr index 38c7334322503..24fda87cd16aa 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.stderr +++ b/src/tools/clippy/tests/ui/must_use_candidates.stderr @@ -1,5 +1,5 @@ error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:16:8 + --> tests/ui/must_use_candidates.rs:10:8 | LL | pub fn pure(i: u8) -> u8 { | ^^^^ @@ -13,7 +13,7 @@ LL | pub fn pure(i: u8) -> u8 { | error: this method could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:22:12 + --> tests/ui/must_use_candidates.rs:16:12 | LL | pub fn inherent_pure(&self) -> u8 { | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL ~ pub fn inherent_pure(&self) -> u8 { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:54:8 + --> tests/ui/must_use_candidates.rs:48:8 | LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:67:8 + --> tests/ui/must_use_candidates.rs:61:8 | LL | pub fn rcd(_x: Rc) -> bool { | ^^^ @@ -49,7 +49,7 @@ LL | pub fn rcd(_x: Rc) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:76:8 + --> tests/ui/must_use_candidates.rs:70:8 | LL | pub fn arcd(_x: Arc) -> bool { | ^^^^ @@ -61,7 +61,7 @@ LL | pub fn arcd(_x: Arc) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:108:8 + --> tests/ui/must_use_candidates.rs:102:8 | LL | pub fn result_uninhabited() -> Result { | ^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL | pub fn result_uninhabited() -> Result { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:113:8 + --> tests/ui/must_use_candidates.rs:107:8 | LL | pub fn controlflow_uninhabited() -> std::ops::ControlFlow { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed index b9871991b16f0..5c1993f6575a2 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.fixed +++ b/src/tools/clippy/tests/ui/must_use_unit.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] -#![allow(clippy::unused_unit)] +#![expect(clippy::unused_unit)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs index a5865681c8a8e..86b4380a477aa 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.rs +++ b/src/tools/clippy/tests/ui/must_use_unit.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] -#![allow(clippy::unused_unit)] +#![expect(clippy::unused_unit)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs index 1b0b351518cbb..f91e2992406b7 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.rs +++ b/src/tools/clippy/tests/ui/mut_from_ref.rs @@ -1,11 +1,10 @@ -#![allow( - unused, +#![warn(clippy::mut_from_ref)] +#![expect( + clippy::boxed_local, clippy::needless_lifetimes, clippy::needless_pass_by_ref_mut, - clippy::redundant_allocation, - clippy::boxed_local + clippy::redundant_allocation )] -#![warn(clippy::mut_from_ref)] struct Foo; diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr index 0974268734653..92165eb9ae735 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.stderr +++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr @@ -1,11 +1,11 @@ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:13:39 + --> tests/ui/mut_from_ref.rs:12:39 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:13:29 + --> tests/ui/mut_from_ref.rs:12:29 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^ @@ -13,85 +13,85 @@ LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { = help: to override `-D warnings` add `#[allow(clippy::mut_from_ref)]` error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:21:25 + --> tests/ui/mut_from_ref.rs:20:25 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:21:16 + --> tests/ui/mut_from_ref.rs:20:16 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:31:21 + --> tests/ui/mut_from_ref.rs:30:21 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:31:12 + --> tests/ui/mut_from_ref.rs:30:12 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:37:50 + --> tests/ui/mut_from_ref.rs:36:50 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:37:25 + --> tests/ui/mut_from_ref.rs:36:25 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:43:67 + --> tests/ui/mut_from_ref.rs:42:67 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:43:27 + --> tests/ui/mut_from_ref.rs:42:27 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:49:46 + --> tests/ui/mut_from_ref.rs:48:46 | LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:49:24 + --> tests/ui/mut_from_ref.rs:48:24 | LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:55:37 + --> tests/ui/mut_from_ref.rs:54:37 | LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:55:24 + --> tests/ui/mut_from_ref.rs:54:24 | LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:85:35 + --> tests/ui/mut_from_ref.rs:84:35 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:85:26 + --> tests/ui/mut_from_ref.rs:84:26 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^ diff --git a/src/tools/clippy/tests/ui/mut_mut.fixed b/src/tools/clippy/tests/ui/mut_mut.fixed index f9a7f5dcb5a15..c34f605e3da6e 100644 --- a/src/tools/clippy/tests/ui/mut_mut.fixed +++ b/src/tools/clippy/tests/ui/mut_mut.fixed @@ -1,13 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::mut_mut)] -#![allow(unused)] -#![allow( - clippy::no_effect, - clippy::uninlined_format_args, - clippy::unnecessary_operation, - clippy::needless_pass_by_ref_mut -)] extern crate proc_macros; use proc_macros::{external, inline_macros}; @@ -26,7 +19,6 @@ macro_rules! mut_ptr { }; } -#[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { let mut x = &mut 1u32; diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs index bbec48011a17e..d17394dfe3f69 100644 --- a/src/tools/clippy/tests/ui/mut_mut.rs +++ b/src/tools/clippy/tests/ui/mut_mut.rs @@ -1,13 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::mut_mut)] -#![allow(unused)] -#![allow( - clippy::no_effect, - clippy::uninlined_format_args, - clippy::unnecessary_operation, - clippy::needless_pass_by_ref_mut -)] extern crate proc_macros; use proc_macros::{external, inline_macros}; @@ -26,7 +19,6 @@ macro_rules! mut_ptr { }; } -#[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { let mut x = &mut &mut 1u32; diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr index 85e9c5649b89a..0a7a923e0f48f 100644 --- a/src/tools/clippy/tests/ui/mut_mut.stderr +++ b/src/tools/clippy/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:15:11 + --> tests/ui/mut_mut.rs:8:11 | LL | fn fun(x: &mut &mut u32) { | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` @@ -8,37 +8,37 @@ LL | fn fun(x: &mut &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:32:17 + --> tests/ui/mut_mut.rs:24:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference - --> tests/ui/mut_mut.rs:35:21 + --> tests/ui/mut_mut.rs:27:21 | LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:40:32 + --> tests/ui/mut_mut.rs:32:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:40:16 + --> tests/ui/mut_mut.rs:32:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:37 + --> tests/ui/mut_mut.rs:38:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:16 + --> tests/ui/mut_mut.rs:38:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut u32` diff --git a/src/tools/clippy/tests/ui/mut_mut_unfixable.rs b/src/tools/clippy/tests/ui/mut_mut_unfixable.rs index 271cb7b968898..0c6444fd9fa35 100644 --- a/src/tools/clippy/tests/ui/mut_mut_unfixable.rs +++ b/src/tools/clippy/tests/ui/mut_mut_unfixable.rs @@ -1,7 +1,6 @@ //@no-rustfix #![warn(clippy::mut_mut)] -#![allow(unused)] #![expect(clippy::no_effect)] //! removing the extra `&mut`s will break the derefs diff --git a/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr b/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr index cf66eb2ed1eca..7e7fb801ce1e7 100644 --- a/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr +++ b/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr @@ -1,5 +1,5 @@ error: a type of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:9:11 + --> tests/ui/mut_mut_unfixable.rs:8:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` @@ -8,31 +8,31 @@ LL | fn fun(x: &mut &mut u32) -> bool { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:15:17 + --> tests/ui/mut_mut_unfixable.rs:14:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference - --> tests/ui/mut_mut_unfixable.rs:18:21 + --> tests/ui/mut_mut_unfixable.rs:17:21 | LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:24:17 + --> tests/ui/mut_mut_unfixable.rs:23:17 | LL | let y = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:30:17 + --> tests/ui/mut_mut_unfixable.rs:29:17 | LL | let y = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:39:17 + --> tests/ui/mut_mut_unfixable.rs:38:17 | LL | let y = &mut &mut x; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut x` diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.fixed b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed index 5790ee8c1b022..44587b89ec836 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.fixed +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.rs b/src/tools/clippy/tests/ui/mut_mutex_lock.rs index 7286afac823b7..eef4f26c09595 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.rs +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.stderr b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr index 67543c73c6eef..587405449523c 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.stderr +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr @@ -1,5 +1,5 @@ error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> tests/ui/mut_mutex_lock.rs:10:33 + --> tests/ui/mut_mutex_lock.rs:9:33 | LL | let mut value = value_mutex.lock().unwrap(); | ^^^^ help: change this to: `get_mut` @@ -8,7 +8,7 @@ LL | let mut value = value_mutex.lock().unwrap(); = help: to override `-D warnings` add `#[allow(clippy::mut_mutex_lock)]` error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> tests/ui/mut_mutex_lock.rs:16:43 + --> tests/ui/mut_mutex_lock.rs:15:43 | LL | let mut value = mut_ref_mut_ref_mutex.lock().unwrap(); | ^^^^ help: change this to: `get_mut` diff --git a/src/tools/clippy/tests/ui/mut_range_bound.rs b/src/tools/clippy/tests/ui/mut_range_bound.rs index 107a6229b86d8..4e1192a86c2c1 100644 --- a/src/tools/clippy/tests/ui/mut_range_bound.rs +++ b/src/tools/clippy/tests/ui/mut_range_bound.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![warn(clippy::mut_range_bound)] fn main() {} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.fixed b/src/tools/clippy/tests/ui/mutex_atomic.fixed index dc05d8a2c61fe..711aba8da37b6 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.fixed +++ b/src/tools/clippy/tests/ui/mutex_atomic.fixed @@ -1,6 +1,5 @@ -#![warn(clippy::mutex_integer)] -#![warn(clippy::mutex_atomic)] -#![allow(clippy::borrow_as_ptr)] +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] +#![expect(clippy::borrow_as_ptr)] use std::sync::Mutex; diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index 33745f8fc5e13..f0ca75458d100 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -1,6 +1,5 @@ -#![warn(clippy::mutex_integer)] -#![warn(clippy::mutex_atomic)] -#![allow(clippy::borrow_as_ptr)] +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] +#![expect(clippy::borrow_as_ptr)] use std::sync::Mutex; diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index c1391e0111c8d..85c1829f44121 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:8:13 + --> tests/ui/mutex_atomic.rs:7:13 | LL | let _ = Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL + let _ = std::sync::atomic::AtomicBool::new(true); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:11:13 + --> tests/ui/mutex_atomic.rs:10:13 | LL | let _ = Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL + let _ = std::sync::atomic::AtomicUsize::new(5usize); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:14:13 + --> tests/ui/mutex_atomic.rs:13:13 | LL | let _ = Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL + let _ = std::sync::atomic::AtomicIsize::new(9isize); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:21:13 + --> tests/ui/mutex_atomic.rs:20:13 | LL | let _ = Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + let _ = std::sync::atomic::AtomicPtr::new(&mut x as *mut u32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:24:13 + --> tests/ui/mutex_atomic.rs:23:13 | LL | let _ = Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL + let _ = std::sync::atomic::AtomicU32::new(0u32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:27:13 + --> tests/ui/mutex_atomic.rs:26:13 | LL | let _ = Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL + let _ = std::sync::atomic::AtomicI32::new(0i32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:31:13 + --> tests/ui/mutex_atomic.rs:30:13 | LL | let _ = Mutex::new(0u8); | ^^^^^^^^^^^^^^^ @@ -94,7 +94,7 @@ LL + let _ = std::sync::atomic::AtomicU8::new(0u8); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:34:13 + --> tests/ui/mutex_atomic.rs:33:13 | LL | let _ = Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = std::sync::atomic::AtomicI16::new(0i16); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:37:25 + --> tests/ui/mutex_atomic.rs:36:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + let _x = std::sync::atomic::AtomicI8::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:41:13 + --> tests/ui/mutex_atomic.rs:40:13 | LL | let _ = Mutex::new(X); | ^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + let _ = std::sync::atomic::AtomicI64::new(X); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:53:30 + --> tests/ui/mutex_atomic.rs:52:30 | LL | static MTX: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -146,7 +146,7 @@ LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32 | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:56:15 + --> tests/ui/mutex_atomic.rs:55:15 | LL | let mtx = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL + let mtx = std::sync::atomic::AtomicI32::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:60:22 + --> tests/ui/mutex_atomic.rs:59:22 | LL | let reassigned = mtx; | ^^^ @@ -168,7 +168,7 @@ LL | let reassigned = mtx; = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:65:35 + --> tests/ui/mutex_atomic.rs:64:35 | LL | let (funky_mtx): Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:76:13 + --> tests/ui/mutex_atomic.rs:75:13 | LL | let _ = Mutex::new(test_expr!(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed index adb9096c9f242..0eef094895dd8 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_arbitrary_self_type)] -#![allow(unused_mut, clippy::needless_lifetimes)] +#![expect(clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs index 550546ed24fd7..0ea16ce89946b 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_arbitrary_self_type)] -#![allow(unused_mut, clippy::needless_lifetimes)] +#![expect(clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed index eedeecd8a8cbc..a5c34939a5c9d 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] -#![allow(clippy::const_is_empty)] +#![expect(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs index 5c0a4ccf266c3..ca03d888f3650 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] -#![allow(clippy::const_is_empty)] +#![expect(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed index 0af4f87bec677..c3eed4e3fd717 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -1,15 +1,6 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::equatable_if_let, - clippy::needless_ifs, - clippy::needless_return, - clippy::self_named_constructors, - clippy::struct_field_names -)] +#![allow(clippy::no_effect)] +#![expect(clippy::needless_return)] use std::cell::Cell; @@ -21,7 +12,6 @@ macro_rules! bool_comparison_trigger { $($i: (Cell, bool, bool)),+ } - #[allow(dead_code)] impl Trigger { pub fn trigger(&self, key: &str) -> bool { $( diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs index 2e8719bd041e0..4571628be20ea 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -1,15 +1,6 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::equatable_if_let, - clippy::needless_ifs, - clippy::needless_return, - clippy::self_named_constructors, - clippy::struct_field_names -)] +#![allow(clippy::no_effect)] +#![expect(clippy::needless_return)] use std::cell::Cell; @@ -21,7 +12,6 @@ macro_rules! bool_comparison_trigger { $($i: (Cell, bool, bool)),+ } - #[allow(dead_code)] impl Trigger { pub fn trigger(&self, key: &str) -> bool { $( diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr index e15260242f4dd..f8cad52c2bbf2 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:41:5 + --> tests/ui/needless_bool/fixable.rs:31:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:47:5 + --> tests/ui/needless_bool/fixable.rs:37:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:53:5 + --> tests/ui/needless_bool/fixable.rs:43:5 | LL | / if x && y { LL | | false @@ -32,7 +32,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!(x && y)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:62:5 + --> tests/ui/needless_bool/fixable.rs:52:5 | LL | / if a == b { LL | | false @@ -42,7 +42,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a != b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:68:5 + --> tests/ui/needless_bool/fixable.rs:58:5 | LL | / if a != b { LL | | false @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a == b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:74:5 + --> tests/ui/needless_bool/fixable.rs:64:5 | LL | / if a < b { LL | | false @@ -62,7 +62,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a >= b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:80:5 + --> tests/ui/needless_bool/fixable.rs:70:5 | LL | / if a <= b { LL | | false @@ -72,7 +72,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a > b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:86:5 + --> tests/ui/needless_bool/fixable.rs:76:5 | LL | / if a > b { LL | | false @@ -82,7 +82,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a <= b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:92:5 + --> tests/ui/needless_bool/fixable.rs:82:5 | LL | / if a >= b { LL | | false @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a < b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:121:5 + --> tests/ui/needless_bool/fixable.rs:111:5 | LL | / if x { LL | | return true; @@ -102,7 +102,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:130:5 + --> tests/ui/needless_bool/fixable.rs:120:5 | LL | / if x { LL | | return false; @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:139:5 + --> tests/ui/needless_bool/fixable.rs:129:5 | LL | / if x && y { LL | | return true; @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:148:5 + --> tests/ui/needless_bool/fixable.rs:138:5 | LL | / if x && y { LL | | return false; @@ -132,7 +132,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> tests/ui/needless_bool/fixable.rs:157:8 + --> tests/ui/needless_bool/fixable.rs:147:8 | LL | if x == true {}; | ^^^^^^^^^ help: try: `x` @@ -141,25 +141,25 @@ LL | if x == true {}; = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against false can be replaced by a negation - --> tests/ui/needless_bool/fixable.rs:162:8 + --> tests/ui/needless_bool/fixable.rs:152:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary - --> tests/ui/needless_bool/fixable.rs:173:8 + --> tests/ui/needless_bool/fixable.rs:163:8 | LL | if x == true {}; | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation - --> tests/ui/needless_bool/fixable.rs:175:8 + --> tests/ui/needless_bool/fixable.rs:165:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try: `!x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:185:12 + --> tests/ui/needless_bool/fixable.rs:175:12 | LL | } else if returns_bool() { | ____________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: you can reduce it to: `{ !returns_bool() }` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:199:5 + --> tests/ui/needless_bool/fixable.rs:189:5 | LL | / if unsafe { no(4) } & 1 != 0 { LL | | true @@ -180,37 +180,37 @@ LL | | }; | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:205:30 + --> tests/ui/needless_bool/fixable.rs:195:30 | LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:209:9 + --> tests/ui/needless_bool/fixable.rs:199:9 | LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:221:14 + --> tests/ui/needless_bool/fixable.rs:211:14 | LL | let _x = if a && b { true } else { false }.then(|| todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:223:14 + --> tests/ui/needless_bool/fixable.rs:213:14 | LL | let _x = if a && b { true } else { false } as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:227:14 + --> tests/ui/needless_bool/fixable.rs:217:14 | LL | let _x = if a { true } else { false }.then(|| todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `a` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:239:5 + --> tests/ui/needless_bool/fixable.rs:229:5 | LL | / if test_expr!(x) { LL | | true diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.rs b/src/tools/clippy/tests/ui/needless_bool/simple.rs index 40ffeae6c56fa..c5d2ef51fade5 100644 --- a/src/tools/clippy/tests/ui/needless_bool/simple.rs +++ b/src/tools/clippy/tests/ui/needless_bool/simple.rs @@ -1,12 +1,5 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::needless_return, - clippy::branches_sharing_code -)] +#![expect(clippy::if_same_then_else, clippy::needless_return)] fn main() { let x = true; diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.stderr b/src/tools/clippy/tests/ui/needless_bool/simple.stderr index 077e9b5df47cf..07711b9234cdd 100644 --- a/src/tools/clippy/tests/ui/needless_bool/simple.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/simple.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression will always return true - --> tests/ui/needless_bool/simple.rs:14:5 + --> tests/ui/needless_bool/simple.rs:7:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression will always return false - --> tests/ui/needless_bool/simple.rs:20:5 + --> tests/ui/needless_bool/simple.rs:13:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return true - --> tests/ui/needless_bool/simple.rs:36:5 + --> tests/ui/needless_bool/simple.rs:29:5 | LL | / if x { LL | | return true; @@ -32,7 +32,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return false - --> tests/ui/needless_bool/simple.rs:45:5 + --> tests/ui/needless_bool/simple.rs:38:5 | LL | / if x { LL | | return false; diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.fixed b/src/tools/clippy/tests/ui/needless_bool_assign.fixed index 8fd5720381404..3bd592b471e4b 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.fixed +++ b/src/tools/clippy/tests/ui/needless_bool_assign.fixed @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::needless_bool_assign)] fn random() -> bool { diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.rs b/src/tools/clippy/tests/ui/needless_bool_assign.rs index 4721ab433b327..1279176cb20a2 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.rs +++ b/src/tools/clippy/tests/ui/needless_bool_assign.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::needless_bool_assign)] fn random() -> bool { diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.stderr b/src/tools/clippy/tests/ui/needless_bool_assign.stderr index 34ff782f34a35..3dfea7cd1af99 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.stderr +++ b/src/tools/clippy/tests/ui/needless_bool_assign.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:13:5 + --> tests/ui/needless_bool_assign.rs:12:5 | LL | / if random() && random() { LL | | a.field = true; @@ -12,7 +12,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::needless_bool_assign)]` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:19:5 + --> tests/ui/needless_bool_assign.rs:18:5 | LL | / if random() && random() { LL | | a.field = false; @@ -22,7 +22,7 @@ LL | | } | |_____^ help: you can reduce it to: `a.field = !(random() && random());` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:34:5 + --> tests/ui/needless_bool_assign.rs:33:5 | LL | / if random() { LL | | a.field = true; @@ -32,7 +32,7 @@ LL | | } | |_____^ help: you can reduce it to: `random(); a.field = true;` error: this `if` has identical blocks - --> tests/ui/needless_bool_assign.rs:34:17 + --> tests/ui/needless_bool_assign.rs:33:17 | LL | if random() { | _________________^ @@ -41,7 +41,7 @@ LL | | } else { | |_____^ | note: same as this - --> tests/ui/needless_bool_assign.rs:36:12 + --> tests/ui/needless_bool_assign.rs:35:12 | LL | } else { | ____________^ @@ -52,7 +52,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:54:12 + --> tests/ui/needless_bool_assign.rs:53:12 | LL | } else if x || y { | ____________^ @@ -63,7 +63,7 @@ LL | | } | |_____^ help: you can reduce it to: `{ z = x || y; }` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:77:5 + --> tests/ui/needless_bool_assign.rs:76:5 | LL | / if invoke!(must_keep, x, y) { LL | | dot_0!(skip) = false; diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index b686a8e9f1a06..a5c9d435b3eea 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -185,25 +185,24 @@ fn does_not_lint() { }; // using tuples would be possible, but not always preferable - let x; - let y; - if true { - x = 1; - y = 2; + + + let (x, y) = if true { + //~^ needless_late_init + (1, 2) } else { - x = 3; - y = 4; - } + (3, 4) + }; - // could match with a smarter heuristic to avoid multiple assignments - let x; - if true { + + //~^ needless_late_init + let x = if true { let mut y = 5; y = 6; - x = y; + y } else { - x = 2; - } + 2 + }; let (x, y); if true { @@ -299,3 +298,167 @@ fn issue9895() { //~^ needless_late_init let r = 5; } + +fn if_or_match_in_block_expr() { + + //~^ needless_late_init + let z = if true { + 1 + } else { + 2 + }; +} + +fn issue16330() { + // Late init in both if branches, should lint + + + let (a, b) = if true { + //~^ needless_late_init + (1, 2) + } else { + (3, 4) + }; + + // One of the variables is not late init, should not lint + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + // One of the variables is defined outside the block, should not lint + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + // Late init in all match arms, should lint + + + + let (a, b, c) = match 1 { + //~^ needless_late_init + 1 => { + (1, 2, 3) + }, + _ if false => { + (4, 5, 6) + }, + _ => { + (7, 8, 9) + }, + }; + + // Late init in all if branches, should lint + + + + let (a, b, c) = if true { + //~^ needless_late_init + (1, 2, 3) + } else if false { + (4, 5, 6) + } else { + (7, 8, 9) + }; + + // One of the variables is not assigned in all branches, should not lint + let a; + let b; + let c; + if true { + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is assigned multiple times, should not lint + let mut a; + let b; + if true { + a = 1; + b = 2; + a = 3; + } else { + a = 4; + b = 5; + } + + // One of the variables is assigned in a nested block, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 4; + { + b = 5; + } + } + + // The order of the variables is different in different branches, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + b = 5; + a = 4; + } + + // Later assignments depend on the earlier ones, should only lint the last ones + let a; + let b; + + //~^ needless_late_init + let c = if true { + a = 1; + b = a + 1; + b + 1 + } else { + a = 4; + b = a + 2; + b + 2 + }; + let a; + let b; + + + let (c, d) = if true { + //~^ needless_late_init + a = 1; + b = a + 1; + (b + 1, 1) + } else { + a = 4; + b = a + 2; + (b + 2, 2) + }; +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index 23772ff702931..ee413814750c7 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -188,6 +188,7 @@ fn does_not_lint() { let x; let y; if true { + //~^ needless_late_init x = 1; y = 2; } else { @@ -195,8 +196,8 @@ fn does_not_lint() { y = 4; } - // could match with a smarter heuristic to avoid multiple assignments let x; + //~^ needless_late_init if true { let mut y = 5; y = 6; @@ -299,3 +300,183 @@ fn issue9895() { //~^ needless_late_init (r = 5); } + +fn if_or_match_in_block_expr() { + let z; + //~^ needless_late_init + if true { + z = 1; + } else { + z = 2; + } +} + +fn issue16330() { + // Late init in both if branches, should lint + let a; + let b; + if true { + //~^ needless_late_init + a = 1; + b = 2; + } else { + a = 3; + b = 4; + } + + // One of the variables is not late init, should not lint + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + // One of the variables is defined outside the block, should not lint + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + // Late init in all match arms, should lint + let a; + let b; + let c; + match 1 { + //~^ needless_late_init + 1 => { + a = 1; + b = 2; + c = 3; + }, + _ if false => { + a = 4; + b = 5; + c = 6; + }, + _ => { + a = 7; + b = 8; + c = 9; + }, + } + + // Late init in all if branches, should lint + let a; + let b; + let c; + if true { + //~^ needless_late_init + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + b = 5; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is not assigned in all branches, should not lint + let a; + let b; + let c; + if true { + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is assigned multiple times, should not lint + let mut a; + let b; + if true { + a = 1; + b = 2; + a = 3; + } else { + a = 4; + b = 5; + } + + // One of the variables is assigned in a nested block, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 4; + { + b = 5; + } + } + + // The order of the variables is different in different branches, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + b = 5; + a = 4; + } + + // Later assignments depend on the earlier ones, should only lint the last ones + let a; + let b; + let c; + //~^ needless_late_init + if true { + a = 1; + b = a + 1; + c = b + 1; + } else { + a = 4; + b = a + 2; + c = b + 2; + } + let a; + let b; + let c; + let d; + if true { + //~^ needless_late_init + a = 1; + b = a + 1; + c = b + 1; + d = 1; + } else { + a = 4; + b = a + 2; + c = b + 2; + d = 2; + } +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index e3e25cdc8d769..12bfc029d9488 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -276,7 +276,50 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:298:5 + --> tests/ui/needless_late_init.rs:199:5 + | +LL | let x; + | ^^^^^^ + | +help: move the declaration `x` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let x = if true { +LL | let mut y = 5; +LL | y = 6; +LL ~ y +LL | } else { +LL ~ 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:190:5 + | +LL | / if true { +LL | | +LL | | x = 1; +LL | | y = 2; +... | +LL | | y = 4; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (x, y) = if true { +LL | +LL ~ (1, 2) +LL | } else { +LL ~ (3, 4) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:299:5 | LL | let r; | ^^^^^^ created here @@ -291,5 +334,153 @@ LL | LL ~ let r = 5; | -error: aborting due to 17 previous errors +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:305:5 + | +LL | let z; + | ^^^^^^ + | +help: move the declaration `z` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let z = if true { +LL ~ 1 +LL | } else { +LL ~ 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:455:5 + | +LL | let c; + | ^^^^^^ + | +help: move the declaration `c` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let c = if true { +LL | a = 1; +LL | b = a + 1; +LL ~ b + 1 +LL | } else { +LL | a = 4; +LL | b = a + 2; +LL ~ b + 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:318:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = 2; +... | +LL | | b = 4; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (a, b) = if true { +LL | +LL ~ (1, 2) +LL | } else { +LL ~ (3, 4) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:361:5 + | +LL | / match 1 { +LL | | +LL | | 1 => { +LL | | a = 1; +... | +LL | | }, +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the `match` arms + | +LL ~ +LL ~ +LL ~ +LL ~ let (a, b, c) = match 1 { +LL | +LL | 1 => { +LL ~ (1, 2, 3) +LL | }, +LL | _ if false => { +LL ~ (4, 5, 6) +LL | }, +LL | _ => { +LL ~ (7, 8, 9) +LL | }, +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:384:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = 2; +... | +LL | | c = 9; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ +LL ~ let (a, b, c) = if true { +LL | +LL ~ (1, 2, 3) +LL | } else if false { +LL ~ (4, 5, 6) +LL | } else { +LL ~ (7, 8, 9) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:470:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = a + 1; +... | +LL | | d = 2; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (c, d) = if true { +LL | +LL | a = 1; +LL | b = a + 1; +LL ~ (b + 1, 1) +LL | } else { +LL | a = 4; +LL | b = a + 2; +LL ~ (b + 2, 2) +LL ~ }; + | + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/ref_patterns.rs b/src/tools/clippy/tests/ui/ref_patterns.rs index 2905af13ccb63..3628cc3d537c0 100644 --- a/src/tools/clippy/tests/ui/ref_patterns.rs +++ b/src/tools/clippy/tests/ui/ref_patterns.rs @@ -19,4 +19,14 @@ fn use_in_binding() { fn use_in_parameter(ref x: i32) {} //~^ ref_patterns +struct Foo {} + +// shouldn't trigger the lint +#[automatically_derived] +impl Foo { + fn foo() { + if let Some(ref x) = Some(1) {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index c27cec558242d..3020ed5c6f39d 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -96,3 +96,23 @@ fn issue15579() { let layout = alloc::Layout::new::(); } + +#[warn(clippy::std_instead_of_core)] +fn issue13158_core_io() { + // items moved from std::io into core::io are stable in an unstable module. + use std::io::ErrorKind; +} + +#[clippy::msrv = "1.40"] +fn issue13158_msrv_1_40(_: &dyn std::panic::UnwindSafe) {} + +#[clippy::msrv = "1.41"] +fn issue13158_msrv_1_41(_: &dyn core::panic::UnwindSafe) {} +//~^ std_instead_of_core + +#[clippy::msrv = "1.80"] +fn issue13158_msrv_1_80(_: &dyn std::error::Error) {} + +#[clippy::msrv = "1.81"] +fn issue13158_msrv_1_81(_: &dyn core::error::Error) {} +//~^ std_instead_of_core diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index 7d53f7fc3072b..49b4218aa8983 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -96,3 +96,23 @@ fn issue15579() { let layout = alloc::Layout::new::(); } + +#[warn(clippy::std_instead_of_core)] +fn issue13158_core_io() { + // items moved from std::io into core::io are stable in an unstable module. + use std::io::ErrorKind; +} + +#[clippy::msrv = "1.40"] +fn issue13158_msrv_1_40(_: &dyn std::panic::UnwindSafe) {} + +#[clippy::msrv = "1.41"] +fn issue13158_msrv_1_41(_: &dyn std::panic::UnwindSafe) {} +//~^ std_instead_of_core + +#[clippy::msrv = "1.80"] +fn issue13158_msrv_1_80(_: &dyn std::error::Error) {} + +#[clippy::msrv = "1.81"] +fn issue13158_msrv_1_81(_: &dyn std::error::Error) {} +//~^ std_instead_of_core diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index a5f8fbbe37cb8..0363852cf8d49 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -97,5 +97,17 @@ error: used import from `std` instead of `core` LL | fn msrv_1_77(_: std::net::IpAddr) {} | ^^^ help: consider importing the item from `core`: `core` -error: aborting due to 15 previous errors +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:110:33 + | +LL | fn issue13158_msrv_1_41(_: &dyn std::panic::UnwindSafe) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:117:33 + | +LL | fn issue13158_msrv_1_81(_: &dyn std::error::Error) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs index 5e71159ac6c5d..66d834b5e4279 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs @@ -14,3 +14,14 @@ fn issue15143() { //~^ std_instead_of_core //~| std_instead_of_alloc } + +#[rustfmt::skip] +fn pr16964() { + use std::{ + borrow::Cow, + //~^ std_instead_of_alloc + collections::BTreeSet, + //~^ std_instead_of_alloc + ffi::OsString, + }; +} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr index 147b46022126b..6fa8f47a4d6f4 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr @@ -26,5 +26,21 @@ LL | use std::{error::Error, vec::Vec, fs::File}; = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` -error: aborting due to 3 previous errors +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:21:17 + | +LL | borrow::Cow, + | ^^^ + | + = help: consider importing the item from `alloc` + +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:23:22 + | +LL | collections::BTreeSet, + | ^^^^^^^^ + | + = help: consider importing the item from `alloc` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index a0bac28e87292..ae5c685d89abd 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -205,3 +205,125 @@ fn main() { } } } + +mod issue_11715 { + use std::mem::MaybeUninit; + #[cfg(target_pointer_width = "64")] + const HUGE: usize = 4_294_967_296; + #[cfg(target_pointer_width = "32")] + const HUGE: usize = 268_435_455; + + fn large_maybeuninit_vec_ice() { + let mut v: Vec<[MaybeUninit; HUGE]> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + fn large_u8_vec_ice() { + let mut v: Vec<[u8; HUGE]> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + fn large_nested_maybeuninit_vec_ice() { + let mut v: Vec<[[MaybeUninit; HUGE]; 2]> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + struct HeavyWrapperSafe([MaybeUninit; HUGE]); + fn large_struct_maybeuninit_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + struct HeavyWrapperUnsafe([u8; HUGE]); + fn large_struct_u8_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + #[allow(clippy::large_enum_variant)] + enum HeavyEnum { + A([MaybeUninit; HUGE]), + B, + } + fn large_enum_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + enum Uninhabited {} + fn uninhabited_enum() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum SingleVariant { + OnlyOne, + } + fn single_variant_enum() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum OneVariantU8 { + ThisOne([u8; HUGE]), + } + fn one_variant_u8() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + enum OneVariantMaybeUninit { + ThisOne([MaybeUninit; HUGE]), + } + fn one_variant_maybe_uninit() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + fn generic_vec_lints() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + fn generic_vec_maybeuninit() { + let mut v: Vec> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + trait Assoc { + type Item; + } + fn projection_vec_lints() { + let mut v: Vec<::Item> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + struct Concrete; + impl Assoc for Concrete { + type Item = MaybeUninit; + } + fn normalized_projection_vec_ok() { + let mut v: Vec<::Item> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum E { + Foo(MaybeUninit), + Bar(U), + } + fn enum_uninhabited_zst() { + let mut v: Vec> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + fn enum_uninhabited_non_zst() { + let mut v: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } +} diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr index 1b821ef004e6f..56a182614b64b 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.stderr +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -159,5 +159,82 @@ LL | vec.set_len(1); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 15 previous errors +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:222:9 + | +LL | let mut v: Vec<[u8; HUGE]> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:240:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:251:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:274:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:288:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:302:9 + | +LL | let mut v: Vec<::Item> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:325:9 + | +LL | let mut v: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.rs b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs index a7ab7e90be06b..e550996256f44 100644 --- a/src/tools/clippy/tests/ui/unnecessary_box_returns.rs +++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs @@ -71,6 +71,23 @@ impl HasHuge { } } +// don't lint (issue #17202): the size of `[T; N]` depends on a const generic parameter +fn const_generic_array(value: Box<[T; N]>) -> Box<[T; N]> { + value +} + +// don't lint (issue #17202): the size of `[T; 10]` depends on the generic element type +fn generic_element_array(value: Box<[T; 10]>) -> Box<[T; 10]> { + value +} + +// lint: the size of `Vec` is known regardless of the generic element type +#[expect(clippy::box_collection)] +fn generic_vec(value: Box>) -> Box> { + //~^ unnecessary_box_returns + value +} + fn main() { // don't lint: this is a closure let a = || -> Box { Box::new(5) }; diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr index ab1d90f13b922..8eb39fca5680b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr @@ -32,5 +32,13 @@ LL | fn _bxed_foo() -> Box { | = help: changing this also requires a change to the return expressions in this function -error: aborting due to 4 previous errors +error: boxed return of the sized type `std::vec::Vec` + --> tests/ui/unnecessary_box_returns.rs:86:42 + | +LL | fn generic_vec(value: Box>) -> Box> { + | ^^^^^^^^^^^ help: try: `std::vec::Vec` + | + = help: changing this also requires a change to the return expressions in this function + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed index 1ecc3ecf57a00..9e07c52e82e67 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed @@ -1,7 +1,9 @@ //@aux-build:extern_fake_libc.rs +#![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow( clippy::borrow_as_ptr, + clippy::identity_op, clippy::multiple_bound_locations, clippy::no_effect, clippy::nonstandard_macro_braces, @@ -656,3 +658,25 @@ fn issue16475() -> *const u8 { //~^ unnecessary_cast } } + +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + use std::convert::identity; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0 == (!identity(!0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0_u64 + 0)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!((0 + 0) as u64)).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0_u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast +}; diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs index 1a6cc831aa4b8..43d3e56e365a2 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -1,7 +1,9 @@ //@aux-build:extern_fake_libc.rs +#![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow( clippy::borrow_as_ptr, + clippy::identity_op, clippy::multiple_bound_locations, clippy::no_effect, clippy::nonstandard_macro_braces, @@ -656,3 +658,25 @@ fn issue16475() -> *const u8 { //~^ unnecessary_cast } } + +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + use std::convert::identity; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0 == (!identity(!0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0 as u64 + 0)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!((0 + 0) as u64)).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0 as u64)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast +}; diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr index 19d2afcb4f255..9807ae0341421 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -1,5 +1,5 @@ error: casting raw pointers to the same type and constness is unnecessary (`*const T` -> `*const T`) - --> tests/ui/unnecessary_cast.rs:19:5 + --> tests/ui/unnecessary_cast.rs:21:5 | LL | ptr as *const T | ^^^^^^^^^^^^^^^ help: try: `ptr` @@ -8,388 +8,430 @@ LL | ptr as *const T = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:55:5 + --> tests/ui/unnecessary_cast.rs:57:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:57:5 + --> tests/ui/unnecessary_cast.rs:59:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> tests/ui/unnecessary_cast.rs:59:5 + --> tests/ui/unnecessary_cast.rs:61:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:63:5 + --> tests/ui/unnecessary_cast.rs:65:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:65:5 + --> tests/ui/unnecessary_cast.rs:67:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:67:5 + --> tests/ui/unnecessary_cast.rs:69:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:69:5 + --> tests/ui/unnecessary_cast.rs:71:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:71:5 + --> tests/ui/unnecessary_cast.rs:73:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:74:22 + --> tests/ui/unnecessary_cast.rs:76:22 | LL | let _: *mut u8 = [1u8, 2].as_ptr() as *const u8 as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:77:5 + --> tests/ui/unnecessary_cast.rs:79:5 | LL | [1u8, 2].as_ptr() as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*mut u8` -> `*mut u8`) - --> tests/ui/unnecessary_cast.rs:80:5 + --> tests/ui/unnecessary_cast.rs:82:5 | LL | [1u8, 2].as_mut_ptr() as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_mut_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> tests/ui/unnecessary_cast.rs:92:5 + --> tests/ui/unnecessary_cast.rs:94:5 | LL | owo::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `owo::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:94:5 + --> tests/ui/unnecessary_cast.rs:96:5 | LL | uwu::([1u32].as_ptr()) as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> tests/ui/unnecessary_cast.rs:97:5 + --> tests/ui/unnecessary_cast.rs:99:5 | LL | uwu::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:133:5 + --> tests/ui/unnecessary_cast.rs:135:5 | LL | aaa() as u32; | ^^^^^^^^^^^^ help: try: `aaa()` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:136:5 + --> tests/ui/unnecessary_cast.rs:138:5 | LL | x as u32; | ^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:138:5 + --> tests/ui/unnecessary_cast.rs:140:5 | LL | bbb() as u32; | ^^^^^^^^^^^^ help: try: `bbb()` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:141:5 + --> tests/ui/unnecessary_cast.rs:143:5 | LL | x as u32; | ^^^^^^^^ help: try: `x` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:174:9 + --> tests/ui/unnecessary_cast.rs:176:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:176:9 + --> tests/ui/unnecessary_cast.rs:178:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:178:9 + --> tests/ui/unnecessary_cast.rs:180:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:180:17 + --> tests/ui/unnecessary_cast.rs:182:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:182:17 + --> tests/ui/unnecessary_cast.rs:184:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:184:17 + --> tests/ui/unnecessary_cast.rs:186:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:186:9 + --> tests/ui/unnecessary_cast.rs:188:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:188:9 + --> tests/ui/unnecessary_cast.rs:190:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:201:9 + --> tests/ui/unnecessary_cast.rs:203:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:203:9 + --> tests/ui/unnecessary_cast.rs:205:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> tests/ui/unnecessary_cast.rs:205:9 + --> tests/ui/unnecessary_cast.rs:207:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> tests/ui/unnecessary_cast.rs:207:9 + --> tests/ui/unnecessary_cast.rs:209:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:209:9 + --> tests/ui/unnecessary_cast.rs:211:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:212:9 + --> tests/ui/unnecessary_cast.rs:214:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:214:9 + --> tests/ui/unnecessary_cast.rs:216:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:219:17 + --> tests/ui/unnecessary_cast.rs:221:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:221:17 + --> tests/ui/unnecessary_cast.rs:223:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting to the same type is unnecessary (`i32` -> `i32`) - --> tests/ui/unnecessary_cast.rs:228:18 + --> tests/ui/unnecessary_cast.rs:230:18 | LL | let _ = &(x as i32); | ^^^^^^^^^^ help: try: `{ x }` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:235:22 + --> tests/ui/unnecessary_cast.rs:237:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> tests/ui/unnecessary_cast.rs:238:22 + --> tests/ui/unnecessary_cast.rs:240:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:246:22 + --> tests/ui/unnecessary_cast.rs:248:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:249:23 + --> tests/ui/unnecessary_cast.rs:251:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> tests/ui/unnecessary_cast.rs:259:20 + --> tests/ui/unnecessary_cast.rs:261:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:270:9 + --> tests/ui/unnecessary_cast.rs:272:9 | LL | (*x as usize).pow(2) | ^^^^^^^^^^^^^ help: try: `(*x)` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:278:31 + --> tests/ui/unnecessary_cast.rs:280:31 | LL | assert_eq!(vec.len(), x as usize); | ^^^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:281:17 + --> tests/ui/unnecessary_cast.rs:283:17 | LL | let _ = (5i32 as i64 as i64).abs(); | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:284:17 + --> tests/ui/unnecessary_cast.rs:286:17 | LL | let _ = 5i32 as i64 as i64; | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:563:24 + --> tests/ui/unnecessary_cast.rs:565:24 | LL | id::>(1.0_f64.pow_like(2) as f64 * &a).view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:567:26 + --> tests/ui/unnecessary_cast.rs:569:26 | LL | s.id::>(1.0_f64.pow_like(2) as f64 * &a).view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:571:26 + --> tests/ui/unnecessary_cast.rs:573:26 | LL | wrap::>(1.0_f64.pow_like(2) as f64 * &a).inner.view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:575:28 + --> tests/ui/unnecessary_cast.rs:577:28 | LL | s.wrap::>(1.0_f64.pow_like(2) as f64 * &a).inner.view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:591:31 + --> tests/ui/unnecessary_cast.rs:593:31 | LL | let _ = receiver.take(1.0_f64.pow_like_single_impl(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:601:20 + --> tests/ui/unnecessary_cast.rs:603:20 | LL | let _ = id(1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:604:22 + --> tests/ui/unnecessary_cast.rs:606:22 | LL | let _ = wrap(1.0_f64.powi(2) as f64).inner.abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:607:22 + --> tests/ui/unnecessary_cast.rs:609:22 | LL | let _ = s.id(1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:610:24 + --> tests/ui/unnecessary_cast.rs:612:24 | LL | let _ = s.wrap(1.0_f64.powi(2) as f64).inner.abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:613:20 + --> tests/ui/unnecessary_cast.rs:615:20 | LL | let _ = id(1.0_f64.powi(2) as f64 * &a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:616:22 + --> tests/ui/unnecessary_cast.rs:618:22 | LL | let _ = s.id(1.0_f64.powi(2) as f64 * &a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:629:17 + --> tests/ui/unnecessary_cast.rs:631:17 | LL | let _ = 1.0_f64.pow_like(0.5) as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(0.5)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:632:17 + --> tests/ui/unnecessary_cast.rs:634:17 | LL | let _ = 1.0_f64.pow_like(2) as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:635:17 + --> tests/ui/unnecessary_cast.rs:637:17 | LL | let _ = (1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:638:17 + --> tests/ui/unnecessary_cast.rs:640:17 | LL | let _ = ((Y + 2) as f64).abs(); | ^^^^^^^^^^^^^^^^ help: try: `((Y + 2))` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:641:18 + --> tests/ui/unnecessary_cast.rs:643:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + 1.0_f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:644:18 + --> tests/ui/unnecessary_cast.rs:646:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + ONE).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:647:18 + --> tests/ui/unnecessary_cast.rs:649:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + one).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting raw pointers to the same type and constness is unnecessary (`*const *const u8` -> `*const *const u8`) - --> tests/ui/unnecessary_cast.rs:655:10 + --> tests/ui/unnecessary_cast.rs:657:10 | LL | *(&NONE as *const _ as *const _ as *const *const u8 as *const *const u8) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&NONE as *const _ as *const _ as *const *const u8)` -error: aborting due to 65 previous errors +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:665:45 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:667:45 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:670:46 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64 + 0).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:672:48 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0 as u64 + 0)).overflowing_shr(1_u32).0); + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:675:54 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:678:56 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0 as u64)).overflowing_shr(1_u32).0); + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:680:54 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64 + 0).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: aborting due to 72 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed index 409a8efbfeb95..c8d6b1832a710 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -240,6 +240,18 @@ fn main() { let _: Result = res.or_else(|err| Ok(err)); } +fn issue11672() { + // needs turbofish to disambiguate + let _ = true.then_some::<&[u8]>({ &[] }); + //~^ unnecessary_lazy_evaluations +} + +fn issue11672_as() { + // Return type annotation helps type inference and removing it can break code + let _ = None.get_or_insert({ &[] } as &[u8]); + //~^ unnecessary_lazy_evaluations +} + #[allow(unused)] fn issue9485() { // should not lint, is in proc macro diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs index 54735023a935d..87e903c5bbb8c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -240,6 +240,18 @@ fn main() { let _: Result = res.or_else(|err| Ok(err)); } +fn issue11672() { + // needs turbofish to disambiguate + let _ = true.then(|| -> &[u8] { &[] }); + //~^ unnecessary_lazy_evaluations +} + +fn issue11672_as() { + // Return type annotation helps type inference and removing it can break code + let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); + //~^ unnecessary_lazy_evaluations +} + #[allow(unused)] fn issue9485() { // should not lint, is in proc macro diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr index 75a06a419c71b..9b91b22f80d3a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr @@ -484,7 +484,31 @@ LL + or(Ok(ext_str.some_field)); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:259:14 + --> tests/ui/unnecessary_lazy_eval.rs:245:13 + | +LL | let _ = true.then(|| -> &[u8] { &[] }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL - let _ = true.then(|| -> &[u8] { &[] }); +LL + let _ = true.then_some::<&[u8]>({ &[] }); + | + +error: unnecessary closure used to substitute value for `Option::None` + --> tests/ui/unnecessary_lazy_eval.rs:251:13 + | +LL | let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `get_or_insert` instead + | +LL - let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); +LL + let _ = None.get_or_insert({ &[] } as &[u8]); + | + +error: unnecessary closure used with `bool::then` + --> tests/ui/unnecessary_lazy_eval.rs:271:14 | LL | let _x = false.then(|| i32::MAX + 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -496,7 +520,7 @@ LL + let _x = false.then_some(i32::MAX + 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:261:14 + --> tests/ui/unnecessary_lazy_eval.rs:273:14 | LL | let _x = false.then(|| i32::MAX * 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -508,7 +532,7 @@ LL + let _x = false.then_some(i32::MAX * 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:263:14 + --> tests/ui/unnecessary_lazy_eval.rs:275:14 | LL | let _x = false.then(|| i32::MAX - 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -520,7 +544,7 @@ LL + let _x = false.then_some(i32::MAX - 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:265:14 + --> tests/ui/unnecessary_lazy_eval.rs:277:14 | LL | let _x = false.then(|| i32::MIN - 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -532,7 +556,7 @@ LL + let _x = false.then_some(i32::MIN - 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:267:14 + --> tests/ui/unnecessary_lazy_eval.rs:279:14 | LL | let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -544,7 +568,7 @@ LL + let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:269:14 + --> tests/ui/unnecessary_lazy_eval.rs:281:14 | LL | let _x = false.then(|| 255u8 << 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -556,7 +580,7 @@ LL + let _x = false.then_some(255u8 << 7); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:271:14 + --> tests/ui/unnecessary_lazy_eval.rs:283:14 | LL | let _x = false.then(|| 255u8 << 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -568,7 +592,7 @@ LL + let _x = false.then_some(255u8 << 8); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:273:14 + --> tests/ui/unnecessary_lazy_eval.rs:285:14 | LL | let _x = false.then(|| 255u8 >> 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -580,7 +604,7 @@ LL + let _x = false.then_some(255u8 >> 8); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:276:14 + --> tests/ui/unnecessary_lazy_eval.rs:288:14 | LL | let _x = false.then(|| i32::MAX + -1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -592,7 +616,7 @@ LL + let _x = false.then_some(i32::MAX + -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:278:14 + --> tests/ui/unnecessary_lazy_eval.rs:290:14 | LL | let _x = false.then(|| -i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -604,7 +628,7 @@ LL + let _x = false.then_some(-i32::MAX); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:280:14 + --> tests/ui/unnecessary_lazy_eval.rs:292:14 | LL | let _x = false.then(|| -i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -616,7 +640,7 @@ LL + let _x = false.then_some(-i32::MIN); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:283:14 + --> tests/ui/unnecessary_lazy_eval.rs:295:14 | LL | let _x = false.then(|| 255 >> -7); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -628,7 +652,7 @@ LL + let _x = false.then_some(255 >> -7); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:285:14 + --> tests/ui/unnecessary_lazy_eval.rs:297:14 | LL | let _x = false.then(|| 255 << -1); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -640,7 +664,7 @@ LL + let _x = false.then_some(255 << -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:287:14 + --> tests/ui/unnecessary_lazy_eval.rs:299:14 | LL | let _x = false.then(|| 1 / 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -652,7 +676,7 @@ LL + let _x = false.then_some(1 / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:289:14 + --> tests/ui/unnecessary_lazy_eval.rs:301:14 | LL | let _x = false.then(|| x << -1); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -664,7 +688,7 @@ LL + let _x = false.then_some(x << -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:291:14 + --> tests/ui/unnecessary_lazy_eval.rs:303:14 | LL | let _x = false.then(|| x << 2); | ^^^^^^^^^^^^^^^^^^^^^ @@ -676,7 +700,7 @@ LL + let _x = false.then_some(x << 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:301:14 + --> tests/ui/unnecessary_lazy_eval.rs:313:14 | LL | let _x = false.then(|| x / 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -688,7 +712,7 @@ LL + let _x = false.then_some(x / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:303:14 + --> tests/ui/unnecessary_lazy_eval.rs:315:14 | LL | let _x = false.then(|| x % 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -700,7 +724,7 @@ LL + let _x = false.then_some(x % 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:306:14 + --> tests/ui/unnecessary_lazy_eval.rs:318:14 | LL | let _x = false.then(|| 1 / -1); | ^^^^^^^^^^^^^^^^^^^^^ @@ -712,7 +736,7 @@ LL + let _x = false.then_some(1 / -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:308:14 + --> tests/ui/unnecessary_lazy_eval.rs:320:14 | LL | let _x = false.then(|| i32::MIN / -1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -724,7 +748,7 @@ LL + let _x = false.then_some(i32::MIN / -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:311:14 + --> tests/ui/unnecessary_lazy_eval.rs:323:14 | LL | let _x = false.then(|| i32::MIN / 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -736,7 +760,7 @@ LL + let _x = false.then_some(i32::MIN / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:313:14 + --> tests/ui/unnecessary_lazy_eval.rs:325:14 | LL | let _x = false.then(|| 4 / 2); | ^^^^^^^^^^^^^^^^^^^^ @@ -748,7 +772,7 @@ LL + let _x = false.then_some(4 / 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:321:14 + --> tests/ui/unnecessary_lazy_eval.rs:333:14 | LL | let _x = false.then(|| f1 + f2); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -759,5 +783,5 @@ LL - let _x = false.then(|| f1 + f2); LL + let _x = false.then_some(f1 + f2); | -error: aborting due to 63 previous errors +error: aborting due to 65 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs index 6d28d544dfe0b..7ceda40597942 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -12,6 +12,8 @@ fn main() { // fix will break type inference let _ = Ok(1).unwrap_or_else(|()| 2); //~^ unnecessary_lazy_evaluations + let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); + //~^ unnecessary_lazy_evaluations mod e { pub struct E; @@ -26,9 +28,3 @@ fn main() { let arr = [(Some(1),)]; Some(&0).and_then(|&i| arr[i].0); } - -fn issue11672() { - // Return type annotation helps type inference and removing it can break code - let _ = true.then(|| -> &[u8] { &[] }); - //~^ unnecessary_lazy_evaluations -} diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 5174acc1e9f95..97e9ef621cee0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -13,7 +13,19 @@ LL + let _ = Ok(1).unwrap_or(2); | error: unnecessary closure used to substitute value for `Result::Err` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:19:13 + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:15:13 + | +LL | let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL - let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); +LL + let _ = Ok(1).unwrap_or({ 2 } as i32); + | + +error: unnecessary closure used to substitute value for `Result::Err` + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:21:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +37,7 @@ LL + let _ = Ok(1).unwrap_or(2); | error: unnecessary closure used to substitute value for `Result::Err` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:22:13 + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:24:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,17 +48,5 @@ LL - let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); LL + let _ = Ok(1).unwrap_or(2); | -error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:32:13 - | -LL | let _ = true.then(|| -> &[u8] { &[] }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `then_some` instead - | -LL - let _ = true.then(|| -> &[u8] { &[] }); -LL + let _ = true.then_some({ &[] }); - | - error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed index 317140eacc785..db31b339a08b0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed @@ -19,9 +19,9 @@ fn unnecessary_sort_by() { //~^ unnecessary_sort_by // Reverse examples vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow - vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs())); + vec.sort_by_key(|a| std::cmp::Reverse((a + 5).abs())); //~^ unnecessary_sort_by - vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b))); + vec.sort_unstable_by_key(|a| std::cmp::Reverse(id(-a))); //~^ unnecessary_sort_by // Negative examples (shouldn't be changed) let c = &7; @@ -99,9 +99,9 @@ mod issue_6001 { args.sort_unstable_by_key(|a| a.name()); //~^ unnecessary_sort_by // Reverse - args.sort_by_key(|b| std::cmp::Reverse(b.name())); + args.sort_by_key(|a| std::cmp::Reverse(a.name())); //~^ unnecessary_sort_by - args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); + args.sort_unstable_by_key(|a| std::cmp::Reverse(a.name())); //~^ unnecessary_sort_by } } diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr index 56d4831eb70aa..868f7895fc069 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr @@ -57,7 +57,7 @@ LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); help: try | LL - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); -LL + vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs())); +LL + vec.sort_by_key(|a| std::cmp::Reverse((a + 5).abs())); | error: consider using `sort_unstable_by_key` @@ -69,7 +69,7 @@ LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); help: try | LL - vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); -LL + vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b))); +LL + vec.sort_unstable_by_key(|a| std::cmp::Reverse(id(-a))); | error: consider using `sort_by_key` @@ -129,7 +129,7 @@ LL | args.sort_by(|a, b| b.name().cmp(&a.name())); help: try | LL - args.sort_by(|a, b| b.name().cmp(&a.name())); -LL + args.sort_by_key(|b| std::cmp::Reverse(b.name())); +LL + args.sort_by_key(|a| std::cmp::Reverse(a.name())); | error: consider using `sort_unstable_by_key` @@ -141,7 +141,7 @@ LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); help: try | LL - args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); -LL + args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); +LL + args.sort_unstable_by_key(|a| std::cmp::Reverse(a.name())); | error: consider using `sort_by_key` diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed index 40e6aecd1b6d0..6fd740d87757a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed @@ -15,8 +15,8 @@ fn issue_11524() -> Vec { fn issue_11524_2() -> Vec { let mut vec = vec![1, 2, 3]; - // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` - vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); + // Should lint and suggest `vec.sort_by_key(|a| core::cmp::Reverse(a + 1));` + vec.sort_by_key(|a| core::cmp::Reverse(a + 1)); //~^ unnecessary_sort_by vec } diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs index 184c90d959eb5..266553e7ace6b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs @@ -15,7 +15,7 @@ fn issue_11524() -> Vec { fn issue_11524_2() -> Vec { let mut vec = vec![1, 2, 3]; - // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` + // Should lint and suggest `vec.sort_by_key(|a| core::cmp::Reverse(a + 1));` vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); //~^ unnecessary_sort_by vec diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr index b4dd6a6dbdc52..26a8ad38fcd32 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr @@ -21,7 +21,7 @@ LL | vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); help: try | LL - vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); -LL + vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); +LL + vec.sort_by_key(|a| core::cmp::Reverse(a + 1)); | error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed index 499d9ee1ca235..f418fe954d400 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed @@ -26,9 +26,9 @@ fn simple() { println!{"Foo{{,}}"}; //~ unnecessary_trailing_comma println!{"Foo(,"}; //~ unnecessary_trailing_comma println!{"Foo[,"}; //~ unnecessary_trailing_comma + println!(concat!("Foo", "=", "{}"), 1); //~ unnecessary_trailing_comma // This should eventually work, but requires more work - println!(concat!("Foo", "=", "{}"), 1,); println!("No params", /*"a,){ */); println!("No params" /* "a,){*/, /*"a,){ */); diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs index 15dea27b887b6..26770ffaeee18 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs @@ -26,9 +26,9 @@ fn simple() { println!{"Foo{{,}}", }; //~ unnecessary_trailing_comma println!{"Foo(,", }; //~ unnecessary_trailing_comma println!{"Foo[,", }; //~ unnecessary_trailing_comma + println!(concat!("Foo", "=", "{}"), 1,); //~ unnecessary_trailing_comma // This should eventually work, but requires more work - println!(concat!("Foo", "=", "{}"), 1,); println!("No params", /*"a,){ */); println!("No params" /* "a,){*/, /*"a,){ */); diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr index 06fd5b1861a55..8ff7195a147b0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr @@ -115,5 +115,11 @@ error: unnecessary trailing comma LL | println!{"Foo[,", }; | ^^ help: remove the trailing comma -error: aborting due to 19 previous errors +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:29:42 + | +LL | println!(concat!("Foo", "=", "{}"), 1,); + | ^ help: remove the trailing comma + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed new file mode 100644 index 0000000000000..274bb7a00bfb0 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed @@ -0,0 +1,104 @@ +//@aux-build:unnecessary_unwrap_unchecked_helper.rs +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_unwrap_unchecked)] + +use proc_macros::{external, with_span}; + +#[rustfmt::skip] +use unnecessary_unwrap_unchecked_helper::{lol, kek}; + +mod b { + pub fn test_fn() -> Option { + Some(0) + } + + pub unsafe fn test_fn_unchecked() -> u32 { + 0 + } +} + +fn test_fn() -> Option { + Some(0) +} + +unsafe fn test_fn_unchecked() -> u32 { + 0 +} + +struct A; + +impl A { + fn a(&self) -> Option { + Some(0) + } + + unsafe fn a_unchecked(&self) -> u32 { + 0 + } + + fn an_assoc_fn() -> Option { + Some(0) + } + + unsafe fn an_assoc_fn_unchecked() -> u32 { + 0 + } +} + +struct B; + +impl B { + fn b(&self) -> Option { + Some(0) + } +} + +impl B { + unsafe fn b_unchecked(&self) -> u32 { + 0 + } +} + +fn main() { + let string_slice = unsafe { std::str::from_utf8_unchecked(&[]) }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { A::a_unchecked(&A) }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { + let a = A; + a.a_unchecked() + }; + //~^^ unnecessary_unwrap_unchecked + let an_assoc_fn = unsafe { A::an_assoc_fn_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let local_fn = unsafe { b::test_fn_unchecked() }; + //~^ unnecessary_unwrap_unchecked + + // Don't lint: unlike `kek`, `kek_unchecked` isn't accessible from here + let extern_pub_fn = unsafe { kek().unwrap_unchecked() }; + + // Don't lint: `B::b` and `B::b_unchecked` come from distinct impl blocks + // (even though that wouldn't actually make the suggestion invalid in this case) + let b = unsafe { + let b = B; + b.b().unwrap_unchecked() + }; + + macro_rules! local { + () => {{ + unsafe { ::std::str::from_utf8(&[]).unwrap_unchecked() }; + }}; + } + local!(); + external! { + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } + with_span! { + span + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs new file mode 100644 index 0000000000000..006a541365a58 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs @@ -0,0 +1,104 @@ +//@aux-build:unnecessary_unwrap_unchecked_helper.rs +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_unwrap_unchecked)] + +use proc_macros::{external, with_span}; + +#[rustfmt::skip] +use unnecessary_unwrap_unchecked_helper::{lol, kek}; + +mod b { + pub fn test_fn() -> Option { + Some(0) + } + + pub unsafe fn test_fn_unchecked() -> u32 { + 0 + } +} + +fn test_fn() -> Option { + Some(0) +} + +unsafe fn test_fn_unchecked() -> u32 { + 0 +} + +struct A; + +impl A { + fn a(&self) -> Option { + Some(0) + } + + unsafe fn a_unchecked(&self) -> u32 { + 0 + } + + fn an_assoc_fn() -> Option { + Some(0) + } + + unsafe fn an_assoc_fn_unchecked() -> u32 { + 0 + } +} + +struct B; + +impl B { + fn b(&self) -> Option { + Some(0) + } +} + +impl B { + unsafe fn b_unchecked(&self) -> u32 { + 0 + } +} + +fn main() { + let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { A::a(&A).unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { + let a = A; + a.a().unwrap_unchecked() + }; + //~^^ unnecessary_unwrap_unchecked + let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn = unsafe { lol().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + + // Don't lint: unlike `kek`, `kek_unchecked` isn't accessible from here + let extern_pub_fn = unsafe { kek().unwrap_unchecked() }; + + // Don't lint: `B::b` and `B::b_unchecked` come from distinct impl blocks + // (even though that wouldn't actually make the suggestion invalid in this case) + let b = unsafe { + let b = B; + b.b().unwrap_unchecked() + }; + + macro_rules! local { + () => {{ + unsafe { ::std::str::from_utf8(&[]).unwrap_unchecked() }; + }}; + } + local!(); + external! { + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } + with_span! { + span + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr new file mode 100644 index 0000000000000..359e50a33c9b7 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr @@ -0,0 +1,88 @@ +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:63:33 + | +LL | let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-unwrap-unchecked` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap_unchecked)]` +help: use `std::str::from_utf8_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; +LL + let string_slice = unsafe { std::str::from_utf8_unchecked(&[]) }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:65:22 + | +LL | let a = unsafe { A::a(&A).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::a_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let a = unsafe { A::a(&A).unwrap_unchecked() }; +LL + let a = unsafe { A::a_unchecked(&A) }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:69:9 + | +LL | a.a().unwrap_unchecked() + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::a_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - a.a().unwrap_unchecked() +LL + a.a_unchecked() + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the associated function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:72:32 + | +LL | let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::an_assoc_fn_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; +LL + let an_assoc_fn = unsafe { A::an_assoc_fn_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:74:30 + | +LL | let extern_fn = unsafe { lol().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unnecessary_unwrap_unchecked_helper::lol_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let extern_fn = unsafe { lol().unwrap_unchecked() }; +LL + let extern_fn = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:76:40 + | +LL | let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unnecessary_unwrap_unchecked_helper::lol_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; +LL + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:78:29 + | +LL | let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `b::test_fn_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; +LL + let local_fn = unsafe { b::test_fn_unchecked() }; + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 667468474b3c1..13ea2b250758c 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -60,6 +60,11 @@ labels = ["S-waiting-on-concerns"] [view-all-comments-link] threshold = 20 +# Allows `merge` and `delegate` commands for merging a PR +# Documentation at: https://forge.rust-lang.org/triagebot/merge.html +[merge] +type = "merge-queue" + [notify-zulip."lint-nominated"] zulip_stream = 577190 # #clippy/fcp topic = "FCP rust-clippy#{number}: {title}" diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index b7130c0870800..7c4469afda282 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -106,6 +106,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-nvptx64-nvidia-cuda", "ignore-openbsd", "ignore-parallel-frontend", + "ignore-pauthtest", "ignore-powerpc", "ignore-powerpc64", "ignore-remote", @@ -247,6 +248,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-musl", "only-nightly", "only-nvptx64", + "only-pauthtest", "only-powerpc", "only-riscv32", "only-riscv64", diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 60d526d3d46ea..4243c60001244 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2580,31 +2580,52 @@ impl<'test> TestCx<'test> { // that actually appear in the output. // We use uppercase ALLOC to distinguish from the non-normalized version. { - let mut seen_allocs = indexmap::IndexSet::new(); - - // The alloc-id appears in pretty-printed allocations. - normalized = static_regex!( - r"╾─*a(lloc)?([0-9]+)(\+0x[0-9a-f]+)?()?( \([0-9]+ ptr bytes\))?─*╼" - ) - .replace_all(&normalized, |caps: &Captures<'_>| { - // Renumber the captured index. - let index = caps.get(2).unwrap().as_str().to_string(); - let (index, _) = seen_allocs.insert_full(index); - let offset = caps.get(3).map_or("", |c| c.as_str()); - let imm = caps.get(4).map_or("", |c| c.as_str()); - // Do not bother keeping it pretty, just make it deterministic. - format!("╾ALLOC{index}{offset}{imm}╼") - }) - .into_owned(); + match self.config.mode { + // Unfortunately, due to parallel frontend assigning alloc-ids + // nondeterministically we resort to dropping ids altogether for now + // in ui tests + TestMode::Ui => { + // The alloc-id appears in pretty-printed allocations. + normalized = static_regex!( + r"╾─*(a(lloc)?|A(LLOC)?)\d+(\+0x[0-9a-f]+)?()?( ?\(\d+ ptr bytes\))?─*╼" + ) + .replace_all(&normalized, |_: &Captures<'_>| "╾ALLOC$ID╼".to_string()) + .into_owned(); - // The alloc-id appears in a sentence. - normalized = static_regex!(r"\balloc([0-9]+)\b") - .replace_all(&normalized, |caps: &Captures<'_>| { - let index = caps.get(1).unwrap().as_str().to_string(); - let (index, _) = seen_allocs.insert_full(index); - format!("ALLOC{index}") - }) - .into_owned(); + // The alloc-id appears in a sentence. + normalized = static_regex!(r"\b(alloc|ALLOC)\d+\b") + .replace_all(&normalized, |_: &Captures<'_>| "ALLOC$ID".to_string()) + .into_owned(); + } + // use consistent `AllocId`s in other test modes, where parallel frontend + // should not (theoretically) be an issue + _ => { + let mut seen_allocs = indexmap::IndexSet::new(); + // The alloc-id appears in pretty-printed allocations. + normalized = static_regex!( + r"╾─*a(lloc)?([0-9]+)(\+0x[0-9a-f]+)?()?( \([0-9]+ ptr bytes\))?─*╼" + ) + .replace_all(&normalized, |caps: &Captures<'_>| { + // Renumber the captured index. + let index = caps.get(2).unwrap().as_str().to_string(); + let (index, _) = seen_allocs.insert_full(index); + let offset = caps.get(3).map_or("", |c| c.as_str()); + let imm = caps.get(4).map_or("", |c| c.as_str()); + // Do not bother keeping it pretty, just make it deterministic. + format!("╾ALLOC{index}{offset}{imm}╼") + }) + .into_owned(); + + // The alloc-id appears in a sentence. + normalized = static_regex!(r"\balloc([0-9]+)\b") + .replace_all(&normalized, |caps: &Captures<'_>| { + let index = caps.get(1).unwrap().as_str().to_string(); + let (index, _) = seen_allocs.insert_full(index); + format!("ALLOC{index}") + }) + .into_owned(); + } + } } // Custom normalization rules diff --git a/src/tools/coverage-dump/Cargo.toml b/src/tools/coverage-dump/Cargo.toml index 36a66f16030ec..cb5755a22968a 100644 --- a/src/tools/coverage-dump/Cargo.toml +++ b/src/tools/coverage-dump/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.71" -itertools = "0.12" +itertools = "0.15" leb128 = "0.2.5" md5 = { package = "md-5" , version = "0.10.5" } miniz_oxide = "0.8.8" diff --git a/src/tools/enzyme b/src/tools/enzyme index 7c0141f133a35..a8668c7ca3579 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit 7c0141f133a3592daa12cc6cc07f297a5222a42e +Subproject commit a8668c7ca3579c3304d628bca518bafc0fcc62d1 diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index 80fc26cbe6680..f2ae68f7fbd69 100644 --- a/src/tools/jsondocck/Cargo.toml +++ b/src/tools/jsondocck/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsondocck" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] jsonpath-rust = "1.0.0" diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml index cc8ecefd530b4..848c0b37ae94e 100644 --- a/src/tools/jsondoclint/Cargo.toml +++ b/src/tools/jsondoclint/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsondoclint" version = "0.1.0" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 01deafe20b354..76a47c4f4fecf 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -97,7 +97,7 @@ impl<'a> Validator<'a> { ItemEnum::StructField(x) => self.check_struct_field(x), ItemEnum::Enum(x) => self.check_enum(x), ItemEnum::Variant(x) => self.check_variant(x, id), - ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Function(x) => self.check_function(x, id), ItemEnum::Trait(x) => self.check_trait(x, id), ItemEnum::TraitAlias(x) => self.check_trait_alias(x), ItemEnum::Impl(x) => self.check_impl(x, id), @@ -114,12 +114,35 @@ impl<'a> Validator<'a> { ItemEnum::Module(x) => self.check_module(x, id), // FIXME: Why don't these have their own structs? ItemEnum::ExternCrate { .. } => {} - ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_), - ItemEnum::AssocType { generics, bounds, type_ } => { + ItemEnum::AssocConst { type_, value, default_unstable } => { + self.check_type(type_); + if value.is_none() + && let Some(default_unstable) = default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } + } + ItemEnum::AssocType { generics, bounds, type_, default_unstable } => { self.check_generics(generics); bounds.iter().for_each(|b| self.check_generic_bound(b)); if let Some(ty) = type_ { self.check_type(ty); + } else if let Some(default_unstable) = default_unstable { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); } } } @@ -194,9 +217,21 @@ impl<'a> Validator<'a> { } } - fn check_function(&mut self, x: &'a Function) { + fn check_function(&mut self, x: &'a Function, id: &Id) { self.check_generics(&x.generics); self.check_function_signature(&x.sig); + if !x.has_body + && let Some(default_unstable) = &x.default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } } fn check_trait(&mut self, x: &'a Trait, id: &Id) { diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index cd64bdb94ffbe..f9b54a2cc0936 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -1,5 +1,7 @@ use rustc_hash::FxHashMap; -use rustdoc_json_types::{Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, Visibility}; +use rustdoc_json_types::{ + Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, ProvidedDefaultUnstable, Visibility, +}; use super::*; use crate::json_find::SelectorPart; @@ -34,6 +36,7 @@ fn errors_on_missing_links() { attrs: vec![], deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Module(Module { is_crate: true, items: vec![], @@ -83,6 +86,7 @@ fn errors_on_local_in_paths_and_not_index() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Module(Module { is_crate: true, items: vec![Id(1)], @@ -103,6 +107,7 @@ fn errors_on_local_in_paths_and_not_index() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }), }, ), @@ -157,6 +162,7 @@ fn errors_on_missing_path() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Module(Module { is_crate: true, items: vec![Id(1), Id(2)], @@ -177,6 +183,7 @@ fn errors_on_missing_path() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Struct(Struct { kind: StructKind::Unit, generics: generics.clone(), @@ -197,6 +204,7 @@ fn errors_on_missing_path() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Function(Function { sig: FunctionSignature { inputs: vec![], @@ -215,6 +223,7 @@ fn errors_on_missing_path() { abi: Abi::Rust, }, has_body: true, + default_unstable: None, }), }, ), @@ -240,6 +249,155 @@ fn errors_on_missing_path() { ); } +fn krate_with_trait_item(inner: ItemEnum) -> Crate { + let item_id = Id(2); + Crate { + root: Id(0), + crate_version: None, + includes_private: false, + index: FxHashMap::from_iter([ + ( + Id(0), + Item { + id: Id(0), + crate_id: 0, + name: Some("root".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Module(Module { + is_crate: true, + items: vec![Id(1)], + is_stripped: false, + }), + }, + ), + ( + Id(1), + Item { + id: Id(1), + crate_id: 0, + name: Some("Trait".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Trait(Trait { + is_auto: false, + is_unsafe: false, + is_dyn_compatible: true, + items: vec![item_id], + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + implementations: vec![], + }), + }, + ), + ( + item_id, + Item { + id: item_id, + crate_id: 0, + name: Some("TraitItem".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner, + }, + ), + ]), + paths: FxHashMap::default(), + external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, + format_version: FORMAT_VERSION, + } +} + +#[test] +fn errors_on_default_unstable_without_function_body() { + let krate = krate_with_trait_item(ItemEnum::Function(Function { + sig: FunctionSignature { inputs: vec![], output: None, is_c_variadic: false }, + generics: Generics { params: vec![], where_predicates: vec![] }, + header: FunctionHeader { + is_const: false, + is_unsafe: false, + is_async: false, + abi: Abi::Rust, + }, + has_body: false, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + })); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_const_value() { + let krate = krate_with_trait_item(ItemEnum::AssocConst { + type_: Type::Primitive("usize".to_owned()), + value: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_type_default() { + let krate = krate_with_trait_item(ItemEnum::AssocType { + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + type_: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + #[test] #[should_panic = "LOCAL_CRATE_ID is wrong"] fn checks_local_crate_id_is_correct() { @@ -260,6 +418,7 @@ fn checks_local_crate_id_is_correct() { attrs: Vec::new(), deprecation: None, stability: None, + const_stability: None, inner: ItemEnum::Module(Module { is_crate: true, items: vec![], diff --git a/src/tools/linkchecker/Cargo.toml b/src/tools/linkchecker/Cargo.toml index f0886e31b243f..0950122a1710f 100644 --- a/src/tools/linkchecker/Cargo.toml +++ b/src/tools/linkchecker/Cargo.toml @@ -9,5 +9,5 @@ path = "main.rs" [dependencies] regex = "1" -html5ever = "0.29.0" +html5ever = "0.39.0" urlencoding = "2.1.3" diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index e7455fc701cfb..0d3870f456a07 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -92,6 +92,22 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ "core\\io\\slice::sort_by_key", "#method.sort_by_cached_key" ]), + ("alloc/io/struct.IoSlice.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "alloc/io/slice::sort_by_key", + "alloc\\io\\slice::sort_by_key", + "#method.sort_by_key", + "#method.sort_by_cached_key" + ]), + ("alloc/io/struct.IoSliceMut.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "alloc/io/slice::sort_by_key", + "alloc\\io\\slice::sort_by_key", + "#method.sort_by_key", + "#method.sort_by_cached_key" + ]), ]; #[rustfmt::skip] diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml index ab99eb8ea3b72..2d7d6a6fc5e79 100644 --- a/src/tools/lint-docs/Cargo.toml +++ b/src/tools/lint-docs/Cargo.toml @@ -7,7 +7,7 @@ description = "A script to extract the lint documentation for the rustc book." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rustc-literal-escaper = "0.0.7" +rustc-literal-escaper = "0.0.8" serde_json = "1.0.57" tempfile = "3.1.0" walkdir = "2.3.1" diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 6b8d8ed1f8018..fdc746ead51f5 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -171,6 +171,38 @@ available (which affects `cfg(target_feature)`), and it tells Miri to consider t that the interpreted program runs on as having the feature available (meaning the code is allowed to invoke the corresponding intrinsics). +### Nextest integration + +Miri can be combined with [`cargo-nextest`](https://nexte.st): + +``` +cargo install --locked cargo-nextest +cargo miri nextest run +``` + +Nextest spawns a separate instance of Miri for each test, which has several advantages: +- Tests can run in parallel. Miri itself only uses a single thread per interpreter so this can + give a massive speedup (but see the caveat below). +- Tests do not stop when a single problem is found. Miri aborts execution when it encounters + Undefined Behavior or an unsupported operation (there is often not really any way to continue), + so once a single test fails, the remaining tests cannot be executed. Nextest's process-per-test + model means that you end up with a full list of which tests worked in Miri and which tests had a + problem. + +However, there is also a big caveat: Miri will [re-compile the test crate every time it is +invoked](https://github.com/rust-lang/miri/issues/5013), which means a crate with N tests will be +compiled N+1 times. If the test crate takes a long time to build, this can outweigh the benefits of +parallelization. + +For more information about nextest, see the [`cargo-nextest` Miri +documentation](https://nexte.st/book/miri.html). + +Note: Nextest's one-test-per-process model means that `cargo miri test` is able to detect data +races where two tests race on a shared resource, but `cargo miri nextest run` will not detect +such races. + +Note: `cargo-nextest` [does not support doctests](https://github.com/nextest-rs/nextest/issues/16). + ### Testing multiple different executions Certain parts of the execution are picked randomly by Miri, such as the exact base address @@ -184,6 +216,7 @@ MIRIFLAGS="-Zmiri-many-seeds" cargo miri test # tries the seeds in 0..64 MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo miri test ``` +Miri will test the given range of seeds with parallel interpreter instances. The default of 64 different seeds can be quite slow, so you often want to specify a smaller range. ### Running Miri on CI @@ -243,26 +276,6 @@ However, even for targets that we do support, the degree of support for accessin (such as the file system) differs between targets: generally, Linux targets have the best support, and macOS targets are usually on par. Windows is supported less well. -### Running tests in parallel - -Though it implements Rust threading, Miri itself is a single-threaded interpreter -(it works like a multi-threaded OS on a single-core CPU). -This means that when running `cargo miri test`, you will probably see a dramatic -increase in the amount of time it takes to run your whole test suite due to the -inherent interpreter slowdown and a loss of parallelism. - -You can get your test suite's parallelism back by running `cargo miri nextest run -jN` -(note that you will need [`cargo-nextest`](https://nexte.st) installed). -This works because `cargo-nextest` collects a list of all tests then launches a -separate `cargo miri run` for each test. For more information about nextest, see the -[`cargo-nextest` Miri documentation](https://nexte.st/book/miri.html). - -Note: This one-test-per-process model means that `cargo miri test` is able to detect data -races where two tests race on a shared resource, but `cargo miri nextest run` will not detect -such races. - -Note: `cargo-nextest` does not support doctests, see https://github.com/nextest-rs/nextest/issues/16 - ### Directly invoking the `miri` driver The recommended way to invoke Miri is via `cargo miri`. Directly invoking the underlying `miri` diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index f58cec827cf5d..caf6987291970 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -36,6 +36,9 @@ Examples: This will print the path to the generated sysroot (and nothing else) on stdout. stderr will still contain progress information about how the build is doing. +For documentation on `-Zmiri-...` flags, see Miri's README.md, available at: +- $(rustc --print sysroot)/share/doc/miri/README.md +- https://github.com/rust-lang/miri/blob/master/README.md "; fn show_help() { diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index f7c0b739c1dd4..42107adfe566a 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -168,6 +168,7 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-netbsd run_tests_minimal hello ;; armv7-unknown-linux-gnueabihf) # Host diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 2739b041b0b48..fe67b21d2c134 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -6,6 +6,7 @@ extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_index; extern crate rustc_interface; extern crate rustc_log; extern crate rustc_middle; @@ -19,13 +20,15 @@ use std::path::PathBuf; use miri::*; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; +use rustc_index::IndexVec; use rustc_interface::interface; use rustc_middle::mir; +use rustc_middle::mir::{Local, VarDebugInfoContents}; use rustc_middle::ty::TyCtxt; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; -use rustc_span::Span; use rustc_span::source_map::SourceMap; +use rustc_span::{Span, Symbol}; fn find_sysroot() -> String { std::env::var("MIRI_SYSROOT") @@ -129,6 +132,11 @@ struct PrirodaContext<'tcx> { last_location: Option, } +struct LocalDesc { + name: Option, + local: Local, + ty: String, +} /// Controls when execution returns to the frontend. enum ResumeMode { /// Stop at the next visible MIR instruction. @@ -336,15 +344,49 @@ impl<'tcx> PrirodaContext<'tcx> { } } - /// Returns the names of all user-visible locals in the innermost stack frame. + /// Returns structured descriptions for locals in the innermost stack frame. /// - /// Uses `var_debug_info` from the MIR body, which is the same source that - /// DWARF debug info is built from, so the names match what the user wrote. - fn list_locals(&self) -> Vec { + /// Starts from all MIR locals, then enriches them with source names from + /// `var_debug_info` when a debug entry maps directly to a whole local. + fn list_locals(&self) -> Vec { let Some(frame) = self.ecx.active_thread_stack().last() else { return Vec::new(); }; - frame.body().var_debug_info.iter().map(|info| info.name.to_string()).collect() + + self.local_desc_map(frame).into_iter().collect() + } + + fn local_desc_map( + &self, + frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>, + ) -> IndexVec { + // Initialize one description per MIR local so the table can be indexed by Local. + let mut locals: IndexVec = frame + .body() + .local_decls + .iter_enumerated() + .map(|(id, local_decl)| { + LocalDesc { name: None, local: id, ty: local_decl.ty.to_string() } + }) + .collect(); + + // FIXME: Some debug-info entries do not have a backing MIR local, for example + // because the source variable was optimized out or is represented as a + // projection. This local-indexed table cannot represent those entries yet; + // the final locals list should become a `Vec` with `id : Option`, `id` + // could be renamed to `local`. + + // Attach source names from debug info when the debug entry maps directly to a whole MIR local. + for var_debug_info in &frame.body().var_debug_info { + if let VarDebugInfoContents::Place(place) = var_debug_info.value + && let Some(local) = place.as_local() + && locals[local].name.is_none() + { + locals[local].name = Some(var_debug_info.name); + } + } + + locals } } @@ -366,7 +408,7 @@ enum BreakpointSetResult { enum CommandResult { ExecutionStopped(StepResult), BreakpointResult(BreakpointSetResult), - Locals(Vec), + Locals(Vec), // FIXME: distinguish terminating the debugger session from disconnecting a // frontend and terminating the interpreted program once multiple frontends exist. TerminateSession, @@ -403,12 +445,21 @@ impl Cli { BreakpointSetResult::Duplicate => println!("Duplicate breakpoint"), }, - CommandResult::Locals(names) => - if names.is_empty() { + CommandResult::Locals(locals_desc) => + if locals_desc.is_empty() { println!("no locals"); } else { - for name in &names { - println!("{name}"); + for local_desc in &locals_desc { + let mut name_str = "None".to_string(); + if let Some(name) = local_desc.name { + name_str = name.to_string(); + } + println!( + "Name: {}, Id: _{}, Ty: {}", + name_str, + local_desc.local.index(), + local_desc.ty, + ); } }, CommandResult::TerminateSession => { diff --git a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout index ed5889f5836e7..4f2786d3e17b7 100644 --- a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout @@ -1,6 +1,10 @@ (priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 -(priroda) x -y +(priroda) Name: None, Id: _0, Ty: () +Name: x, Id: _1, Ty: i32 +Name: y, Id: _2, Ty: bool +Name: None, Id: _3, Ty: (i32, bool) +Name: None, Id: _4, Ty: i32 +Name: None, Id: _5, Ty: bool (priroda) quitting diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4a2bfdb2cd558..d7711535dbf29 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -01f54e80e888b66d6486a3a95d481b87353016df +16761606d606b6ec4d0c88fc9251670742ad9fd2 diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 296de803d5828..b58a7cfeba92e 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -574,7 +574,9 @@ impl<'tcx> Tree { // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). // Related to https://github.com/rust-lang/rust/issues/55005. && !perm.permission.is_cell() - // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. + // Only trigger UB if the accessed bit is set, i.e. if the protector + // is actually protecting this offset. See #4579. Note that this + // takes into account the access we just did above! && perm.accessed { Err(TbError { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 730c7d9fac611..f5761f4712470 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -129,7 +129,13 @@ pub fn iter_exported_symbols<'tcx>( || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) }; - if exported { + // FIXME: `#[no_mangle]` makes no sense on a generic item, but still causes it to be + // considered "extern". Remove this once `no_mangle_generic_items` is a hard error. + let exported_mono = exported && { + let generics = tcx.generics_of(def_id); + !generics.requires_monomorphization(tcx) + }; + if exported_mono { f(LOCAL_CRATE, def_id.into())?; } } @@ -406,6 +412,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let sig = this.tcx.mk_fn_sig( args.iter().map(|a| a.layout.ty), dest.layout.ty, + // FIXME(splat): Do we need to set splatted here? + // (Currently this also ignores c_variadic) FnSigKind::default().set_abi(caller_abi).set_safety(rustc_hir::Safety::Safe), ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index deab856b3e24f..3d8946fb36720 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -146,6 +146,45 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + // Signed saturating doubling multiply returning the high half. + // + // Used by the `vqdmulh*` functions. + // + // This LLVM intrinsic multiplies the values of corresponding elements of the two source + // vector registers (which are signed integers), doubles the results, places the most significant half of the + // final results (using a saturating cast to fit the element type) into a vector, and writes the vector to the destination register. + // + // https://developer.arm.com/architectures/instruction-sets/intrinsics#f:@navigationhierarchiessimdisa=[Neon]&q=vqdmulh + name if name.starts_with("neon.sqdmulh.") => { + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + assert_eq!(left_len, right_len); + assert_eq!(left_len, dest_len); + + let elem_size = dest.layout.field(this, 0).size; + let bits = elem_size.bits(); + let min = elem_size.signed_int_min(); + let max = elem_size.signed_int_max(); + + for i in 0..dest_len { + let a = this.read_scalar(&this.project_index(&left, i)?)?.to_int(elem_size)?; + let b = this.read_scalar(&this.project_index(&right, i)?)?.to_int(elem_size)?; + + // Uses i128 arithmetic, which cannot overflow because the intrinsic takes at most i32. + let doubled = a.strict_mul(b).strict_mul(2); + let res = (doubled >> bits).clamp(min, max); + + this.write_scalar( + Scalar::from_int(res, elem_size), + &this.project_index(&dest, i)?, + )?; + } + } + // Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0. // Used to implement vtbl1_u8 function. // LLVM does not have a portable shuffle that takes non-const indices diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index e4daab7a2005d..5fd86769e96b5 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -789,7 +789,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } // LLVM intrinsics - "llvm.prefetch" => { + "llvm.prefetch.p0" => { let [p, rw, loc, ty] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 27571a9eb7c87..5014541faeb01 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -321,7 +321,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { imm.layout ) }; - a.size(this).align_to(b.align(this).abi).bytes_usize() + a.size(this).align_to(b.default_align(this).abi).bytes_usize() }; write_scalar(this, sc_first, 0)?; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index 68b13a6ed58a0..99673319240fe 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -274,7 +274,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { inputs_and_output.push(shim_sig.ret); let fn_sig_binder = Binder::dummy(FnSig { inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), - // Safety does not matter for the ABI. + // Safety and splatted do not matter for the ABI. fn_sig_kind: FnSigKind::default() .set_abi(shim_sig.abi) .set_safety(rustc_hir::Safety::Safe), diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index c2ae65091ee2c..14eb7af4b52df 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -840,18 +840,46 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "mmap" => { - let [addr, length, prot, flags, fd, offset] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [addr, length, prot, flags, fd, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32, i32, i32, libc::off_t) -> *mut _), + link_name, + abi, + args, + )?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } "munmap" => { - let [addr, length] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [addr, length] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize) -> i32), + link_name, + abi, + args, + )?; let result = this.munmap(addr, length)?; this.write_scalar(result, dest)?; } + "mprotect" => { + let [addr, length, prot] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.mprotect(addr, length, prot)?; + this.write_scalar(result, dest)?; + } + "madvise" => { + let [addr, length, advice] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.madvise(addr, length, advice)?; + this.write_scalar(result, dest)?; + } "reallocarray" => { // Currently this function does not exist on all Unixes, e.g. on macOS. @@ -1410,7 +1438,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } - "sigaction" | "mprotect" if this.frame_in_std() => { + "sigaction" if this.frame_in_std() => { let [_, _, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index c2ad7c0e9d0ab..50be3c7ae15e6 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -53,9 +53,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(Scalar::from_maybe_pointer(Pointer::without_provenance(addr), this)); } - let prot_read = this.eval_libc_i32("PROT_READ"); - let prot_write = this.eval_libc_i32("PROT_WRITE"); - // First, we do some basic argument validation as required by mmap if (flags & (map_private | map_shared)).count_ones() != 1 { this.set_last_error(LibcError("EINVAL"))?; @@ -80,13 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - // Miri doesn't support protections other than PROT_READ|PROT_WRITE. - if prot != prot_read | prot_write { - throw_unsup_format!( - "Miri does not support calls to mmap with protections other than \ - PROT_READ|PROT_WRITE", - ); - } + verify_prot(this, prot)?; // Miri does not support shared mappings, or any of the other extensions that for example // Linux has added to the flags arguments. @@ -103,14 +94,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let align = this.machine.page_align(); - let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { + let Some(map_length) = round_up_to_page_size(this, length) else { this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); }; - if map_length > this.target_usize_max() { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(this.eval_libc("MAP_FAILED")); - } let ptr = this.allocate_ptr( Size::from_bytes(map_length), @@ -135,13 +122,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); } - let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { + let Some(length) = round_up_to_page_size(this, length) else { return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); }; - if length > this.target_usize_max() { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(this.eval_libc("MAP_FAILED")); - } let length = Size::from_bytes(length); this.deallocate_ptr( @@ -152,4 +135,106 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(0)) } + + fn mprotect( + &mut self, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + prot: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let prot = this.read_scalar(prot)?.to_i32()?; + + // addr must be a multiple of the page size. + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { + return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); + } + + verify_prot(this, prot)?; + + // The pages from `[addr, addr + length)` must be mapped, so length definitely must not overflow. + let Some(length) = round_up_to_page_size(this, length) else { + return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM")); + }; + // Ensure this is actually allocated memory we can access. + this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess) + .map_err_kind(|_| err_ub_format!("`mprotect` called on out-of-bounds memory"))?; + + // If the memory comes from memory the Rust program has allocated with mmap, we only support + // `PROT_READ|PROT_WRITE`, so this `mprotect` is a no-op. If the memory was mmaped by the + // runtime (e.g. if it's the stack, executable memory, or static memory), POSIX also allows + // us to remap it. In those cases, such a call to `PROT_READ|PROT_WRITE` might actually change the permissions, + // but treating them as the new permissions is still UB. Therefore, we just pretend that we + // did the permission change by returning success, and will still reject if you try to use + // it with the "new" permissions. + interp_ok(Scalar::from_i32(0)) + } + + fn madvise( + &mut self, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + advice: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let advise = this.read_scalar(advice)?.to_i32()?; + + // addr must be a multiple of the page size. + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { + return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); + } + + // advise must be supported. + let madv_normal = this.eval_libc_i32("MADV_NORMAL"); + let madv_random = this.eval_libc_i32("MADV_RANDOM"); + let madv_sequential = this.eval_libc_i32("MADV_SEQUENTIAL"); + let madv_willneed = this.eval_libc_i32("MADV_WILLNEED"); + if advise != madv_normal + && advise != madv_random + && advise != madv_sequential + && advise != madv_willneed + { + throw_unsup_format!( + "Miri does not support calls to madvise with advice other than MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, or MADV_WILLNEED", + ); + } + + // The pages from `[addr, addr + length)` must be mapped, so length definitely must not overflow. + let Some(length) = round_up_to_page_size(this, length) else { + return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM")); + }; + // Ensure this is actually allocated memory we can access. + this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess) + .map_err_kind(|_| err_ub_format!("`madvise` called on out-of-bounds memory"))?; + + // All advises we support are no-ops. + interp_ok(Scalar::from_i32(0)) + } +} + +fn round_up_to_page_size(this: &MiriInterpCx<'_>, length: u64) -> Option { + length + .checked_next_multiple_of(this.machine.page_size) + .filter(|length| *length <= this.target_isize_max().try_into().unwrap()) +} + +fn verify_prot<'tcx>(this: &mut MiriInterpCx<'tcx>, prot: i32) -> InterpResult<'tcx> { + let prot_read = this.eval_libc_i32("PROT_READ"); + let prot_write = this.eval_libc_i32("PROT_WRITE"); + + // Miri doesn't support protections other than PROT_READ|PROT_WRITE. + if prot != prot_read | prot_write { + throw_unsup_format!( + "Miri does not support calls to mmap/mprotect with protections other than \ + PROT_READ|PROT_WRITE", + ); + } + + interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index ae882f8ff3a40..72b614f58d0a4 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -1487,9 +1487,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Only shutting down the write end doesn't cause an EPOLLHUP event // and thus we won't set the `write_closed` readiness for it here. readiness.write_closed |= is_read_write_shutdown; - // The Linux kernel also sets EPOLLIN when both ends of a socket are closed: + // The Linux kernel also sets EPOLLIN when the read end of a socket is closed: // - readiness.readable |= is_read_write_shutdown; + readiness.readable |= is_read_shutdown || is_read_write_shutdown; drop(readiness); @@ -1697,12 +1697,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else { + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut *state else { panic!("try_non_block_send must only be called when the socket is connected") }; // This is a *non-blocking* write. let result = this.write_to_host(stream, length, buffer_ptr)?; + + drop(state); + match result { Err(IoError::HostError(e)) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) => @@ -1715,8 +1719,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_written) if bytes_written < length => { - // We had a short write. On Unix hosts using the `epoll` and `kqueue` backends, a + Ok(bytes_written) + if bytes_written < length && !socket.io_readiness.borrow().write_closed => + { + // We had a short write. (Note that we don't want to clear the writable readiness for + // sockets whose write end has already been closed as those never block a write, i.e., + // they are always write-ready.) + // On Unix hosts using the `epoll` and `kqueue` backends, a // short write means that the write buffer is full. We update the readiness // accordingly, which means that next time we see "writable" we will report an epoll // edge. Some applications (e.g. tokio) rely on this behavior; see @@ -1820,7 +1829,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else { + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut *state else { panic!("try_non_block_recv must only be called when the socket is connected") }; @@ -1832,6 +1842,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { length, buffer_ptr, )?; + + drop(state); + match result { Err(IoError::HostError(e)) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) => @@ -1844,9 +1857,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_read) if !should_peek && bytes_read < length && bytes_read > 0 => { + Ok(bytes_read) + if !should_peek + && bytes_read < length + && bytes_read > 0 + && !socket.io_readiness.borrow().read_closed => + { // We had a short read (and were not peeking). (Note that reading 0 bytes is guaranteed - // to indicate EOF, and can never happen spuriously, so we have to exclude that case.) + // to indicate EOF, and can never happen spuriously, so we have to exclude that case. + // We also don't want to clear the readable readiness for sockets whose read end has + // already been closed as those never block a read, i.e., they are always read-ready.) // On Unix hosts using the `epoll` and `kqueue` backends, a short read means that the // read buffer is empty. We update the readiness accordingly, which means that next time // we see "readable" we will report an epoll edge. Some applications (e.g. tokio) rely on diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 4e351c1571218..a34314f3fbea6 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -35,7 +35,13 @@ const PTHREAD_INIT: u8 = 1; #[inline] fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { interp_ok(match &ecx.tcx.sess.target.os { - Os::Linux | Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android => 0, + Os::Linux + | Os::Illumos + | Os::Solaris + | Os::MacOs + | Os::FreeBsd + | Os::Android + | Os::NetBsd => 0, os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), }) } @@ -135,8 +141,8 @@ impl SyncObj for PthreadMutex { fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> { let offset = match &ecx.tcx.sess.target.os { Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0, - // macOS stores a signature in the first bytes, so we move to offset 4. - Os::MacOs => 4, + // macOS and NetBSD store a signature in the first bytes, so we move to offset 4. + Os::MacOs | Os::NetBsd => 4, os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), }; let offset = Size::from_bytes(offset); @@ -163,7 +169,7 @@ fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP"); check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP"); } - Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android => { + Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android | Os::NetBsd => { // No non-standard initializers. } os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 64b5b786c6c08..b3a8291b9361b 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -879,33 +879,39 @@ fn mpsadbw<'tcx>( assert_eq!(left.layout.size, dest.layout.size); let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?; + assert!(num_chunks <= 2); + let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?; let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?; assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2)); let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?; - // Bit 2 of `imm` specifies the offset for indices of `left`. - // The offset is 0 when the bit is 0 or 4 when the bit is 1. - let left_offset = u64::try_from((imm >> 2) & 1).unwrap().strict_mul(4); - // Bits 0..=1 of `imm` specify the offset for indices of - // `right` in blocks of 4 elements. - let right_offset = u64::try_from(imm & 0b11).unwrap().strict_mul(4); for i in 0..num_chunks { let left = ecx.project_index(&left, i)?; let right = ecx.project_index(&right, i)?; let dest = ecx.project_index(&dest, i)?; + // The first 128-bit chunk uses the low 3 bits of IMM, the second chunk uses bits 3..6. + let lane_imm = imm.strict_shr(i.strict_mul(3).try_into().unwrap()); + + // Bit 2 of `lane_imm` specifies the offset for indices of `left`. + // The offset is 0 when the bit is 0 or 4 when the bit is 1. + let left_base = u64::try_from((lane_imm >> 2) & 1).unwrap().strict_mul(4); + // Bits 0..=1 of `lane_imm` specify the offset for indices of + // `right` in blocks of 4 elements. + let right_base = u64::try_from(lane_imm & 0b11).unwrap().strict_mul(4); + for j in 0..dest_items_per_chunk { - let left_offset = left_offset.strict_add(j); + let left_offset = left_base.strict_add(j); let mut res: u16 = 0; for k in 0..4 { let left = ecx .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)? .to_u8()?; let right = ecx - .read_scalar(&ecx.project_index(&right, right_offset.strict_add(k))?)? + .read_scalar(&ecx.project_index(&right, right_base.strict_add(k))?)? .to_u8()?; res = res.strict_add(left.abs_diff(right).into()); } diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 60c7e192f4805..164b61f597065 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -170,9 +170,10 @@ def test_cargo_miri_test(): "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) - test("`cargo miri test` (proc-macro crate)", - cargo_miri("test") + ["-p", "proc_macro_crate"], - "test.proc-macro.stdout.ref", "test.proc-macro.stderr.ref", + test("`cargo miri test` (entire workspace, no isolation)", + cargo_miri("test") + ["--workspace"], + "test.workspace.stdout.ref", "test.workspace.stderr.ref", + env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) test("`cargo miri test` (custom target dir)", cargo_miri("test") + ["--target-dir=custom-test"], diff --git a/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref b/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref deleted file mode 100644 index 7326c0a25a069..0000000000000 --- a/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref +++ /dev/null @@ -1,5 +0,0 @@ - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - diff --git a/src/tools/miri/test-cargo-miri/test.proc-macro.stderr.ref b/src/tools/miri/test-cargo-miri/test.workspace.stderr.ref similarity index 100% rename from src/tools/miri/test-cargo-miri/test.proc-macro.stderr.ref rename to src/tools/miri/test-cargo-miri/test.workspace.stderr.ref diff --git a/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref b/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref new file mode 100644 index 0000000000000..a4e224a329797 --- /dev/null +++ b/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref @@ -0,0 +1,103 @@ + +running 2 tests +.. +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +imported main + +running 6 tests +...i.. +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +subcrate testing + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +all doctests ran in $TIME; merged doctests compilation took $TIME + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 1 test +. +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index cbd2aa22b5236..4691588eefb40 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -290,7 +290,6 @@ dependencies = [ name = "miri-test-deps" version = "0.1.0" dependencies = [ - "cfg-if", "futures", "getrandom 0.1.16", "getrandom 0.2.17", diff --git a/src/tools/miri/tests/deps/Cargo.toml b/src/tools/miri/tests/deps/Cargo.toml index a4d06b628081c..bbbcc316f31c2 100644 --- a/src/tools/miri/tests/deps/Cargo.toml +++ b/src/tools/miri/tests/deps/Cargo.toml @@ -10,7 +10,6 @@ edition = "2021" # all dependencies (and their transitive ones) listed here can be used in `tests/*-dep`. libc = "0.2" num_cpus = "1.10.1" -cfg-if = "1" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs index 98cc80b6b4ea2..d4d14d449aa68 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs @@ -4,6 +4,10 @@ use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // Test the behaviour of a thread being blocked on an eventfd read, get unblocked, and then // get blocked again. @@ -18,7 +22,7 @@ fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs index 1a1d76eda2003..39d00a7522761 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs @@ -4,6 +4,10 @@ use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // Test the behaviour of a thread being blocked on an eventfd `write`, get unblocked, and then // get blocked again. @@ -17,7 +21,7 @@ fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs index eecf6abb9379f..6a6d363eea6bb 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs @@ -16,8 +16,7 @@ use libc_utils::*; fn main() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create two socketpair instances. let mut fds_a = [-1, -1]; diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs index 3eb79121a2f8d..22fe014d5831c 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs @@ -7,6 +7,7 @@ use std::thread; #[path = "../../utils/libc.rs"] mod libc_utils; use libc_utils::epoll::*; +use libc_utils::*; // Test if only one thread is unblocked if multiple threads blocked on same epfd. // Expected execution: @@ -16,14 +17,13 @@ use libc_utils::epoll::*; // 4. Thread 1 deadlocks. fn main() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create an eventfd instance. let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd1 = unsafe { libc::eventfd(0, flags) }; + let fd1 = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Make a duplicate so that we have two file descriptors for the same file description. - let fd2 = unsafe { libc::dup(fd1) }; + let fd2 = errno_result(unsafe { libc::dup(fd1) }).unwrap(); // Register both with epoll. epoll_ctl_add(epfd, fd1, EPOLLIN | EPOLLOUT | EPOLLET).unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs index 59cf0fc2ba026..d611feb30a703 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs @@ -1,13 +1,15 @@ //@only-target: linux android illumos +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // This is a test for registering unsupported fd with epoll. // Register epoll fd with epoll is allowed in real system, but we do not support this. fn main() { // Create two epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register epoll with epoll. let mut ev = diff --git a/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs new file mode 100644 index 0000000000000..29d804fd0d99a --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target: windows # No mmap on Windows +//@normalize-stderr-test: "only .*? bytes" -> "only SIZE bytes" + +fn main() { + unsafe { + let page_size = page_size::get(); + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert!(!ptr.is_null()); + + libc::madvise(ptr, page_size + 1, libc::MADV_NORMAL); //~ ERROR: `madvise` called on out-of-bounds memory + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr new file mode 100644 index 0000000000000..dbf38c7e4dd0f --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `madvise` called on out-of-bounds memory + --> tests/fail-dep/libc/madvise_out_of_bounds.rs:LL:CC + | +LL | libc::madvise(ptr, page_size + 1, libc::MADV_NORMAL); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs new file mode 100644 index 0000000000000..1e6868b714a81 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target: windows # No mmap on Windows +//@normalize-stderr-test: "only .*? bytes" -> "only SIZE bytes" + +fn main() { + unsafe { + let page_size = page_size::get(); + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert!(!ptr.is_null()); + + libc::mprotect(ptr, page_size + 1, libc::PROT_READ | libc::PROT_WRITE); //~ ERROR: `mprotect` called on out-of-bounds memory + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr new file mode 100644 index 0000000000000..ac48880150486 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `mprotect` called on out-of-bounds memory + --> tests/fail-dep/libc/mprotect_out_of_bounds.rs:LL:CC + | +LL | libc::mprotect(ptr, page_size + 1, libc::PROT_READ | libc::PROT_WRITE); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs index 37aa4590647b4..edf3f405056f4 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No libc socketpair on Windows -// test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock //@require-annotations-for-level: error diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs index 2f8b5be0c0c57..d062aeaf34873 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No libc socketpair on Windows -// test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock //@require-annotations-for-level: error diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs similarity index 50% rename from src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs rename to src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs index a34df7c7fe3ae..211d74caadd8a 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs @@ -1,4 +1,8 @@ -//@error-in-other-file: /deallocating while item \[Unique for .*\] is strongly protected/ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +//@[stack]error-in-other-file: /deallocating while item \[Unique for .*\] is strongly protected/ +//@[tree]error-in-other-file: /deallocation through .* is forbidden/ fn inner(x: &mut i32, f: fn(&mut i32)) { // `f` may mutate, but it may not deallocate! diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr similarity index 76% rename from src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr rename to src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr index 9f0df14ee4ddd..b8a763a5fc3f4 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr @@ -14,13 +14,13 @@ LL | self.1.deallocate(From::from(ptr.cast()), layout); 2: std::mem::drop::> at RUSTLIB/core/src/mem/mod.rs:LL:CC 3: main::{closure#0} - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC - 4: <{closure@tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 4: <{closure@tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim at RUSTLIB/core/src/ops/function.rs:LL:CC 5: inner - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC 6: main - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr new file mode 100644 index 0000000000000..3e97a15333ac6 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr @@ -0,0 +1,40 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x0] is forbidden + --> RUSTLIB/alloc/src/boxed.rs:LL:CC + | +LL | self.1.deallocate(From::from(ptr.cast()), layout); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the allocation of the accessed tag also contains the strongly protected tag + = help: the strongly protected tag disallows deallocations +help: the accessed tag was created here + --> tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + | +LL | drop(unsafe { Box::from_raw(raw) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the strongly protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + | +LL | inner(Box::leak(Box::new(0)), |x| { + | ^ + = note: stack backtrace: + 0: as std::ops::Drop>::drop + at RUSTLIB/alloc/src/boxed.rs:LL:CC + 1: std::ptr::drop_glue::> - shim(Some(std::boxed::Box)) + at RUSTLIB/core/src/ptr/mod.rs:LL:CC + 2: std::mem::drop::> + at RUSTLIB/core/src/mem/mod.rs:LL:CC + 3: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 4: <{closure@tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 5: inner + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 6: main + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs new file mode 100644 index 0000000000000..4cac0cdd03f8b --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs @@ -0,0 +1,23 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +// Ensure that even a ZST prevents the reference from being used for deallocation. +// The `nofree` attributes we add in LLVM IR rely on this. + +use std::alloc::Layout; + +fn inner(x: &mut (), f: fn(&mut ())) { + // `f` may mutate, but it may not deallocate! + f(x) +} + +fn main() { + let ptr = Box::leak(Box::new(0i32)) as *mut i32; + inner(unsafe { &mut *(ptr as *mut ()) }, |x| unsafe { + let raw = x as *mut _ as *mut i32; + // Avoid ever creating a `Box`, we don't want any implicit accesses. + std::alloc::dealloc(raw.cast(), Layout::new::()); + //~[tree]^ERROR: /deallocation through .* is forbidden/ + //~[stack]|ERROR: tag does not exist in the borrow stack for this location + }); +} diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr new file mode 100644 index 0000000000000..20993ef5072b1 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr @@ -0,0 +1,27 @@ +error: Undefined Behavior: attempting deallocation using at ALLOC, but that tag does not exist in the borrow stack for this location + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | std::alloc::dealloc(raw.cast(), Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: would have been created here, but this is a zero-size retag ([0x0..0x0]) so the tag in question does not exist anywhere + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | let raw = x as *mut _ as *mut i32; + | ^ + = note: stack backtrace: + 0: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 1: <{closure@tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC} as std::ops::FnOnce<(&mut (),)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 2: inner + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 3: main + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr new file mode 100644 index 0000000000000..7fae0b6a07773 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr @@ -0,0 +1,29 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | std::alloc::dealloc(raw.cast(), Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the allocation of the accessed tag also contains the strongly protected tag + = help: the strongly protected tag disallows deallocations +help: the strongly protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | inner(unsafe { &mut *(ptr as *mut ()) }, |x| unsafe { + | ^ + = note: stack backtrace: + 0: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 1: <{closure@tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC} as std::ops::FnOnce<(&mut (),)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 2: inner + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 3: main + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index 75eb06bc12cfb..90f70064932c1 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -146,7 +146,7 @@ fn test_epoll_ctl_del() { events: (EPOLLIN | EPOLLOUT | EPOLLET_OR_ZERO) as u32, u64: u64::try_from(fds[1]).unwrap(), }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; + let res = unsafe { libc::epoll_ctl(epfd, EPOLL_CTL_ADD, fds[1], &mut ev) }; assert_eq!(res, 0); // Test EPOLL_CTL_DEL. @@ -158,10 +158,8 @@ fn test_epoll_ctl_del() { // This test is for one fd registered under two different epoll instance. fn test_two_epoll_instance() { // Create two epoll instance. - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); - let epfd2 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd2, -1); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + let epfd2 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; @@ -570,8 +568,7 @@ fn test_epoll_ctl_epfd_equal_fd() { // epfd that shouldn't receive a notification in edge-triggered mode. fn test_epoll_ctl_notification() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; @@ -584,8 +581,7 @@ fn test_epoll_ctl_notification() { check_epoll_wait_noblock(epfd0, &[Ev { events: EPOLLOUT, data: fds[0] }]); // Create another epoll instance. - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register the same file description for epfd1. epoll_ctl_add(epfd1, fds[0], EPOLLIN | EPOLLOUT | EPOLLET_OR_ZERO).unwrap(); @@ -692,8 +688,7 @@ fn test_issue_3858() { /// Ensure that if a socket becomes un-writable, we don't see it any more. fn test_issue_4374() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance, make it non-blocking. let mut fds = [-1, -1]; @@ -721,8 +716,7 @@ fn test_issue_4374() { /// Same as above, but for becoming un-readable. fn test_issue_4374_reads() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance, make it non-blocking. let mut fds = [-1, -1]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index e86c70b590b36..e753983344bd4 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -1,6 +1,7 @@ //@only-target: linux android illumos // test_race, test_blocking_read and test_blocking_write depend on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -9,6 +10,7 @@ use std::{io, thread}; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::*; fn main() { test_read_write(); @@ -35,8 +37,8 @@ fn write_bytes(fd: i32, data: [u8; N]) -> io::Result { } fn test_read_write() { - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = + errno_result(unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }).unwrap(); let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); // Write 1 to the counter. let res = write_bytes(fd, sized_8_data).unwrap(); @@ -97,9 +99,15 @@ fn test_read_write() { fn test_race() { static mut VAL: u8 = 0; - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + + let fd = + errno_result(unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }).unwrap(); let thread1 = thread::spawn(move || { + if !cfg!(miri) { + // Make sure the write goes first. + thread::sleep(std::time::Duration::from_millis(10)); + } + let mut buf: [u8; 8] = [0; 8]; let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. @@ -130,8 +138,7 @@ fn test_syscall() { // This test will block on eventfd read then get unblocked by `write`. fn test_blocking_read() { // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; // This will block. @@ -154,8 +161,7 @@ fn test_blocking_read() { fn test_blocking_write() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { @@ -192,8 +198,7 @@ fn test_blocking_write() { fn test_two_threads_blocked_on_eventfd() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index a131112ee258d..d9d00d7ba239b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -272,7 +272,7 @@ fn test_dup() { let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); unsafe { - let fd = libc::open(name.as_ptr(), libc::O_RDONLY); + let fd = errno_result(libc::open(name.as_ptr(), libc::O_RDONLY)).unwrap(); let new_fd = libc::dup(fd); let new_fd2 = libc::dup2(fd, 8); @@ -856,8 +856,8 @@ fn test_readdir() { assert!(!dirp.is_null()); let mut entries = Vec::new(); loop { - cfg_if::cfg_if! { - if #[cfg(target_os = "macos")] { + cfg_select! { + target_os = "macos" => { // On macos we only support readdir_r as that's what std uses there. use std::mem::MaybeUninit; use libc::dirent; @@ -866,7 +866,8 @@ fn test_readdir() { let ret = libc::readdir_r(dirp, entry.as_mut_ptr(), &mut result); assert_eq!(ret, 0); let entry_ptr = result; - } else { + } + _ => { let entry_ptr = libc::readdir(dirp); } } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index 98d7340fa9db3..12b5ae5666cfd 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # No libc pipe on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native use std::thread; #[path = "../../utils/libc.rs"] @@ -39,7 +40,7 @@ fn test_pipe() { let data = b"123"; write_all(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let (part1, rest) = read_split_slice(fds[0], &mut buf4).unwrap(); + let (part1, rest) = read_partial(fds[0], &mut buf4).unwrap(); assert_eq!(part1[..], data[..part1.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[1], b"34").unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index 9ed0b9c735979..b7918581bc649 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -28,6 +28,8 @@ fn main() { test_readiness_after_short_read(); test_readiness_after_short_peek(); test_readiness_after_short_write(); + test_readable_after_read_shutdown_and_short_read(); + test_writable_after_write_shutdown_with_full_buffer(); } /// Test that connecting to a server socket works when the client @@ -359,7 +361,7 @@ fn test_shutdown_read_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); @@ -387,7 +389,7 @@ fn test_shutdown_read() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); @@ -411,7 +413,7 @@ fn test_shutdown_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || { @@ -562,24 +564,14 @@ fn test_readiness_after_short_write() { unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); - // Spawn the server thread. - let server_thread = thread::spawn(move || { - let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); - // Return the peer socket file descriptor such that we can use - // it after joining the server thread. - peerfd - }); - net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); unsafe { // Change client socket to be non-blocking. errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); } - // The peer socket is a blocking socket. - let peerfd = server_thread.join().unwrap(); - // Add client socket with writable interest to epoll. epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLOUT).unwrap(); @@ -636,3 +628,153 @@ fn test_readiness_after_short_write() { // We should again be able to write into the socket. libc_utils::write_all(client_sockfd, &buffer).unwrap(); } + +/// Test that Miri correctly keeps the readable readiness when the read end of the client +/// socket has been closed -- even after a short read. +fn test_readable_after_read_shutdown_and_short_read() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + // Write `TEST_BYTES` into the stream. + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + + // Close the write end, so that the reader will get an EOF. + // (We could alternatively test this by closing the read end of the client socket, + // but Windows has some special behavior when closing a read end while there's still + // data coming in, so we avoid that.) + unsafe { errno_check(libc::shutdown(peerfd, libc::SHUT_WR)) }; + }); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + + // Change client socket to be non-blocking. + unsafe { errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)) }; + + server_thread.join().unwrap(); + + // Add client socket with "read closed" and "readable" interest to epoll. + epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLIN | EPOLLRDHUP).unwrap(); + + // Ensure that the socket is readable and that its read end is closed. + check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLRDHUP, data: client_sockfd }], -1); + + let mut buffer = [0u8; 1024]; + + // We want to read in chunks of 16 bytes. To ensure we get a short read, `TEST_BYTES.len()` + // must not be dividable by 16. + assert!(TEST_BYTES.len() % 16 != 0); + + let mut total_bytes_read = 0; + // Read everything from the socket until we get a short read. + // We don't want to provide `TEST_BYTES.len()` as `count` because then we won't trigger + // a short read. + loop { + let bytes_read = unsafe { + errno_result(libc::read( + client_sockfd, + buffer.as_mut_ptr().byte_add(total_bytes_read).cast(), + // Read a chunk of 16 bytes. + 16, + )) + .unwrap() + }; + + total_bytes_read += bytes_read as usize; + if bytes_read < 16 { + // We had a short read; we thus assume the read buffer is empty. + break; + } + } + assert_eq!(total_bytes_read, TEST_BYTES.len()); + + // We had a short read because `buffer` is bigger than `TEST_BYTES`. + // Because the read end of the socket is closed, we should still be able to + // read to detect EOFs. + + // Ensure that the "readable" and "read closed" readiness flags are still set. + assert_eq!( + current_epoll_readiness::<8>(client_sockfd, EPOLLIN | EPOLLET | EPOLLRDHUP), + EPOLLIN | EPOLLRDHUP + ); + + // A read should not block and return 0, indicating EOF. + let mut buffer = [1u8; 16]; + let bytes_read = unsafe { + errno_result(libc::read(client_sockfd, buffer.as_mut_ptr().cast(), buffer.len())).unwrap() + }; + assert_eq!(bytes_read, 0); +} + +/// Test that the writable readiness gets set when the write end of a socket +/// is closed -- even when the socket write buffer is full. +fn test_writable_after_write_shutdown_with_full_buffer() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // Add client socket with level-triggered "writable" and "write closed" interest to epoll. + epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLHUP).unwrap(); + + // Wait until the socket becomes writable. + check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); + + // We now want to fill the write buffer of the socket by repeatedly writing + // `buffer` into it. The last write should then be a short write. + // We assume/hope that the write buffer length is not divisible by 1039. + let buffer = [123u8; 1039]; + + loop { + let result = unsafe { + errno_result(libc::write(client_sockfd, buffer.as_ptr().cast(), buffer.len())) + }; + + match result { + Ok(bytes_written) => { + if (bytes_written as usize) < buffer.len() { + // We had a short write; we thus assume the write buffer is full. + break; + } + } + Err(err) if err.kind() == ErrorKind::WouldBlock => { + // Windows and Apple hosts behave weirdly when attempting to fill up the write buffer. + // Instead of doing a short write to completely fill the buffer, they can return an + // EWOULDBLOCK when the next write wouldn't fit into the buffer. + // When we get such an error, we also assume the write buffer is full. + break; + } + Err(err) => panic!("unexpected error whilst filling up buffer: {err}"), + } + } + + // The write buffer is full; because this is a level-triggered interest, + // a readiness of 0 means that the socket would now block on writes. + check_epoll_wait(epfd, &[], 0); + + // Close the socket write end. + unsafe { + errno_check(libc::shutdown(client_sockfd, libc::SHUT_WR)); + } + + // The socket should no longer block on writes after its write end is closed. + check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); + + // A write should not block and return an error. + let result = + unsafe { errno_result(libc::write(client_sockfd, buffer.as_ptr().cast(), buffer.len())) }; + assert_eq!(result.unwrap_err().kind(), ErrorKind::BrokenPipe); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index da521600d84a6..f994dc28a9349 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # No libc socketpair on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -34,7 +35,7 @@ fn test_socketpair() { let data = b"abc"; write_all(fds[0], data).unwrap(); let mut buf2: [u8; 5] = [0; 5]; - let (read, rest) = read_split_slice(fds[1], &mut buf2).unwrap(); + let (read, rest) = read_partial(fds[1], &mut buf2).unwrap(); assert_eq!(read[..], data[..read.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[0], b"12").unwrap(); @@ -52,7 +53,7 @@ fn test_socketpair() { let data = b"abc"; write_all(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let (read, rest) = read_split_slice(fds[0], &mut buf4).unwrap(); + let (read, rest) = read_partial(fds[0], &mut buf4).unwrap(); assert_eq!(read[..], data[..read.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[1], b"12").unwrap(); @@ -64,9 +65,9 @@ fn test_socketpair() { errno_check(unsafe { libc::close(fds[0]) }); // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - let (read, _tail) = read_split_slice(fds[1], &mut buf).unwrap(); + let (read, _tail) = read_partial(fds[1], &mut buf).unwrap(); assert_eq!(read, data); - let (read, _tail) = read_split_slice(fds[1], &mut buf).unwrap(); + let (read, _tail) = read_partial(fds[1], &mut buf).unwrap(); assert_eq!(read, &[]); // Writing the other end should emit EPIPE. let err = write_all(fds[1], &mut buf).unwrap_err(); @@ -132,6 +133,11 @@ fn test_blocking_read() { // Test the behaviour of a socketpair getting blocked on write and subsequently unblocked. fn test_blocking_write() { + // The test uses Miri's exact buffer size. + if !cfg!(miri) { + return; + } + let mut fds = [-1, -1]; errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let arr1: [u8; 0x34000] = [1; 0x34000]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index f315d2ab117f9..3bfcfab099d7b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -1,5 +1,6 @@ //@ignore-target: windows # no libc time APIs on Windows //@compile-flags: -Zmiri-disable-isolation +//@run-native #[path = "../../utils/libc.rs"] mod libc_utils; @@ -9,6 +10,17 @@ use std::{env, mem, ptr}; use libc_utils::errno_check; +fn set_tz(name: &str) { + extern "C" { + fn tzset(); + } + + env::set_var("TZ", name); + if !cfg!(miri) { + unsafe { tzset() }; // re-read TZ env var (natively, it may be cached) + } +} + fn main() { test_clocks(); test_posix_gettimeofday(); @@ -66,11 +78,13 @@ fn test_posix_gettimeofday() { assert!(tv.tv_sec > 0); assert!(tv.tv_usec >= 0); // Theoretically this could be 0. - // Test that non-null tz returns an error (because we don't support it). - let mut tz = mem::MaybeUninit::::uninit(); - let tz_ptr = tz.as_mut_ptr(); - let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; - assert_eq!(is_error, -1); + if cfg!(miri) { + // Test that non-null tz returns an error (because we don't support it). + let mut tz = mem::MaybeUninit::::uninit(); + let tz_ptr = tz.as_mut_ptr(); + let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; + assert_eq!(is_error, -1); + } } /// Helper function to create an empty tm struct. @@ -104,9 +118,8 @@ fn create_empty_tm() -> libc::tm { /// Original GMT test fn test_localtime_r_gmt() { - // Set timezone to GMT. - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -120,7 +133,9 @@ fn test_localtime_r_gmt() { assert_eq!(tm.tm_year, 124); assert_eq!(tm.tm_wday, 0); assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", target_os = "macos", @@ -130,21 +145,21 @@ fn test_localtime_r_gmt() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } // The returned value is the pointer passed in. assert!(ptr::eq(res, &mut tm)); - - // Remove timezone setting. - env::remove_var(key); } /// PST timezone test (testing different timezone handling). fn test_localtime_r_pst() { - let key = "TZ"; - env::set_var(key, "PST8PDT"); + set_tz("PST8PDT"); + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -159,7 +174,9 @@ fn test_localtime_r_pst() { assert_eq!(tm.tm_year, 124); assert_eq!(tm.tm_wday, 0); assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); // DST information unavailable + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); // DST information unavailable + } #[cfg(any( target_os = "linux", @@ -170,18 +187,20 @@ fn test_localtime_r_pst() { { assert_eq!(tm.tm_gmtoff, -7 * 3600); // -7 hours in seconds unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "-07"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "-07" } else { "PDT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Unix epoch test (edge case testing). fn test_localtime_r_epoch() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); + const TIME_SINCE_EPOCH: libc::time_t = 0; // 1970-01-01 00:00:00 let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -196,7 +215,9 @@ fn test_localtime_r_epoch() { assert_eq!(tm.tm_year, 70); assert_eq!(tm.tm_wday, 4); // Thursday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -207,19 +228,20 @@ fn test_localtime_r_epoch() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Future date test (testing large values). #[cfg(target_pointer_width = "64")] fn test_localtime_r_future_64b() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); // Using 2050-01-01 00:00:00 for 64-bit systems // value that's safe for 64-bit time_t @@ -237,7 +259,9 @@ fn test_localtime_r_future_64b() { assert_eq!(tm.tm_year, 150); // 2050 - 1900 assert_eq!(tm.tm_wday, 6); // Saturday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -248,19 +272,20 @@ fn test_localtime_r_future_64b() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Future date test (testing large values for 32b target). #[cfg(target_pointer_width = "32")] fn test_localtime_r_future_32b() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); // Using 2030-01-01 00:00:00 for 32-bit systems // Safe value within i32 range @@ -279,7 +304,9 @@ fn test_localtime_r_future_32b() { assert_eq!(tm.tm_year, 130); // 2030 - 1900 assert_eq!(tm.tm_wday, 2); // Tuesday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -290,19 +317,20 @@ fn test_localtime_r_future_32b() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Tests the behavior of `localtime_r` with multiple calls to ensure deduplication of `tm_zone` pointers. #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "android"))] fn test_localtime_r_multiple_calls_deduplication() { - let key = "TZ"; - env::set_var(key, "PST8PDT"); + set_tz("PST8PDT"); const TIME_SINCE_EPOCH_BASE: libc::time_t = 1712475836; // Base timestamp: 2024-04-07 07:43:56 GMT const NUM_CALLS: usize = 50; @@ -321,9 +349,11 @@ fn test_localtime_r_multiple_calls_deduplication() { let unique_count = unique_pointers.len(); + // Miri non-determinisitcally de-duplicates. Native always deduplicates. + let min = if cfg!(miri) { 2 } else { 1 }; assert!( - unique_count >= 2 && unique_count <= (NUM_CALLS - 1), - "Unexpected number of unique tm_zone pointers: {} (expected between 2 and {})", + unique_count >= min && unique_count <= (NUM_CALLS - 1), + "Unexpected number of unique tm_zone pointers: {} (expected between {min} and {})", unique_count, NUM_CALLS - 1 ); diff --git a/src/tools/miri/tests/pass-dep/libc/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs index bfd840d2fb89d..692b59d443ae8 100644 --- a/src/tools/miri/tests/pass-dep/libc/mmap.rs +++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs @@ -94,6 +94,78 @@ fn test_mmap( assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); } +fn test_mprotect() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + 4 * page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + Default::default(), + ) + }; + assert!(!ptr.is_null()); + + // Protect part of it redundantly. + let res = unsafe { + libc::mprotect(ptr.byte_add(2 * page_size), 42, libc::PROT_READ | libc::PROT_WRITE) + }; + assert_eq!(res, 0i32); + + // Protect everything redundantly. + let res = unsafe { libc::mprotect(ptr, 4 * page_size, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, 0i32); + + // We report an error when the address is not a multiple of the page size. + let res = + unsafe { libc::mprotect(ptr.byte_add(11), page_size, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error if the length cannot be rounded up to a multiple of the page size. + let res = unsafe { libc::mprotect(ptr, usize::MAX - 1, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOMEM); +} + +fn test_madvise() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + 4 * page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + Default::default(), + ) + }; + assert!(!ptr.is_null()); + + for advice in [libc::MADV_NORMAL, libc::MADV_RANDOM, libc::MADV_SEQUENTIAL, libc::MADV_WILLNEED] + { + // Advise part of it redundantly. + let res = unsafe { libc::madvise(ptr.byte_add(2 * page_size), 42, advice) }; + assert_eq!(res, 0i32); + + // Protect everything redundantly. + let res = unsafe { libc::madvise(ptr, 4 * page_size, advice) }; + assert_eq!(res, 0i32); + } + + // We report an error when the address is not a multiple of the page size. + let res = unsafe { libc::madvise(ptr.byte_add(11), page_size, libc::MADV_NORMAL) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error if the length cannot be rounded up to a multiple of the page size. + let res = unsafe { libc::madvise(ptr, usize::MAX - 1, libc::MADV_NORMAL) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOMEM); +} + #[cfg(target_os = "linux")] fn test_mremap() { let page_size = page_size::get(); @@ -145,6 +217,8 @@ fn main() { test_mmap(libc::mmap); #[cfg(target_os = "linux")] test_mmap(libc::mmap64); + test_mprotect(); + test_madvise(); #[cfg(target_os = "linux")] test_mremap(); } diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index cf7ea4c6abba1..6cba672c6569c 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -4,17 +4,21 @@ use std::ffi::{CStr, CString}; use std::thread; const MAX_THREAD_NAME_LEN: usize = { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux"))] { + cfg_select! { + target_os = "linux" => { 16 - } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + } + any(target_os = "illumos", target_os = "solaris") => { 32 - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { libc::MAXTHREADNAMESIZE // 64, at the time of writing - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { usize::MAX // as far as I can tell - } else { - panic!() + } + _ => { + compile_error!("unsupported OS"); } } }; @@ -28,35 +32,38 @@ fn main() { .collect::(); fn set_thread_name(name: &CStr) -> i32 { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "freebsd", target_os = "illumos", target_os = "solaris" - ))] { + ) => { unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) } - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { unsafe { libc::pthread_setname_np(name.as_ptr().cast()) } - } else { + } + _ => { compile_error!("set_thread_name not supported for this OS") } } } fn get_thread_name(name: &mut [u8]) -> i32 { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "freebsd", target_os = "illumos", target_os = "solaris", target_os = "macos" - ))] { + ) => { unsafe { libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) } - } else { + } + _ => { compile_error!("get_thread_name not supported for this OS") } } @@ -95,13 +102,14 @@ fn main() { // Test what happens when the buffer is shorter than 16, but still long enough. let res = get_thread_name(&mut buf[..15]); - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { // For glibc used by linux-gnu there should be a failue, // if a shorter than 16 bytes buffer is provided, even if that would be // large enough for the thread name. assert_eq!(res, libc::ERANGE); - } else { + } + _ => { // Everywhere else, this should work. assert_eq!(res, 0); // POSIX seems to promise at least 15 chars excluding a null terminator. @@ -112,15 +120,16 @@ fn main() { // Test what happens when the buffer is too short even for the short name. let res = get_thread_name(&mut buf[..4]); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- they truncate instead. assert_eq!(res, 0); let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert_eq!(cstr.to_bytes_with_nul().len(), 4); assert!(short_name.as_bytes().starts_with(cstr.to_bytes())); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -128,12 +137,13 @@ fn main() { // Test zero-sized buffer. let res = get_thread_name(&mut []); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- even with size 0. assert_eq!(res, 0); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -149,16 +159,18 @@ fn main() { // Set full thread name. let cstr = CString::new(long_name.clone()).unwrap(); let res = set_thread_name(&cstr); - cfg_if::cfg_if! { - if #[cfg(target_os = "freebsd")] { + cfg_select! { + target_os = "freebsd" => { // Names of all size are supported. assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); assert_eq!(res, 0); - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { // Name is too long. assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); assert_eq!(res, libc::ENAMETOOLONG); - } else { + } + _ => { // Name is too long. assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); assert_eq!(res, libc::ERANGE); @@ -179,14 +191,15 @@ fn main() { // Test what happens when our buffer is just one byte too small. let res = get_thread_name(&mut buf[..truncated_name.len()]); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- they truncate instead. assert_eq!(res, 0); let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -199,10 +212,11 @@ fn main() { // Now set the name for a non-existing thread and verify error codes. let invalid_thread = 0xdeadbeef; let error = { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { libc::ENOENT - } else { + } + _ => { libc::ESRCH } } diff --git a/src/tools/miri/tests/pass-dep/shims/gettid.rs b/src/tools/miri/tests/pass-dep/shims/gettid.rs index 0dce7bdc0c52e..05885ac277f76 100644 --- a/src/tools/miri/tests/pass-dep/shims/gettid.rs +++ b/src/tools/miri/tests/pass-dep/shims/gettid.rs @@ -5,29 +5,37 @@ #![feature(linkage)] fn gettid() -> u64 { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + cfg_select! { + any(target_os = "android", target_os = "linux") => { gettid_linux_like() - } else if #[cfg(target_os = "nto")] { + } + any(target_os = "nto", target_os = "qnx") => { unsafe { libc::gettid() as u64 } - } else if #[cfg(target_os = "openbsd")] { + } + target_os = "openbsd" => { unsafe { libc::getthrid() as u64 } - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { unsafe { libc::pthread_getthreadid_np() as u64 } - } else if #[cfg(target_os = "netbsd")] { + } + target_os = "netbsd" => { unsafe { libc::_lwp_self() as u64 } - } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + } + any(target_os = "solaris", target_os = "illumos") => { // On Solaris and Illumos, the `pthread_t` is the OS TID. unsafe { libc::pthread_self() as u64 } - } else if #[cfg(target_vendor = "apple")] { + } + target_vendor = "apple" => { let mut id = 0u64; let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; assert_eq!(status, 0); id - } else if #[cfg(windows)] { + } + windows => { use windows_sys::Win32::System::Threading::GetCurrentThreadId; unsafe { GetCurrentThreadId() as u64 } - } else { + } + _ => { compile_error!("platform has no gettid") } } diff --git a/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs b/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs index 6791382f8e0f9..5f4e68b913335 100644 --- a/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs +++ b/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs @@ -3,8 +3,6 @@ // Tests operations not performable through C++'s atomic API // but doable in safe (at least sound) Rust. -#![feature(atomic_from_mut)] - use std::sync::atomic::Ordering::*; use std::sync::atomic::{AtomicU16, AtomicU32}; use std::thread::spawn; diff --git a/src/tools/miri/tests/pass/c-variadic.rs b/src/tools/miri/tests/pass/c-variadic.rs index 4179c7090830c..e17d6820c5b0b 100644 --- a/src/tools/miri/tests/pass/c-variadic.rs +++ b/src/tools/miri/tests/pass/c-variadic.rs @@ -106,6 +106,20 @@ fn various_types() { } } +fn equal_up_to_free_lifetime() { + // Types are considered equal up to free lifetimes: `*const &'static str` + // is the same as `*const &'a str`. + // Bound lifetimes (using e.g. `for<'_>`) are different. + #[expect(improper_ctypes_definitions)] + pub unsafe extern "C" fn foo(mut args: ...) -> &'static str { + unsafe { *args.next_arg::<*const &'static str>() } + } + + let data = String::from("abc"); + let x: &str = data.as_str(); + assert_eq!(unsafe { foo(&raw const x) }, "abc"); +} + fn clone() { if cfg!(force_intrinsic_fallback) { // Skip this test when we use the fallback bodies. The fallback body does @@ -170,6 +184,7 @@ fn main() { forward_by_ref(); nested(); various_types(); + equal_up_to_free_lifetime(); clone(); clone_and_advance(); } diff --git a/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs new file mode 100644 index 0000000000000..0989cdd86a362 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs @@ -0,0 +1,9 @@ +fn main() { + foo(1234); +} + +#[allow(no_mangle_generic_items)] +#[unsafe(no_mangle)] +fn foo(value: T) { + println!("{value:?}"); +} diff --git a/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout new file mode 100644 index 0000000000000..81c545efebe5f --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout @@ -0,0 +1 @@ +1234 diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs index 1345924beecf0..c640dde5afaf5 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs @@ -1,6 +1,7 @@ // We're testing aarch64 AES target specific features. //@only-target: aarch64 //@compile-flags: -C target-feature=+neon,+aes +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs index 849f99ee36cce..8d50d152f2ebe 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs @@ -1,6 +1,7 @@ // We're testing aarch64 CRC32 target specific features //@only-target: aarch64 //@compile-flags: -C target-feature=+crc +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs index 884f8eff41bdb..fdd0ff6ca6dfb 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs @@ -1,6 +1,7 @@ // We're testing aarch64 target specific features //@only-target: aarch64 //@compile-flags: -C target-feature=+neon +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; @@ -14,6 +15,7 @@ fn main() { test_tbl1_v16i8_basic(); test_vpadd(); test_vpaddl(); + test_vqdmulh(); } } @@ -157,3 +159,51 @@ unsafe fn test_vpaddl() { vst1q_u64(r.as_mut_ptr(), vpaddlq_u32(a)); assert_eq!(r, e); } + +#[target_feature(enable = "neon")] +unsafe fn test_vqdmulh() { + let a = vld1_s32([i32::MIN, i32::MAX].as_ptr()); + let r: [i32; 2] = transmute(vqdmulh_n_s32(a, i32::MIN)); + assert_eq!(r, [i32::MAX, -i32::MAX]); + + // This is the actual calculation that happens. + let product = i32::MIN as i128 * i32::MIN as i128 * 2; + assert_eq!(i32::MAX, (product >> 32).clamp(i32::MIN as i128, i32::MAX as i128) as i32); + + let product = i32::MAX as i128 * i32::MIN as i128 * 2; + assert_eq!(-i32::MAX, (product >> 32).clamp(i32::MIN as i128, i32::MAX as i128) as i32); + + let b = vld1_s32([123, i32::MIN].as_ptr()); + let r: [i32; 2] = transmute(vqdmulh_s32(a, b)); + assert_eq!(r, [-123, -i32::MAX]); + + // Wider 32-bit versions. + let a = vld1q_s32([0x4000_0000, -0x4000_0000, i32::MIN, i32::MAX].as_ptr()); + + let b = vld1q_s32([123, 456, 0x4000_0000, 789].as_ptr()); + let r: [i32; 4] = transmute(vqdmulhq_s32(a, b)); + assert_eq!(r, [61, -228, -1073741824, 788]); + + let r: [i32; 4] = transmute(vqdmulhq_n_s32(a, 0x4000_0000)); + assert_eq!(r, [536870912, -536870912, -1073741824, 1073741823]); + + // 16-bit versions. + + let a = vld1_s16([i16::MIN, i16::MAX, 0, 16384].as_ptr()); + let r: [i16; 4] = transmute(vqdmulh_n_s16(a, i16::MIN)); + assert_eq!(r, [i16::MAX, -i16::MAX, 0, -16384]); + + let b = vld1_s16([123, i16::MIN, 456, 789].as_ptr()); + let r: [i16; 4] = transmute(vqdmulh_s16(a, b)); + assert_eq!(r, [-123, -i16::MAX, 0, 394]); + + // Wider 16-bit versions. + + let a = vld1q_s16([i16::MIN, i16::MAX, 0, 16384, -16384, 8192, 1, -1].as_ptr()); + let b = vld1q_s16([123, 456, 789, i16::MIN, 1, 2, 3, 4].as_ptr()); + let r: [i16; 8] = transmute(vqdmulhq_s16(a, b)); + assert_eq!(r, [-123, 455, 0, -16384, -1, 0, 0, -1]); + + let r: [i16; 8] = transmute(vqdmulhq_n_s16(a, i16::MIN)); + assert_eq!(r, [32767, -32767, 0, -16384, 16384, -8192, -1, 1]); +} diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs index ae5731bc8a6a3..1e36867c4a0b6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sha,+sse2,+ssse3,+sse4.1 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs index baa984e68d83b..87b2f5d3d80b3 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+adx +//@run-native #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86 { diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 8936ae8e91268..ba51dcea35cc6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+aes,+vaes,+avx512f +//@run-native use core::mem::transmute; #[cfg(target_arch = "x86")] @@ -11,7 +12,6 @@ use std::arch::x86_64::*; fn main() { assert!(is_x86_feature_detected!("aes")); assert!(is_x86_feature_detected!("vaes")); - assert!(is_x86_feature_detected!("avx512f")); unsafe { test_aes(); @@ -86,7 +86,7 @@ unsafe fn test_aes() { // be interpreted as integers; signedness does not make sense for them, but // __m128i happens to be defined in terms of signed integers. #[allow(overflowing_literals)] -#[target_feature(enable = "vaes,avx512f")] +#[target_feature(enable = "vaes")] unsafe fn test_vaes() { #[target_feature(enable = "avx")] unsafe fn get_a256() -> __m256i { @@ -177,6 +177,12 @@ unsafe fn test_vaes() { } test_mm256_aesenclast_epi128(); + // The tests below require avx512. + if !is_x86_feature_detected!("avx512f") { + println!("warning: skipping avx512 tests"); + return; + } + #[target_feature(enable = "avx512f")] unsafe fn get_a512() -> __m512i { // Constants are random diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs index 9f7c12c4393b5..8709a17e8aeb4 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs index de1abc818420c..7fe75254c2dd8 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; @@ -1068,23 +1069,31 @@ unsafe fn test_avx2() { 18, 20, 22, 24, 26, 28, 30, ); - let r = _mm256_mpsadbw_epu8::<0b000>(a, a); + let r = _mm256_mpsadbw_epu8::<0b00000>(a, a); let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b001>(a, a); + let r = _mm256_mpsadbw_epu8::<0b001001>(a, a); let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 32, 24, 16, 8, 0, 8, 16, 24); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b100>(a, a); + let r = _mm256_mpsadbw_epu8::<0b000001>(a, a); + let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 0, 8, 16, 24, 32, 40, 48, 56); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b001000>(a, a); + let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 32, 24, 16, 8, 0, 8, 16, 24); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b100100>(a, a); let e = _mm256_setr_epi16(16, 20, 24, 28, 32, 36, 40, 44, 32, 40, 48, 56, 64, 72, 80, 88); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b101>(a, a); + let r = _mm256_mpsadbw_epu8::<0b101101>(a, a); let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b111>(a, a); + let r = _mm256_mpsadbw_epu8::<0b111111>(a, a); let e = _mm256_setr_epi16(32, 28, 24, 20, 16, 12, 8, 4, 64, 56, 48, 40, 32, 24, 16, 8); assert_eq_m256i(r, e); } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index 31f47b57fd285..0541e9a6cc6e0 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512bitalg,+avx512vpopcntdq,+avx512vnni,+avx512vbmi +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; @@ -9,6 +10,13 @@ use std::arch::x86_64::*; use std::mem::transmute; fn main() { + if !is_x86_feature_detected!("avx512f") { + // GH runners don't have this, but we still want to run this natively if + // the machine happens to have gfni. So we bail out dynamically. + println!("warning: skipping AVX512 tests"); + return; + } + assert!(is_x86_feature_detected!("avx512f")); assert!(is_x86_feature_detected!("avx512vl")); assert!(is_x86_feature_detected!("avx512bw")); diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs index 030258f21fa51..d6c05b601f4eb 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+bmi1,+bmi2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs index 48958ef581096..ef0fd62da5f3f 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+gfni,+avx512f +//@run-native // The constants in the tests below are just bit patterns. They should not // be interpreted as integers; signedness does not make sense for them, but @@ -20,8 +21,14 @@ const CONSTANT_BYTE: i32 = 0x63; fn main() { // Mostly copied from library/stdarch/crates/core_arch/src/x86/gfni.rs - assert!(is_x86_feature_detected!("avx512f")); - assert!(is_x86_feature_detected!("gfni")); + assert!(is_x86_feature_detected!("avx")); + + if !is_x86_feature_detected!("gfni") { + // GH runners don't have this, but we still want to run this natively if + // the machine happens to have gfni. So we bail out dynamically. + println!("warning: skipping gfni tests"); + return; + } unsafe { let byte_mul_test_data = generate_byte_mul_test_data(); @@ -29,15 +36,20 @@ fn main() { let affine_mul_test_data_constant = generate_affine_mul_test_data(CONSTANT_BYTE as u8); let inv_tests_data = generate_inv_tests_data(); - test_mm512_gf2p8mul_epi8(&byte_mul_test_data); test_mm256_gf2p8mul_epi8(&byte_mul_test_data); test_mm_gf2p8mul_epi8(&byte_mul_test_data); - test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); test_mm256_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); test_mm_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); - test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); test_mm256_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); test_mm_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + + if is_x86_feature_detected!("avx512f") { + test_mm512_gf2p8mul_epi8(&byte_mul_test_data); + test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); + test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + } else { + println!("warning: skipping avx512 tests"); + } } } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs index 6051987f8d4c1..50ce9928ab7ed 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+pclmulqdq +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs index 9136b5eda3870..f42c6db676839 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +//@run-native #![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs index 570da30f0b622..8e4b284b1d150 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +//@run-native #![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs index 10842160abdc6..e226f3512ac6e 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs @@ -2,6 +2,7 @@ //@only-target: x86_64 i686 // SSSE3 implicitly enables SSE3 //@compile-flags: -C target-feature=+ssse3 +//@run-native use core::mem::transmute; #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs index 7331c6ed0db33..48510b46ff327 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sse4.1 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs index 30908baa6c15e..c998fe800df45 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sse4.2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs index e2a045bf81ff1..5ceaf405f4040 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs @@ -3,6 +3,7 @@ //@only-target: x86_64 i686 //@[avx512]compile-flags: -C target-feature=+vpclmulqdq,+avx512f //@[avx]compile-flags: -C target-feature=+vpclmulqdq,+avx2 +//@run-native // The constants in the tests below are just bit patterns. They should not // be interpreted as integers; signedness does not make sense for them, but diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs index a18b6d01524e8..9745a437eccb6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs @@ -1,4 +1,6 @@ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +//@only-target: x86_64 i686 +//@run-native + mod x86 { #[cfg(target_arch = "x86")] use core::arch::x86 as arch; @@ -84,7 +86,6 @@ mod x86_64 { } fn main() { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] x86::main(); #[cfg(target_arch = "x86_64")] x86_64::main(); diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 633452f6052d7..3e46ee17c8a65 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -182,8 +182,9 @@ fn miri_config( .map(Into::into) .collect(), envs: vec![ - // Reset `RUSTFLAGS` to work around . + // Reset `RUSTFLAGS`/`CARGO_ENCODED_RUSTFLAGS` to work around . ("RUSTFLAGS".into(), None), + ("CARGO_ENCODED_RUSTFLAGS".into(), None), // Reset `MIRIFLAGS` because it caused trouble in the past and should not be needed. ("MIRIFLAGS".into(), None), // Allow `cargo miri build`. diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs index f46e6aad01a5b..0f762ed916de5 100644 --- a/src/tools/miri/tests/utils/libc.rs +++ b/src/tools/miri/tests/utils/libc.rs @@ -86,7 +86,7 @@ pub fn read_exact_array(fd: libc::c_int) -> io::Result<[u8; N]> /// Do a single read from `fd` and return the part of the buffer that was written into, /// and the rest. #[track_caller] -pub fn read_split_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { +pub fn read_partial(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { let res = errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) })?; Ok(buf.split_at_mut(res as usize)) } diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 918f5ef0d5069..c1bff4f94272c 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -13,7 +13,7 @@ edition = "2024" bstr = "1.12" gimli = "0.32" libc = "0.2" -object = "0.37" +object = { version = "0.37", features = ["wasm"] } regex = "1.11" serde_json = "1.0" similar = "2.7" diff --git a/src/tools/rust-analyzer/.github/FUNDING.yml b/src/tools/rust-analyzer/.github/FUNDING.yml new file mode 100644 index 0000000000000..1d270e78949f8 --- /dev/null +++ b/src/tools/rust-analyzer/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: rustfoundation +custom: ["rust-lang.org/funding"] diff --git a/src/tools/rust-analyzer/.github/actions/github-release/Dockerfile b/src/tools/rust-analyzer/.github/actions/github-release/Dockerfile index 5849eac7d246a..ad296ce74152f 100644 --- a/src/tools/rust-analyzer/.github/actions/github-release/Dockerfile +++ b/src/tools/rust-analyzer/.github/actions/github-release/Dockerfile @@ -1,4 +1,4 @@ -FROM node:slim +FROM node:24.16-slim@sha256:ca520832af80fa37a57c14077ed0fcdd83b5aefccc356059fdc3a9a05b78ae1f COPY . /action WORKDIR /action diff --git a/src/tools/rust-analyzer/.gitignore b/src/tools/rust-analyzer/.gitignore index 3beabe39bc7a3..32374dac91818 100644 --- a/src/tools/rust-analyzer/.gitignore +++ b/src/tools/rust-analyzer/.gitignore @@ -7,6 +7,7 @@ vendor/ *.log *.iml .vscode/settings.json +.vim/coc-settings.json .DS_Store /out/ /dump.lsif diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 477ecfba2f752..602af6f1222d7 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1002,15 +1002,6 @@ dependencies = [ "typed-arena", ] -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "icu_collections" version = "2.2.0" @@ -3116,7 +3107,6 @@ name = "toolchain" version = "0.0.0" dependencies = [ "camino", - "home", ] [[package]] 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 3094e08a53c3c..a69755baf632c 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 @@ -671,6 +671,7 @@ impl<'db> ExprCollector<'db> { } pl.params() + .filter(|it| it.dotdotdot_token().is_none()) .map(|it| { let type_ref = self.lower_type_ref_opt(it.ty(), impl_trait_lower_fn); let name = match it.pat() { @@ -2403,6 +2404,10 @@ impl<'db> ExprCollector<'db> { statements: &mut Vec, mac: ast::MacroExpr, ) -> Option { + if !self.check_cfg(&ast::Expr::MacroExpr(mac.clone())) { + return None; + } + let mac_call = mac.macro_call()?; let syntax_ptr = AstPtr::new(&ast::Expr::from(mac)); let macro_ptr = AstPtr::new(&mac_call); @@ -2446,10 +2451,6 @@ impl<'db> ExprCollector<'db> { } ast::Stmt::ExprStmt(stmt) => { let expr = stmt.expr(); - match &expr { - Some(expr) if !self.check_cfg(expr) => return, - _ => (), - } let has_semi = stmt.semicolon_token().is_some(); // Note that macro could be expanded to multiple statements if let Some(ast::Expr::MacroExpr(mac)) = expr { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index e97718ca22c32..da412a620d0a7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs @@ -688,3 +688,31 @@ fn foo() { }"#]], ); } + +#[test] +fn foo() { + pretty_print( + r#" +macro_rules! foo { + () => { + 1 + }; +} + +fn foo() -> i64 { + #[cfg(true)] + { + 5 + } + #[cfg(false)] + foo!() +} + "#, + expect![[r#" + fn foo() { + { + 5 + } + }"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 513115684f872..33672d10fa6d5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -141,11 +141,6 @@ pub trait ExpandDatabase: SourceDatabase { fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext; } -#[salsa_macros::interned(no_lifetime, id = span::SyntaxContext, revisions = usize::MAX)] -pub struct SyntaxContextWrapper { - pub data: SyntaxContext, -} - fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> SyntaxContext { match file { HirFileId::FileId(_) => SyntaxContext::root(edition), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index d6580d3752f6b..a880ae5353b66 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -87,18 +87,24 @@ fn intern_const_ref<'db>( let valtree = match (ty.kind(), value) { (TyKind::Uint(uint), Literal::Uint(value, _)) => { let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); - let scalar = ScalarInt::try_from_uint(*value, size).unwrap(); + let Some(scalar) = ScalarInt::try_from_uint(*value, size) else { + return Ok(Const::error(interner)); + }; ValTreeKind::Leaf(scalar) } (TyKind::Uint(uint), Literal::Int(value, _)) => { // `Literal::Int` is the default, so we also need to account for the type being uint. let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); - let scalar = ScalarInt::try_from_uint(*value as u128, size).unwrap(); + let Some(scalar) = ScalarInt::try_from_uint(*value as u128, size) else { + return Ok(Const::error(interner)); + }; ValTreeKind::Leaf(scalar) } (TyKind::Int(int), Literal::Int(value, _)) => { let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); - let scalar = ScalarInt::try_from_int(*value, size).unwrap(); + let Some(scalar) = ScalarInt::try_from_int(*value, size) else { + return Ok(Const::error(interner)); + }; ValTreeKind::Leaf(scalar) } (TyKind::Bool, Literal::Bool(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)), @@ -219,7 +225,9 @@ pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Cr return Const::error(interner); }; let usize_ty = interner.default_types().types.usize; - let scalar = ScalarInt::try_from_uint(value, data_layout.pointer_size()).unwrap(); + let Some(scalar) = ScalarInt::try_from_uint(value, data_layout.pointer_size()) else { + return Const::error(interner); + }; Const::new_valtree(interner, usize_ty, ValTreeKind::Leaf(scalar)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 5421b97db2386..0e51594a78d06 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -2633,6 +2633,19 @@ fn const_generic_subst_fn() { ); } +#[test] +fn const_generic_fixed_width() { + check_number( + r#" + const fn m() -> u64 { + N + } + const GOAL: u64 = m::<0>(); + "#, + 0, + ); +} + #[test] fn layout_of_type_with_associated_type_field_defined_inside_body() { check_number( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index baf8bbd56fb1e..93ae26abce88b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -6,8 +6,8 @@ use base_db::{Crate, target::TargetLoadError}; use either::Either; use hir_def::{ AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, EnumVariantId, - ExpressionStoreOwnerId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, - LocalFieldId, ModuleId, StaticId, TraitId, TypeAliasId, VariantId, + ExpressionStoreOwnerId, FunctionId, GenericDefId, HasModule, ImplId, LocalFieldId, ModuleId, + StaticId, TraitId, TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, db::DefDatabase, expr_store::ExpressionStore, @@ -294,19 +294,6 @@ fn hir_database_is_dyn_compatible() { fn _assert_dyn_compatible(_: &dyn HirDatabase) {} } -#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] -#[derive(PartialOrd, Ord)] -pub struct InternedLifetimeParamId { - /// This stores the param and its index. - pub loc: (LifetimeParamId, u32), -} - -#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] -#[derive(PartialOrd, Ord)] -pub struct InternedConstParamId { - pub loc: ConstParamId, -} - #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedOpaqueTyId { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 057ba7fa868fe..a661731e60f46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -173,6 +173,8 @@ impl<'db> InferenceContext<'_, 'db> { // impl forces the closure kind to `FnOnce` i.e. `u8`. let kind_ty = autoderef.ctx().table.next_ty_var(call_expr.into()); let interner = autoderef.ctx().interner(); + + // Ignore splatting, it is unsupported on closures. let call_sig = interner.mk_fn_sig( [coroutine_closure_sig.tupled_inputs_ty], coroutine_closure_sig.to_coroutine( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 34f9508e763f0..d8a8cceee6583 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -1016,8 +1016,9 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { } } Pat::Expr(expr) => { - // Destructuring assignment. this.mutate_expr(expr)?; + // Destructuring assignment moves + this.consume_or_copy(place); } Pat::Or(_) | Pat::Box { .. } 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 cc48ba06dbfc5..1fddfc09c666f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -464,6 +464,7 @@ pub fn callable_sig_from_fn_trait<'db>( args.tuple_fields(), ret, false, + // FIXME(splat): handle splatted arguments Safety::Safe, ExternAbi::Rust, )); 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 fae63ddc2deae..d63ac7f7a0ed4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -637,6 +637,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn_.abi, if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, fn_.is_varargs, + // FIXME(splat): handle splatted arguments ), inputs_and_output: Tys::new_from_slice(&args), }), 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 eb0e4c60200f2..c69febd019672 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 @@ -1935,25 +1935,37 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { Ok(Interval::new(addr, 4)) } TyKind::Int(int_ty) => { - let size = int_ty.bit_width().unwrap_or(self.ptr_size() as u64); - let value = valtree.inner().to_leaf().to_int(Size::from_bytes(size)); - let addr = self.heap_allocate(size as usize, size as usize)?; - self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; - Ok(Interval::new(addr, size as usize)) + let size = int_ty + .bit_width() + .map(Size::from_bits) + .unwrap_or_else(|| Size::from_bytes(self.ptr_size() as u64)); + let bytes = size.bytes_usize(); + + let value = valtree.inner().to_leaf().to_int(size); + let addr = self.heap_allocate(bytes, bytes)?; + self.write_memory(addr, &value.to_le_bytes()[..bytes])?; + Ok(Interval::new(addr, bytes)) } TyKind::Uint(uint_ty) => { - let size = uint_ty.bit_width().unwrap_or(self.ptr_size() as u64); - let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size)); - let addr = self.heap_allocate(size as usize, size as usize)?; - self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; - Ok(Interval::new(addr, size as usize)) + let size = uint_ty + .bit_width() + .map(Size::from_bits) + .unwrap_or_else(|| Size::from_bytes(self.ptr_size() as u64)); + let bytes = size.bytes_usize(); + + let value = valtree.inner().to_leaf().to_uint(size); + let addr = self.heap_allocate(bytes, bytes)?; + self.write_memory(addr, &value.to_le_bytes()[..bytes])?; + Ok(Interval::new(addr, bytes)) } TyKind::Float(float_ty) => { - let size = float_ty.bit_width(); - let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size)); - let addr = self.heap_allocate(size as usize, size as usize)?; - self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; - Ok(Interval::new(addr, size as usize)) + let size = Size::from_bits(float_ty.bit_width()); + let bytes = size.bytes_usize(); + + let value = valtree.inner().to_leaf().to_uint(size); + let addr = self.heap_allocate(bytes, bytes)?; + self.write_memory(addr, &value.to_le_bytes()[..bytes])?; + Ok(Interval::new(addr, bytes)) } TyKind::RawPtr(..) => { let size = self.ptr_size(); @@ -1995,15 +2007,13 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { _ => not_supported!("unsupported const"), }) .collect::>>()?; + let item_size = item_layout.size.bytes_usize(); let items_addr = self.heap_allocate( - items.len() * (item_layout.size.bits() as usize), - item_layout.align.bits_usize(), + items.len() * item_size, + item_layout.align.bytes() as usize, )?; for (i, item) in items.iter().enumerate() { - self.copy_from_interval( - items_addr.offset(i * (item_layout.size.bits() as usize)), - *item, - )?; + self.copy_from_interval(items_addr.offset(i * item_size), *item)?; } let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?; self.write_memory(ref_addr, &items_addr.to_bytes())?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 6b6dd549b34e0..2c2f7dbf67301 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -843,7 +843,7 @@ impl<'db> InferCtxt<'db> { GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind, span)) } - /// Like `fresh_args_for_item()`, but first uses the args from `first`. + /// Like [`Self::fresh_args_for_item`], but first uses the args from `first`. pub fn fill_rest_fresh_args( &self, span: Span, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index b466fe0f1810b..3c2976eccd980 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1128,6 +1128,9 @@ impl<'db> Interner for DbInterner<'db> { SolverDefId::ConstId(def_id) => { AliasTermKind::UnevaluatedConst { def_id: GeneralConstIdWrapper(def_id.into()) } } + SolverDefId::StaticId(def_id) => { + AliasTermKind::UnevaluatedConst { def_id: GeneralConstIdWrapper(def_id.into()) } + } SolverDefId::AnonConstId(def_id) => { AliasTermKind::UnevaluatedConst { def_id: GeneralConstIdWrapper(def_id.into()) } } @@ -2235,6 +2238,7 @@ impl<'db> DbInterner<'db> { self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) } + // FIXME: add splat support when the experiment is complete pub fn mk_fn_sig( self, inputs: I, @@ -2626,8 +2630,8 @@ macro_rules! impl_gc_visit_slice { } #[inline] - fn visit_slice(header: &[::SliceType], gc: &mut ::intern::GarbageCollector) { - header.generic_visit_with(gc); + fn visit_slice(slice: &[::SliceType], gc: &mut ::intern::GarbageCollector) { + slice.generic_visit_with(gc); } } )* diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index fe31d44207dff..d57d824e325fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -1514,6 +1514,7 @@ impl<'db> DbInterner<'db> { TyKind::Tuple(params) => params, _ => panic!(), }; + // Ignore splatting, it is unsupported on closures. self.mk_fn_sig(params, s.output(), s.c_variadic(), safety, ExternAbi::Rust) }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 3942a7ae27afa..bf0e60cf21b80 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -617,3 +617,40 @@ fn foo(foo: &Foo) { expect!["102..126;85..88;114..117 ByRef(Immutable) *foo &' Foo"], ); } + +#[test] +fn method_call_field_access_regression() { + check_closure_captures( + r#" +//- minicore:copy, fn +struct NonCopy; + +struct Wrapper { + field: NonCopy, +} + +impl Wrapper { + fn wrapped(&self) -> NonCopy { + NonCopy + } +} + +pub struct Wrapper2 { + field1: NonCopy, + field2: NonCopy, +} + +fn fun(wrapper: Wrapper) { + pub fn update(_: impl FnOnce(&mut T)) { + todo!() + } + + update::(|this| { + this.field1 = wrapper.wrapped(); + this.field2 = wrapper.field; + }); +} + "#, + expect!["319..411;206..213;350..357,391..398 ByValue wrapper Wrapper"], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index c77b20f4b54c1..fb00a755fa055 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -394,6 +394,26 @@ fn main() { ); } +#[test] +fn static_as_array_len_does_not_panic() { + check_no_mismatches( + r#" +static S: usize = 8; +const A: [u8; S] = [0; 8]; + "#, + ); +} + +#[test] +fn oversized_array_len_does_not_panic() { + // The array length literal does not fit in `usize`; interning it must not panic. + check_no_mismatches( + r#" +fn f(_: [u8; 18446744073709551616]) {} + "#, + ); +} + #[test] fn another_20654_case() { check_no_mismatches( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 4c76ae901da8c..2ca9ebe070bc9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -20,7 +20,7 @@ use hir_expand::name::Name; use intern::sym; use rustc_type_ir::{ TypeVisitableExt, TypingMode, - inherent::{BoundExistentialPredicates, IntoKind}, + inherent::{BoundExistentialPredicates, IntoKind, Ty as _}, }; use crate::{ @@ -154,6 +154,9 @@ pub fn implements_trait_unique_with_infcx<'db>( let args = create_args(&infcx); let trait_ref = rustc_type_ir::TraitRef::new_from_args(interner, trait_.into(), args); + if trait_ref.self_ty().is_ty_error() { + return false; + } let obligation = Obligation::new(interner, ObligationCause::dummy(), env.param_env, trait_ref); infcx.predicate_must_hold_modulo_regions(&obligation) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 2755962362677..a272351e134cc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -1236,15 +1236,11 @@ fn main { fn destructure_in_sub_pattern() { check_sub_pattern_assist( r#" -#![feature(bindings_after_at)] - fn main() { let $0t = (1,2); } "#, r#" -#![feature(bindings_after_at)] - fn main() { let t @ ($0_0, _1) = (1,2); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index 40eaed0080a0a..40c54e98820a8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -17,9 +17,10 @@ use syntax::{ ast::{ self, HasVisibility, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, - match_ast, ted, + match_ast, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{AssistContext, Assists}; @@ -121,15 +122,18 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) = module.get_usages_and_record_fields(ctx, module_text_range); + let make = SyntaxFactory::without_mappings(); + builder.edit_file(ctx.vfs_file_id()); use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| { builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}")); }); - let import_items = module.resolve_imports(curr_parent_module, ctx); - module.change_visibility(record_fields); + let import_items = module.resolve_imports(curr_parent_module, ctx, &make); + module.change_visibility(record_fields, &make); - let module_def = generate_module_def(&impl_parent, &module).indent(old_item_indent); + let module_def = + generate_module_def(&impl_parent, &module, &make).indent(old_item_indent); let mut usages_to_be_processed_for_cur_file = vec![]; for (file_id, usages) in usages_to_be_processed { @@ -185,6 +189,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> fn generate_module_def( parent_impl: &Option, Module { name, body_items, use_items }: &Module, + make: &SyntaxFactory, ) -> ast::Module { let items: Vec<_> = if let Some(impl_) = parent_impl.as_ref() && let Some(self_ty) = impl_.self_ty() @@ -195,11 +200,15 @@ fn generate_module_def( .filter_map(ast::AssocItem::cast) .map(|it| it.indent(IndentLevel(1))) .collect_vec(); - let assoc_item_list = make::assoc_item_list(Some(assoc_items)).clone_for_update(); - let impl_ = impl_.reset_indent(); - ted::replace(impl_.get_or_create_assoc_item_list().syntax(), assoc_item_list.syntax()); + let impl_reset = impl_.reset_indent(); + let (editor, impl_root) = SyntaxEditor::with_ast_node(&impl_reset); + let assoc_item_list = editor.make().assoc_item_list(assoc_items); + if let Some(existing_list) = impl_root.assoc_item_list() { + editor.replace(existing_list.syntax(), assoc_item_list.syntax()); + } + let impl_ = ast::Impl::cast(editor.finish().new_root().clone()).unwrap(); // Add the import for enum/struct corresponding to given impl block - let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax()); + let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax(), make); once(use_impl) .chain(use_items.iter().cloned()) .chain(once(ast::Item::Impl(impl_))) @@ -209,19 +218,18 @@ fn generate_module_def( }; let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec(); - let module_body = make::item_list(Some(items)); - - let module_name = make::name(name); - make::mod_(module_name, Some(module_body)) + let module_body = make.item_list(items); + let module_name = make.name(name); + make.mod_(module_name, Some(module_body)) } -fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item { - let super_path = make::ext::ident_path("super"); - let node_path = make::ext::ident_path(&node_syntax.to_string()); - let use_ = make::use_( +fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode, make: &SyntaxFactory) -> ast::Item { + let super_path = make.ident_path("super"); + let node_path = make.path_from_text(&node_syntax.to_string()); + let use_ = make.use_( + [], None, - None, - make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), + make.use_tree(make.path_concat(super_path, node_path), None, None, false), ); ast::Item::from(use_) @@ -385,18 +393,30 @@ impl Module { if use_.syntax().parent().is_some_and(|parent| parent == covering_node) && use_stmts_set.insert(use_.syntax().text_range().start()) { - let use_ = use_stmts_to_be_inserted - .entry(use_.syntax().text_range().start()) - .or_insert_with(|| use_.clone_subtree().clone_for_update()); - for seg in use_ - .syntax() - .descendants() - .filter_map(ast::NameRef::cast) - .filter(|seg| seg.syntax().to_string() == name_ref.to_string()) - { - let new_ref = make::path_from_text(&format!("{mod_name}::{seg}")) - .clone_for_update(); - ted::replace(seg.syntax().parent()?, new_ref.syntax()); + let key = use_.syntax().text_range().start(); + let entry = + use_stmts_to_be_inserted.entry(key).or_insert_with(|| use_.clone()); + let (editor, edit_root) = SyntaxEditor::with_ast_node(&*entry); + let replacements: Vec<_> = { + let make = editor.make(); + edit_root + .syntax() + .descendants() + .filter_map(ast::NameRef::cast) + .filter(|seg| seg.syntax().to_string() == name_ref.to_string()) + .filter_map(|seg| { + Some(( + seg.syntax().parent()?, + make.path_from_text(&format!("{mod_name}::{seg}")), + )) + }) + .collect() + }; + if !replacements.is_empty() { + for (parent, new_ref) in &replacements { + editor.replace(parent, new_ref.syntax()); + } + *entry = ast::Use::cast(editor.finish().new_root().clone()).unwrap(); } } } @@ -407,18 +427,23 @@ impl Module { } } - fn change_visibility(&mut self, record_fields: Vec) { + fn change_visibility(&mut self, record_fields: Vec, make: &SyntaxFactory) { + for item in &mut self.body_items { + let (_, root) = SyntaxEditor::with_ast_node(&item.reset_indent()); + *item = root; + } + let (mut replacements, record_field_parents, impls) = - get_replacements_for_visibility_change(&mut self.body_items, false); + get_replacements_for_visibility_change(&self.body_items); - let mut impl_items = impls + let impl_items = impls .into_iter() .flat_map(|impl_| impl_.syntax().descendants()) .filter_map(ast::Item::cast) .collect_vec(); let (mut impl_item_replacements, _, _) = - get_replacements_for_visibility_change(&mut impl_items, true); + get_replacements_for_visibility_change(&impl_items); replacements.append(&mut impl_item_replacements); @@ -432,17 +457,39 @@ impl Module { } } - for (vis, syntax) in replacements { - let item = syntax.children_with_tokens().find(|node_or_token| { - match node_or_token.kind() { - // We're skipping comments, doc comments, and attribute macros that may precede the keyword - // that the visibility should be placed before. - SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE => false, - _ => true, - } - }); + for body_item in &mut self.body_items { + let insert_targets: Vec<_> = replacements + .iter() + .filter(|(vis, syntax)| { + vis.is_none() + && (syntax == body_item.syntax() + || syntax.ancestors().any(|a| &a == body_item.syntax())) + }) + .filter_map(|(_, syntax)| { + syntax.children_with_tokens().find(|nt| { + !matches!( + nt.kind(), + SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE + ) + }) + }) + .collect(); - add_change_vis(vis, item); + if insert_targets.is_empty() { + continue; + } + + let (editor, _) = SyntaxEditor::new(body_item.syntax().clone()); + for target in insert_targets { + editor.insert_all( + Position::before(target), + vec![ + make.visibility_pub_crate().syntax().clone().into(), + make.whitespace(" ").into(), + ], + ); + } + *body_item = ast::Item::cast(editor.finish().new_root().clone()).unwrap(); } } @@ -450,6 +497,7 @@ impl Module { &mut self, module: Option, ctx: &AssistContext<'_, '_>, + make: &SyntaxFactory, ) -> Vec { let mut imports_to_remove = vec![]; let mut node_set = FxHashSet::default(); @@ -476,7 +524,8 @@ impl Module { }) .for_each(|(node, def)| { if node_set.insert(node.to_string()) - && let Some(import) = self.process_def_in_sel(def, &node, &module, ctx) + && let Some(import) = + self.process_def_in_sel(def, &node, &module, ctx, make) { check_intersection_and_push(&mut imports_to_remove, import); } @@ -492,6 +541,7 @@ impl Module { use_node: &SyntaxNode, curr_parent_module: &Option, ctx: &AssistContext<'_, '_>, + make: &SyntaxFactory, ) -> Option { //We only need to find in the current file let selection_range = ctx.selection_trimmed(); @@ -567,7 +617,7 @@ impl Module { // mod -> ust_stmt transversal // true | false -> super import insertion // true | true -> super import insertion - let super_use_node = make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node, make); self.use_items.insert(0, super_use_node); } None => {} @@ -590,14 +640,14 @@ impl Module { if !first_path_in_use_tree_str.contains("super") && !first_path_in_use_tree_str.contains("crate") { - let super_path = make::ext::ident_path("super"); + let super_path = make.ident_path("super"); use_tree_str.push(super_path); } } use_tree_paths = Some(use_tree_str); } else if def_in_mod && def_out_sel { - let super_use_node = make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node, make); self.use_items.insert(0, super_use_node); } } @@ -609,7 +659,7 @@ impl Module { && let Some(first_path_in_use_tree) = use_tree_paths.first() && first_path_in_use_tree.to_string().contains("super") { - use_tree_paths.insert(0, make::ext::ident_path("super")); + use_tree_paths.insert(0, make.ident_path("super")); } let is_item = matches!( @@ -624,12 +674,12 @@ impl Module { | Definition::TypeAlias(_) ); - if (def_out_sel || !is_item) && use_stmt_not_in_sel { - let use_ = make::use_( - None, - None, - make::use_tree(make::join_paths(use_tree_paths), None, None, false), - ); + if (def_out_sel || !is_item) + && use_stmt_not_in_sel + && let Some(joined) = + use_tree_paths.into_iter().reduce(|acc, p| make.path_concat(acc, p)) + { + let use_ = make.use_([], None, make.use_tree(joined, None, None, false)); self.use_items.insert(0, ast::Item::from(use_)); } } @@ -741,8 +791,7 @@ fn check_def_in_mod_and_out_sel( } fn get_replacements_for_visibility_change( - items: &mut [ast::Item], - is_clone_for_updated: bool, + items: &[ast::Item], ) -> ( Vec<(Option, SyntaxNode)>, Vec<(Option, SyntaxNode)>, @@ -753,9 +802,6 @@ fn get_replacements_for_visibility_change( let mut impls = Vec::new(); for item in items { - if !is_clone_for_updated { - *item = item.clone_for_update(); - } //Use stmts are ignored macro_rules! push_to_replacement { ($it:ident) => { @@ -812,15 +858,6 @@ fn get_use_tree_paths_from_path( Some(use_tree_str) } -fn add_change_vis(vis: Option, node_or_token_opt: Option) { - if vis.is_none() - && let Some(node_or_token) = node_or_token_opt - { - let pub_crate_vis = make::visibility_pub_crate().clone_for_update(); - ted::insert(ted::Position::before(node_or_token), pub_crate_vis.syntax()); - } -} - fn indent_range_before_given_node(node: &SyntaxNode) -> Option { node.siblings_with_tokens(syntax::Direction::Prev) .find(|x| x.kind() == WHITESPACE) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index 531adf62ba3f5..7a82e8b2f3ac8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -1,19 +1,20 @@ use either::{Either, for_both}; -use hir::{PathResolution, Semantics}; +use hir::{EditionedFileId, PathResolution, Semantics}; use ide_db::{ - EditionedFileId, RootDatabase, + RootDatabase, defs::Definition, - search::{FileReference, FileReferenceNode, UsageSearchResult}, + search::{FileReference, UsageSearchResult}, }; use syntax::{ - Direction, TextRange, + Direction, T, TextRange, ast::{self, AstNode, AstToken, HasName}, - syntax_editor::{Element, SyntaxEditor}, + syntax_editor::{Element, Position, SyntaxEditor}, }; use crate::{ AssistId, assist_context::{AssistContext, Assists}, + utils::{cover_edit_range, original_range_in}, }; // Assist: inline_local_variable @@ -34,11 +35,12 @@ use crate::{ // ``` pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let file_id = ctx.file_id(); + let source = ctx.source_file().syntax(); let range = ctx.selection_trimmed(); let InlineData { let_stmt, delete_let, references, target } = - if let Some(path_expr) = ctx.find_node_at_offset::() { + if let Some(path_expr) = ctx.find_node_at_offset_with_descend::() { inline_usage(&ctx.sema, path_expr, range, file_id) - } else if let Some(let_stmt) = ctx.find_node_at_offset() { + } else if let Some(let_stmt) = ctx.find_node_at_offset_with_descend() { inline_let(&ctx.sema, let_stmt, range, file_id) } else { None @@ -47,71 +49,65 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_, ' either::Either::Left(it) => it.initializer()?, either::Either::Right(it) => it.expr()?, }; - - let wrap_in_parens = references - .into_iter() - .filter_map(|FileReference { range, name, .. }| match name { - FileReferenceNode::NameRef(name) => Some((range, name)), - _ => None, - }) - .map(|(range, name_ref)| { - if range != name_ref.syntax().text_range() { - // Do not rename inside macros - // FIXME: This feels like a bad heuristic for macros - return None; + let needs_parens = |name_ref: &ast::NameRef| { + let usage_node = + name_ref.syntax().ancestors().find(|it| ast::PathExpr::can_cast(it.kind())); + let usage_parent = usage_node.as_ref().and_then(|it| it.parent()); + match (usage_node, usage_parent) { + (Some(usage), Some(parent)) => { + initializer_expr.needs_parens_in_place_of(&parent, &usage) } - let usage_node = - name_ref.syntax().ancestors().find(|it| ast::PathExpr::can_cast(it.kind())); - let usage_parent_option = usage_node.as_ref().and_then(|it| it.parent()); - let usage_parent = match usage_parent_option { - Some(u) => u, - None => return Some((name_ref, false)), - }; - let should_wrap = initializer_expr - .needs_parens_in_place_of(&usage_parent, usage_node.as_ref().unwrap()); - Some((name_ref, should_wrap)) - }) - .collect::>>()?; - - let target = match target { - ast::NameOrNameRef::Name(it) => it.syntax().clone(), - ast::NameOrNameRef::NameRef(it) => it.syntax().clone(), + _ => false, + } }; acc.add( AssistId::refactor_inline("inline_local_variable"), "Inline variable", - target.text_range(), - move |builder| { - let editor = builder.make_editor(&target); + target, + |builder| { + let editor = builder.make_editor(source); let make = editor.make(); - if delete_let { - editor.delete(let_stmt.syntax()); - + if delete_let + && let Some(original) = original_range_in(file_id, &ctx.sema, let_stmt.syntax()) + { + let place = cover_edit_range(source, original); + editor.delete_all(place.clone()); + + // Processing let-expr in let-chain + // FIXME: process let-expr in macro, but this case is very rare if let Some(bin_expr) = let_stmt.syntax().parent().and_then(ast::BinExpr::cast) && let Some(op_token) = bin_expr.op_token() { editor.delete(&op_token); remove_whitespace(op_token, Direction::Prev, &editor); - remove_whitespace(let_stmt.syntax(), Direction::Prev, &editor); + remove_whitespace(place.start(), Direction::Prev, &editor); } else { - remove_whitespace(let_stmt.syntax(), Direction::Next, &editor); + remove_whitespace(place.end(), Direction::Next, &editor); } } - for (name, should_wrap) in wrap_in_parens { - let replacement = if should_wrap { + for FileReference { range, name, .. } in references { + let Some(name) = name.as_name_ref().cloned() else { continue }; + let replacement = if needs_parens(&name) { make.expr_paren(initializer_expr.clone()).into() } else { initializer_expr.clone() }; - if let Some(record_field) = ast::RecordExprField::for_field_name(&name) { + let place = cover_edit_range(source, range); + if ast::RecordExprField::for_field_name(&name).is_some() { cov_mark::hit!(inline_field_shorthand); - let replacement = make.record_expr_field(name, Some(replacement)); - editor.replace(record_field.syntax(), replacement.syntax()); + editor.insert_all( + Position::after(place.end()), + vec![ + make.token(T![:]).into(), + make.whitespace(" ").into(), + replacement.syntax().clone().into(), + ], + ); } else { - editor.replace(name.syntax(), replacement.syntax()); + editor.replace_all(place, vec![replacement.syntax().clone().into()]); } } builder.add_file_edits(ctx.vfs_file_id(), editor); @@ -122,7 +118,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_, ' struct InlineData { let_stmt: Either, delete_let: bool, - target: ast::NameOrNameRef, + target: TextRange, references: Vec, } @@ -140,20 +136,18 @@ fn inline_let( cov_mark::hit!(test_not_inline_mut_variable); return None; } - if !bind_pat.syntax().text_range().contains_range(range) { + let target = original_range_in(file_id, sema, bind_pat.name()?.syntax())?; + if !target.contains_range(range) { cov_mark::hit!(not_applicable_outside_of_bind_pat); return None; } let local = sema.to_def(&bind_pat)?; - let UsageSearchResult { mut references } = Definition::Local(local).usages(sema).all(); - match references.remove(&file_id) { - Some(references) => Some(InlineData { - let_stmt, - delete_let: true, - target: ast::NameOrNameRef::Name(bind_pat.name()?), - references, - }), + let UsageSearchResult { references } = Definition::Local(local).usages(sema).all(); + let references = references.into_iter().flat_map(|it| it.1).collect::>(); + + match references.first() { + Some(_) => Some(InlineData { let_stmt, delete_let: true, target, references }), None => { cov_mark::hit!(test_not_applicable_if_variable_unused); None @@ -169,7 +163,8 @@ fn inline_usage( ) -> Option { let path = path_expr.path()?; let name = path.as_single_name_ref()?; - if !name.syntax().text_range().contains_range(range) { + let target = original_range_in(file_id, sema, name.syntax())?; + if !target.contains_range(range) { cov_mark::hit!(test_not_inline_selection_too_broad); return None; } @@ -193,12 +188,12 @@ fn inline_usage( let let_stmt = AstNode::cast(bind_pat.syntax().parent()?)?; - let UsageSearchResult { mut references } = Definition::Local(local).usages(sema).all(); - let mut references = references.remove(&file_id)?; + let UsageSearchResult { references } = Definition::Local(local).usages(sema).all(); + let mut references = references.into_iter().flat_map(|it| it.1).collect::>(); let delete_let = references.len() == 1; references.retain(|fref| fref.name.as_name_ref() == Some(&name)); - Some(InlineData { let_stmt, delete_let, target: ast::NameOrNameRef::NameRef(name), references }) + Some(InlineData { let_stmt, delete_let, target, references }) } fn remove_whitespace(elem: impl Element, dir: Direction, editor: &SyntaxEditor) { @@ -969,8 +964,8 @@ fn main() { } #[test] - fn not_applicable_on_local_usage_in_macro() { - check_assist_not_applicable( + fn local_usage_in_macro() { + check_assist( inline_local_variable, r#" macro_rules! m { @@ -978,11 +973,19 @@ macro_rules! m { } fn f() { let xyz = 0; - m!(xyz$0); // replacing it would break the macro + m!(xyz$0); // some macros may break, but it's best to support them +} +"#, + r#" +macro_rules! m { + ($i:ident) => { $i } +} +fn f() { + m!(0); // some macros may break, but it's best to support them } "#, ); - check_assist_not_applicable( + check_assist( inline_local_variable, r#" macro_rules! m { @@ -990,7 +993,84 @@ macro_rules! m { } fn f() { let xyz$0 = 0; - m!(xyz); // replacing it would break the macro + m!(xyz); // some macros may break, but it's best to support them +} +"#, + r#" +macro_rules! m { + ($i:ident) => { $i } +} +fn f() { + m!(0); // some macros may break, but it's best to support them +} +"#, + ); + } + + #[test] + fn local_def_in_macro() { + check_assist( + inline_local_variable, + r#" +macro_rules! i { + ($($t:tt)*) => { $($t)* } +} +fn f() { + i!(let xyz = 0;); + _ = xyz$0; +} +"#, + r#" +macro_rules! i { + ($($t:tt)*) => { $($t)* } +} +fn f() { + i!(); + _ = 0; +} +"#, + ); + check_assist( + inline_local_variable, + r#" +macro_rules! i { + ($($t:tt)*) => { $($t)* } +} +fn f() { + i!(let xyz = 0;); + _ = xyz$0; + _ = xyz; +} +"#, + r#" +macro_rules! i { + ($($t:tt)*) => { $($t)* } +} +fn f() { + i!(let xyz = 0;); + _ = 0; + _ = xyz; +} +"#, + ); + check_assist( + inline_local_variable, + r#" +macro_rules! i { + ($($t:tt)*) => { $($t)* } +} +fn f() { + i!(let $0xyz = 0;); + _ = xyz; +} +"#, + r#" +macro_rules! i { + ($($t:tt)*) => { $($t)* } +} +fn f() { + i!(); + _ = 0; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs index 002791b88df64..f4ac75d2290dd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs @@ -1,8 +1,11 @@ use hir::db::ExpandDatabase; use ide_db::syntax_helpers::prettify_macro_expansion; -use syntax::ast::{self, AstNode, edit::AstNodeEdit}; +use syntax::ast::{self, AstNode, edit::IndentLevel}; -use crate::{AssistContext, AssistId, Assists}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{cover_edit_range, original_range_in}, +}; // Assist: inline_macro // @@ -36,24 +39,40 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { - let unexpanded = ctx.find_node_at_offset::()?; - let macro_call = ctx.sema.to_def(&unexpanded)?; + let source = ctx.source_file().syntax(); + let sel = ctx.selection_trimmed(); + let (macro_call, text_range) = ctx + .sema + .find_nodes_at_offset_with_descend::(source, ctx.offset()) + .find_map(|macro_call_node| { + let macro_call = ctx.sema.to_def(¯o_call_node)?; + let original_range = + original_range_in(ctx.file_id(), &ctx.sema, macro_call_node.syntax())?; + original_range.contains_range(sel).then_some((macro_call, original_range)) + })?; let target_crate_id = ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into(); - let text_range = unexpanded.syntax().text_range(); acc.add( AssistId::refactor_inline("inline_macro"), "Inline macro".to_owned(), text_range, |builder| { - let editor = builder.make_editor(unexpanded.syntax()); let expanded = ctx.sema.parse_or_expand(macro_call.into()); let span_map = ctx.sema.db.expansion_span_map(macro_call); // Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation, // which can be very costly for big macros when it is done *even without the assist being invoked*. let expanded = prettify_macro_expansion(ctx.db(), expanded, span_map, target_crate_id); - let expanded = ast::edit::indent(&expanded, unexpanded.indent_level()); - editor.replace(unexpanded.syntax(), expanded); + + // macro_call is from an expansion, use source position for indent + let indent = source + .token_at_offset(text_range.start()) + .right_biased() + .map_or_else(IndentLevel::zero, |t| IndentLevel::from_token(&t)); + let expanded = ast::edit::indent(&expanded, indent); + + let editor = builder.make_editor(source); + let place = cover_edit_range(source, text_range); + editor.replace_all(place, vec![expanded.into()]); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -103,6 +122,7 @@ macro_rules! num { "# }; } + #[test] fn inline_macro_target() { check_assist_target( @@ -376,6 +396,37 @@ fn bar() { fn bar() { a::Foo; } +"#, + ); + } + + #[test] + fn inline_macro_in_macro() { + check_assist( + inline_macro, + r#" +macro_rules! foo { () => { 2 }; } +macro_rules! m { ($($tt:tt)*) => { $($tt)* }; } +fn f() { m! { $0foo!(); } } +"#, + r#" +macro_rules! foo { () => { 2 }; } +macro_rules! m { ($($tt:tt)*) => { $($tt)* }; } +fn f() { m! { 2; } } +"#, + ); + check_assist( + inline_macro, + r#" +//- proc_macros: identity +macro_rules! foo { () => { 2 }; } +#[proc_macros::identity] +fn f() { $0foo!(); } +"#, + r#" +macro_rules! foo { () => { 2 }; } +#[proc_macros::identity] +fn f() { 2; } "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index e4a1314f5bb29..bb76e2743c377 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -135,7 +135,20 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) PathResolution::SelfType(imp) => { concrete_type = imp.source(ctx.db())?.value.self_ty()?; } - // FIXME: should also work in ADT definitions + PathResolution::Def(hir::ModuleDef::Adt(adt)) => { + let make = SyntaxFactory::without_mappings(); + let src = adt.source(ctx.db())?.value; + let name = src.name()?; + let generic_params = src.generic_param_list(); + let name_ref = make.name_ref(&name.text()); + let segment = match generic_params { + Some(params) => { + make.path_segment_generics(name_ref, params.to_generic_args(&make)) + } + None => make.path_segment(name_ref), + }; + concrete_type = make.ty_path_from_segments([segment], false); + } _ => return None, } @@ -996,6 +1009,68 @@ trait Tr { ); } + #[test] + fn inline_self_type_in_adt_definition() { + check_assist( + inline_type_alias, + r#" +enum Foo { + A(i32), + B(Box), +} +"#, + r#" +enum Foo { + A(i32), + B(Box), +} +"#, + ); + check_assist( + inline_type_alias, + r#" +struct Foo { + a: Box, +} +"#, + r#" +struct Foo { + a: Box, +} +"#, + ); + check_assist( + inline_type_alias, + r#" +struct Foo { + a: T, + b: Box, +} +"#, + r#" +struct Foo { + a: T, + b: Box>, +} +"#, + ); + check_assist( + inline_type_alias, + r#" +union Foo { + a: u32, + b: std::mem::ManuallyDrop>, +} +"#, + r#" +union Foo { + a: u32, + b: std::mem::ManuallyDrop>, +} +"#, + ); + } + #[test] fn inline_types_with_lifetime() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs index dc40a6a640e00..4234a6090dc3f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs @@ -1,10 +1,12 @@ -use either::Either; use ide_db::imports::{ insert_use::{ImportGranularity, InsertUseConfig}, merge_imports::{MergeBehavior, try_merge_imports, try_merge_trees}, }; use syntax::{ - AstNode, SyntaxElement, SyntaxNode, algo::neighbor, ast, match_ast, syntax_editor::Removable, + AstNode, SyntaxElement, + algo::neighbor, + ast, match_ast, + syntax_editor::{Removable, SyntaxEditor}, }; use crate::{ @@ -13,8 +15,6 @@ use crate::{ utils::next_prev, }; -use Edit::*; - // Assist: merge_imports // // Merges neighbor imports with a common prefix. @@ -28,16 +28,17 @@ use Edit::*; // use std::{fmt::Formatter, io}; // ``` pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { - let (target, edits) = if ctx.has_empty_selection() { + let (target, editor) = if ctx.has_empty_selection() { // Merge a neighbor cov_mark::hit!(merge_with_use_item_neighbors); let tree = ctx.find_node_at_offset::()?.top_use_tree(); let target = tree.syntax().text_range(); let use_item = tree.syntax().parent().and_then(ast::Use::cast)?; - let mut neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir)).into_iter(); - let edits = use_item.try_merge_from(&mut neighbor, &ctx.config.insert_use); - (target, edits?) + let neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir))?; + let (editor, _) = SyntaxEditor::new(use_item.syntax().parent()?.ancestors().last()?); + merge_uses(use_item, vec![neighbor], &ctx.config.insert_use, &editor)?; + (target, editor) } else { // Merge selected let selection_range = ctx.selection_trimmed(); @@ -50,104 +51,80 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> O }); let first_selected = selected_nodes.next()?; - let edits = match_ast! { + let (editor, _) = SyntaxEditor::new(parent_node.ancestors().last().unwrap()); + match_ast! { match first_selected { ast::Use(use_item) => { cov_mark::hit!(merge_with_selected_use_item_neighbors); - use_item.try_merge_from(&mut selected_nodes.filter_map(ast::Use::cast), &ctx.config.insert_use) + merge_uses( + use_item, + selected_nodes.filter_map(ast::Use::cast).collect(), + &ctx.config.insert_use, + &editor, + )?; }, ast::UseTree(use_tree) => { cov_mark::hit!(merge_with_selected_use_tree_neighbors); - use_tree.try_merge_from(&mut selected_nodes.filter_map(ast::UseTree::cast), &ctx.config.insert_use) + merge_use_trees( + use_tree, + selected_nodes.filter_map(ast::UseTree::cast).collect(), + &editor, + )?; }, _ => return None, } - }; - (selection_range, edits?) - }; - - let parent_node = match ctx.covering_element() { - SyntaxElement::Node(n) => n, - SyntaxElement::Token(t) => t.parent()?, + } + (selection_range, editor) }; acc.add(AssistId::refactor_rewrite("merge_imports"), "Merge imports", target, |builder| { - let editor = builder.make_editor(&parent_node); - - for edit in edits { - match edit { - Remove(it) => { - let node = it.as_ref(); - if let Some(left) = node.left() { - left.remove(&editor); - } else if let Some(right) = node.right() { - right.remove(&editor); - } - } - Replace(old, new) => { - editor.replace(old, &new); - } - } - } builder.add_file_edits(ctx.vfs_file_id(), editor); }) } -trait Merge: AstNode + Clone { - fn try_merge_from( - self, - items: &mut dyn Iterator, - cfg: &InsertUseConfig, - ) -> Option> { - let mut edits = Vec::new(); - let mut merged = self.clone(); - for item in items { - merged = merged.try_merge(&item, cfg)?; - edits.push(Edit::Remove(item.into_either())); - } - if !edits.is_empty() { - edits.push(Edit::replace(self, merged)); - Some(edits) - } else { - None - } +fn merge_uses( + first: ast::Use, + rest: Vec, + cfg: &InsertUseConfig, + editor: &SyntaxEditor, +) -> Option<()> { + if rest.is_empty() { + return None; } - fn try_merge(&self, other: &Self, cfg: &InsertUseConfig) -> Option; - fn into_either(self) -> Either; -} -impl Merge for ast::Use { - fn try_merge(&self, other: &Self, cfg: &InsertUseConfig) -> Option { - let mb = match cfg.granularity { - ImportGranularity::One => MergeBehavior::One, - _ => MergeBehavior::Crate, - }; - try_merge_imports(self, other, mb) + let mb = match cfg.granularity { + ImportGranularity::One => MergeBehavior::One, + _ => MergeBehavior::Crate, + }; + let mut merged = first.clone(); + for item in &rest { + merged = try_merge_imports(editor.make(), &merged, item, mb)?; } - fn into_either(self) -> Either { - Either::Left(self) + for item in rest { + item.remove(editor); } + editor.replace(first.syntax(), merged.syntax()); + Some(()) } -impl Merge for ast::UseTree { - fn try_merge(&self, other: &Self, _: &InsertUseConfig) -> Option { - try_merge_trees(self, other, MergeBehavior::Crate) +fn merge_use_trees( + first: ast::UseTree, + rest: Vec, + editor: &SyntaxEditor, +) -> Option<()> { + if rest.is_empty() { + return None; } - fn into_either(self) -> Either { - Either::Right(self) - } -} -#[derive(Debug)] -enum Edit { - Remove(Either), - Replace(SyntaxNode, SyntaxNode), -} - -impl Edit { - fn replace(old: impl AstNode, new: impl AstNode) -> Self { - Edit::Replace(old.syntax().clone(), new.syntax().clone()) + let mut merged = first.clone(); + for item in &rest { + merged = try_merge_trees(editor.make(), &merged, item, MergeBehavior::Crate)?; + } + for item in rest { + item.remove(editor); } + editor.replace(first.syntax(), merged.syntax()); + Some(()) } #[cfg(test)] @@ -558,6 +535,62 @@ use foo::{bar, baz}; ); } + #[test] + fn mod_indent_whitespace() { + check_assist( + merge_imports, + r" +mod tests { + use foo$0::bar; + use foo::baz; + fn feature() {} +} +", + r" +mod tests { + use foo::{bar, baz}; + fn feature() {} +} +", + ); + check_assist( + merge_imports, + r" +mod tests { + use foo$0::bar; + use foo::baz; + + fn feature() {} +} +", + r" +mod tests { + use foo::{bar, baz}; + + fn feature() {} +} +", + ); + check_assist( + merge_imports, + r" +mod tests { + use foo::bar; + use foo$0::baz; + + fn feature() {} +} +", + r" +mod tests { + use foo::{bar, baz}; + + fn feature() {} +} +", + ); + } + #[test] fn works_with_trailing_comma() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs index f97a3e583f030..906f38cba407f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs @@ -1,5 +1,5 @@ use ide_db::imports::merge_imports::try_normalize_import; -use syntax::{AstNode, ast}; +use syntax::{AstNode, ast, syntax_editor::SyntaxEditor}; use crate::{ AssistId, @@ -25,11 +25,13 @@ pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - }; let target = use_item.syntax().text_range(); + let (editor, _) = SyntaxEditor::new(use_item.syntax().ancestors().last().unwrap()); let normalized_use_item = - try_normalize_import(&use_item, ctx.config.insert_use.granularity.into())?; + try_normalize_import(editor.make(), &use_item, ctx.config.insert_use.granularity.into())?; + editor.replace(use_item.syntax(), normalized_use_item.syntax()); acc.add(AssistId::refactor_rewrite("normalize_import"), "Normalize import", target, |builder| { - builder.replace_ast(use_item, normalized_use_item); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index ff2d0544b2a18..5225202177111 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -17,7 +17,7 @@ use crate::{ AssistContext, AssistId, Assists, utils::{ does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block, - wrap_paren, + wrap_paren_in_guard_chain, }, }; @@ -303,7 +303,7 @@ pub(crate) fn replace_match_with_if_let( _ => make.expr_let(if_let_pat, scrutinee).into(), }; let condition = if let Some(guard) = guard { - let guard = wrap_paren(guard, make, ast::prec::ExprPrecedence::LAnd); + let guard = wrap_paren_in_guard_chain(guard, make); make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into() } else { condition @@ -712,13 +712,11 @@ impl VariantData { check_assist( replace_if_let_with_match, r#" -#![feature(if_let_guard)] fn main() { if $0let true = true && let Some(1) = None {} else { other() } } "#, r#" -#![feature(if_let_guard)] fn main() { match true { true if let Some(1) = None => {} @@ -731,7 +729,6 @@ fn main() { check_assist( replace_if_let_with_match, r#" -#![feature(if_let_guard)] fn main() { if true { $0if let ParenExpr(expr) = cond @@ -758,7 +755,6 @@ fn main() { } "#, r#" -#![feature(if_let_guard)] fn main() { if true { match cond { @@ -816,13 +812,11 @@ fn main() { check_assist( replace_if_let_with_match, r#" -#![feature(if_let_guard)] fn main() { if $0let true = true && let Some(1) = None {} } "#, r#" -#![feature(if_let_guard)] fn main() { match true { true if let Some(1) = None => {} @@ -2460,7 +2454,7 @@ fn main() { } #[test] - fn test_replace_match_with_if_let_chain() { + fn test_replace_match_with_if_let_with_simple_guard() { check_assist( replace_match_with_if_let, r#" @@ -2504,6 +2498,73 @@ fn main() { ); } + #[test] + fn test_replace_match_with_if_let_with_if_let_guard() { + check_assist( + replace_match_with_if_let, + r#" +fn main() { + match$0 Some(0) { + Some(n) if let Some(m) = n.checked_add(1) => (), + _ => code(), + } +} +"#, + r#" +fn main() { + if let Some(n) = Some(0) && let Some(m) = n.checked_add(1) { + () + } else { + code() + } +} +"#, + ); + + check_assist( + replace_match_with_if_let, + r#" +fn main() { + match$0 Some(0) { + Some(n) if let Some(m) = n.checked_add(1) && m > 5 => (), + _ => code(), + } +} + "#, + r#" +fn main() { + if let Some(n) = Some(0) && let Some(m) = n.checked_add(1) && m > 5 { + () + } else { + code() + } +} + "#, + ); + + // what if the `let` expr is not the first one in the guard? + check_assist( + replace_match_with_if_let, + r#" +fn main() { + match$0 Some(0) { + Some(n) if n > 5 && let Some(m) = n.checked_add(1) => (), + _ => code(), + } +} + "#, + r#" +fn main() { + if let Some(n) = Some(0) && n > 5 && let Some(m) = n.checked_add(1) { + () + } else { + code() + } +} + "#, + ); + } + #[test] fn test_replace_match_with_if_let_not_applicable_pat2_is_ident_pat() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 1b6c9a579aba0..2c4fb5f405a77 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -110,6 +110,26 @@ fn needs_parens_in_call(make: &SyntaxFactory, param: &ast::Expr) -> bool { param.needs_parens_in_place_of(call.syntax(), callable.syntax()) } +pub(crate) fn wrap_paren_in_guard_chain(guard: ast::Expr, make: &SyntaxFactory) -> ast::Expr { + if needs_parens_in_guard_chain(make, &guard) { make.expr_paren(guard).into() } else { guard } +} + +fn needs_parens_in_guard_chain(make: &SyntaxFactory, guard: &ast::Expr) -> bool { + let ast::Expr::BinExpr(if_let_and_guard) = make.expr_bin_op( + make.expr_unit(), + ast::BinaryOp::LogicOp(ast::LogicOp::And), + make.expr_unit(), + ) else { + stdx::never!("`SyntaxFactory::expr_bin_op` returns a `BinExpr`"); + return false; + }; + let Some(fake_guard) = if_let_and_guard.rhs() else { + stdx::never!("invalid make call"); + return false; + }; + guard.needs_parens_in_place_of(if_let_and_guard.syntax(), fake_guard.syntax()) +} + /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as /// `#[test_case(...)]`, `#[tokio::test]` and similar. /// Also a regular `#[test]` annotation is supported. @@ -1177,6 +1197,15 @@ pub fn is_body_const(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> bo is_const } +pub(crate) fn original_range_in( + file_id: hir::EditionedFileId, + sema: &Semantics<'_, RootDatabase>, + value: &SyntaxNode, +) -> Option { + let original = sema.original_range_opt(value)?; + (original.file_id == file_id).then_some(original.range) +} + // FIXME: #20460 When hir-ty can analyze the `never` statement at the end of block, remove it pub(crate) fn is_never_block( sema: &Semantics<'_, RootDatabase>, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index ac52c816e52a9..0739084381b3b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -45,6 +45,7 @@ pub enum AutoImportExclusionType { Always, Methods, SubItems, + Variants, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index f7bb14391b7de..507ecaff0dca0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -868,10 +868,22 @@ impl<'a, 'db> CompletionContext<'a, 'db> { _ => None, }) .collect::>(); + let exclude_variants = exclude_flyimport + .iter() + .flat_map(|it| match it { + (ModuleDef::Adt(hir::Adt::Enum(enum_)), AutoImportExclusionType::Variants) => { + enum_.variants(db) + } + _ => vec![], + }) + .collect::>(); exclude_flyimport .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always))); exclude_flyimport .extend(exclude_subitems.into_iter().map(|it| (it, AutoImportExclusionType::Always))); + exclude_flyimport.extend( + exclude_variants.into_iter().map(|it| (it.into(), AutoImportExclusionType::Always)), + ); // FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze` let complete_semicolon = if !config.add_semicolon_to_unit { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index e48847c983b4e..7cb1eaa061f3c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -2372,7 +2372,7 @@ impl S { } fn foo(s: S) { s.$0 } "#, - CompletionItemKind::SymbolKind(SymbolKind::Method), + SymbolKind::Method, expect![[r#" [ CompletionItem { 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 e49afa66ad738..adf4dda18441f 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 @@ -3041,6 +3041,76 @@ fn foo() { ); } +#[test] +fn flyimport_excluded_enum_variants_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_flyimport: vec![( + "ra_test_fixture::Foo".to_owned(), + AutoImportExclusionType::Variants, + )], + ..TEST_CONFIG + }, + r#" +enum Foo { + Variant1, + Variant2, +} +fn foo() { + V$0 +} + "#, + expect![[r#" + ct CONST Unit + en Enum Enum + en Foo Foo + fn foo() fn() + fn function() fn() + ma makro!(…) macro_rules! makro + md module:: + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + #[test] fn excluded_trait_method_is_excluded_from_path_completion() { check_with_config( @@ -4109,3 +4179,31 @@ fn main() { "#]], ); } + +#[test] +fn no_await_on_error_type() { + check( + r#" +//- minicore: future +fn foo(t: T) { + let _ = t.$0; +} + "#, + expect![[r#" + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn if if expr {} + sn match match expr {} + sn not !expr + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + sn while while expr {} + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index b77a18f56ea21..7a3c466daa50c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -1,16 +1,21 @@ //! Applies changes to the IDE state transactionally. +use std::time::{Duration, Instant}; + use profile::Bytes; use salsa::Database as _; use crate::{ChangeWithProcMacros, RootDatabase}; impl RootDatabase { - pub fn apply_change(&mut self, change: ChangeWithProcMacros) { + pub fn apply_change(&mut self, change: ChangeWithProcMacros) -> Duration { let _p = tracing::info_span!("RootDatabase::apply_change").entered(); + let now = Instant::now(); self.trigger_cancellation(); + let elapsed = now.elapsed(); tracing::trace!("apply_change {:?}", change); change.apply(self); + elapsed } // Feature: Memory Usage diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index c3949f871314f..27e3ed6bdb52e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -17,7 +17,7 @@ use crate::{ RootDatabase, imports::merge_imports::{ MergeBehavior, NormalizationStyle, common_prefix, eq_attrs, eq_visibility, - try_merge_imports, use_tree_cmp, + try_merge_imports, use_tree_cmp, wrap_in_tree_list, }, }; @@ -251,7 +251,7 @@ fn insert_use_with_alias_option_with_editor( let mut use_tree = make.use_tree(path, None, alias, false); if mb == Some(MergeBehavior::One) && use_tree.path().is_some() - && let Some(wrapped) = use_tree.wrap_in_tree_list_with_editor() + && let Some(wrapped) = wrap_in_tree_list(&use_tree, make) { use_tree = wrapped; } @@ -263,7 +263,9 @@ fn insert_use_with_alias_option_with_editor( for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast).filter(filter) { - if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { + if let Some(merged) = + try_merge_imports(syntax_editor.make(), &existing_use, &use_item, mb) + { syntax_editor.replace(existing_use.syntax(), merged.syntax()); return; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index 4fa05c4603461..34ff7c8d5b426 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs @@ -1,4 +1,5 @@ use stdx::trim_indent; +use syntax::ast::syntax_factory::SyntaxFactory; use test_fixture::WithFixture; use test_utils::{CURSOR_MARKER, assert_eq_text}; @@ -1430,7 +1431,8 @@ fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior .find_map(ast::Use::cast) .unwrap(); - let result = try_merge_imports(&use0, &use1, mb); + let make = SyntaxFactory::without_mappings(); + let result = try_merge_imports(&make, &use0, &use1, mb); assert_eq!(result.map(|u| u.to_string()), None); } @@ -1495,7 +1497,8 @@ fn check_merge(ra_fixture0: &str, ra_fixture1: &str, last: &str, mb: MergeBehavi .find_map(ast::Use::cast) .unwrap(); - let result = try_merge_imports(&use0, &use1, mb); + let make = SyntaxFactory::without_mappings(); + let result = try_merge_imports(&make, &use0, &use1, mb); assert_eq!(result.map(|u| u.to_string().trim().to_owned()), Some(last.trim().to_owned())); } @@ -1525,7 +1528,8 @@ fn merge_gated_imports_with_different_values() { .find_map(ast::Use::cast) .unwrap(); - let result = try_merge_imports(&use0, &use1, MergeBehavior::Crate); + let make = SyntaxFactory::without_mappings(); + let result = try_merge_imports(&make, &use0, &use1, MergeBehavior::Crate); assert_eq!(result, None); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index bbd351a4cc14c..9b68c27ae3443 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -4,12 +4,12 @@ use std::cmp::Ordering; use itertools::{EitherOrBoth, Itertools}; use parser::T; use syntax::{ - Direction, SyntaxElement, ToSmolStr, algo, + ToSmolStr, ast::{ - self, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind, edit_in_place::Removable, - make, + self, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind, + syntax_factory::SyntaxFactory, }, - ted::{self, Position}, + syntax_editor::{Position, SyntaxEditor}, }; use crate::syntax_helpers::node_ext::vis_eq; @@ -39,8 +39,8 @@ impl MergeBehavior { } /// Merge `rhs` into `lhs` keeping both intact. -/// Returned AST is mutable. pub fn try_merge_imports( + make: &SyntaxFactory, lhs: &ast::Use, rhs: &ast::Use, merge_behavior: MergeBehavior, @@ -53,39 +53,39 @@ pub fn try_merge_imports( return None; } - let lhs = lhs.clone_subtree().clone_for_update(); - let rhs = rhs.clone_subtree().clone_for_update(); let lhs_tree = lhs.use_tree()?; let rhs_tree = rhs.use_tree()?; - try_merge_trees_mut(&lhs_tree, &rhs_tree, merge_behavior)?; + let merged_tree = try_merge_trees_with_factory(lhs_tree, rhs_tree, merge_behavior, make)?; // Ignore `None` result because normalization should not affect the merge result. - try_normalize_use_tree_mut(&lhs_tree, merge_behavior.into()); + let use_tree = try_normalize_use_tree(merged_tree.clone(), merge_behavior.into(), make) + .unwrap_or(merged_tree); - Some(lhs) + make_use_with_tree(lhs, use_tree) } /// Merge `rhs` into `lhs` keeping both intact. -/// Returned AST is mutable. pub fn try_merge_trees( + make: &SyntaxFactory, lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior, ) -> Option { - let lhs = lhs.clone_subtree().clone_for_update(); - let rhs = rhs.clone_subtree().clone_for_update(); - try_merge_trees_mut(&lhs, &rhs, merge)?; + let merged = try_merge_trees_with_factory(lhs.clone(), rhs.clone(), merge, make)?; // Ignore `None` result because normalization should not affect the merge result. - try_normalize_use_tree_mut(&lhs, merge.into()); - - Some(lhs) + Some(try_normalize_use_tree(merged.clone(), merge.into(), make).unwrap_or(merged)) } -fn try_merge_trees_mut(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) -> Option<()> { +fn try_merge_trees_with_factory( + mut lhs: ast::UseTree, + mut rhs: ast::UseTree, + merge: MergeBehavior, + make: &SyntaxFactory, +) -> Option { if merge == MergeBehavior::One { - lhs.wrap_in_tree_list(); - rhs.wrap_in_tree_list(); + lhs = wrap_in_tree_list(&lhs, make).unwrap_or(lhs); + rhs = wrap_in_tree_list(&rhs, make).unwrap_or(rhs); } else { let lhs_path = lhs.path()?; let rhs_path = rhs.path()?; @@ -100,46 +100,53 @@ fn try_merge_trees_mut(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehav // and we can safely return here let lhs_name = lhs.rename().and_then(|lhs_name| lhs_name.name()); let rhs_name = rhs.rename().and_then(|rhs_name| rhs_name.name()); - if lhs_name != rhs_name { + if lhs_name.as_ref().map(|name| name.text()) + != rhs_name.as_ref().map(|name| name.text()) + { return None; } - ted::replace(lhs.syntax(), rhs.syntax()); - // we can safely return here, in this case `recursive_merge` doesn't do anything - return Some(()); + return Some(rhs); } else { - lhs.split_prefix(&lhs_prefix); - rhs.split_prefix(&rhs_prefix); + lhs = split_prefix(&lhs, &lhs_prefix, make)?; + rhs = split_prefix(&rhs, &rhs_prefix, make)?; } } - recursive_merge(lhs, rhs, merge) + recursive_merge(lhs, rhs, merge, make) } /// Recursively merges rhs to lhs #[must_use] -fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) -> Option<()> { +fn recursive_merge( + lhs: ast::UseTree, + rhs: ast::UseTree, + merge: MergeBehavior, + make: &SyntaxFactory, +) -> Option { let mut use_trees: Vec = lhs - .use_tree_list() - .into_iter() - .flat_map(|list| list.use_trees()) - // We use Option here to early return from this function(this is not the - // same as a `filter` op). + .use_tree_list()? + .use_trees() + // We use Option here to early return from this function. This is not the + // same as a `filter` op. .map(|tree| merge.is_tree_allowed(&tree).then_some(tree)) .collect::>()?; + // Sorts the use trees similar to rustfmt's algorithm for ordering imports // (see `use_tree_cmp` doc). use_trees.sort_unstable_by(use_tree_cmp); - for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { + + for rhs_t in rhs.use_tree_list()?.use_trees() { if !merge.is_tree_allowed(&rhs_t) { return None; } match use_trees.binary_search_by(|lhs_t| use_tree_cmp_bin_search(lhs_t, &rhs_t)) { Ok(idx) => { - let lhs_t = &mut use_trees[idx]; + let mut lhs_t = use_trees[idx].clone(); let lhs_path = lhs_t.path()?; let rhs_path = rhs_t.path()?; let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; + if lhs_prefix == lhs_path && rhs_prefix == rhs_path { let tree_is_self = |tree: &ast::UseTree| { tree.path().as_ref().map(path_is_self).unwrap_or(false) @@ -157,20 +164,20 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) }; if lhs_t.rename().and_then(|x| x.underscore_token()).is_some() { - ted::replace(lhs_t.syntax(), rhs_t.syntax()); - *lhs_t = rhs_t; + use_trees[idx] = rhs_t; continue; } - match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) { + match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) { (Some(true), None) => { - remove_subtree_if_only_self(lhs_t); + lhs_t = remove_subtree_if_only_self(lhs_t, make)?; + use_trees[idx] = lhs_t; continue; } (None, Some(true)) => { - ted::replace(lhs_t.syntax(), rhs_t.syntax()); - *lhs_t = rhs_t; - remove_subtree_if_only_self(lhs_t); + lhs_t = rhs_t; + lhs_t = remove_subtree_if_only_self(lhs_t, make)?; + use_trees[idx] = lhs_t; continue; } _ => (), @@ -180,9 +187,11 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) continue; } } - lhs_t.split_prefix(&lhs_prefix); - rhs_t.split_prefix(&rhs_prefix); - recursive_merge(lhs_t, &rhs_t, merge)?; + + lhs_t = split_prefix(&lhs_t, &lhs_prefix, make)?; + let rhs_t = split_prefix(&rhs_t, &rhs_prefix, make)?; + lhs_t = recursive_merge(lhs_t, rhs_t, merge, make)?; + use_trees[idx] = lhs_t; } Err(_) if merge == MergeBehavior::Module @@ -192,15 +201,12 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) return None; } Err(insert_idx) => { - use_trees.insert(insert_idx, rhs_t.clone()); - // We simply add the use tree to the end of tree list. Ordering of use trees - // and imports is done by the `try_normalize_*` functions. The sorted `use_trees` - // vec is only used for binary search. - lhs.get_or_create_use_tree_list().add_use_tree(rhs_t); + use_trees.insert(insert_idx, rhs_t); } } } - Some(()) + + with_use_tree_list(&lhs, use_trees, make) } /// Style to follow when normalizing a use tree. @@ -250,241 +256,216 @@ impl From for NormalizationStyle { /// - `foo::{bar::Qux, bar::{self}}` -> `{foo::bar::{self, Qux}}` /// - `foo::bar::{self}` -> `{foo::bar}` /// - `foo::bar` -> `{foo::bar}` -pub fn try_normalize_import(use_item: &ast::Use, style: NormalizationStyle) -> Option { - let use_item = use_item.clone_subtree().clone_for_update(); - try_normalize_use_tree_mut(&use_item.use_tree()?, style)?; - Some(use_item) +pub fn try_normalize_import( + make: &SyntaxFactory, + use_item: &ast::Use, + style: NormalizationStyle, +) -> Option { + let use_tree = try_normalize_use_tree(use_item.use_tree()?, style, make)?; + + make_use_with_tree(use_item, use_tree) } -fn try_normalize_use_tree_mut(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> { +fn try_normalize_use_tree( + use_tree: ast::UseTree, + style: NormalizationStyle, + make: &SyntaxFactory, +) -> Option { if style == NormalizationStyle::One { + let mut use_tree = use_tree; let mut modified = false; - modified |= use_tree.wrap_in_tree_list().is_some(); - modified |= recursive_normalize(use_tree, style).is_some(); - if !modified { - // Either the use tree was already normalized or its semantically empty. - return None; + if let Some(wrapped) = wrap_in_tree_list(&use_tree, make) { + use_tree = wrapped; + modified = true; } - } else { - recursive_normalize(use_tree, NormalizationStyle::Default)?; + if let Some(normalized) = recursive_normalize(use_tree.clone(), style, make) { + use_tree = normalized; + modified = true; + } + return modified.then_some(use_tree); } - Some(()) + + recursive_normalize(use_tree, NormalizationStyle::Default, make) } /// Recursively normalizes a use tree and its subtrees (if any). -fn recursive_normalize(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> { +fn recursive_normalize( + use_tree: ast::UseTree, + style: NormalizationStyle, + make: &SyntaxFactory, +) -> Option { let use_tree_list = use_tree.use_tree_list()?; - let merge_subtree_into_parent_tree = |single_subtree: &ast::UseTree| { - let subtree_is_only_self = single_subtree.path().as_ref().is_some_and(path_is_self); - - let merged_path = match (use_tree.path(), single_subtree.path()) { - // If the subtree is `{self}` then we cannot merge: `use - // foo::bar::{self}` is not equivalent to `use foo::bar`. See - // https://github.com/rust-lang/rust-analyzer/pull/17140#issuecomment-2079189725. - _ if subtree_is_only_self => None, - - (None, None) => None, - (Some(outer), None) => Some(outer), - (None, Some(inner)) => Some(inner), - (Some(outer), Some(inner)) => Some(make::path_concat(outer, inner).clone_for_update()), - }; - - if merged_path.is_some() - || single_subtree.use_tree_list().is_some() - || single_subtree.star_token().is_some() + let mut subtrees = use_tree_list.use_trees().collect::>(); + if subtrees.len() == 1 { + if style == NormalizationStyle::One { + let subtree = subtrees.pop()?; + let normalized = recursive_normalize(subtree, NormalizationStyle::Default, make)?; + return with_use_tree_list(&use_tree, vec![normalized], make); + } + + let merged = merge_single_subtree_into_parent_tree(use_tree, make)?; + return Some(recursive_normalize(merged.clone(), style, make).unwrap_or(merged)); + } + + let mut modified = false; + let mut new_use_tree_list = Vec::new(); + for subtree in subtrees { + if one_style_tree_list(&subtree).is_some() { + let mut elements = Vec::new(); + flatten_one_style_tree(subtree, &mut elements, &mut modified, make); + new_use_tree_list.extend(elements); + modified = true; + } else if let Some(normalized) = + recursive_normalize(subtree.clone(), NormalizationStyle::Default, make) { - ted::remove_all_iter(use_tree.syntax().children_with_tokens()); - if let Some(path) = merged_path { - ted::insert_raw(Position::first_child_of(use_tree.syntax()), path.syntax()); - if single_subtree.use_tree_list().is_some() || single_subtree.star_token().is_some() + new_use_tree_list.push(normalized); + modified = true; + } else { + new_use_tree_list.push(subtree); + } + } + + let mut use_tree = + if modified { with_use_tree_list(&use_tree, new_use_tree_list, make)? } else { use_tree }; + + let mut use_tree_list = use_tree.use_tree_list()?.use_trees().collect::>(); + let mut anchor_idx = 0; + let mut merged_any = false; + while anchor_idx < use_tree_list.len() { + let mut candidate_idx = anchor_idx + 1; + while candidate_idx < use_tree_list.len() { + if let Some(mut merged) = try_merge_trees_with_factory( + use_tree_list[anchor_idx].clone(), + use_tree_list[candidate_idx].clone(), + MergeBehavior::Crate, + make, + ) { + if let Some(normalized) = + recursive_normalize(merged.clone(), NormalizationStyle::Default, make) { - ted::insert_raw( - Position::last_child_of(use_tree.syntax()), - make::token(T![::]), - ); + merged = normalized; } + + use_tree_list[anchor_idx] = merged; + use_tree_list.remove(candidate_idx); + merged_any = true; + } else { + candidate_idx += 1; } - if let Some(inner_use_tree_list) = single_subtree.use_tree_list() { - ted::insert_raw( - Position::last_child_of(use_tree.syntax()), - inner_use_tree_list.syntax(), - ); - } else if single_subtree.star_token().is_some() { - ted::insert_raw(Position::last_child_of(use_tree.syntax()), make::token(T![*])); - } else if let Some(rename) = single_subtree.rename() { - ted::insert_raw( - Position::last_child_of(use_tree.syntax()), - make::tokens::single_space(), - ); - ted::insert_raw(Position::last_child_of(use_tree.syntax()), rename.syntax()); - } - Some(()) - } else { - // Bail on semantically empty use trees. - None } - }; - let one_style_tree_list = |subtree: &ast::UseTree| match ( - subtree.path().is_none() && subtree.star_token().is_none() && subtree.rename().is_none(), - subtree.use_tree_list(), - ) { - (true, tree_list) => tree_list, - _ => None, - }; - let add_element_to_list = |elem: SyntaxElement, elements: &mut Vec| { - if !elements.is_empty() { - elements.push(make::token(T![,]).into()); - elements.push(make::tokens::single_space().into()); + + anchor_idx += 1; + } + if merged_any { + use_tree = with_use_tree_list(&use_tree, use_tree_list, make)?; + modified = true; + } + + if style != NormalizationStyle::One { + let subtrees = use_tree.use_tree_list()?.use_trees().collect::>(); + if subtrees.len() == 1 + && let Some(merged) = merge_single_subtree_into_parent_tree(use_tree.clone(), make) + { + use_tree = merged; + modified = true; } - elements.push(elem); - }; - if let Some((single_subtree,)) = use_tree_list.use_trees().collect_tuple() { - if style == NormalizationStyle::One { - // Only normalize descendant subtrees if the normalization style is "one". - recursive_normalize(&single_subtree, NormalizationStyle::Default)?; - } else { - // Otherwise, merge the single subtree into it's parent (if possible) - // and then normalize the result. - merge_subtree_into_parent_tree(&single_subtree)?; - recursive_normalize(use_tree, style); + } + + if let Some(list) = use_tree.use_tree_list() { + let mut use_tree_list = list.use_trees().collect::>(); + if use_tree_list + .windows(2) + .any(|trees| use_tree_cmp_bin_search(&trees[0], &trees[1]).is_gt()) + { + use_tree_list.sort_unstable_by(use_tree_cmp_bin_search); + use_tree = with_use_tree_list(&use_tree, use_tree_list, make)?; + modified = true; } - } else { - // Tracks whether any changes have been made to the use tree. - let mut modified = false; + } - // Recursively un-nests (if necessary) and then normalizes each subtree in the tree list. - for subtree in use_tree_list.use_trees() { - if let Some(one_tree_list) = one_style_tree_list(&subtree) { - let mut elements = Vec::new(); - let mut one_tree_list_iter = one_tree_list.use_trees(); - let mut prev_skipped = Vec::new(); - loop { - let mut prev_skipped_iter = prev_skipped.into_iter(); - let mut curr_skipped = Vec::new(); - - while let Some(sub_sub_tree) = - one_tree_list_iter.next().or(prev_skipped_iter.next()) - { - if let Some(sub_one_tree_list) = one_style_tree_list(&sub_sub_tree) { - curr_skipped.extend(sub_one_tree_list.use_trees()); - } else { - modified |= - recursive_normalize(&sub_sub_tree, NormalizationStyle::Default) - .is_some(); - add_element_to_list( - sub_sub_tree.syntax().clone().into(), - &mut elements, - ); - } - } + modified.then_some(use_tree) +} - if curr_skipped.is_empty() { - // Un-nesting is complete. - break; - } - prev_skipped = curr_skipped; - } +fn flatten_one_style_tree( + subtree: ast::UseTree, + elements: &mut Vec, + modified: &mut bool, + make: &SyntaxFactory, +) { + let Some(one_tree_list) = one_style_tree_list(&subtree) else { return }; + let mut one_tree_list_iter = one_tree_list.use_trees(); + let mut prev_skipped = Vec::new(); + loop { + let mut prev_skipped_iter = prev_skipped.into_iter(); + let mut curr_skipped = Vec::new(); - // Either removes the subtree (if its semantically empty) or replaces it with - // the un-nested elements. - if elements.is_empty() { - subtree.remove(); - } else { - ted::replace_with_many(subtree.syntax(), elements); - } - // Silence unused assignment warning on `modified`. - let _ = modified; - modified = true; + while let Some(sub_sub_tree) = + one_tree_list_iter.next().or_else(|| prev_skipped_iter.next()) + { + if let Some(sub_one_tree_list) = one_style_tree_list(&sub_sub_tree) { + curr_skipped.extend(sub_one_tree_list.use_trees()); + } else if let Some(normalized) = + recursive_normalize(sub_sub_tree.clone(), NormalizationStyle::Default, make) + { + *modified = true; + elements.push(normalized); } else { - modified |= recursive_normalize(&subtree, NormalizationStyle::Default).is_some(); + elements.push(sub_sub_tree); } } - // Merge all merge-able subtrees. - let mut tree_list_iter = use_tree_list.use_trees(); - let mut anchor = tree_list_iter.next()?; - let mut prev_skipped = Vec::new(); - loop { - let mut has_merged = false; - let mut prev_skipped_iter = prev_skipped.into_iter(); - let mut next_anchor = None; - let mut curr_skipped = Vec::new(); - - while let Some(candidate) = tree_list_iter.next().or(prev_skipped_iter.next()) { - let result = try_merge_trees_mut(&anchor, &candidate, MergeBehavior::Crate); - if result.is_some() { - // Remove merged subtree. - candidate.remove(); - has_merged = true; - } else if next_anchor.is_none() { - next_anchor = Some(candidate); - } else { - curr_skipped.push(candidate); - } - } - - if has_merged { - // Normalize the merge result. - recursive_normalize(&anchor, NormalizationStyle::Default); - modified = true; - } + if curr_skipped.is_empty() { + break; + } + prev_skipped = curr_skipped; + } +} - let (Some(next_anchor), true) = (next_anchor, !curr_skipped.is_empty()) else { - // Merging is complete. - break; - }; +fn merge_single_subtree_into_parent_tree( + use_tree: ast::UseTree, + make: &SyntaxFactory, +) -> Option { + let single_subtree = get_single_subtree(&use_tree)?; + let subtree_is_only_self = single_subtree.path().as_ref().is_some_and(path_is_self); + + let merged_path = match (use_tree.path(), single_subtree.path()) { + _ if subtree_is_only_self => None, + (None, None) => None, + (Some(outer), None) => Some(outer), + (None, Some(inner)) => Some(inner), + (Some(outer), Some(inner)) => Some(make.path_concat(outer, inner)), + }; - // Try to merge the remaining subtrees in the next iteration. - anchor = next_anchor; - prev_skipped = curr_skipped; - } + let list = single_subtree.use_tree_list(); + let list_is_none = list.is_none(); + let star = single_subtree.star_token().is_some(); + if merged_path.is_some() || list.is_some() || star { + let rename = (!star && list_is_none).then(|| single_subtree.rename()).flatten(); + make_use_tree_from_parts(make, merged_path, list, rename, star) + } else { + None + } +} - let mut subtrees: Vec<_> = use_tree_list.use_trees().collect(); - // Merge the remaining subtree into its parent, if its only one and - // the normalization style is not "one". - if subtrees.len() == 1 && style != NormalizationStyle::One { - modified |= merge_subtree_into_parent_tree(&subtrees[0]).is_some(); - } - // Order the remaining subtrees (if necessary). - if subtrees.len() > 1 { - let mut did_sort = false; - subtrees.sort_unstable_by(|a, b| { - let order = use_tree_cmp_bin_search(a, b); - if !did_sort && order == Ordering::Less { - did_sort = true; - } - order - }); - if did_sort { - let start = use_tree_list - .l_curly_token() - .and_then(|l_curly| algo::non_trivia_sibling(l_curly.into(), Direction::Next)) - .filter(|it| it.kind() != T!['}']); - let end = use_tree_list - .r_curly_token() - .and_then(|r_curly| algo::non_trivia_sibling(r_curly.into(), Direction::Prev)) - .filter(|it| it.kind() != T!['{']); - if let Some((start, end)) = start.zip(end) { - // Attempt to insert elements while preserving preceding and trailing trivia. - let mut elements = Vec::new(); - for subtree in subtrees { - add_element_to_list(subtree.syntax().clone().into(), &mut elements); - } - ted::replace_all(start..=end, elements); - } else { - let new_use_tree_list = make::use_tree_list(subtrees).clone_for_update(); - ted::replace(use_tree_list.syntax(), new_use_tree_list.syntax()); - } - modified = true; - } - } +fn one_style_tree_list(subtree: &ast::UseTree) -> Option { + (subtree.path().is_none() && subtree.star_token().is_none() && subtree.rename().is_none()) + .then(|| subtree.use_tree_list()) + .flatten() +} - if !modified { - // Either the use tree was already normalized or its semantically empty. - return None; +fn remove_subtree_if_only_self( + use_tree: ast::UseTree, + make: &SyntaxFactory, +) -> Option { + let Some(single_subtree) = get_single_subtree(&use_tree) else { + return Some(use_tree); + }; + match (use_tree.path(), single_subtree.path()) { + (Some(path), Some(inner)) if path_is_self(&inner) => { + Some(make.use_tree(path, None, use_tree.rename(), false)) } + _ => Some(use_tree), } - Some(()) } /// Traverses both paths until they differ, returning the common prefix of both. @@ -513,10 +494,9 @@ pub fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering { let lhs_is_simple_path = lhs.is_simple_path() && lhs.rename().is_none(); let rhs_is_simple_path = rhs.is_simple_path() && rhs.rename().is_none(); - match ( - lhs.path().as_ref().and_then(ast::Path::first_segment), - rhs.path().as_ref().and_then(ast::Path::first_segment), - ) { + let lhs_segment = lhs.path().and_then(|path| path.first_segment()); + let rhs_segment = rhs.path().and_then(|path| path.first_segment()); + match (lhs_segment, rhs_segment) { (None, None) => match (lhs_is_simple_path, rhs_is_simple_path) { (true, true) => Ordering::Equal, (true, false) => Ordering::Less, @@ -701,14 +681,164 @@ fn get_single_subtree(use_tree: &ast::UseTree) -> Option { .map(|(single_subtree,)| single_subtree) } -fn remove_subtree_if_only_self(use_tree: &ast::UseTree) { - let Some(single_subtree) = get_single_subtree(use_tree) else { return }; - match (use_tree.path(), single_subtree.path()) { - (Some(_), Some(inner)) if path_is_self(&inner) => { - ted::remove_all_iter(single_subtree.syntax().children_with_tokens()); - } - _ => (), +fn make_use_with_tree(original: &ast::Use, use_tree: ast::UseTree) -> Option { + let (editor, use_item) = SyntaxEditor::with_ast_node(original); + let original_tree = use_item.use_tree()?; + editor.replace(original_tree.syntax(), use_tree.syntax()); + let edit = editor.finish(); + ast::Use::cast(edit.new_root().clone()) +} + +fn make_use_tree_list( + make: &SyntaxFactory, + use_trees: Vec, + style_source: Option<&ast::UseTreeList>, +) -> Option { + let use_tree_list = make.use_tree_list(use_trees); + let Some(style_source) = style_source else { + return Some(use_tree_list); + }; + + let source_l_curly = style_source.l_curly_token()?; + let source_r_curly = style_source.r_curly_token()?; + + let leading_ws = source_l_curly.next_token().filter(|token| token.kind().is_trivia()); + + let trailing_ws = source_r_curly.prev_token().filter(|token| token.kind().is_trivia()); + + let source_trailing_token = trailing_ws + .as_ref() + .and_then(|token| token.prev_token()) + .or_else(|| source_r_curly.prev_token()); + + let source_has_trailing_comma = + source_trailing_token.is_some_and(|token| token.kind() == T![,]); + + let (editor, use_tree_list) = SyntaxEditor::with_ast_node(&use_tree_list); + let make = editor.make(); + + if let Some(leading_ws) = leading_ws { + editor.insert( + Position::after(use_tree_list.l_curly_token()?), + make.whitespace(leading_ws.text()), + ); + } + + let r_curly = use_tree_list.r_curly_token()?; + + let generated_has_trailing_comma = r_curly + .prev_token() + .and_then(|token| if token.kind().is_trivia() { token.prev_token() } else { Some(token) }) + .is_some_and(|token| token.kind() == T![,]); + + let mut trailing = Vec::new(); + + if source_has_trailing_comma + && !generated_has_trailing_comma + && use_tree_list.use_trees().next().is_some() + { + trailing.push(make.token(T![,]).into()); + } + + if let Some(trailing_ws) = trailing_ws { + trailing.push(make.whitespace(trailing_ws.text()).into()); + } + + if !trailing.is_empty() { + editor.insert_all(Position::before(r_curly), trailing); + } + + let edit = editor.finish(); + ast::UseTreeList::cast(edit.new_root().clone()) +} + +fn make_use_tree_from_list(make: &SyntaxFactory, list: ast::UseTreeList) -> Option { + let placeholder = make.use_tree_glob(); + let (editor, use_tree) = SyntaxEditor::with_ast_node(&placeholder); + let first_child = use_tree.syntax().first_child_or_token()?; + let last_child = use_tree.syntax().last_child_or_token()?; + editor.replace_all(first_child..=last_child, vec![list.syntax().clone().into()]); + let edit = editor.finish(); + ast::UseTree::cast(edit.new_root().clone()) +} + +fn make_use_tree_from_parts( + make: &SyntaxFactory, + path: Option, + list: Option, + rename: Option, + star: bool, +) -> Option { + match (path, list, star) { + (Some(path), list, star) => Some(make.use_tree(path, list, rename, star)), + (None, Some(list), false) if rename.is_none() => make_use_tree_from_list(make, list), + (None, None, true) if rename.is_none() => Some(make.use_tree_glob()), + (None, None, false) if rename.is_none() => None, + _ => None, + } +} + +fn with_use_tree_list( + use_tree: &ast::UseTree, + use_trees: Vec, + make: &SyntaxFactory, +) -> Option { + let list = make_use_tree_list(make, use_trees, use_tree.use_tree_list().as_ref())?; + make_use_tree_from_parts( + make, + use_tree.path(), + Some(list), + use_tree.rename(), + use_tree.star_token().is_some(), + ) +} + +pub(crate) fn wrap_in_tree_list( + use_tree: &ast::UseTree, + make: &SyntaxFactory, +) -> Option { + if use_tree.path().is_none() + && use_tree.use_tree_list().is_some() + && use_tree.rename().is_none() + && use_tree.star_token().is_none() + { + return None; } + + let list = make_use_tree_list(make, vec![use_tree.clone()], None)?; + make_use_tree_from_list(make, list) +} + +fn split_prefix( + use_tree: &ast::UseTree, + prefix: &ast::Path, + make: &SyntaxFactory, +) -> Option { + let path = use_tree.path()?; + if path == *prefix && use_tree.use_tree_list().is_some() { + return Some(use_tree.clone()); + } + + let suffix = if path == *prefix { + if use_tree.star_token().is_some() { + make.use_tree_glob() + } else { + let self_path = make.path_unqualified(make.path_segment_self()); + make.use_tree(self_path, None, use_tree.rename(), false) + } + } else { + let suffix_segments = path.segments().skip(prefix.segments().count()); + let suffix_path = make.path_from_segments(suffix_segments, false); + make.use_tree( + suffix_path, + use_tree.use_tree_list(), + use_tree.rename(), + use_tree.star_token().is_some(), + ) + }; + + let list = make_use_tree_list(make, vec![suffix], None)?; + Some(make.use_tree(prefix.clone(), Some(list), None, false)) } // Taken from rustfmt diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index bd8fa69e28707..e1c2053289b96 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -1025,7 +1025,6 @@ fn _slice(bar: &[i32]) -> bool { check_diagnostics( r#" //- minicore: coerce_unsized, dispatch_from_dyn -#![feature(trait_upcasting)] trait Foo {} trait Bar: Foo {} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index f6293e35d0c37..d1c3d1c5df096 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -354,6 +354,30 @@ fn f() { ) } + #[test] + fn varargs_fn_pointer() { + check_diagnostics( + r#" +struct Funcs { + f: unsafe extern "C" fn(u8, u8, ...) -> i32, + g: unsafe extern "C" fn(...) -> i32, +} + +fn f(funcs: Funcs) { + unsafe { + (funcs.f)(0, 1); + (funcs.f)(0, 1, 2); + (funcs.f)(0); + //^ error: expected 2 arguments, found 1 + (funcs.g)(); + (funcs.g)(0); + (funcs.g)(0, 1); + } +} + "#, + ) + } + #[test] fn arg_count_lambda() { check_diagnostics( diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 88cb570c6b0f3..dded01520ffbc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -60,6 +60,7 @@ mod view_mir; mod view_syntax_tree; use std::panic::{AssertUnwindSafe, UnwindSafe}; +use std::time::Duration; use cfg::CfgOptions; use fetch_crates::CrateInfo; @@ -197,8 +198,8 @@ impl AnalysisHost { /// Applies changes to the current state of the world. If there are /// outstanding snapshots, they will be canceled. - pub fn apply_change(&mut self, change: ChangeWithProcMacros) { - self.db.apply_change(change); + pub fn apply_change(&mut self, change: ChangeWithProcMacros) -> Duration { + self.db.apply_change(change) } /// NB: this clears the database diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index b8c14dc09f9a2..24031429db88f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -50,7 +50,6 @@ pub struct NavigationTarget { pub kind: Option, pub container_name: Option, pub description: Option, - // FIXME: Use the database lifetime here. pub docs: Option>, /// In addition to a `name` field, a `NavigationTarget` may also be aliased /// In such cases we want a `NavigationTarget` to be accessible by its alias diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 1184739cc2589..2f2c7f251a703 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -128,9 +128,9 @@ let y = &mut x; let z = &y; - let Foo { x: z, y } = Foo { x: z, y }; + let Foo { x: z, y } = Foo { x: z, y }; - y; + y; let mut foo = Foo { x, y: x }; let foo2 = Foo { x, y: x }; @@ -143,7 +143,7 @@ copy.qux(); copy.baz(copy); - let a = |x| x; + let a = |x| x; let bar = Foo::baz; let baz = (-42,); @@ -173,13 +173,13 @@ } async fn learn_and_sing() { - let song = learn_song().await; - sing_song(song).await; + let song = learn_song().await; + sing_song(song).await; } async fn async_main() { let f1 = learn_and_sing(); - let f2 = dance(); + let f2 = dance(); futures::join!(f1, f2); } diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 2ba802f05706c..adf49ca7b6c29 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -12,7 +12,6 @@ rust-version.workspace = true [lib] doctest = false - [dependencies] dashmap.workspace = true # We need to freeze the version of the crate, as it needs to match with dashmap diff --git a/src/tools/rust-analyzer/crates/intern/src/gc.rs b/src/tools/rust-analyzer/crates/intern/src/gc.rs index f4e8f75e7194b..596b05eb1cb13 100644 --- a/src/tools/rust-analyzer/crates/intern/src/gc.rs +++ b/src/tools/rust-analyzer/crates/intern/src/gc.rs @@ -34,9 +34,7 @@ impl Storage for InternedStorage { for item in storage { let item = item.key(); let addr = Arc::as_ptr(item).addr(); - if Arc::strong_count(item) > 1 { - // The item is referenced from the outside. - gc.alive.insert(addr); + if Arc::strong_count(item) > 1 && gc.alive.insert(addr) { item.visit_with(gc); } } @@ -60,9 +58,7 @@ impl Storage for InternedSliceStorage for item in storage { let item = item.key(); let addr = ThinArc::as_ptr(item).addr(); - if ThinArc::strong_count(item) > 1 { - // The item is referenced from the outside. - gc.alive.insert(addr); + if ThinArc::strong_count(item) > 1 && gc.alive.insert(addr) { T::visit_header(&item.header.header, gc); T::visit_slice(&item.slice, gc); } @@ -81,7 +77,7 @@ pub trait GcInternedVisit { pub trait GcInternedSliceVisit: SliceInternable { fn visit_header(header: &Self::Header, gc: &mut GarbageCollector); - fn visit_slice(header: &[Self::SliceType], gc: &mut GarbageCollector); + fn visit_slice(slice: &[Self::SliceType], gc: &mut GarbageCollector); } #[derive(Default)] @@ -103,11 +99,13 @@ impl GarbageCollector { self.storages.push(&InternedSliceStorage::(PhantomData)); } + /// Collects unreachable GC-managed interned values. + /// /// # Safety /// /// - This cannot be called if there are some not-yet-recorded type values. - /// - All relevant storages must have been added; that is, within the full graph of values, - /// the added storages must form a DAG. + /// - All storages that can contain live GC-managed values must have been added, and those + /// storages must be closed over the GC-managed values reachable from them. /// - [`GcInternedVisit`] and [`GcInternedSliceVisit`] must mark all values reachable from the node. pub unsafe fn collect(mut self) { if cfg!(feature = "prevent-gc") { @@ -136,8 +134,9 @@ impl GarbageCollector { &mut self, interned: InternedRef<'_, T>, ) -> ControlFlow<()> { + const { assert!(T::USE_GC) }; + if interned.strong_count() > 1 { - // It will be visited anyway, so short-circuit return ControlFlow::Break(()); } let addr = interned.as_raw().addr(); @@ -148,8 +147,9 @@ impl GarbageCollector { &mut self, interned: InternedSliceRef<'_, T>, ) -> ControlFlow<()> { + const { assert!(T::USE_GC) }; + if interned.strong_count() > 1 { - // It will be visited anyway, so short-circuit return ControlFlow::Break(()); } let addr = interned.as_raw().addr(); @@ -240,7 +240,7 @@ mod tests { impl GcInternedSliceVisit for StringSlice { fn visit_header(_header: &Self::Header, _gc: &mut GarbageCollector) {} - fn visit_slice(_header: &[Self::SliceType], _gc: &mut GarbageCollector) {} + fn visit_slice(_slice: &[Self::SliceType], _gc: &mut GarbageCollector) {} } let (a, d) = { @@ -276,6 +276,10 @@ mod tests { gc.add_storage::(); unsafe { gc.collect() }; + if !cfg!(feature = "prevent-gc") { + assert_eq!(::storage().get().len(), 1); + assert_eq!(::storage().get().len(), 1); + } assert_eq!(a.0, "abc"); assert_eq!(d.header.length, 2); assert_eq!(d.header.header, "abc"); @@ -288,6 +292,11 @@ mod tests { gc.add_slice_storage::(); gc.add_storage::(); unsafe { gc.collect() }; + + if !cfg!(feature = "prevent-gc") { + assert_eq!(::storage().get().len(), 0); + assert_eq!(::storage().get().len(), 0); + } } #[test] @@ -309,7 +318,7 @@ mod tests { impl GcInternedSliceVisit for StringSlice { fn visit_header(_header: &Self::Header, _gc: &mut GarbageCollector) {} - fn visit_slice(_header: &[Self::SliceType], _gc: &mut GarbageCollector) {} + fn visit_slice(_slice: &[Self::SliceType], _gc: &mut GarbageCollector) {} } let outer = { @@ -322,6 +331,10 @@ mod tests { gc.add_storage::(); unsafe { gc.collect() }; + if !cfg!(feature = "prevent-gc") { + assert_eq!(::storage().get().len(), 1); + assert_eq!(::storage().get().len(), 1); + } assert_eq!(outer.0.header.header, "abc"); assert_eq!(outer.0.slice, [123, 456, 789]); @@ -331,5 +344,10 @@ mod tests { gc.add_slice_storage::(); gc.add_storage::(); unsafe { gc.collect() }; + + if !cfg!(feature = "prevent-gc") { + assert_eq!(::storage().get().len(), 0); + assert_eq!(::storage().get().len(), 0); + } } } diff --git a/src/tools/rust-analyzer/crates/parser/src/output.rs b/src/tools/rust-analyzer/crates/parser/src/output.rs index 2f09b1121891b..ce64db8adae90 100644 --- a/src/tools/rust-analyzer/crates/parser/src/output.rs +++ b/src/tools/rust-analyzer/crates/parser/src/output.rs @@ -18,7 +18,7 @@ pub struct Output { /// /// ```text /// |16 bit kind|8 bit n_input_tokens|4 bit tag|4 bit leftover| - /// `````` + /// ``` event: Vec, error: Vec, } 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 f5d40a4b9ffc2..1b86eac0129aa 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -29,7 +29,7 @@ line-index.workspace = true proc-macro-test.path = "./proc-macro-test" [features] -default = ["in-rust-tree"] +default = [] in-rust-tree = [] [lints] 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 e44af96f36435..352f41a82636a 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 @@ -560,7 +560,7 @@ impl WorkspaceBuildScripts { // FIXME: Find a better way to know if it is a dylib. fn is_dylib(path: &Utf8Path) -> bool { - match path.extension().map(|e| e.to_owned().to_lowercase()) { + match path.extension().map(|e| e.to_ascii_lowercase()) { None => false, Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"), } 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 675533645d0db..c9bf803da347b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -811,6 +811,7 @@ impl ProjectWorkspace { .packages() .map(|pkg| { let is_local = cargo[pkg].is_local; + let is_member = cargo[pkg].is_member; let pkg_root = cargo[pkg].manifest.parent().to_path_buf(); let mut include = vec![pkg_root.clone()]; @@ -844,9 +845,11 @@ impl ProjectWorkspace { let mut exclude = vec![pkg_root.join(".git")]; if is_local { include.extend(self.extra_includes.iter().cloned()); - exclude.push(pkg_root.join("target")); - } else { + } + if !is_member { + // For non-workspace-members, we only resolve library targets, + // so none of these need to be loaded into the VFS. exclude.push(pkg_root.join("tests")); exclude.push(pkg_root.join("examples")); exclude.push(pkg_root.join("benches")); @@ -874,6 +877,7 @@ impl ProjectWorkspace { .chain(cargo_script.iter().flat_map(|(cargo, build_scripts, _)| { cargo.packages().map(|pkg| { let is_local = cargo[pkg].is_local; + let is_member = cargo[pkg].is_member; let pkg_root = cargo[pkg].manifest.parent().to_path_buf(); let mut include = vec![pkg_root.clone()]; @@ -909,7 +913,10 @@ impl ProjectWorkspace { include.extend(self.extra_includes.iter().cloned()); exclude.push(pkg_root.join("target")); - } else { + } + if !is_member { + // For non-workspace-members, we only resolve library targets, + // so none of these need to be loaded into the VFS. exclude.push(pkg_root.join("tests")); exclude.push(pkg_root.join("examples")); exclude.push(pkg_root.join("benches")); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 49f28352b6cf6..69405b421eca5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -64,6 +64,7 @@ impl Tester { fn new() -> Result { let mut path = AbsPathBuf::assert_utf8(std::env::temp_dir()); path.push("ra-rustc-test"); + std::fs::create_dir_all(&path)?; let tmp_file = path.join("ra-rustc-test.rs"); std::fs::write(&tmp_file, "")?; let cargo_config = CargoConfig { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index bca38ed82fdd5..d3acbb934d5bb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -237,7 +237,7 @@ impl flags::Scip { let token = si.tokens.get(id).unwrap(); let Some(definition) = token.definition else { - break; + continue; }; let file_id = definition.file_id; 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 6f532e4224885..4ea447333903d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -662,9 +662,12 @@ config_data! { /// For traits the type "methods" can be used to only exclude the methods but not the trait /// itself. /// - /// For modules the type "subItems" can be used to only exclude the all items in it but not the module + /// For modules the type "sub_items" can be used to only exclude the all items in it but not the module /// itself. This does not include items defined in nested modules. /// + /// For enums the type "variants" can be used to only exclude the all variants in it but not the enum + /// itself. + /// /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. completion_autoimport_exclude: Vec = vec![ AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods }, @@ -1944,6 +1947,9 @@ impl Config { AutoImportExclusionType::SubItems => { ide_completion::AutoImportExclusionType::SubItems } + AutoImportExclusionType::Variants => { + ide_completion::AutoImportExclusionType::Variants + } }, ), }) @@ -3004,6 +3010,7 @@ pub enum AutoImportExclusionType { Always, Methods, SubItems, + Variants, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -4135,7 +4142,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "type": { "type": "string", - "enum": ["always", "methods", "subItems"], + "enum": ["always", "methods", "sub_items", "variants"], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", "Do not show this trait's methods in auto-import completions.", diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index afd4162de6227..f91e9532aaf02 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -330,7 +330,7 @@ impl GlobalState { this } - pub(crate) fn process_changes(&mut self) -> bool { + pub(crate) fn process_changes(&mut self) -> (bool, Option) { let _p = span!(Level::INFO, "GlobalState::process_changes").entered(); // We cannot directly resolve a change in a ratoml file to a format // that can be used by the config module because config talks @@ -343,7 +343,7 @@ impl GlobalState { let mut guard = self.vfs.write(); let changed_files = guard.0.take_changes(); if changed_files.is_empty() { - return false; + return (false, None); } let (change, modified_rust_files, workspace_structure_change) = @@ -439,7 +439,7 @@ impl GlobalState { (change, modified_rust_files, workspace_structure_change) }); - self.analysis_host.apply_change(change); + let cancellation_time = self.analysis_host.apply_change(change); if !modified_ratoml_files.is_empty() || !self.config.same_source_root_parent_map(&self.local_roots_parent_map) @@ -561,7 +561,7 @@ impl GlobalState { } } - true + (true, Some(cancellation_time)) } pub(crate) fn snapshot(&self) -> GlobalStateSnapshot { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index edf3da5e6c07a..d966e29ede0c4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -307,15 +307,11 @@ impl GlobalState { let _p = tracing::info_span!("GlobalState::handle_event", event = %event).entered(); let event_dbg_msg = format!("{event:?}"); - tracing::debug!(?loop_start, ?event, "handle_event"); - if tracing::enabled!(tracing::Level::TRACE) { - let task_queue_len = self.task_pool.handle.len(); - if task_queue_len > 0 { - tracing::trace!("task queue len: {}", task_queue_len); - } - } + tracing::debug!(?event, "handle_event"); let was_quiescent = self.is_quiescent(); + + let mut cancellation_time = None; match event { Event::Lsp(msg) => match msg { lsp_server::Message::Request(req) => self.on_new_request(loop_start, req), @@ -326,7 +322,9 @@ impl GlobalState { let _p = tracing::info_span!("GlobalState::handle_event/queued_task").entered(); self.handle_deferred_task(task); // Coalesce multiple deferred task events into one loop turn - while let Ok(task) = self.deferred_task_queue.receiver.try_recv() { + while loop_start.elapsed() < Duration::from_millis(50) + && let Ok(task) = self.deferred_task_queue.receiver.try_recv() + { self.handle_deferred_task(task); } } @@ -334,14 +332,16 @@ impl GlobalState { let _p = tracing::info_span!("GlobalState::handle_event/task").entered(); let mut prime_caches_progress = Vec::new(); - self.handle_task(&mut prime_caches_progress, task); + cancellation_time = self.handle_task(&mut prime_caches_progress, task); // Coalesce multiple task events into one loop turn - while let Ok(task) = self.task_pool.receiver.try_recv() { + while loop_start.elapsed() < Duration::from_millis(50) + && let Ok(task) = self.task_pool.receiver.try_recv() + { self.handle_task(&mut prime_caches_progress, task); } let title = "Indexing"; - let cancel_token = Some("rustAnalyzer/cachePriming".to_owned()); + let cancel_token = || Some("rustAnalyzer/cachePriming".to_owned()); let mut last_report = None; for progress in prime_caches_progress { @@ -352,7 +352,7 @@ impl GlobalState { Progress::Begin, None, Some(0.0), - cancel_token.clone(), + cancel_token(), ); } PrimeCachesProgress::Report(report) => { @@ -387,6 +387,16 @@ impl GlobalState { if cancelled { self.prime_caches_queue .request_op("restart after cancellation".to_owned(), ()); + } else if self.config.check_on_save(None) + && self.config.flycheck_workspace(None) + && !self.fetch_build_data_queue.op_requested() + { + // Priming finished; now run the deferred initial workspace flycheck + // (kept off the critical path so `cargo check` doesn't contend with + // cache priming for CPU). + self.flycheck + .iter() + .for_each(|flycheck| flycheck.restart_workspace(None)); } if let Some((message, fraction, title)) = last_report.take() { self.report_progress( @@ -394,7 +404,7 @@ impl GlobalState { Progress::Report, message, Some(fraction), - cancel_token.clone(), + cancel_token(), ); } self.report_progress( @@ -402,7 +412,7 @@ impl GlobalState { Progress::End, None, Some(1.0), - cancel_token.clone(), + cancel_token(), ); } }; @@ -413,7 +423,7 @@ impl GlobalState { Progress::Report, message, Some(fraction), - cancel_token.clone(), + cancel_token(), ); } } @@ -422,7 +432,9 @@ impl GlobalState { let mut last_progress_report = None; self.handle_vfs_msg(message, &mut last_progress_report); // Coalesce many VFS event into a single loop turn - while let Ok(message) = self.loader.receiver.try_recv() { + while loop_start.elapsed() < Duration::from_millis(50) + && let Ok(message) = self.loader.receiver.try_recv() + { self.handle_vfs_msg(message, &mut last_progress_report); } if let Some((message, fraction)) = last_progress_report { @@ -439,7 +451,9 @@ impl GlobalState { let mut cargo_finished = false; self.handle_flycheck_msg(message, &mut cargo_finished); // Coalesce many flycheck updates into a single loop turn - while let Ok(message) = self.flycheck_receiver.try_recv() { + while loop_start.elapsed() < Duration::from_millis(50) + && let Ok(message) = self.flycheck_receiver.try_recv() + { self.handle_flycheck_msg(message, &mut cargo_finished); } if cargo_finished { @@ -453,14 +467,18 @@ impl GlobalState { let _p = tracing::info_span!("GlobalState::handle_event/test_result").entered(); self.handle_cargo_test_msg(message); // Coalesce many test result event into a single loop turn - while let Ok(message) = self.test_run_receiver.try_recv() { + while loop_start.elapsed() < Duration::from_millis(50) + && let Ok(message) = self.test_run_receiver.try_recv() + { self.handle_cargo_test_msg(message); } } Event::DiscoverProject(message) => { self.handle_discover_msg(message); // Coalesce many project discovery events into a single loop turn. - while let Ok(message) = self.discover_receiver.try_recv() { + while loop_start.elapsed() < Duration::from_millis(50) + && let Ok(message) = self.discover_receiver.try_recv() + { self.handle_discover_msg(message); } } @@ -469,26 +487,29 @@ impl GlobalState { } } let event_handling_duration = loop_start.elapsed(); - let (state_changed, memdocs_added_or_removed) = if self.vfs_done { - if let Some(cause) = self.wants_to_switch.take() { - self.switch_workspaces(cause); - } - (self.process_changes(), self.mem_docs.take_changes()) - } else { - (false, false) + let ((state_changed, changes_cancellation_time), memdocs_added_or_removed) = + if self.vfs_done { + if let Some(cause) = self.wants_to_switch.take() { + cancellation_time = match (cancellation_time, self.switch_workspaces(cause)) { + (Some(a), Some(b)) => Some(a + b), + (Some(d), None) | (None, Some(d)) => Some(d), + (None, None) => None, + }; + } + (self.process_changes(), self.mem_docs.take_changes()) + } else { + ((false, None), false) + }; + cancellation_time = match (cancellation_time, changes_cancellation_time) { + (Some(a), Some(b)) => Some(a + b), + (Some(d), None) | (None, Some(d)) => Some(d), + (None, None) => None, }; let mut gc_elapsed = None; if self.is_quiescent() { let became_quiescent = !was_quiescent; if became_quiescent { - if self.config.check_on_save(None) - && self.config.flycheck_workspace(None) - && !self.fetch_build_data_queue.op_requested() - { - // Project has loaded properly, kick off initial flycheck - self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None)); - } // delay initial cache priming until proc macros are loaded, or we will load up a bunch of garbage into salsa let proc_macros_loaded = self.config.prefill_caches() && (!self.config.expand_proc_macros() @@ -496,6 +517,19 @@ impl GlobalState { if proc_macros_loaded { self.prime_caches_queue.request_op("became quiescent".to_owned(), ()); } + if self.config.check_on_save(None) + && self.config.flycheck_workspace(None) + && !self.fetch_build_data_queue.op_requested() + { + if !self.config.prefill_caches() { + self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None)); + } else if proc_macros_loaded + && !self.prime_caches_queue.op_in_progress() + && !self.prime_caches_queue.op_requested() + { + self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None)); + } + } } let client_refresh = became_quiescent || state_changed; @@ -593,11 +627,13 @@ impl GlobalState { tracing::warn!( "overly long loop turn took {loop_duration:?}:\n\ (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (cancellation took {cancellation_time:?}) (garbage collection took {gc_elapsed:?})" ); self.poke_rust_analyzer_developer(format!( "overly long loop turn took {loop_duration:?}:\n\ (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (cancellation took {cancellation_time:?}) (garbage collection took {gc_elapsed:?})" )); } @@ -803,7 +839,12 @@ impl GlobalState { } } - fn handle_task(&mut self, prime_caches_progress: &mut Vec, task: Task) { + fn handle_task( + &mut self, + prime_caches_progress: &mut Vec, + task: Task, + ) -> Option { + let mut cancellation_time = None; match task { Task::Response(response) => self.respond(response), // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work. @@ -906,8 +947,9 @@ impl GlobalState { ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)), ProcMacroProgress::End(change) => { self.fetch_proc_macros_queue.op_completed(true); - self.analysis_host.apply_change(change); - self.finish_loading_crate_graph(); + cancellation_time = Some(self.analysis_host.apply_change(change)); + // FIXME This feels a bit off, this should go through similar machinery as build scripts? + _ = self.finish_loading_crate_graph(); (Some(Progress::End), None) } }; @@ -921,6 +963,7 @@ impl GlobalState { self.send_notification::(tests); } } + cancellation_time } fn handle_vfs_msg( 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 74fd0e653398b..4940defed2ac2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -13,7 +13,7 @@ //! project is currently loading and we don't have a full project model, we //! still want to respond to various requests. // FIXME: This is a mess that needs some untangling work -use std::{iter, mem, sync::atomic::AtomicUsize}; +use std::{iter, mem, sync::atomic::AtomicUsize, time::Duration}; use hir::{ChangeWithProcMacros, ProcMacrosBuilder, db::DefDatabase}; use ide_db::{ @@ -468,25 +468,22 @@ impl GlobalState { }); } - pub(crate) fn switch_workspaces(&mut self, cause: Cause) { + pub(crate) fn switch_workspaces(&mut self, cause: Cause) -> Option { let _p = tracing::info_span!("GlobalState::switch_workspaces").entered(); tracing::info!(%cause, "will switch workspaces"); - let Some(FetchWorkspaceResponse { workspaces, force_crate_graph_reload }) = - self.fetch_workspaces_queue.last_op_result() - else { - return; - }; + let FetchWorkspaceResponse { workspaces, force_crate_graph_reload } = + self.fetch_workspaces_queue.last_op_result()?; let switching_from_empty_workspace = self.workspaces.is_empty(); info!(%cause, ?force_crate_graph_reload, %switching_from_empty_workspace); if self.fetch_workspace_error().is_err() && !switching_from_empty_workspace { if *force_crate_graph_reload { - self.recreate_crate_graph(cause, false); + return self.recreate_crate_graph(cause, false); } // It only makes sense to switch to a partially broken workspace // if we don't have any workspace at all yet. - return; + return None; } let workspaces = @@ -501,7 +498,7 @@ impl GlobalState { if same_workspaces { if switching_from_empty_workspace { // Switching from empty to empty is a no-op - return; + return None; } if let Some(FetchBuildDataResponse { workspaces, build_scripts }) = self.fetch_build_data_queue.last_op_result() @@ -524,20 +521,20 @@ impl GlobalState { } else { info!("build scripts do not match the version of the active workspace"); if *force_crate_graph_reload { - self.recreate_crate_graph(cause, switching_from_empty_workspace); + return self.recreate_crate_graph(cause, switching_from_empty_workspace); } // Current build scripts do not match the version of the active // workspace, so there's nothing for us to update. - return; + return None; } } else { if *force_crate_graph_reload { - self.recreate_crate_graph(cause, switching_from_empty_workspace); + return self.recreate_crate_graph(cause, switching_from_empty_workspace); } // No build scripts but unchanged workspaces, nothing to do here - return; + return None; } } else { info!("abandon build scripts for workspaces"); @@ -560,7 +557,7 @@ impl GlobalState { // `switch_workspaces()` will be called again when build scripts already run, which should // take a short time. If we update the workspace now we will invalidate proc macros and cfgs, // and then when build scripts complete we will invalidate them again. - return; + return None; } } } @@ -733,13 +730,15 @@ impl GlobalState { self.local_roots_parent_map = Arc::new(self.source_root_config.source_root_parent_map()); info!(?cause, "recreating the crate graph"); - self.recreate_crate_graph(cause, switching_from_empty_workspace); + let cancellation_time = self.recreate_crate_graph(cause, switching_from_empty_workspace); info!("did switch workspaces"); + cancellation_time } - fn recreate_crate_graph(&mut self, cause: String, initial_build: bool) { + fn recreate_crate_graph(&mut self, cause: String, initial_build: bool) -> Option { info!(?cause, "Building Crate Graph"); + let mut cancellation_time = None; self.report_progress( "Building CrateGraph", crate::lsp::utils::Progress::Begin, @@ -795,9 +794,8 @@ impl GlobalState { } change.set_crate_graph(crate_graph); - self.analysis_host.apply_change(change); - - self.finish_loading_crate_graph(); + cancellation_time = Some(self.analysis_host.apply_change(change)); + _ = self.finish_loading_crate_graph(); } else { change.set_crate_graph(crate_graph); self.fetch_proc_macros_queue.request_op(cause, (change, proc_macro_paths)); @@ -810,11 +808,13 @@ impl GlobalState { None, None, ); + cancellation_time } - pub(crate) fn finish_loading_crate_graph(&mut self) { - self.process_changes(); + pub(crate) fn finish_loading_crate_graph(&mut self) -> Option { + let (_, cancellation_time) = self.process_changes(); self.reload_flycheck(); + cancellation_time } pub(super) fn fetch_workspace_error(&self) -> Result<(), String> { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 81d9786809b72..fdcd2c98b904d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -153,6 +153,9 @@ impl CargoTargetSpec { Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => { config.test_command } + Some(CargoTargetSpec { target_kind: TargetKind::Bench, .. }) => { + config.bench_command + } _ => "run".to_owned(), }; cargo_args.push(subcommand); @@ -225,6 +228,9 @@ impl CargoTargetSpec { Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => { (config.test_override_command, None) } + Some(CargoTargetSpec { target_kind: TargetKind::Bench, .. }) => { + (config.bench_override_command, None) + } _ => (None, None), }, }; @@ -319,7 +325,11 @@ impl CargoTargetSpec { pub(crate) fn push_to(self, buf: &mut Vec, kind: &RunnableKind) { buf.push("--package".to_owned()); - buf.push(self.package); + if self.package.contains(":") { + buf.push(self.package_id.to_string()); + } else { + buf.push(self.package); + } // Can't mix --doc with other target flags if let RunnableKind::DocTest { .. } = kind { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs index 104cd3d2eae9e..2da52f7480d8a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs @@ -40,10 +40,6 @@ impl TaskPool { }) } - pub(crate) fn len(&self) -> usize { - self.pool.len() - } - pub(crate) fn is_empty(&self) -> bool { self.pool.is_empty() } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index dc592a43727b4..d8c7e1583031d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -1,7 +1,6 @@ //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s pub mod edit; -pub mod edit_in_place; mod expr_ext; mod generated; pub mod make; diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs index 2e3a4016ee89f..eaa36903bf26f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -13,7 +13,7 @@ use crate::{ SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, ast::{self, AstNode, HasName, make}, - syntax_editor::{Position, SyntaxEditor, SyntaxMappingBuilder}, + syntax_editor::{Position, Removable, SyntaxEditor, SyntaxMappingBuilder}, }; use super::syntax_factory::SyntaxFactory; @@ -263,6 +263,109 @@ pub fn indent(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode { level.clone_increase_indent(node) } +impl ast::GenericParamList { + /// Constructs a matching [`ast::GenericArgList`] + pub fn to_generic_args(&self, make: &SyntaxFactory) -> ast::GenericArgList { + let args = self.generic_params().filter_map(|param| match param { + ast::GenericParam::LifetimeParam(it) => { + Some(ast::GenericArg::LifetimeArg(make.lifetime_arg(it.lifetime()?))) + } + ast::GenericParam::TypeParam(it) => { + Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?)))) + } + ast::GenericParam::ConstParam(it) => { + // Name-only const params get parsed as `TypeArg`s + Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?)))) + } + }); + + make::generic_arg_list(args) + } +} + +impl ast::UseTree { + /// Deletes the usetree node represented by the input. Recursively removes parents, including use nodes that become empty. + pub fn remove_recursive(self, editor: &SyntaxEditor) { + let parent = self.syntax().parent(); + + if let Some(u) = parent.clone().and_then(ast::Use::cast) { + u.remove(editor); + } else if let Some(u) = parent.and_then(ast::UseTreeList::cast) { + if u.use_trees().nth(1).is_none() + || u.use_trees().all(|use_tree| { + use_tree.syntax() == self.syntax() || editor.deleted(use_tree.syntax()) + }) + { + u.parent_use_tree().remove_recursive(editor); + return; + } + self.remove(editor); + u.remove_unnecessary_braces(editor); + } + } + + /// Splits off the given prefix, making it the path component of the use tree, + /// appending the rest of the path to all UseTreeList items. + /// + /// # Examples + /// + /// `prefix$0::suffix` -> `prefix::{suffix}` + /// + /// `prefix$0` -> `prefix::{self}` + /// + /// `prefix$0::*` -> `prefix::{*}` + pub fn split_prefix_with_editor(&self, editor: &SyntaxEditor, prefix: &ast::Path) { + debug_assert_eq!(self.path(), Some(prefix.top_path())); + + let make = editor.make(); + let path = self.path().unwrap(); + let suffix = if path == *prefix { + if self.use_tree_list().is_some() { + return; + } else if self.star_token().is_some() { + make.use_tree_glob() + } else { + let self_path = make.path_unqualified(make.path_segment_self()); + make.use_tree(self_path, None, self.rename(), false) + } + } else { + let suffix_segments = path.segments().skip(prefix.segments().count()); + let suffix_path = make.path_from_segments(suffix_segments, false); + make.use_tree( + suffix_path, + self.use_tree_list(), + self.rename(), + self.star_token().is_some(), + ) + }; + let use_tree_list = make.use_tree_list(once(suffix)); + let new_use_tree = make.use_tree(prefix.clone(), Some(use_tree_list), None, false); + + editor.replace(self.syntax(), new_use_tree.syntax()); + } +} + +impl ast::RecordExprField { + /// This will either replace the initializer, or in the case that this is a shorthand convert + /// the initializer into the name ref and insert the expr as the new initializer. + pub fn replace_expr(&self, editor: &SyntaxEditor, expr: ast::Expr) { + if self.name_ref().is_some() { + if let Some(prev) = self.expr() { + editor.replace(prev.syntax(), expr.syntax()); + } + } else if let Some(ast::Expr::PathExpr(path_expr)) = self.expr() + && let Some(path) = path_expr.path() + && let Some(name_ref) = path.as_single_name_ref() + { + // shorthand `{ x }` → expand to `{ x: expr }` + let new_field = editor + .make() + .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr)); + editor.replace(self.syntax(), new_field.syntax()); + } + } +} + #[test] fn test_increase_indent() { let arm_list = { @@ -285,3 +388,29 @@ fn test_increase_indent() { }" ); } + +#[test] +fn split_prefix_inserts_self() { + check_split_prefix("use foo;", "foo::{self}"); +} + +#[test] +fn split_prefix_preserves_rename() { + check_split_prefix("use foo as bar;", "foo::{self as bar}"); +} + +#[test] +fn split_prefix_wraps_glob() { + check_split_prefix("use foo::*;", "foo::{*}"); +} + +#[cfg(test)] +fn check_split_prefix(before: &str, expected: &str) { + let source = crate::SourceFile::parse(before, parser::Edition::CURRENT).tree(); + let use_tree = source.syntax().descendants().find_map(ast::UseTree::cast).unwrap(); + let (editor, use_tree) = SyntaxEditor::with_ast_node(&use_tree); + let prefix = use_tree.path().unwrap(); + use_tree.split_prefix_with_editor(&editor, &prefix); + let edit = editor.finish(); + assert_eq!(edit.new_root().to_string(), expected); +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs deleted file mode 100644 index 4a8c9d450c45c..0000000000000 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ /dev/null @@ -1,318 +0,0 @@ -//! Structural editing for ast. - -use std::iter::{empty, once, successors}; - -use parser::T; - -use crate::{ - AstNode, AstToken, Direction, - algo::{self, neighbor}, - ast::{self, make, syntax_factory::SyntaxFactory}, - syntax_editor::SyntaxEditor, - ted, -}; - -use super::HasName; - -impl ast::GenericParamList { - /// Constructs a matching [`ast::GenericArgList`] - pub fn to_generic_args(&self, make: &SyntaxFactory) -> ast::GenericArgList { - let args = self.generic_params().filter_map(|param| match param { - ast::GenericParam::LifetimeParam(it) => { - Some(ast::GenericArg::LifetimeArg(make.lifetime_arg(it.lifetime()?))) - } - ast::GenericParam::TypeParam(it) => { - Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?)))) - } - ast::GenericParam::ConstParam(it) => { - // Name-only const params get parsed as `TypeArg`s - Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?)))) - } - }); - - make::generic_arg_list(args) - } -} - -pub trait Removable: AstNode { - fn remove(&self); -} - -impl Removable for ast::UseTree { - fn remove(&self) { - for dir in [Direction::Next, Direction::Prev] { - if let Some(next_use_tree) = neighbor(self, dir) { - let separators = self - .syntax() - .siblings_with_tokens(dir) - .skip(1) - .take_while(|it| it.as_node() != Some(next_use_tree.syntax())); - ted::remove_all_iter(separators); - break; - } - } - ted::remove(self.syntax()); - } -} - -impl ast::UseTree { - /// Editor variant of UseTree remove - fn remove_with_editor(&self, editor: &SyntaxEditor) { - for dir in [Direction::Next, Direction::Prev] { - if let Some(next_use_tree) = neighbor(self, dir) { - let separators = self - .syntax() - .siblings_with_tokens(dir) - .skip(1) - .take_while(|it| it.as_node() != Some(next_use_tree.syntax())); - for separator in separators { - editor.delete(separator); - } - break; - } - } - editor.delete(self.syntax()); - } - - /// Deletes the usetree node represented by the input. Recursively removes parents, including use nodes that become empty. - pub fn remove_recursive(self, editor: &SyntaxEditor) { - let parent = self.syntax().parent(); - - if let Some(u) = parent.clone().and_then(ast::Use::cast) { - u.remove(editor); - } else if let Some(u) = parent.and_then(ast::UseTreeList::cast) { - if u.use_trees().nth(1).is_none() - || u.use_trees().all(|use_tree| { - use_tree.syntax() == self.syntax() || editor.deleted(use_tree.syntax()) - }) - { - u.parent_use_tree().remove_recursive(editor); - return; - } - self.remove_with_editor(editor); - u.remove_unnecessary_braces(editor); - } - } - - pub fn get_or_create_use_tree_list(&self) -> ast::UseTreeList { - match self.use_tree_list() { - Some(it) => it, - None => { - let position = ted::Position::last_child_of(self.syntax()); - let use_tree_list = make::use_tree_list(empty()).clone_for_update(); - let mut elements = Vec::with_capacity(2); - if self.coloncolon_token().is_none() { - elements.push(make::token(T![::]).into()); - } - elements.push(use_tree_list.syntax().clone().into()); - ted::insert_all_raw(position, elements); - use_tree_list - } - } - } - - /// Splits off the given prefix, making it the path component of the use tree, - /// appending the rest of the path to all UseTreeList items. - /// - /// # Examples - /// - /// `prefix$0::suffix` -> `prefix::{suffix}` - /// - /// `prefix$0` -> `prefix::{self}` - /// - /// `prefix$0::*` -> `prefix::{*}` - pub fn split_prefix(&self, prefix: &ast::Path) { - debug_assert_eq!(self.path(), Some(prefix.top_path())); - let path = self.path().unwrap(); - if &path == prefix && self.use_tree_list().is_none() { - if self.star_token().is_some() { - // path$0::* -> * - if let Some(a) = self.coloncolon_token() { - ted::remove(a) - } - ted::remove(prefix.syntax()); - } else { - // path$0 -> self - let self_suffix = - make::path_unqualified(make::path_segment_self()).clone_for_update(); - ted::replace(path.syntax(), self_suffix.syntax()); - } - } else if split_path_prefix(prefix).is_none() { - return; - } - // At this point, prefix path is detached; _self_ use tree has suffix path. - // Next, transform 'suffix' use tree into 'prefix::{suffix}' - let subtree = self.clone_subtree().clone_for_update(); - ted::remove_all_iter(self.syntax().children_with_tokens()); - ted::insert(ted::Position::first_child_of(self.syntax()), prefix.syntax()); - self.get_or_create_use_tree_list().add_use_tree(subtree); - - fn split_path_prefix(prefix: &ast::Path) -> Option<()> { - let parent = prefix.parent_path()?; - let segment = parent.segment()?; - if algo::has_errors(segment.syntax()) { - return None; - } - for p in successors(parent.parent_path(), |it| it.parent_path()) { - p.segment()?; - } - if let Some(a) = prefix.parent_path().and_then(|p| p.coloncolon_token()) { - ted::remove(a) - } - ted::remove(prefix.syntax()); - Some(()) - } - } - - /// Editor variant of `split_prefix` - pub fn split_prefix_with_editor(&self, editor: &SyntaxEditor, prefix: &ast::Path) { - debug_assert_eq!(self.path(), Some(prefix.top_path())); - - let make = editor.make(); - let path = self.path().unwrap(); - let suffix = if path == *prefix && self.use_tree_list().is_none() { - if self.star_token().is_some() { - make.use_tree_glob() - } else { - let self_path = make.path_unqualified(make.path_segment_self()); - make.use_tree(self_path, None, None, false) - } - } else { - let suffix_segments = path.segments().skip(prefix.segments().count()); - let suffix_path = make.path_from_segments(suffix_segments, false); - make.use_tree( - suffix_path, - self.use_tree_list(), - self.rename(), - self.star_token().is_some(), - ) - }; - let use_tree_list = make.use_tree_list(once(suffix)); - let new_use_tree = make.use_tree(prefix.clone(), Some(use_tree_list), None, false); - - editor.replace(self.syntax(), new_use_tree.syntax()); - } - - /// Wraps the use tree in use tree list with no top level path (if it isn't already). - /// - /// # Examples - /// - /// `foo::bar` -> `{foo::bar}` - /// - /// `{foo::bar}` -> `{foo::bar}` - pub fn wrap_in_tree_list(&self) -> Option<()> { - if self.use_tree_list().is_some() - && self.path().is_none() - && self.star_token().is_none() - && self.rename().is_none() - { - return None; - } - let subtree = self.clone_subtree().clone_for_update(); - ted::remove_all_iter(self.syntax().children_with_tokens()); - ted::append_child( - self.syntax(), - make::use_tree_list(once(subtree)).clone_for_update().syntax(), - ); - Some(()) - } -} - -impl ast::UseTreeList { - pub fn add_use_tree(&self, use_tree: ast::UseTree) { - let (position, elements) = match self.use_trees().last() { - Some(last_tree) => ( - ted::Position::after(last_tree.syntax()), - vec![ - make::token(T![,]).into(), - make::tokens::single_space().into(), - use_tree.syntax.into(), - ], - ), - None => { - let position = match self.l_curly_token() { - Some(l_curly) => ted::Position::after(l_curly), - None => ted::Position::last_child_of(self.syntax()), - }; - (position, vec![use_tree.syntax.into()]) - } - }; - ted::insert_all_raw(position, elements); - } -} - -impl ast::Use { - fn remove(&self, editor: &SyntaxEditor) { - let make = editor.make(); - let next_ws = self - .syntax() - .next_sibling_or_token() - .and_then(|it| it.into_token()) - .and_then(ast::Whitespace::cast); - if let Some(next_ws) = next_ws { - let ws_text = next_ws.syntax().text(); - if let Some(rest) = ws_text.strip_prefix('\n') { - let next_use_removed = next_ws - .syntax() - .next_sibling_or_token() - .and_then(|it| it.into_node()) - .and_then(ast::Use::cast) - .and_then(|use_| use_.use_tree()) - .is_some_and(|use_tree| editor.deleted(use_tree.syntax())); - if rest.is_empty() || next_use_removed { - editor.delete(next_ws.syntax()); - } else { - editor.replace(next_ws.syntax(), make.whitespace(rest)); - } - } - } - let prev_ws = self - .syntax() - .prev_sibling_or_token() - .and_then(|it| it.into_token()) - .and_then(ast::Whitespace::cast); - if let Some(prev_ws) = prev_ws { - let ws_text = prev_ws.syntax().text(); - let prev_newline = ws_text.rfind('\n').map(|x| x + 1).unwrap_or(0); - let rest = &ws_text[0..prev_newline]; - if rest.is_empty() { - editor.delete(prev_ws.syntax()); - } else { - editor.replace(prev_ws.syntax(), make.whitespace(rest)); - } - } - - editor.delete(self.syntax()); - } -} - -impl ast::Impl { - pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList { - if self.assoc_item_list().is_none() { - let assoc_item_list = make::assoc_item_list(None).clone_for_update(); - ted::append_child(self.syntax(), assoc_item_list.syntax()); - } - self.assoc_item_list().unwrap() - } -} - -impl ast::RecordExprField { - /// This will either replace the initializer, or in the case that this is a shorthand convert - /// the initializer into the name ref and insert the expr as the new initializer. - pub fn replace_expr(&self, editor: &SyntaxEditor, expr: ast::Expr) { - if self.name_ref().is_some() { - if let Some(prev) = self.expr() { - editor.replace(prev.syntax(), expr.syntax()); - } - } else if let Some(ast::Expr::PathExpr(path_expr)) = self.expr() - && let Some(path) = path_expr.path() - && let Some(name_ref) = path.as_single_name_ref() - { - // shorthand `{ x }` → expand to `{ x: expr }` - let new_field = editor - .make() - .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr)); - editor.replace(self.syntax(), new_field.syntax()); - } - } -} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index 8411275350542..2a50d233c3bfb 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -264,6 +264,14 @@ impl Expr { return false; } + // Special-case `cond && ` + if let ast::Expr::BinExpr(parent) = parent + && parent.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) + && self.contains_let_expr() + { + return false; + } + let (left, right, inv) = match self.is_ordered_before_parent_in_place_of(parent, place_of) { true => (self, parent, false), false => (parent, self, true), @@ -551,4 +559,19 @@ impl Expr { ForExpr(_) | IfExpr(_) | MatchExpr(_) | WhileExpr(_) | IncludeBytesExpr(_) => true, } } + + fn contains_let_expr(&self) -> bool { + use Expr::*; + + match self { + LetExpr(_) => true, + BinExpr(e) => { + // if we find something other than a `&&`, then this can't be a let chain + e.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) + && (e.lhs().is_none_or(|it| it.contains_let_expr()) + || e.rhs().is_none_or(|it| it.contains_let_expr())) + } + _ => false, + } + } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 2f7eab2423ce5..7f9cd1fce94eb 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -4,8 +4,8 @@ use either::Either; use crate::{ AstNode, Edition, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ - self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, - HasTypeBounds, HasVisibility, Lifetime, Param, RangeItem, make, + self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasModuleItem, + HasName, HasTypeBounds, HasVisibility, Lifetime, Param, RangeItem, make, }, syntax_editor::SyntaxMappingBuilder, }; @@ -2015,6 +2015,35 @@ impl SyntaxFactory { make::assoc_item_list(None).clone_for_update() } + pub fn item_list(&self, items: impl IntoIterator) -> ast::ItemList { + let (items, input) = iterator_input(items); + let items_vec: Vec<_> = items.into_iter().collect(); + let ast = make::item_list(Some(items_vec)).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input, ast.items().map(|item: ast::Item| item.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + + pub fn mod_(&self, name: ast::Name, body: Option) -> ast::Module { + let ast = make::mod_(name.clone(), body.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone()); + if let Some(body) = body { + builder.map_node(body.syntax().clone(), ast.item_list().unwrap().syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast + } + pub fn attr_outer(&self, meta: ast::Meta) -> ast::Attr { let ast = make::attr_outer(meta.clone()).clone_for_update(); diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index cda3e69b7c625..924e72ee40397 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -39,7 +39,6 @@ pub mod ast; pub mod fuzz; pub mod hacks; pub mod syntax_editor; -pub mod ted; pub mod utils; use std::{marker::PhantomData, ops::Range}; diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index f2b979eb9d1ae..9fab8716b412f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -459,13 +459,35 @@ impl Removable for ast::Use { if let Some(next_ws) = next_ws { let ws_text = next_ws.syntax().text(); if let Some(rest) = ws_text.strip_prefix('\n') { - if rest.is_empty() { + let next_use_removed = next_ws + .syntax() + .next_sibling_or_token() + .and_then(|it| it.into_node()) + .and_then(ast::Use::cast) + .and_then(|use_| use_.use_tree()) + .is_some_and(|use_tree| editor.deleted(use_tree.syntax())); + if rest.is_empty() || next_use_removed { editor.delete(next_ws.syntax()); } else { editor.replace(next_ws.syntax(), make.whitespace(rest)); } } } + let prev_ws = self + .syntax() + .prev_sibling_or_token() + .and_then(|it| it.into_token()) + .and_then(ast::Whitespace::cast); + if let Some(prev_ws) = prev_ws { + let ws_text = prev_ws.syntax().text(); + let prev_newline = ws_text.rfind('\n').map(|x| x + 1).unwrap_or(0); + let rest = &ws_text[0..prev_newline]; + if rest.is_empty() { + editor.delete(prev_ws.syntax()); + } else { + editor.replace(prev_ws.syntax(), make.whitespace(rest)); + } + } editor.delete(self.syntax()); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ted.rs b/src/tools/rust-analyzer/crates/syntax/src/ted.rs deleted file mode 100644 index 5c286479c4e3d..0000000000000 --- a/src/tools/rust-analyzer/crates/syntax/src/ted.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! Primitive tree editor, ed for trees. -//! -//! The `_raw`-suffixed functions insert elements as is, unsuffixed versions fix -//! up elements around the edges. -use std::{mem, ops::RangeInclusive}; - -use parser::T; -use rowan::TextSize; - -use crate::{ - SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, - ast::{self, AstNode, edit::IndentLevel, make}, -}; - -/// Utility trait to allow calling `ted` functions with references or owned -/// nodes. Do not use outside of this module. -pub trait Element { - fn syntax_element(self) -> SyntaxElement; -} - -impl Element for &'_ E { - fn syntax_element(self) -> SyntaxElement { - self.clone().syntax_element() - } -} -impl Element for SyntaxElement { - fn syntax_element(self) -> SyntaxElement { - self - } -} -impl Element for SyntaxNode { - fn syntax_element(self) -> SyntaxElement { - self.into() - } -} -impl Element for SyntaxToken { - fn syntax_element(self) -> SyntaxElement { - self.into() - } -} - -#[derive(Debug)] -pub struct Position { - repr: PositionRepr, -} - -#[derive(Debug)] -enum PositionRepr { - FirstChild(SyntaxNode), - After(SyntaxElement), -} - -impl Position { - pub fn after(elem: impl Element) -> Position { - let repr = PositionRepr::After(elem.syntax_element()); - Position { repr } - } - pub fn before(elem: impl Element) -> Position { - let elem = elem.syntax_element(); - let repr = match elem.prev_sibling_or_token() { - Some(it) => PositionRepr::After(it), - None => PositionRepr::FirstChild(elem.parent().unwrap()), - }; - Position { repr } - } - pub fn first_child_of(node: &(impl Into + Clone)) -> Position { - let repr = PositionRepr::FirstChild(node.clone().into()); - Position { repr } - } - pub fn last_child_of(node: &(impl Into + Clone)) -> Position { - let node = node.clone().into(); - let repr = match node.last_child_or_token() { - Some(it) => PositionRepr::After(it), - None => PositionRepr::FirstChild(node), - }; - Position { repr } - } - pub fn offset(&self) -> TextSize { - match &self.repr { - PositionRepr::FirstChild(node) => node.text_range().start(), - PositionRepr::After(elem) => elem.text_range().end(), - } - } -} - -pub fn insert(position: Position, elem: impl Element) { - insert_all(position, vec![elem.syntax_element()]); -} -pub fn insert_raw(position: Position, elem: impl Element) { - insert_all_raw(position, vec![elem.syntax_element()]); -} -pub fn insert_all(position: Position, mut elements: Vec) { - if let Some(first) = elements.first() - && let Some(ws) = ws_before(&position, first) - { - elements.insert(0, ws.into()); - } - if let Some(last) = elements.last() - && let Some(ws) = ws_after(&position, last) - { - elements.push(ws.into()); - } - insert_all_raw(position, elements); -} -pub fn insert_all_raw(position: Position, elements: Vec) { - let (parent, index) = match position.repr { - PositionRepr::FirstChild(parent) => (parent, 0), - PositionRepr::After(child) => (child.parent().unwrap(), child.index() + 1), - }; - parent.splice_children(index..index, elements); -} - -pub fn remove(elem: impl Element) { - elem.syntax_element().detach(); -} -pub fn remove_all(range: RangeInclusive) { - replace_all(range, Vec::new()); -} -pub fn remove_all_iter(range: impl IntoIterator) { - let mut it = range.into_iter(); - if let Some(mut first) = it.next() { - match it.last() { - Some(mut last) => { - if first.index() > last.index() { - mem::swap(&mut first, &mut last); - } - remove_all(first..=last); - } - None => remove(first), - } - } -} - -pub fn replace(old: impl Element, new: impl Element) { - replace_with_many(old, vec![new.syntax_element()]); -} -pub fn replace_with_many(old: impl Element, new: Vec) { - let old = old.syntax_element(); - replace_all(old.clone()..=old, new); -} -pub fn replace_all(range: RangeInclusive, new: Vec) { - let start = range.start().index(); - let end = range.end().index(); - let parent = range.start().parent().unwrap(); - parent.splice_children(start..end + 1, new); -} - -pub fn append_child(node: &(impl Into + Clone), child: impl Element) { - let position = Position::last_child_of(node); - insert(position, child); -} -pub fn append_child_raw(node: &(impl Into + Clone), child: impl Element) { - let position = Position::last_child_of(node); - insert_raw(position, child); -} - -pub fn prepend_child(node: &(impl Into + Clone), child: impl Element) { - let position = Position::first_child_of(node); - insert(position, child); -} - -fn ws_before(position: &Position, new: &SyntaxElement) -> Option { - let prev = match &position.repr { - PositionRepr::FirstChild(_) => return None, - PositionRepr::After(it) => it, - }; - - if prev.kind() == T!['{'] - && new.kind() == SyntaxKind::USE - && let Some(item_list) = prev.parent().and_then(ast::ItemList::cast) - { - let mut indent = IndentLevel::from_element(&item_list.syntax().clone().into()); - indent.0 += 1; - return Some(make::tokens::whitespace(&format!("\n{indent}"))); - } - - if prev.kind() == T!['{'] - && ast::Stmt::can_cast(new.kind()) - && let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast) - { - let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into()); - indent.0 += 1; - return Some(make::tokens::whitespace(&format!("\n{indent}"))); - } - - ws_between(prev, new) -} -fn ws_after(position: &Position, new: &SyntaxElement) -> Option { - let next = match &position.repr { - PositionRepr::FirstChild(parent) => parent.first_child_or_token()?, - PositionRepr::After(sibling) => sibling.next_sibling_or_token()?, - }; - ws_between(new, &next) -} -fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option { - if left.kind() == SyntaxKind::WHITESPACE || right.kind() == SyntaxKind::WHITESPACE { - return None; - } - if right.kind() == T![;] || right.kind() == T![,] { - return None; - } - if left.kind() == T![<] || right.kind() == T![>] { - return None; - } - if left.kind() == T![&] && right.kind() == SyntaxKind::LIFETIME { - return None; - } - if right.kind() == SyntaxKind::GENERIC_ARG_LIST { - return None; - } - - if right.kind() == SyntaxKind::USE { - let mut indent = IndentLevel::from_element(left); - if left.kind() == SyntaxKind::USE { - indent.0 = IndentLevel::from_element(right).0.max(indent.0); - } - return Some(make::tokens::whitespace(&format!("\n{indent}"))); - } - if left.kind() == SyntaxKind::ATTR { - let mut indent = IndentLevel::from_element(right); - if right.kind() == SyntaxKind::ATTR { - indent.0 = IndentLevel::from_element(left).0.max(indent.0); - } - return Some(make::tokens::whitespace(&format!("\n{indent}"))); - } - Some(make::tokens::single_space()) -} diff --git a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml index f561c1c0e2b0e..d0d5840e785a0 100644 --- a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml +++ b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml @@ -13,7 +13,6 @@ rust-version.workspace = true doctest = false [dependencies] -home = "0.5.11" camino.workspace = true [lints] diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index 39319886cfe4a..ea08f7aacac4e 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -119,7 +119,7 @@ fn get_cargo_home() -> Option { return Utf8PathBuf::try_from(PathBuf::from(path)).ok(); } - if let Some(mut path) = home::home_dir() { + if let Some(mut path) = env::home_dir() { path.push(".cargo"); return Utf8PathBuf::try_from(path).ok(); } 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 7bbb9e0258169..76b626b09fb5b 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -446,9 +446,12 @@ verbose form `{ "path": "path::to::item", type: "always" }`. For traits the type "methods" can be used to only exclude the methods but not the trait itself. -For modules the type "subItems" can be used to only exclude the all items in it but not the module +For modules the type "sub_items" can be used to only exclude the all items in it but not the module itself. This does not include items defined in nested modules. +For enums the type "variants" can be used to only exclude the all variants in it but not the enum +itself. + This setting also inherits `#rust-analyzer.completion.excludeTraits#`. diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/README.md b/src/tools/rust-analyzer/docs/book/src/contributing/README.md index bb2b6081ad956..d3e2acd9f7307 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/README.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/README.md @@ -206,13 +206,6 @@ To measure time for from-scratch analysis, use something like this: cargo run --release -p rust-analyzer -- analysis-stats ../chalk/ ``` -For measuring time of incremental analysis, use either of these: - -```bash -cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight ../chalk/chalk-engine/src/logic.rs -cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0 -``` - Look for `fn benchmark_xxx` tests for a quick way to reproduce performance problems. ## Release Process diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 53c5a295ceede..13d32283d2290 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -214,6 +214,11 @@ "title": "Reload workspace", "category": "rust-analyzer" }, + { + "command": "rust-analyzer.newProject", + "title": "Create New Project...", + "category": "rust-analyzer" + }, { "command": "rust-analyzer.rebuildProcMacros", "title": "Rebuild proc macros and build scripts", @@ -491,6 +496,23 @@ "markdownDescription": "Do not start rust-analyzer server when the extension is activated.", "default": false, "type": "boolean" + }, + "rust-analyzer.projectCreation.openAfterCreate": { + "markdownDescription": "Control what happens after `rust-analyzer: Create New Project...` finishes creating a Cargo project.", + "default": "ask", + "enum": [ + "ask", + "open", + "openNewWindow", + "addToWorkspace" + ], + "enumDescriptions": [ + "Prompt for how to open the new project.", + "Open the new project in the current window.", + "Open the new project in a new window.", + "Add the new project to the current workspace, or open it if no workspace is open." + ], + "type": "string" } } }, @@ -1333,7 +1355,7 @@ "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.exclude": { - "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the all items in it but not the module\nitself. This does not include items defined in nested modules.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"sub_items\" can be used to only exclude the all items in it but not the module\nitself. This does not include items defined in nested modules.\n\nFor enums the type \"variants\" can be used to only exclude the all variants in it but not the enum\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", "default": [ { "path": "core::borrow::Borrow", @@ -1361,7 +1383,8 @@ "enum": [ "always", "methods", - "subItems" + "sub_items", + "variants" ], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", @@ -3811,6 +3834,9 @@ "command": "rust-analyzer.memoryUsage", "when": "inRustProject" }, + { + "command": "rust-analyzer.newProject" + }, { "command": "rust-analyzer.reloadWorkspace", "when": "inRustProject" @@ -3930,6 +3956,13 @@ } ] }, + "viewsWelcome": [ + { + "view": "explorer", + "contents": "Create a new Rust project.\n[Create Rust Project](command:rust-analyzer.newProject)", + "when": "workspaceFolderCount == 0" + } + ], "viewsContainers": { "activitybar": [ { @@ -3955,6 +3988,14 @@ "title": "Learn about rust-analyzer", "description": "A brief introduction to get started with rust-analyzer. Learn about key features and resources to help you get the most out of the extension.", "steps": [ + { + "id": "create-project", + "title": "Create a Rust project", + "description": "Start a new Cargo binary or library project from VS Code.\n\n[Create a Rust Project](command:rust-analyzer.newProject)", + "media": { + "markdown": "./walkthrough-create-project.md" + } + }, { "id": "setup", "title": "Useful Setup Tips", diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index 9c8b7071b3094..5514319c698cc 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -33,6 +33,7 @@ import { log } from "./util"; import type { SyntaxElement } from "./syntax_tree_provider"; export * from "./run"; +export { newProject } from "./new_project"; export function analyzerStatus(ctx: CtxInit): Cmd { const tdcp = new (class implements vscode.TextDocumentContentProvider { diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index d65f011c754be..0f783c71b9541 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -447,6 +447,10 @@ export class Config { return this.get("initializeStopped"); } + get projectCreationOpenAfterCreate() { + return this.get("projectCreation.openAfterCreate"); + } + get askBeforeUpdateTest() { return this.get("runnables.askBeforeUpdateTest"); } diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 6567bcd2f170c..7136314e7ca2c 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -161,6 +161,12 @@ function createCommands(): Record { memoryUsage: { enabled: commands.memoryUsage }, reloadWorkspace: { enabled: commands.reloadWorkspace }, rebuildProcMacros: { enabled: commands.rebuildProcMacros }, + newProject: { + // Project creation is a pure VS Code-side workflow and should stay available even in + // empty windows before rust-analyzer has started or a Rust workspace exists. + enabled: commands.newProject, + disabled: commands.newProject, + }, matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, parentModule: { enabled: commands.parentModule }, diff --git a/src/tools/rust-analyzer/editors/code/src/new_project.ts b/src/tools/rust-analyzer/editors/code/src/new_project.ts new file mode 100644 index 0000000000000..f5f6d86da04df --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/new_project.ts @@ -0,0 +1,311 @@ +import * as vscode from "vscode"; + +import type { Ctx, Cmd } from "./ctx"; +import * as ra from "./lsp_ext"; +import { cargoPath } from "./toolchain"; +import { log, spawnAsync } from "./util"; + +type NewProjectKind = "bin" | "lib"; +type NewProjectOpenAction = "open" | "openNewWindow" | "addToWorkspace"; + +type NewProjectTemplate = { + detail: string; + id: NewProjectKind; + label: string; +}; + +type NewProjectCargo = { + cargo: string; + cargoEnv: NodeJS.ProcessEnv; +}; + +const NEW_PROJECT_TEMPLATES: readonly NewProjectTemplate[] = [ + { + id: "bin", + label: "Binary Application", + detail: "Create a Cargo binary package (`cargo new --bin`)", + }, + { + id: "lib", + label: "Library", + detail: "Create a Cargo library package (`cargo new --lib`)", + }, +] as const; + +export function newProject(ctx: Ctx): Cmd { + return async () => { + const cargo = await resolveNewProjectCargo(ctx); + + const selectedKind = await promptForNewProjectTemplate(); + if (!selectedKind) { + return; + } + + const parentFolder = await promptForNewProjectParentFolder(); + if (!parentFolder) { + return; + } + + const projectName = await promptForNewProjectName(parentFolder); + if (!projectName) { + return; + } + + if (!(await createNewProject(cargo, parentFolder, selectedKind, projectName))) { + return; + } + + const projectUri = vscode.Uri.joinPath(parentFolder, projectName); + const defaultAction = determineNewProjectOpenAction( + ctx.config.projectCreationOpenAfterCreate, + Boolean(vscode.workspace.workspaceFolders?.length), + ); + const action = + defaultAction === "ask" + ? await promptForNewProjectOpenAction( + projectName, + Boolean(vscode.workspace.workspaceFolders?.length), + ) + : defaultAction; + + if (action) { + await executeNewProjectOpenAction(ctx, action, projectUri); + } + }; +} + +async function resolveNewProjectCargo(ctx: Ctx): Promise { + // Use the same effective environment rust-analyzer uses elsewhere so project creation sees + // toolchain wrappers, PATH overrides, and CARGO_HOME changes from configuration. + const cargoEnv = { ...process.env, ...ctx.config.serverExtraEnv }; + return { cargo: await cargoPath(cargoEnv), cargoEnv }; +} + +async function promptForNewProjectTemplate(): Promise { + const selected = await vscode.window.showQuickPick(NEW_PROJECT_TEMPLATES, { + placeHolder: "Select a Rust project kind", + }); + return selected?.id; +} + +async function promptForNewProjectParentFolder(): Promise { + const selectedFolder = await vscode.window.showOpenDialog({ + title: "Select the parent folder for the new Rust project", + openLabel: "Select parent folder", + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + }); + if (!selectedFolder?.length) { + return undefined; + } + return selectedFolder[0]; +} + +const CARGO_MANIFEST_NAME_PATTERN = /^[\p{Alphabetic}\p{Number}_-]+$/u; + +// Keep local validation focused on stable checks that can be reported in the input box, then let +// `cargo new` remain the source of truth for package-name-specific identifier and keyword rules. +export function validateNewProjectName( + value: string, + existingNames: readonly string[], +): string | undefined { + const trimmedValue = value.trim(); + if (trimmedValue.length === 0) { + return "Project name cannot be empty."; + } + if (trimmedValue.includes("/") || trimmedValue.includes("\\")) { + return "Project name cannot contain '/' or '\\' characters."; + } + if (trimmedValue === "." || trimmedValue === "..") { + return "Project name cannot be '.' or '..'."; + } + if (!CARGO_MANIFEST_NAME_PATTERN.test(trimmedValue)) { + return "Project name can contain only alphanumeric characters, '-' or '_'."; + } + if (existingNames.includes(trimmedValue)) { + return "A file or folder with this name already exists."; + } + return undefined; +} + +async function promptForNewProjectName(parentFolder: vscode.Uri): Promise { + let existingNames: string[] = []; + try { + const entries = await vscode.workspace.fs.readDirectory(parentFolder); + existingNames = entries.map(([name]) => name); + } catch (error) { + log.error("Failed to read project parent folder", error); + void vscode.window.showErrorMessage("Failed to read the selected parent folder."); + return undefined; + } + + const projectName = await vscode.window.showInputBox({ + prompt: `Enter the new project name to create inside ${parentFolder.fsPath}`, + validateInput: async (value) => validateNewProjectName(value, existingNames), + }); + return projectName?.trim(); +} + +export function cargoNewArgs(kind: NewProjectKind, name: string): string[] { + return ["new", kind === "bin" ? "--bin" : "--lib", name]; +} + +async function createNewProject( + cargo: NewProjectCargo, + parentFolder: vscode.Uri, + kind: NewProjectKind, + projectName: string, +): Promise { + const args = cargoNewArgs(kind, projectName); + const createResult = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: `Creating Rust project ${projectName}`, + }, + async () => + spawnAsync(cargo.cargo, args, { + cwd: parentFolder.fsPath, + env: cargo.cargoEnv, + }), + ); + + if (createResult.error || createResult.status !== 0) { + const details = formatProcessDetails(createResult); + await showNewProjectError("Failed to create Rust project.", details || undefined, { + cargo: cargo.cargo, + args, + cwd: parentFolder.fsPath, + error: createResult.error?.message, + status: createResult.status, + stderr: createResult.stderr || undefined, + stdout: createResult.stdout || undefined, + }); + return false; + } + + return true; +} + +function formatProcessDetails(result: { error?: Error; stderr: string; stdout: string }): string { + return [result.stderr, result.stdout, result.error?.message] + .filter((value): value is string => Boolean(value && value.trim().length > 0)) + .join("\n") + .trim(); +} + +async function showNewProjectError( + message: string, + details: string | undefined, + logContext: { + cargo: string; + args: string[]; + cwd?: string; + error?: string; + status: number | null; + stderr?: string; + stdout?: string; + }, +): Promise { + // Keep command-failure logging focused on the invocation and process output. Environment + // variables may contain secrets such as API keys, tokens, and credentials, so failure logs + // must not dump the merged env here. + const commandLine = [logContext.cargo, ...logContext.args].join(" "); + log.error(message); + log.error(`command: ${commandLine}`); + if (logContext.cwd) { + log.error(`cwd: ${logContext.cwd}`); + } + log.error(`exit status: ${String(logContext.status)}`); + if (logContext.error) { + log.error(`error: ${logContext.error}`); + } + if (logContext.stderr) { + log.error(`stderr:\n${logContext.stderr}`); + } + if (logContext.stdout) { + log.error(`stdout:\n${logContext.stdout}`); + } + const selection = await vscode.window.showErrorMessage( + details ? `${message}\n${details}` : message, + "Open Extension Logs", + ); + if (selection === "Open Extension Logs") { + log.show(); + } +} + +export function determineNewProjectOpenAction( + configuredAction: string | undefined, + hasWorkspaceFolders: boolean, +): "ask" | NewProjectOpenAction { + switch (configuredAction) { + case "open": + case "openNewWindow": + return configuredAction; + case "addToWorkspace": + // Adding to a workspace only makes sense when one is already open. Falling back to + // "open" keeps the setting usable in empty windows without adding another prompt path. + return hasWorkspaceFolders ? configuredAction : "open"; + default: + return "ask"; + } +} + +async function promptForNewProjectOpenAction( + projectName: string, + hasWorkspaceFolders: boolean, +): Promise { + let message = `Would you like to open ${projectName}?`; + const open = "Open"; + const openNewWindow = "Open in New Window"; + const choices = [open, openNewWindow]; + + const addToWorkspace = "Add to VS Code Workspace"; + if (hasWorkspaceFolders) { + message = `Would you like to open ${projectName}, or add it to the current VS Code workspace?`; + choices.push(addToWorkspace); + } + + const result = await vscode.window.showInformationMessage( + message, + { modal: true, detail: "The default action can be configured in settings." }, + ...choices, + ); + + const actionMap: Record = { + [open]: "open", + [openNewWindow]: "openNewWindow", + [addToWorkspace]: "addToWorkspace", + }; + return result ? actionMap[result] : undefined; +} + +async function executeNewProjectOpenAction( + ctx: Ctx, + action: NewProjectOpenAction, + projectUri: vscode.Uri, +): Promise { + if (action === "open") { + await vscode.commands.executeCommand("vscode.openFolder", projectUri, { + forceReuseWindow: true, + }); + return; + } + + if (action === "openNewWindow") { + await vscode.commands.executeCommand("vscode.openFolder", projectUri, { + forceNewWindow: true, + }); + return; + } + + const index = vscode.workspace.workspaceFolders?.length ?? 0; + vscode.workspace.updateWorkspaceFolders(index, 0, { uri: projectUri }); + // Reuse the existing workspace window when requested, but nudge rust-analyzer afterwards so + // the newly added Cargo project is discovered immediately instead of waiting for a later + // background refresh. + if (ctx.client?.isRunning()) { + await ctx.client.sendRequest(ra.reloadWorkspace); + } +} diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts index 76946d1510162..6ae365c71c7ac 100644 --- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -3,7 +3,7 @@ import * as os from "os"; import * as path from "path"; import * as readline from "readline"; import * as vscode from "vscode"; -import { Env, log, memoizeAsync, unwrapUndefinable } from "./util"; +import { Env, isWindows, log, memoizeAsync, unwrapUndefinable } from "./util"; import type { CargoRunnableArgs } from "./lsp_ext"; interface CompilationArtifact { @@ -160,9 +160,46 @@ export function cargoPath(env?: Env): Promise { if (env?.["RUSTC_TOOLCHAIN"]) { return Promise.resolve("cargo"); } + if (env) { + return getPathForExecutableWithEnv("cargo", env); + } return getPathForExecutable("cargo"); } +/** + * Resolves an executable using an explicitly supplied environment instead of the VS Code host + * process environment. + * + * Some extension call sites already construct the exact env they will use for spawning, so path + * resolution needs to honor that same `PATH`/`CARGO_HOME` view to avoid launching a different + * toolchain than the one that was resolved. + */ +async function getPathForExecutableWithEnv( + executableName: "cargo" | "rustc" | "rustup", + env: Env, +): Promise { + const envVar = getEnvVar(env, executableName.toUpperCase()); + if (envVar) { + return envVar; + } + + if (await lookupInPath(executableName, getEnvVar(env, "PATH") ?? "")) { + return executableName; + } + + const cargoHome = getCargoHomeFromPath(getEnvVar(env, "CARGO_HOME")); + if (cargoHome) { + for (const candidate of executableCandidates(executableName)) { + const standardPath = vscode.Uri.joinPath(cargoHome, "bin", candidate); + if (await isFileAtUri(standardPath)) { + return standardPath.fsPath; + } + } + } + + return executableName; +} + /** Mirrors `toolchain::get_path_for_executable()` implementation */ const getPathForExecutable = memoizeAsync( // We apply caching to decrease file-system interactions @@ -172,23 +209,22 @@ const getPathForExecutable = memoizeAsync( if (envVar) return envVar; } - if (await lookupInPath(executableName)) return executableName; + if (await lookupInPath(executableName, process.env["PATH"] ?? "")) return executableName; const cargoHome = getCargoHome(); if (cargoHome) { - const standardPath = vscode.Uri.joinPath(cargoHome, "bin", executableName); - if (await isFileAtUri(standardPath)) return standardPath.fsPath; + for (const candidate of executableCandidates(executableName)) { + const standardPath = vscode.Uri.joinPath(cargoHome, "bin", candidate); + if (await isFileAtUri(standardPath)) return standardPath.fsPath; + } } return executableName; }, ); -async function lookupInPath(exec: string): Promise { - const paths = process.env["PATH"] ?? ""; - +async function lookupInPath(exec: string, paths: string): Promise { const candidates = paths.split(path.delimiter).flatMap((dirInPath) => { - const candidate = path.join(dirInPath, exec); - return os.type() === "Windows_NT" ? [candidate, `${candidate}.exe`] : [candidate]; + return executableCandidates(exec).map((candidate) => path.join(dirInPath, candidate)); }); for await (const isFile of candidates.map(isFileAtPath)) { @@ -199,8 +235,28 @@ async function lookupInPath(exec: string): Promise { return false; } +function executableCandidates(executableName: string): string[] { + // Keep the extension-side probe aligned with `crates/toolchain::probe_for_binary()`, which + // checks both the bare executable name and the platform suffix such as `.exe` on Windows. + // That matters for both PATH lookups and `$CARGO_HOME/bin/` fallbacks. + return isWindows ? [executableName, `${executableName}.exe`] : [executableName]; +} + +function getEnvVar(env: Env, name: string): string | undefined { + if (!isWindows) { + return env[name]; + } + + const foldedName = name.toLowerCase(); + return Object.entries(env).find(([key]) => key.toLowerCase() === foldedName)?.[1]; +} + function getCargoHome(): vscode.Uri | null { const envVar = process.env["CARGO_HOME"]; + return getCargoHomeFromPath(envVar); +} + +function getCargoHomeFromPath(envVar: string | undefined): vscode.Uri | null { if (envVar) return vscode.Uri.file(envVar); try { diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts index 05b475080cbd2..ddeec814b4158 100644 --- a/src/tools/rust-analyzer/editors/code/src/util.ts +++ b/src/tools/rust-analyzer/editors/code/src/util.ts @@ -22,6 +22,10 @@ class Log { log: true, }); + show(): void { + this.output.show(true); + } + trace(...messages: [unknown, ...unknown[]]): void { this.output.trace(this.stringify(messages)); } @@ -40,7 +44,7 @@ class Log { error(...messages: [unknown, ...unknown[]]): void { this.output.error(this.stringify(messages)); - this.output.show(true); + this.show(); } private stringify(messages: unknown[]): string { diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/commands.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/commands.test.ts new file mode 100644 index 0000000000000..900bb2779d565 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/commands.test.ts @@ -0,0 +1,80 @@ +import * as assert from "node:assert/strict"; + +import { + cargoNewArgs, + determineNewProjectOpenAction, + validateNewProjectName, +} from "../../src/new_project"; +import type { Context } from "."; + +export async function getTests(ctx: Context) { + await ctx.suite("New project command", (suite) => { + suite.addTest("rejects empty project name", async () => { + assert.equal(validateNewProjectName("", []), "Project name cannot be empty."); + assert.equal(validateNewProjectName(" ", []), "Project name cannot be empty."); + }); + + suite.addTest("rejects dot project names", async () => { + assert.equal(validateNewProjectName(".", []), "Project name cannot be '.' or '..'."); + assert.equal(validateNewProjectName("..", []), "Project name cannot be '.' or '..'."); + }); + + suite.addTest("rejects path separators", async () => { + assert.equal( + validateNewProjectName("foo/bar", []), + "Project name cannot contain '/' or '\\' characters.", + ); + assert.equal( + validateNewProjectName("foo\\bar", []), + "Project name cannot contain '/' or '\\' characters.", + ); + }); + + suite.addTest("rejects invalid Cargo package name characters", async () => { + assert.equal( + validateNewProjectName("foo.bar", []), + "Project name can contain only alphanumeric characters, '-' or '_'.", + ); + assert.equal( + validateNewProjectName("foo bar", []), + "Project name can contain only alphanumeric characters, '-' or '_'.", + ); + assert.equal( + validateNewProjectName("foo+bar", []), + "Project name can contain only alphanumeric characters, '-' or '_'.", + ); + }); + + suite.addTest("rejects existing child folder collisions", async () => { + assert.equal( + validateNewProjectName("demo", ["demo"]), + "A file or folder with this name already exists.", + ); + }); + + suite.addTest("accepts a normal project name", async () => { + assert.equal(validateNewProjectName("demo-project", []), undefined); + }); + + suite.addTest("resolves addToWorkspace fallback without workspace", async () => { + assert.equal(determineNewProjectOpenAction("addToWorkspace", false), "open"); + }); + + suite.addTest("keeps addToWorkspace when workspace exists", async () => { + assert.equal(determineNewProjectOpenAction("addToWorkspace", true), "addToWorkspace"); + }); + + suite.addTest("defaults to ask for unknown values", async () => { + assert.equal(determineNewProjectOpenAction(undefined, true), "ask"); + assert.equal(determineNewProjectOpenAction("ask", true), "ask"); + }); + + suite.addTest("builds binary cargo args", async () => { + assert.deepEqual(cargoNewArgs("bin", "demo"), ["new", "--bin", "demo"]); + }); + + suite.addTest("builds library cargo args", async () => { + assert.deepEqual(cargoNewArgs("lib", "demo"), ["new", "--lib", "demo"]); + }); + }); +} diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts index eeb8608a42db5..8da62cd241655 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts @@ -1,5 +1,9 @@ import * as assert from "assert"; -import { Cargo } from "../../src/toolchain"; +import * as os from "os"; +import * as path from "path"; +import { mkdtemp, mkdir, rm, writeFile } from "fs/promises"; +import { Cargo, cargoPath } from "../../src/toolchain"; +import { isWindows, normalizeDriveLetter } from "../../src/util"; import type { Context } from "."; export async function getTests(ctx: Context) { @@ -96,4 +100,47 @@ export async function getTests(ctx: Context) { assert.notDeepStrictEqual(args.filter, undefined); }); }); + + await ctx.suite("Toolchain resolution", (suite) => { + suite.addTest("prefers explicit CARGO from provided env", async () => { + const explicitCargo = path.join(os.tmpdir(), "custom-cargo"); + assert.strictEqual(await cargoPath({ CARGO: explicitCargo }), explicitCargo); + }); + + suite.addTest("resolves cargo from provided PATH", async () => { + const tempDir = await mkdtemp(path.join(os.tmpdir(), "ra-cargo-path-")); + try { + const cargoBinary = path.join(tempDir, isWindows ? "cargo.exe" : "cargo"); + await writeFile(cargoBinary, ""); + + const pathKey = isWindows ? "Path" : "PATH"; + assert.strictEqual(await cargoPath({ [pathKey]: tempDir }), "cargo"); + } finally { + await rm(tempDir, { recursive: true, force: true }); + } + }); + + suite.addTest("resolves cargo from provided CARGO_HOME", async () => { + const cargoHome = await mkdtemp(path.join(os.tmpdir(), "ra-cargo-home-")); + try { + const binDir = path.join(cargoHome, "bin"); + await mkdir(binDir); + const cargoBinary = path.join(binDir, isWindows ? "cargo.exe" : "cargo"); + await writeFile(cargoBinary, ""); + + assert.strictEqual( + normalizeComparablePath(await cargoPath({ PATH: "", CARGO_HOME: cargoHome })), + normalizeComparablePath(cargoBinary), + ); + } finally { + await rm(cargoHome, { recursive: true, force: true }); + } + }); + }); +} + +function normalizeComparablePath(filePath: string): string { + // Windows path comparisons should ignore drive-letter casing because `fsPath` + // may normalize it differently while still pointing to the same file. + return normalizeDriveLetter(filePath, isWindows); } diff --git a/src/tools/rust-analyzer/editors/code/walkthrough-create-project.md b/src/tools/rust-analyzer/editors/code/walkthrough-create-project.md new file mode 100644 index 0000000000000..34c04cdd1db4a --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/walkthrough-create-project.md @@ -0,0 +1,9 @@ +# Create a New Cargo Project + +Create a new Cargo project without leaving VS Code. + +The command lets you choose the package kind, parent folder, project name, and +how the new project should be opened afterward. + +The project is created with Cargo, so the generated files and defaults match +the normal `cargo new` experience. diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 387bd8edd2196..5db47ca8fc59b 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -029c9e18dd1f4668e1d42bb187c1c263dfe20093 +942ac9ce4116d4ea784c9882659372b34978b1f8 diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index b63f4d46139c7..db6008da59b34 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2442,7 +2442,6 @@ ui/span/issue-42234-unknown-receiver-type.rs ui/span/issue-43927-non-ADT-derive.rs ui/span/issue-71363.rs ui/span/issue-81800.rs -ui/span/issue28498-reject-ex1.rs ui/span/issue28498-reject-lifetime-param.rs ui/span/issue28498-reject-passed-to-fn.rs ui/span/issue28498-reject-trait-bound.rs diff --git a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs index 1177c1e68ed56..226f177dfb093 100644 --- a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs +++ b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs @@ -2,6 +2,9 @@ //@ compile-flags: -Copt-level=3 //@ only-aarch64 //@ only-linux +// aarch64-unknown-linux-pauthtest requires armv8.3-a, which includes Large System Extensions, +// providing hardware implementations of atomic operations. +//@ ignore-pauthtest #![crate_type = "rlib"] diff --git a/tests/assembly-llvm/asm/loongarch-modifiers.rs b/tests/assembly-llvm/asm/loongarch-modifiers.rs new file mode 100644 index 0000000000000..93a58864ef451 --- /dev/null +++ b/tests/assembly-llvm/asm/loongarch-modifiers.rs @@ -0,0 +1,225 @@ +//@ add-minicore +//@ revisions: loongarch32 loongarch64 + +//@ assembly-output: emit-asm +//@ needs-llvm-components: loongarch + +//@[loongarch32] compile-flags: --target loongarch32-unknown-none +//@[loongarch32] compile-flags: -Ctarget-feature=+32s,+lasx + +//@[loongarch64] compile-flags: --target loongarch64-unknown-none +//@[loongarch64] compile-flags: -Ctarget-feature=+lasx + +//@ compile-flags: -Ctarget-feature=+32s +//@ compile-flags: -Zmerge-functions=disabled + +#![feature(asm_experimental_reg, no_core, repr_simd)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register)] + +extern crate minicore; +use minicore::*; + +#[repr(simd)] +pub struct i8x16([i8; 16]); +#[repr(simd)] +pub struct i16x8([i16; 8]); +#[repr(simd)] +pub struct i32x4([i32; 4]); +#[repr(simd)] +pub struct i64x2([i64; 2]); +#[repr(simd)] +pub struct f32x4([f32; 4]); +#[repr(simd)] +pub struct f64x2([f64; 2]); +#[repr(simd)] +pub struct i8x32([i8; 32]); +#[repr(simd)] +pub struct i16x16([i16; 16]); +#[repr(simd)] +pub struct i32x8([i32; 8]); +#[repr(simd)] +pub struct i64x4([i64; 4]); +#[repr(simd)] +pub struct f32x8([f32; 8]); +#[repr(simd)] +pub struct f64x4([f64; 4]); + +impl Copy for i8x16 {} +impl Copy for i16x8 {} +impl Copy for i32x4 {} +impl Copy for i64x2 {} +impl Copy for f32x4 {} +impl Copy for f64x2 {} +impl Copy for i8x32 {} +impl Copy for i16x16 {} +impl Copy for i32x8 {} +impl Copy for i64x4 {} +impl Copy for f32x8 {} +impl Copy for f64x4 {} + +macro_rules! check { ($func:ident, $ty:ty, $class:ident, $code:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + let y; + asm!($code, out($class) y, in($class) x); + y + } +};} + +// CHECK-LABEL: freg_f32_lsx: +// CHECK: #APP +// CHECK: vfadd.s $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(freg_f32_lsx, f32, freg, "vfadd.s {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: freg_f64_lasx: +// CHECK: #APP +// CHECK: xvfadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(freg_f64_lasx, f64, freg, "xvfadd.d {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_i8x16_lsx: +// CHECK: #APP +// CHECK: vadd.b $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i8x16_lsx, i8x16, vreg, "vadd.b {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_i8x16_lasx: +// CHECK: #APP +// CHECK: xvadd.b $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i8x16_lasx, i8x16, vreg, "xvadd.b {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_i16x8_lsx: +// CHECK: #APP +// CHECK: vadd.h $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i16x8_lsx, i16x8, vreg, "vadd.h {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_i16x8_lasx: +// CHECK: #APP +// CHECK: xvadd.h $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i16x8_lasx, i16x8, vreg, "xvadd.h {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_i32x4_lsx: +// CHECK: #APP +// CHECK: vadd.w $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i32x4_lsx, i32x4, vreg, "vadd.w {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_i32x4_lasx: +// CHECK: #APP +// CHECK: xvadd.w $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i32x4_lasx, i32x4, vreg, "xvadd.w {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_i64x2_lsx: +// CHECK: #APP +// CHECK: vadd.d $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i64x2_lsx, i64x2, vreg, "vadd.d {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_i64x2_lasx: +// CHECK: #APP +// CHECK: xvadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i64x2_lasx, i64x2, vreg, "xvadd.d {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_f32x4_lsx: +// CHECK: #APP +// CHECK: vfadd.s $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_f32x4_lsx, f32x4, vreg, "vfadd.s {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_f32x4_lasx: +// CHECK: #APP +// CHECK: xvfadd.s $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_f32x4_lasx, f32x4, vreg, "xvfadd.s {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_f64x2_lsx: +// CHECK: #APP +// CHECK: vfadd.d $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_f64x2_lsx, f64x2, vreg, "vfadd.d {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_f64x2_lasx: +// CHECK: #APP +// CHECK: xvfadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_f64x2_lasx, f64x2, vreg, "xvfadd.d {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: xreg_i8x32_lasx: +// CHECK: #APP +// CHECK: xvadd.b $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i8x32_lasx, i8x32, xreg, "xvadd.b {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_i8x32_lsx: +// CHECK: #APP +// CHECK: vadd.b $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i8x32_lsx, i8x32, xreg, "vadd.b {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_i16x16_lasx: +// CHECK: #APP +// CHECK: xvadd.h $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i16x16_lasx, i16x16, xreg, "xvadd.h {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_i16x16_lsx: +// CHECK: #APP +// CHECK: vadd.h $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i16x16_lsx, i16x16, xreg, "vadd.h {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_i32x8_lasx: +// CHECK: #APP +// CHECK: xvadd.w $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i32x8_lasx, i32x8, xreg, "xvadd.w {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_i32x8_lsx: +// CHECK: #APP +// CHECK: vadd.w $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i32x8_lsx, i32x8, xreg, "vadd.w {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_i64x4_lasx: +// CHECK: #APP +// CHECK: xvadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i64x4_lasx, i64x4, xreg, "xvadd.d {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_i64x4_lsx: +// CHECK: #APP +// CHECK: vadd.d $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i64x4_lsx, i64x4, xreg, "vadd.d {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_f32x8_lasx: +// CHECK: #APP +// CHECK: xvfadd.s $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_f32x8_lasx, f32x8, xreg, "xvfadd.s {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_f32x8_lsx: +// CHECK: #APP +// CHECK: vfadd.s $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_f32x8_lsx, f32x8, xreg, "vfadd.s {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_f64x4_lasx: +// CHECK: #APP +// CHECK: xvfadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_f64x4_lasx, f64x4, xreg, "xvfadd.d {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_f64x4_lsx: +// CHECK: #APP +// CHECK: vfadd.d $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_f64x4_lsx, f64x4, xreg, "vfadd.d {1:w}, {0:w}, {0:w}"); diff --git a/tests/assembly-llvm/cmse-clear-padding.rs b/tests/assembly-llvm/cmse-clear-padding.rs new file mode 100644 index 0000000000000..b092bf304dcca --- /dev/null +++ b/tests/assembly-llvm/cmse-clear-padding.rs @@ -0,0 +1,204 @@ +//@ add-minicore +//@ min-llvm-version: 22 +//@ assembly-output: emit-asm +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -Copt-level=1 +//@ needs-llvm-components: arm +#![crate_type = "lib"] +#![feature(abi_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items)] +#![no_core] + +// Test that padding and other uninitialized bytes are zeroed when a value crosses the secure +// boundary. +// +// The assembly uses the following instructions for clearing the bits: +// +// - `uxtb` clears bits 8..32 +// - `uxth` clears bits 16..32 +// - `bic` clears bits based on a mask +// +// When passing arguments the current implementation of the C ABI already clears padding sometimes, +// but it's not something that can be relied on. + +extern crate minicore; +use minicore::*; + +#[repr(C)] +struct InnerPadding { + a: u8, + b: u16, +} + +// CHECK-LABEL: c_ret_with_inner_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +extern "C" fn c_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { + InnerPadding { a, b } +} + +// CHECK-LABEL: cmse_ret_with_inner_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +extern "cmse-nonsecure-entry" fn cmse_ret_with_inner_padding(a: u8, b: u16) -> InnerPadding { + InnerPadding { a, b } +} + +// CHECK-LABEL: c_call_with_inner_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r2, r0 +// CHECK-NEXT: bic r0, r1, #65280 +// CHECK-NEXT: pop.w {r7, lr} +#[no_mangle] +extern "C" fn c_call_with_inner_padding(f: unsafe extern "C" fn(InnerPadding), x: InnerPadding) { + unsafe { f(x) } +} + +// CHECK-LABEL: cmse_call_with_inner_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r2, r0 +// CHECK-NEXT: bic r0, r1, #65280 +// CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11} +#[no_mangle] +extern "C" fn cmse_call_with_inner_padding( + f: unsafe extern "cmse-nonsecure-call" fn(InnerPadding), + x: InnerPadding, +) { + unsafe { f(x) } +} + +#[repr(C)] +struct TrailingPadding { + a: u16, + b: u8, +} + +// CHECK-LABEL: c_ret_with_trailing_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +extern "C" fn c_ret_with_trailing_padding(a: u16, b: u8) -> TrailingPadding { + TrailingPadding { a, b } +} + +// CHECK-LABEL: cmse_ret_with_trailing_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r1, r1 +// CHECK-NEXT: uxth r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +extern "cmse-nonsecure-entry" fn cmse_ret_with_trailing_padding(a: u16, b: u8) -> TrailingPadding { + TrailingPadding { a, b } +} + +// CHECK-LABEL: c_call_with_trailing_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r2, r0 +// CHECK-NEXT: bic r0, r1, #-16777216 +// CHECK-NEXT: pop.w {r7, lr} +#[no_mangle] +extern "C" fn c_call_with_trailing_padding( + f: unsafe extern "C" fn(TrailingPadding), + x: TrailingPadding, +) { + unsafe { f(x) } +} + +// CHECK-LABEL: cmse_call_with_trailing_padding: +// CHECK: mov r7, sp +// CHECK-NEXT: mov r2, r0 +// CHECK-NEXT: bic r0, r1, #-16777216 +// CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11} +#[no_mangle] +extern "C" fn cmse_call_with_trailing_padding( + f: unsafe extern "cmse-nonsecure-call" fn(TrailingPadding), + x: TrailingPadding, +) { + unsafe { f(x) } +} + +#[repr(C, align(2))] +struct WideU8 { + a: u8, +} + +// CHECK-LABEL: c_ret_with_wide_u8: +// CHECK: mov r7, sp +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +extern "C" fn c_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { + [WideU8 { a }, WideU8 { a: b }] +} + +// CHECK-LABEL: cmse_ret_with_wide_u8: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r1, r1 +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +#[no_mangle] +extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8(a: u8, b: u8) -> [WideU8; 2] { + [WideU8 { a }, WideU8 { a: b }] +} + +// CHECK-LABEL: c_call_with_inner_wide_u8: +// CHECK: push {r7, lr} +// CHECK-NEXT: .setfp r7, sp +// CHECK-NEXT: mov r7, sp +// CHECK-NEXT: mov lr, r3 +// CHECK-NEXT: mov r12, r0 +// CHECK-NEXT: ldr r3, [r7, #8] +// CHECK-NEXT: mov r0, r1 +// CHECK-NEXT: mov r1, r2 +// CHECK-NEXT: mov r2, lr +// CHECK-NEXT: pop.w {r7, lr} +// CHECK-NEXT: bx r12 +#[no_mangle] +extern "C" fn c_call_with_inner_wide_u8(f: unsafe extern "C" fn([WideU8; 8]), x: [WideU8; 8]) { + unsafe { f(x) } +} + +// CHECK-LABEL: cmse_call_with_inner_wide_u8: +// CHECK: push {r7, lr} +// CHECK-NEXT: .setfp r7, sp +// CHECK-NEXT: mov r7, sp +// CHECK-NEXT: mov r12, r0 +// CHECK-NEXT: bic r0, r1, #-16711936 +// CHECK-NEXT: bic r1, r2, #-16711936 +// CHECK-NEXT: bic r2, r3, #-16711936 +// CHECK-NEXT: ldr r3, [r7, #8] +// CHECK-NEXT: bic r3, r3, #-16711936 +// CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11} +#[no_mangle] +extern "C" fn cmse_call_with_inner_wide_u8( + f: unsafe extern "cmse-nonsecure-call" fn([WideU8; 8]), + x: [WideU8; 8], +) { + unsafe { f(x) } +} + +// CHECK-LABEL: cmse_ret_with_wide_u8_uninit: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +// CHECK-NEXT: bic r0, r0, #-16711936 +#[no_mangle] +extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit( + a: u16, + b: u16, +) -> [MaybeUninit; 2] { + unsafe { [mem::transmute(a), mem::transmute(b)] } +} + +// CHECK-LABEL: cmse_ret_with_wide_u8_uninit_tuple: +// CHECK: mov r7, sp +// CHECK-NEXT: uxtb r0, r0 +// CHECK-NEXT: orr.w r0, r0, r1, lsl #16 +// CHECK-NEXT: bic r0, r0, #-16711936 +#[no_mangle] +extern "cmse-nonsecure-entry" fn cmse_ret_with_wide_u8_uninit_tuple( + a: u16, + b: u16, +) -> (MaybeUninit, MaybeUninit) { + unsafe { (mem::transmute(a), mem::transmute(b)) } +} diff --git a/tests/assembly-llvm/fentry.rs b/tests/assembly-llvm/fentry.rs new file mode 100644 index 0000000000000..598db8fb150df --- /dev/null +++ b/tests/assembly-llvm/fentry.rs @@ -0,0 +1,25 @@ +//@ assembly-output: emit-asm +//@ compile-flags: -Zinstrument-mcount=fentry +//@ add-minicore + +//@ revisions: X86 S390X +//@[X86] compile-flags: --target=x86_64-unknown-linux-gnu -Cllvm-args=-x86-asm-syntax=intel +//@[X86] needs-llvm-components: x86 +//@[S390X] compile-flags: --target=s390x-unknown-linux-gnu +//@[S390X] needs-llvm-components: systemz + +#![crate_type = "lib"] +#![feature(no_core)] +#![no_core] + +extern crate minicore; + +// CHECK-LABEL: mcount_func: +#[no_mangle] +pub fn mcount_func(a: isize, b: isize) -> isize { + // X86: call __fentry__ + // S390X: brasl %r0, __fentry__@PLT + a + b + // X86: ret + // S390X: br %r14 +} diff --git a/tests/assembly-llvm/pauth-basic.rs b/tests/assembly-llvm/pauth-basic.rs new file mode 100644 index 0000000000000..e240e1317f3a3 --- /dev/null +++ b/tests/assembly-llvm/pauth-basic.rs @@ -0,0 +1,44 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ only-pauthtest +//@ revisions: aarch64_unknown_linux_pauthtest +//@ [aarch64_unknown_linux_pauthtest] compile-flags: --target=aarch64-unknown-linux-pauthtest +//@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64 + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; + +#[no_mangle] +#[inline(never)] +pub extern "C" fn c_func(a: i32) -> i32 { + a +} + +#[no_mangle] +#[inline(never)] +fn call_through(f: extern "C" fn(i32) -> i32, x: i32) -> i32 { + f(x) +} + +#[no_mangle] +#[inline(never)] +pub fn call_c_func(x: i32) -> i32 { + call_through(c_func, x) +} + +// CHECK-LABEL: call_through: +// CHECK: mov [[PTR:x[0-9]+]], x0 +// CHECK: mov w0, w1 +// CHECK: braaz [[PTR]] + +// CHECK-LABEL: call_c_func: +// CHECK: adrp [[GOT_REG:x[0-9]+]], :got:c_func +// CHECK: ldr [[GOT_REG]], [[[GOT_REG]], :got_lo12:c_func] +// CHECK: paciza [[FN_REG:x[0-9]+]] +// CHECK: mov w1, w0 +// CHECK: mov x0, [[FN_REG]] +// CHECK: b call_through diff --git a/tests/assembly-llvm/s390x-vector-abi.rs b/tests/assembly-llvm/s390x-vector-abi.rs index 90139df17ca1a..c0f770c3f0c35 100644 --- a/tests/assembly-llvm/s390x-vector-abi.rs +++ b/tests/assembly-llvm/s390x-vector-abi.rs @@ -62,16 +62,11 @@ unsafe extern "C" fn vector_ret(x: &i8x16) -> i8x16 { *x } // CHECK-LABEL: vector_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 0(%r3), 4 -// z13-NEXT: vl %v1, 16(%r3), 4 -// z13-NEXT: vst %v1, 16(%r2), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_ret_large(x: &i8x32) -> i8x32 { @@ -95,16 +90,11 @@ unsafe extern "C" fn vector_wrapper_ret(x: &Wrapper) -> Wrapper { *x } // CHECK-LABEL: vector_wrapper_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 16(%r3), 4 -// z13-NEXT: vst %v0, 16(%r2), 4 -// z13-NEXT: vl %v0, 0(%r3), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_ret_large(x: &Wrapper) -> Wrapper { @@ -141,16 +131,11 @@ unsafe extern "C" fn vector_wrapper_with_zst_ret( *x } // CHECK-LABEL: vector_wrapper_with_zst_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 16(%r3), 4 -// z13-NEXT: vst %v0, 16(%r2), 4 -// z13-NEXT: vl %v0, 0(%r3), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_with_zst_ret_large( @@ -180,16 +165,11 @@ unsafe extern "C" fn vector_transparent_wrapper_ret( *x } // CHECK-LABEL: vector_transparent_wrapper_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 0(%r3), 4 -// z13-NEXT: vl %v1, 16(%r3), 4 -// z13-NEXT: vst %v1, 16(%r2), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_transparent_wrapper_ret_large( diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index 7cc7cb037d4ce..92977143e1f7a 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -28,6 +28,9 @@ //@ revisions: aarch64_nintendo_switch_freestanding //@ [aarch64_nintendo_switch_freestanding] compile-flags: --target aarch64-nintendo-switch-freestanding //@ [aarch64_nintendo_switch_freestanding] needs-llvm-components: aarch64 +//@ revisions: aarch64_oe_linux_gnu +//@ [aarch64_oe_linux_gnu] compile-flags: --target aarch64-oe-linux-gnu +//@ [aarch64_oe_linux_gnu] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_freebsd //@ [aarch64_unknown_freebsd] compile-flags: --target aarch64-unknown-freebsd //@ [aarch64_unknown_freebsd] needs-llvm-components: aarch64 @@ -55,6 +58,9 @@ //@ revisions: aarch64_unknown_linux_ohos //@ [aarch64_unknown_linux_ohos] compile-flags: --target aarch64-unknown-linux-ohos //@ [aarch64_unknown_linux_ohos] needs-llvm-components: aarch64 +//@ revisions: aarch64_unknown_linux_pauthtest +//@ [aarch64_unknown_linux_pauthtest] compile-flags: --target aarch64-unknown-linux-pauthtest +//@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_managarm_mlibc //@ [aarch64_unknown_managarm_mlibc] compile-flags: --target aarch64-unknown-managarm-mlibc //@ [aarch64_unknown_managarm_mlibc] needs-llvm-components: aarch64 @@ -82,9 +88,9 @@ //@ revisions: aarch64_unknown_nto_qnx710_iosock //@ [aarch64_unknown_nto_qnx710_iosock] compile-flags: --target aarch64-unknown-nto-qnx710_iosock //@ [aarch64_unknown_nto_qnx710_iosock] needs-llvm-components: aarch64 -//@ revisions: aarch64_unknown_nto_qnx800 -//@ [aarch64_unknown_nto_qnx800] compile-flags: --target aarch64-unknown-nto-qnx800 -//@ [aarch64_unknown_nto_qnx800] needs-llvm-components: aarch64 +//@ revisions: aarch64_unknown_qnx +//@ [aarch64_unknown_qnx] compile-flags: --target aarch64-unknown-qnx +//@ [aarch64_unknown_qnx] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_openbsd //@ [aarch64_unknown_openbsd] compile-flags: --target aarch64-unknown-openbsd //@ [aarch64_unknown_openbsd] needs-llvm-components: aarch64 @@ -163,6 +169,9 @@ //@ revisions: armv7_linux_androideabi //@ [armv7_linux_androideabi] compile-flags: --target armv7-linux-androideabi //@ [armv7_linux_androideabi] needs-llvm-components: arm +//@ revisions: armv7_oe_linux_gnueabihf +//@ [armv7_oe_linux_gnueabihf] compile-flags: --target armv7-oe-linux-gnueabihf +//@ [armv7_oe_linux_gnueabihf] needs-llvm-components: arm //@ revisions: armv7_rtems_eabihf //@ [armv7_rtems_eabihf] compile-flags: --target armv7-rtems-eabihf //@ [armv7_rtems_eabihf] needs-llvm-components: arm @@ -268,6 +277,9 @@ //@ revisions: i686_linux_android //@ [i686_linux_android] compile-flags: --target i686-linux-android //@ [i686_linux_android] needs-llvm-components: x86 +//@ revisions: i686_oe_linux_gnu +//@ [i686_oe_linux_gnu] compile-flags: --target i686-oe-linux-gnu +//@ [i686_oe_linux_gnu] needs-llvm-components: x86 //@ revisions: i686_unknown_freebsd //@ [i686_unknown_freebsd] compile-flags: --target i686-unknown-freebsd //@ [i686_unknown_freebsd] needs-llvm-components: x86 @@ -499,6 +511,9 @@ //@ revisions: riscv64_linux_android //@ [riscv64_linux_android] compile-flags: --target riscv64-linux-android //@ [riscv64_linux_android] needs-llvm-components: riscv +//@ revisions: riscv64_oe_linux_gnu +//@ [riscv64_oe_linux_gnu] compile-flags: --target riscv64-oe-linux-gnu +//@ [riscv64_oe_linux_gnu] needs-llvm-components: riscv //@ revisions: riscv64_wrs_vxworks //@ [riscv64_wrs_vxworks] compile-flags: --target riscv64-wrs-vxworks //@ [riscv64_wrs_vxworks] needs-llvm-components: riscv @@ -661,15 +676,18 @@ //@ revisions: x86_64_lynx_lynxos178 //@ [x86_64_lynx_lynxos178] compile-flags: --target x86_64-lynx-lynxos178 //@ [x86_64_lynx_lynxos178] needs-llvm-components: x86 +//@ revisions: x86_64_oe_linux_gnu +//@ [x86_64_oe_linux_gnu] compile-flags: --target x86_64-oe-linux-gnu +//@ [x86_64_oe_linux_gnu] needs-llvm-components: x86 //@ revisions: x86_64_pc_nto_qnx710 //@ [x86_64_pc_nto_qnx710] compile-flags: --target x86_64-pc-nto-qnx710 //@ [x86_64_pc_nto_qnx710] needs-llvm-components: x86 //@ revisions: x86_64_pc_nto_qnx710_iosock //@ [x86_64_pc_nto_qnx710_iosock] compile-flags: --target x86_64-pc-nto-qnx710_iosock //@ [x86_64_pc_nto_qnx710_iosock] needs-llvm-components: x86 -//@ revisions: x86_64_pc_nto_qnx800 -//@ [x86_64_pc_nto_qnx800] compile-flags: --target x86_64-pc-nto-qnx800 -//@ [x86_64_pc_nto_qnx800] needs-llvm-components: x86 +//@ revisions: x86_64_pc_qnx +//@ [x86_64_pc_qnx] compile-flags: --target x86_64-pc-qnx +//@ [x86_64_pc_qnx] needs-llvm-components: x86 //@ revisions: x86_64_pc_solaris //@ [x86_64_pc_solaris] compile-flags: --target x86_64-pc-solaris //@ [x86_64_pc_solaris] needs-llvm-components: x86 diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index c949d8861975b..e50b9b785ad48 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -8,7 +8,10 @@ //! items. For identical error output, any `diagnostic` attributes (e.g. `on_unimplemented`) //! should also be replicated here. //! - Be careful of adding new features and things that are only available for a subset of targets. +//! - `Sync` is only provided such that the minimal set of impls required by tests is met (not +//! exhaustive covering of all possible function pointer signatures). //! + //! # References //! //! This is partially adapted from `rustc_codegen_cranelift`: @@ -299,6 +302,16 @@ impl_marker_trait!( impl Sync for () {} impl Sync for [T; N] {} +// Function pointers are treated as `Sync` to match real `core` behavior. +// +// Minicore provides only the minimal set of impls required by tests. Rather +// than exhaustively covering all possible function pointer signatures, +// additional impls should be added as needed. +impl Sync for fn() -> R {} +impl Sync for extern "C" fn() -> R {} +impl Sync for unsafe extern "C" fn() -> R {} +impl Sync for extern "C" fn(A) -> R {} +impl Sync for unsafe extern "C" fn(A) -> R {} #[lang = "drop_glue"] fn drop_glue(_: &mut T) {} @@ -367,6 +380,16 @@ pub mod ptr { } } +pub mod hint { + #[inline] + pub fn black_box(dummy: T) -> T { + #[rustc_intrinsic] + fn black_box(dummy: T) -> T; + + unsafe { black_box(dummy) } + } +} + #[lang = "c_void"] #[repr(u8)] pub enum c_void { diff --git a/tests/codegen-llvm/box-uninit-bytes.rs b/tests/codegen-llvm/box-uninit-bytes.rs index 7ac929646cd4d..fc47e42d6f9db 100644 --- a/tests/codegen-llvm/box-uninit-bytes.rs +++ b/tests/codegen-llvm/box-uninit-bytes.rs @@ -43,4 +43,4 @@ pub fn box_lotsa_padding() -> Box { // from the CHECK-NOT above, and also verify the attributes got set reasonably. // CHECK: declare {{(dso_local )?}}noalias noundef ptr @{{.*}}__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef range(i{{[0-9]+}} 1, {{-2147483647|-9223372036854775807}})) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]] -// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) {{(uwtable )?}}"alloc-family"="__rust_alloc" {{.*}} } +// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned"){{.*}} allocsize(0) {{(uwtable )?}}{{.*}}"alloc-family"="__rust_alloc" {{.*}} } diff --git a/tests/codegen-llvm/cffi/c-variadic.rs b/tests/codegen-llvm/cffi/c-variadic.rs index ceef7e72251d9..63ed4b211a9a2 100644 --- a/tests/codegen-llvm/cffi/c-variadic.rs +++ b/tests/codegen-llvm/cffi/c-variadic.rs @@ -1,5 +1,9 @@ //@ needs-unwind //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 +// Pauthtest generates pointer authentication metadata for call instructions +// and wraps function pointers in ConstPtrAuth. Disable this test for this target +// to avoid clutter from pointer authentication complexity. +//@ ignore-pauthtest #![crate_type = "lib"] #![feature(c_variadic)] diff --git a/tests/codegen-llvm/i128-x86-align.rs b/tests/codegen-llvm/i128-x86-align.rs index 75802b0c5056a..998821d17e80c 100644 --- a/tests/codegen-llvm/i128-x86-align.rs +++ b/tests/codegen-llvm/i128-x86-align.rs @@ -5,8 +5,6 @@ // while rustc wants it to have 16 byte alignment. This test checks that we handle this // correctly. -// CHECK: %ScalarPair = type { i32, [3 x i32], i128 } - #![feature(core_intrinsics)] #[repr(C)] @@ -62,8 +60,8 @@ pub fn load_volatile(x: &ScalarPair) -> ScalarPair { // CHECK-SAME: dereferenceable(32) %_0, // CHECK-SAME: align 16 // CHECK-SAME: dereferenceable(32) %x - // CHECK: [[LOAD:%.*]] = load volatile %ScalarPair, ptr %x, align 16 - // CHECK-NEXT: store %ScalarPair [[LOAD]], ptr %_0, align 16 + // CHECK: [[LOAD:%.*]] = load volatile i256, ptr %x, align 16 + // CHECK-NEXT: store i256 [[LOAD]], ptr %_0, align 16 // CHECK-NEXT: ret void unsafe { std::intrinsics::volatile_load(x) } } diff --git a/tests/codegen-llvm/ilog_known_base.rs b/tests/codegen-llvm/ilog_known_base.rs index 3f0cdd551c28b..d37e9c49ec474 100644 --- a/tests/codegen-llvm/ilog_known_base.rs +++ b/tests/codegen-llvm/ilog_known_base.rs @@ -7,7 +7,7 @@ // CHECK-LABEL: @checked_ilog2 #[no_mangle] pub fn checked_ilog2(val: u32) -> Option { - // CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0 + // CHECK: icmp {{ne|eq}} i32 %val, 0 // CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true) // CHECK: %[[LOG2:.+]] = xor i32 %[[CTZ]], 31 val.checked_ilog(2) @@ -17,7 +17,7 @@ pub fn checked_ilog2(val: u32) -> Option { // CHECK-LABEL: @checked_ilog4 #[no_mangle] pub fn checked_ilog4(val: u32) -> Option { - // CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0 + // CHECK: icmp {{ne|eq}} i32 %val, 0 // CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true) // CHECK: %[[DIV2:.+]] = lshr i32 %[[CTZ]], 1 // CHECK: %[[LOG4:.+]] = xor i32 %[[DIV2]], 15 @@ -28,9 +28,9 @@ pub fn checked_ilog4(val: u32) -> Option { // CHECK-LABEL: @checked_ilog16 #[no_mangle] pub fn checked_ilog16(val: u32) -> Option { - // CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0 + // CHECK: icmp {{ne|eq}} i32 %val, 0 // CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true) // CHECK: %[[DIV4:.+]] = lshr i32 %[[CTZ]], 2 - // CHECK: %[[LOG16:.+]] = xor i32 %[[DIV2]], 7 + // CHECK: %[[LOG16:.+]] = xor i32 %[[DIV4]], 7 val.checked_ilog(16) } diff --git a/tests/codegen-llvm/inline-always-works-always.rs b/tests/codegen-llvm/inline-always-works-always.rs index 07200fd9e373a..e3dfbc819f9af 100644 --- a/tests/codegen-llvm/inline-always-works-always.rs +++ b/tests/codegen-llvm/inline-always-works-always.rs @@ -2,6 +2,8 @@ //@[NO-OPT] compile-flags: -Copt-level=0 //@[SIZE-OPT] compile-flags: -Copt-level=s //@[SPEED-OPT] compile-flags: -Copt-level=3 +// Pointer authenticated calls are not guaranteed to be inlined. +//@ ignore-pauthtest #![crate_type = "rlib"] diff --git a/tests/codegen-llvm/instrument-fentry.rs b/tests/codegen-llvm/instrument-fentry.rs new file mode 100644 index 0000000000000..57cb29d7fc426 --- /dev/null +++ b/tests/codegen-llvm/instrument-fentry.rs @@ -0,0 +1,25 @@ +//@ add-minicore +//@ compile-flags: -Z instrument-mcount=fentry -Copt-level=0 +// +//@ revisions: x86_64-linux +//@[x86_64-linux] compile-flags: --target=x86_64-unknown-linux-gnu +//@[x86_64-linux] needs-llvm-components: x86 +// +//@ revisions: x86-linux +//@[x86-linux] compile-flags: --target=i686-unknown-linux-gnu +//@[x86-linux] needs-llvm-components: x86 +// +//@ revisions: s390x-linux +//@[s390x-linux] compile-flags: --target=s390x-unknown-linux-gnu +//@[s390x-linux] needs-llvm-components: systemz + +#![feature(no_core)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +// CHECK: attributes #{{.*}} "fentry-call"="true" +pub fn foo() {} diff --git a/tests/codegen-llvm/instrument_fn.rs b/tests/codegen-llvm/instrument_fn.rs index cd1e7ddb55cfd..00336d3e7b983 100644 --- a/tests/codegen-llvm/instrument_fn.rs +++ b/tests/codegen-llvm/instrument_fn.rs @@ -1,10 +1,13 @@ // Verify the #[instrument_fn] applies the correct LLVM IR function attributes. // -//@ revisions:XRAY MCOUNT +//@ revisions:XRAY MCOUNT FENTRY //@ add-minicore //@ compile-flags: -Copt-level=0 //@ [XRAY] compile-flags: -Zinstrument-xray --target=x86_64-unknown-linux-gnu //@ [XRAY] needs-llvm-components: x86 +//@ [FENTRY] compile-flags: -Zinstrument-mcount=fentry -Copt-level=0 +//@ [FENTRY] compile-flags: --target=x86_64-unknown-linux-gnu +//@ [FENTRY] needs-llvm-components: x86 //@ [MCOUNT] compile-flags: -Zinstrument-mcount #![feature(no_core)] @@ -27,12 +30,16 @@ fn instrument_off() {} #[no_mangle] #[instrument_fn = "on"] // MCOUNT: define {{.*}}void @instrument_on() {{.*}} [[DFLT_ATTR]] +// FENTRY: define void @instrument_on() {{.*}} [[DFLT_ATTR]] // XRAY: define void @instrument_on() {{.*}} [[ON_ATTR:#[0-9]+]] fn instrument_on() {} // MCOUNT: attributes [[DFLT_ATTR]] {{.*}} "instrument-function-entry-inlined"= // MCOUNT-NOT: attributes [[OFF_ATTR]] {{.*}} "instrument-function-entry-inlined"= +// FENTRY: attributes [[DFLT_ATTR]] {{.*}} "fentry-call"="true" +// FENTRY-NOT: attributes [[OFF_ATTR]] {{.*}} "fentry-call"="true" + // XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "function-instrument"="xray-always" // XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "function-instrument"="xray-never" // XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "xray-skip-exit" diff --git a/tests/codegen-llvm/intrinsics/volatile.rs b/tests/codegen-llvm/intrinsics/volatile.rs index 2dea5ecb2ca92..4ba4c5edd2df6 100644 --- a/tests/codegen-llvm/intrinsics/volatile.rs +++ b/tests/codegen-llvm/intrinsics/volatile.rs @@ -5,6 +5,11 @@ use std::intrinsics; +#[repr(align(32))] +pub struct CustomZst; + +type UninitFatPointer = std::mem::MaybeUninit<&'static dyn std::fmt::Debug>; + // CHECK-LABEL: @volatile_copy_memory #[no_mangle] pub unsafe fn volatile_copy_memory(a: *mut u8, b: *const u8) { @@ -28,8 +33,62 @@ pub unsafe fn volatile_set_memory(a: *mut u8, b: u8) { // CHECK-LABEL: @volatile_load #[no_mangle] -pub unsafe fn volatile_load(a: *const u8) -> u8 { - // CHECK: load volatile +pub unsafe fn volatile_load(a: *const u16) -> u16 { + // CHECK: [[TEMP:%.+]] = load volatile i16, ptr %a + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret i16 [[TEMP]] + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_bool +#[no_mangle] +pub unsafe fn volatile_load_bool(a: *const bool) -> bool { + // CHECK: [[TEMP:%.+]] = load volatile i8, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK: [[TRUNC:%.+]] = trunc nuw i8 [[TEMP]] to i1 + // CHECK: ret i1 [[TRUNC]] + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_zst +#[no_mangle] +pub unsafe fn volatile_load_zst(a: *const CustomZst) -> CustomZst { + // CHECK: start: + // CHECK-NEXT: ret void + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_array +// CHECK-SAME: ptr{{.+}}sret([16 x i8]){{.+}}%_0 +#[no_mangle] +pub unsafe fn volatile_load_array(a: *const [u16; 8]) -> [u16; 8] { + // CHECK-NOT: alloca + // CHECK: [[TEMP:%.+]] = load volatile i128, ptr %a, + // CHECK-SAME: align 2{{,|$}} + // CHECK: store i128 [[TEMP]], ptr %_0, + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret void + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_fat +#[no_mangle] +pub unsafe fn volatile_load_fat(a: *const UninitFatPointer) -> UninitFatPointer { + // CHECK: [[ALLOCA:%.+]] = alloca + // CHECK-SAME: [[SIZE:4|8|16]] x i8 + // CHECK-SAME: align [[ALIGN:2|4|8]] + + // CHECK: [[TEMP:%.+]] = load volatile [[INT:i32|i64|i128]], ptr %a, + // CHECK-SAME: align [[ALIGN]]{{,|$}} + // CHECK: store [[INT]] [[TEMP]], ptr [[ALLOCA]], + // CHECK-SAME: align [[ALIGN]]{{,|$}} + + // CHECK: [[T0:%.+]] = load ptr, ptr [[ALLOCA]], align [[ALIGN]] + // CHECK: [[T1:%.+]] = getelementptr inbounds i8, ptr [[ALLOCA]] + // CHECK: [[T2:%.+]] = load ptr, ptr [[T1]], align [[ALIGN]] + // CHECK: [[P1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[T0]], 0 + // CHECK: [[P2:%.+]] = insertvalue { ptr, ptr } [[P1]], ptr [[T2]], 1 + // CHECK: ret { ptr, ptr } [[P2]] intrinsics::volatile_load(a) } @@ -42,8 +101,62 @@ pub unsafe fn volatile_store(a: *mut u8, b: u8) { // CHECK-LABEL: @unaligned_volatile_load #[no_mangle] -pub unsafe fn unaligned_volatile_load(a: *const u8) -> u8 { - // CHECK: load volatile +pub unsafe fn unaligned_volatile_load(a: *const u16) -> u16 { + // CHECK: [[TEMP:%.+]] = load volatile i16, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK-NEXT: ret i16 [[TEMP]] + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_bool +#[no_mangle] +pub unsafe fn unaligned_volatile_load_bool(a: *const bool) -> bool { + // CHECK: [[TEMP:%.+]] = load volatile i8, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK: [[TRUNC:%.+]] = trunc nuw i8 [[TEMP]] to i1 + // CHECK: ret i1 [[TRUNC]] + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_zst +#[no_mangle] +pub unsafe fn unaligned_volatile_load_zst(a: *const CustomZst) -> CustomZst { + // CHECK: start: + // CHECK-NEXT: ret void + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_array +// CHECK-SAME: ptr{{.+}}sret([16 x i8]){{.+}}%_0, +#[no_mangle] +pub unsafe fn unaligned_volatile_load_array(a: *const [u16; 8]) -> [u16; 8] { + // CHECK-NOT: alloca + // CHECK: [[TEMP:%.+]] = load volatile i128, ptr %a, + // CHECK-SAME: align 1{{,|$}} + // CHECK: store i128 [[TEMP]], ptr %_0, + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret void + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_fat +#[no_mangle] +pub unsafe fn unaligned_volatile_load_fat(a: *const UninitFatPointer) -> UninitFatPointer { + // CHECK: [[ALLOCA:%.+]] = alloca + // CHECK-SAME: [[SIZE]] x i8 + // CHECK-SAME: align [[ALIGN]] + + // CHECK: [[TEMP:%.+]] = load volatile [[INT]], ptr %a, + // CHECK-SAME: align 1{{,|$}} + // CHECK: store [[INT]] [[TEMP]], ptr [[ALLOCA]], + // CHECK-SAME: align [[ALIGN]]{{,|$}} + + // CHECK: [[T0:%.+]] = load ptr, ptr [[ALLOCA]], align [[ALIGN]] + // CHECK: [[T1:%.+]] = getelementptr inbounds i8, ptr [[ALLOCA]] + // CHECK: [[T2:%.+]] = load ptr, ptr [[T1]], align [[ALIGN]] + // CHECK: [[P1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[T0]], 0 + // CHECK: [[P2:%.+]] = insertvalue { ptr, ptr } [[P1]], ptr [[T2]], 1 + // CHECK: ret { ptr, ptr } [[P2]] intrinsics::unaligned_volatile_load(a) } diff --git a/tests/codegen-llvm/issues/issue-73258.rs b/tests/codegen-llvm/issues/issue-73258.rs index c9eceb0dccf73..4962c53853aac 100644 --- a/tests/codegen-llvm/issues/issue-73258.rs +++ b/tests/codegen-llvm/issues/issue-73258.rs @@ -3,6 +3,8 @@ #![crate_type = "lib"] // Adapted from +// We explicitly match against `call{{.*}}(` because the emitted call may carry attributes (e.g. +// `ptrauth-calls`), which would otherwise make a plain `call` pattern too permissive. #[derive(Clone, Copy)] #[repr(u8)] @@ -17,7 +19,7 @@ pub enum Foo { #[no_mangle] pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select @@ -25,14 +27,14 @@ pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-SAME: !range ! // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: ret i8 %[[R]] // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select let k: Option = Some(ptr.read()); diff --git a/tests/codegen-llvm/no_builtins-at-crate.rs b/tests/codegen-llvm/no_builtins-at-crate.rs index ba1d31f60c390..de80a925aebc5 100644 --- a/tests/codegen-llvm/no_builtins-at-crate.rs +++ b/tests/codegen-llvm/no_builtins-at-crate.rs @@ -3,11 +3,13 @@ #![no_builtins] #![crate_type = "lib"] +use std::ffi::c_void; + // CHECK: define // CHECK-SAME: @__aeabi_memcpy // CHECK-SAME: #0 #[no_mangle] -pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usize) { +pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut c_void, src: *const c_void, size: usize) { // CHECK: call // CHECK-SAME: @memcpy( memcpy(dest, src, size); @@ -17,7 +19,7 @@ pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usi // CHECK-SAME: @memcpy // CHECK-SAME: #0 extern "C" { - pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; } // CHECK: attributes #0 diff --git a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs index 72204c78a4906..45aa46d24fe94 100644 --- a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs +++ b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs @@ -39,6 +39,26 @@ pub fn fun5() {} #[patchable_function_entry(prefix_nops = 4)] pub fn fun6() {} +// The attribute should override patchable-function-prefix to 4 +// and patchable-function-entry to the default of 0, clearing it entirely, +// while setting patchable-function-entry-section. +#[no_mangle] +#[patchable_function_entry(prefix_nops = 4, section = "foo")] +pub fn fun7() {} + +// The attribute should override patchable-function-entry-section, +// while passing through the commandline options. +#[no_mangle] +#[patchable_function_entry(section = "bar")] +pub fn fun8() {} + +// The attribute should override patchable-function-entry to 5 +// and patchable-function-prefix to the default of 0, clearing it entirely, +// while setting patchable-function-entry-section. +#[no_mangle] +#[patchable_function_entry(entry_nops = 5, section = "baz")] +pub fn fun9() {} + // CHECK: @fun0() unnamed_addr #0 // CHECK: @fun1() unnamed_addr #1 // CHECK: @fun2() unnamed_addr #2 @@ -46,6 +66,9 @@ pub fn fun6() {} // CHECK: @fun4() unnamed_addr #4 // CHECK: @fun5() unnamed_addr #5 // CHECK: @fun6() unnamed_addr #6 +// CHECK: @fun7() unnamed_addr #7 +// CHECK: @fun8() unnamed_addr #8 +// CHECK: @fun9() unnamed_addr #9 // CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-prefix"="10" {{.*}} } // CHECK: attributes #1 = { {{.*}}"patchable-function-entry"="2"{{.*}}"patchable-function-prefix"="1" {{.*}} } @@ -62,3 +85,12 @@ pub fn fun6() {} // CHECK: attributes #6 = { {{.*}}"patchable-function-prefix"="4"{{.*}} } // CHECK-NOT: attributes #6 = { {{.*}}patchable-function-entry{{.*}} } +// +// CHECK: attributes #7 = { {{.*}}"patchable-function-entry-section"="foo"{{.*}}"patchable-function-prefix"="4" {{.*}} } +// CHECK-NOT: attributes #7 = { {{.*}}"patchable-function-entry"{{.*}} } +// +// CHECK: attributes #8 = { {{.*}}"patchable-function-entry-section"="bar"{{.*}} } +// CHECK-NOT: attributes #8 = { {{.*}}"patchable-function-entry"{{.*}} } +// +// CHECK: attributes #9 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="baz" {{.*}} } +// CHECK-NOT: attributes #9 = { {{.*}}"patchable-function-prefix{{.*}} } diff --git a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs new file mode 100644 index 0000000000000..9ffff5ca7537c --- /dev/null +++ b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Z patchable-function-entry=15,10,default_foo_section +// + +#![feature(patchable_function_entry)] +#![crate_type = "lib"] + +// This should have the default, as set by the compile flags +#[no_mangle] +pub fn fun0() {} + +// This should override the default section name +#[no_mangle] +#[patchable_function_entry(section = "bar_section")] +pub fn fun1() {} + +// CHECK: @fun0() unnamed_addr #0 +// CHECK: @fun1() unnamed_addr #1 + +// CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="default_foo_section"{{.*}}"patchable-function-prefix"="10" {{.*}} } +// CHECK: attributes #1 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="bar_section"{{.*}}"patchable-function-prefix"="10" {{.*}} } diff --git a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs new file mode 100644 index 0000000000000..882cc4c6db971 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs @@ -0,0 +1,31 @@ +//@ only-pauthtest +//@ compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +// Make sure that compiler generated functions (main wrapper and __rust_try) also have ptrauth +// attributes set correctly. Rustc only generates __rust_try at O0, so use that opt level for the +// test. + +//@ needs-llvm-components: aarch64 + +use std::panic; + +// CHECK: define {{.*}} @__rust_try{{.*}} [[ATTR_TRY:#[0-9]+]] +// CHECK: define {{.*}} @main{{.*}} [[ATTR_MAIN:#[0-9]+]] + +// CHECK: attributes [[ATTR_TRY]] = { {{.*}}"aarch64-jump-table-hardening" +// CHECK-DAG: "ptrauth-auth-traps" +// CHECK-DAG: "ptrauth-calls" +// CHECK-DAG: "ptrauth-indirect-gotos" +// CHECK-DAG: "ptrauth-returns" + +// CHECK: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// CHECK-DAG: "ptrauth-auth-traps" +// CHECK-DAG: "ptrauth-calls" +// CHECK-DAG: "ptrauth-indirect-gotos" +// CHECK-DAG: "ptrauth-returns" +fn main() { + let _ = panic::catch_unwind(|| { + panic!("BOOM"); + }); +} + +// CHECK: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} diff --git a/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs new file mode 100644 index 0000000000000..643b428339b73 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs @@ -0,0 +1,98 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +//@ revisions: O0_PAUTH O3_PAUTH + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 + +// Make sure that direct extern "C" calls are not handled by pointer authentication operand bundle +// logic. +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::hint::black_box; +use minicore::*; + +extern "C" { + fn rand() -> bool; + fn add(a: i32, b: i32) -> i32; + fn sub(a: i32, b: i32) -> i32; + + // Corresponds to: void *woof; + static mut woof: *mut c_void; + fn direct_function_taking_void_arg(data: *mut c_void); + fn direct_no_arg(); + fn direct_function_taking_fp_arg(func: unsafe extern "C" fn()); +} + +type CFnPtr = unsafe extern "C" fn(i32, i32) -> i32; + +// CHECK-LABE: test_indirect_call +#[inline(never)] +fn test_indirect_call() { + let fp_add: CFnPtr = black_box(add); + let fp_sub: CFnPtr = black_box(sub); + + unsafe { + let a = black_box(fp_add); + let b = black_box(fp_sub); + + // O0_PAUTH: call i32 %{{.*}}(i32 7, i32 4) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call noundef i32 %{{.*}}(i32 noundef 7, i32 noundef 4) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id1 = a(7, 4); + + // O0_PAUTH: call i32 %{{.*}}(i32 10, i32 6) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call noundef i32 %{{.*}}(i32 noundef 10, i32 noundef 6) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id2 = b(10, 6); + } + + // Also test calling via conditional pointer + unsafe { + // O0_PAUTH: call {{.*}}i1 ptrauth (ptr @rand, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i1 @rand() # + let use_add = rand(); + // O0_PAUTH: store ptr ptrauth (ptr @sub, i32 0), ptr %[[FP_O0:[a-zA-Z0-9_.]+]] + // O0_PAUTH: store ptr ptrauth (ptr @add, i32 0), ptr %[[FP_O0]]{{.*}} + // O0_PAUTH: %[[LOAD_FP_O0:[a-zA-Z0-9_.]+]] = load ptr, ptr %[[FP_O0]]{{.*}} + // O3_PAUTH: %[[FP_O3:.*]] = select i1 %{{.*}}, ptr ptrauth (ptr @add, i32 0), ptr ptrauth (ptr @sub, i32 0) + let fp: CFnPtr = if use_add { add } else { sub }; + // O0_PAUTH: call i32 %[[LOAD_FP_O0]](i32 1, i32 2) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 %[[FP_O3]](i32 noundef 1, i32 noundef 2) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id3 = fp(1, 2); + } + + unsafe { + direct_function_taking_fp_arg(direct_no_arg); + } +} + +// CHECK-LABEL: test_direct_call +#[inline(never)] +fn test_direct_call() { + unsafe { + // O0_PAUTH: call {{.*}}i32 ptrauth (ptr @add, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 @add(i32 {{.*}}2, i32 {{.*}}3) # + let _d1 = add(2, 3); + // O0_PAUTH: call {{.*}}i32 ptrauth (ptr @sub, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 @sub(i32 {{.*}}5, i32 {{.*}}1) # + let _d2 = sub(5, 1); + + // O0_PAUTH: call {{.*}}void ptrauth (ptr @direct_function_taking_void_arg, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: {{(tail )?}}call void @direct_function_taking_void_arg(ptr noundef %{{.*}}) # + direct_function_taking_void_arg(woof); + } +} + +pub fn entry() { + test_indirect_call(); + test_direct_call(); +} + +// O0_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} diff --git a/tests/codegen-llvm/pauth/pauth-extern-c.rs b/tests/codegen-llvm/pauth/pauth-extern-c.rs new file mode 100644 index 0000000000000..ac12729f80699 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-c.rs @@ -0,0 +1,75 @@ +// ignore-tidy-linelength +//@ only-pauthtest +//@ add-minicore + +//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ELF-GOT O3_PAUTH-ELF-GOT O0_NO_PAUTH O3_NO_PAUTH + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 +//@ [O0_PAUTH-ELF-GOT] needs-llvm-components: aarch64 +//@ [O0_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Z ptrauth-elf-got +//@ [O3_PAUTH-ELF-GOT] needs-llvm-components: aarch64 +//@ [O3_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 -Z ptrauth-elf-got +//@ [O0_NO_PAUTH] needs-llvm-components: aarch64 +//@ [O0_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=0 +//@ [O3_NO_PAUTH] needs-llvm-components: aarch64 +//@ [O3_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=3 + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] + +extern crate minicore; + +type FnPtr = unsafe extern "C" fn(i32, i32) -> i32; +// O0_NO_PAUTH-NOT: "ptrauth"(i32 +// O3_NO_PAUTH-NOT: "ptrauth"(i32 + +// O0_PAUTH: define {{.*}}test_entry +// O3_PAUTH: define {{.*}}test_entry +#[no_mangle] +pub unsafe extern "C" fn test_entry(x: usize) { + // O0_PAUTH: call{{.*}}_RNvCshUtaFcP1mZ5_14pauth_extern_c7call_it(ptr ptrauth (ptr @external_c_callee, i32 0), i32 5, i32 7) + // O3_PAUTH: call{{.*}}_RNvCshUtaFcP1mZ5_14pauth_extern_c7call_it(ptr{{.*}}ptrauth (ptr @external_c_callee, i32 0), i32{{.*}}5, i32{{.*}}7) + let _ = call_it(external_c_callee, 5, 7); +} + +// O0_PAUTH: define {{.*}}pauth_extern_c7call_it{{.*}} #[[ATTR_O0_1:[0-9]+]] +// O3_PAUTH: define {{.*}}pauth_extern_c7call_it{{.*}} #[[ATTR_O3_1:[0-9]+]] +#[inline(never)] +pub fn call_it(fn_ptr: FnPtr, arg_1: i32, arg_2: i32) -> i32 { + // O0_PAUTH: call i32 %fn_ptr(i32 %arg_1, i32 %arg_2){{.*}}[ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call{{.*}}i32 %fn_ptr(i32{{.*}}%arg_1, i32{{.*}}%arg_2){{.*}}[ "ptrauth"(i32 0, i64 0) ] + unsafe { fn_ptr(arg_1, arg_2) } +} + +extern "C" { + fn external_c_callee(a: i32, b: i32) -> i32; +} + +// O0_PAUTH-CHECK: attributes #[[ATTR_O0_1]] = { {{.*}}"aarch64-jump-table-hardening" +// O0_PAUTH-CHECK-SAME: "ptrauth-auth-traps" +// O0_PAUTH-CHECK-SAME: "ptrauth-calls" +// O0_PAUTH-CHECK-SAME: "ptrauth-indirect-gotos" +// O0_PAUTH-CHECK-SAME: "ptrauth-returns" + +// O3_PAUTH-CHECK: attributes #[[ATTR_O3_1]] = { {{.*}}"aarch64-jump-table-hardening" +// O3_PAUTH-CHECK-SAME: "ptrauth-auth-traps" +// O3_PAUTH-CHECK-SAME: "ptrauth-calls" +// O3_PAUTH-CHECK-SAME: "ptrauth-indirect-gotos" +// O3_PAUTH-CHECK-SAME: "ptrauth-returns" + +// O0_PAUTH-ELF-GOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_PAUTH-ELF-GOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} + +// O0_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} diff --git a/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs new file mode 100644 index 0000000000000..a83298dd5725c --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs @@ -0,0 +1,38 @@ +// ignore-tidy-linelength +//@ only-pauthtest +//@ revisions: O0_PAUTH O3_PAUTH O0_NO_PAUTH O3_NO_PAUTH +//@ add-minicore + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 +//@ [O0_NO_PAUTH] needs-llvm-components: aarch64 +//@ [O0_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=0 +//@ [O3_NO_PAUTH] needs-llvm-components: aarch64 +//@ [O3_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=3 + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] +#![feature(linkage)] + +extern crate minicore; +use minicore::*; + +// O0_PAUTH: @{{[0-9A-Za-z_]+}}FUNCTION_PTR_DECL = constant ptr ptrauth (ptr @extern_weak_fn, i32 0) +// O0_PAUTH: declare i64 @extern_weak_fn({{.*}}) +// O3_PAUTH: @{{[0-9A-Za-z_]+}}FUNCTION_PTR_DECL = constant ptr ptrauth (ptr @extern_weak_fn, i32 0) +// O3_PAUTH: declare {{.*}} i64 @extern_weak_fn({{.*}}) +// +// O0_NO_PAUTH-NOT: ptr ptrauth +// O3_NO_PAUTH-NOT: ptr ptrauth +extern "C" { + #[link_name = "extern_weak_fn"] + #[linkage = "extern_weak"] + fn extern_weak_fn() -> i64; +} + +#[used] +static FUNCTION_PTR_DECL: unsafe extern "C" fn() -> i64 = extern_weak_fn; diff --git a/tests/codegen-llvm/pauth/pauth-init-fini.rs b/tests/codegen-llvm/pauth/pauth-init-fini.rs new file mode 100644 index 0000000000000..db327644d96cf --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -0,0 +1,34 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +//@ revisions: O0_PAUTH O3_PAUTH + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 + +// Make sure that init/fini metadata uses correct discriminator: 0xd9d4/55764 + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +// O0_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".init_array.90" +// O3_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".init_array.90" +#[used] +#[link_section = ".init_array.90"] +static GLOBAL_INIT: extern "C" fn() = init_fn; + +// O0_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".fini_array.90" +// O3_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".fini_array.90" +#[used] +#[link_section = ".fini_array.90"] +static GLOBAL_FINI: extern "C" fn(i32) = fini_fn; + +extern "C" fn init_fn() {} +extern "C" fn fini_fn(_: i32) {} diff --git a/tests/codegen-llvm/read_write_unaligned.rs b/tests/codegen-llvm/read_write_unaligned.rs new file mode 100644 index 0000000000000..beba4400dc54a --- /dev/null +++ b/tests/codegen-llvm/read_write_unaligned.rs @@ -0,0 +1,71 @@ +//@ compile-flags: -Copt-level=3 +//@ only-64bit + +#![crate_type = "lib"] + +use std::num::NonZero; +use std::ptr::NonNull; + +// CHECK-LABEL: nonnull ptr @read_unaligned_ptr(ptr{{.+}}%ptr) +#[no_mangle] +unsafe fn read_unaligned_ptr(ptr: *const NonNull) -> NonNull { + // CHECK: start: + // CHECK-NEXT: [[TEMP:%.+]] = load ptr, ptr %ptr, align 1 + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK-NEXT: ret ptr [[TEMP]] + ptr.read_unaligned() +} + +// CHECK-LABEL: range(i16 1, 0) i16 @read_unaligned_i16(ptr{{.+}}%ptr) +#[no_mangle] +unsafe fn read_unaligned_i16(ptr: *const NonZero) -> NonZero { + // CHECK: start: + // CHECK-NEXT: [[TEMP:%.+]] = load i16, ptr %ptr, align 1 + // CHECK-NOT: !noundef + // CHECK-NOT: !range + // CHECK-NEXT: ret i16 [[TEMP]] + ptr.read_unaligned() +} + +// CHECK-LABEL: void @typed_copy_unaligned_i32(ptr{{.+}}%src, ptr{{.+}}%dst) +#[no_mangle] +unsafe fn typed_copy_unaligned_i32(src: *const NonZero, dst: *mut NonZero) { + // CHECK: start: + // CHECK-NEXT: [[TEMP:%.+]] = load i32, ptr %src, align 1 + // CHECK-NOT: !noundef + // CHECK-NOT: !range + // CHECK-NEXT: store i32 [[TEMP]], ptr %dst, align 1 + // CHECK-NEXT: ret void + dst.write_unaligned(src.read_unaligned()) +} + +// CHECK-LABEL: void @write_unaligned_i64(ptr{{.+}}%ptr, i64{{.+}}%val) +#[no_mangle] +unsafe fn write_unaligned_i64(ptr: *mut NonZero, val: NonZero) { + // CHECK: start: + // CHECK-NEXT: store i64 %val, ptr %ptr, align 1 + // CHECK-NEXT: ret void + ptr.write_unaligned(val) +} + +#[repr(align(128))] +struct HugeBuffer([u64; 1 << 10]); + +// CHECK-LABEL: void @read_unaligned_huge(ptr{{.+}}%_0, ptr{{.+}}%ptr) +#[no_mangle] +unsafe fn read_unaligned_huge(ptr: *const HugeBuffer) -> HugeBuffer { + // CHECK: start: + // CHECK-NEXT: call void @llvm.memcpy{{.+}} align 128 dereferenceable(8192) %_0, {{.+}} align 1 dereferenceable(8192) %ptr, i64 8192, + // CHECK-NEXT: ret void + ptr.read_unaligned() +} + +// CHECK-LABEL: void @write_unaligned_huge(ptr{{.+}}%ptr, ptr{{.+}}%val) +#[no_mangle] +unsafe fn write_unaligned_huge(ptr: *mut HugeBuffer, val: HugeBuffer) { + // CHECK: start: + // CHECK-NEXT: call void @llvm.memcpy{{.+}} align 1 dereferenceable(8192) %ptr, {{.+}} align 128 dereferenceable(8192) %val, i64 8192, + // CHECK-NEXT: ret void + ptr.write_unaligned(val) +} diff --git a/tests/codegen-llvm/scalable-vectors/memcpy.rs b/tests/codegen-llvm/scalable-vectors/memcpy.rs new file mode 100644 index 0000000000000..859f50b5b1178 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/memcpy.rs @@ -0,0 +1,88 @@ +//@ compile-flags: -Copt-level=0 -Ctarget-feature=+sve,+sve2 +//@ edition: 2021 +//@ only-aarch64 + +#![crate_type = "lib"] +#![feature(simd_ffi)] +#![feature(stdarch_aarch64_sve)] + +// Test that `vscale * size` is generated for `memcpy` of scalable vector types + +use std::arch::aarch64::*; + +#[allow(improper_ctypes)] +unsafe extern "C" { + fn svcreate2_s16_wrapper(__dst: *mut svint16x2_t, x0: *const svint16_t, x1: *const svint16_t); + fn svcreate3_s16_wrapper( + __dst: *mut svint16x3_t, + x0: *const svint16_t, + x1: *const svint16_t, + x2: *const svint16_t, + ); + fn svcreate4_s16_wrapper( + __dst: *mut svint16x4_t, + x0: *const svint16_t, + x1: *const svint16_t, + x2: *const svint16_t, + x3: *const svint16_t, + ); +} + +pub fn foo(x0_val: svint16_t) { + unsafe { + let __pred = svptrue_b16(); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + svcreate2_s16_wrapper(__c_return_value.as_mut_ptr(), &raw const x0_val, &raw const x0_val); + let __c_return_value = __c_return_value.assume_init(); + // CHECK: call void @svcreate2_s16_wrapper( + // CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64() + // CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 32 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]] + + let eq = svcmpeq_s16( + __pred, + svget2_s16::<1>(__c_return_value), + svget2_s16::<1>(__c_return_value), + ); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + svcreate3_s16_wrapper( + __c_return_value.as_mut_ptr(), + &raw const x0_val, + &raw const x0_val, + &raw const x0_val, + ); + let __c_return_value = __c_return_value.assume_init(); + // CHECK: call void @svcreate3_s16_wrapper( + // CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64() + // CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 48 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]] + + let eq = svcmpeq_s16( + __pred, + svget3_s16::<2>(__c_return_value), + svget3_s16::<2>(__c_return_value), + ); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + svcreate4_s16_wrapper( + __c_return_value.as_mut_ptr(), + &raw const x0_val, + &raw const x0_val, + &raw const x0_val, + &raw const x0_val, + ); + let __c_return_value = __c_return_value.assume_init(); + // CHECK: call void @svcreate4_s16_wrapper( + // CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64() + // CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 64 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]] + + let eq = svcmpeq_s16( + __pred, + svget4_s16::<3>(__c_return_value), + svget4_s16::<3>(__c_return_value), + ); + } +} diff --git a/tests/crashes/130310.rs b/tests/crashes/130310.rs deleted file mode 100644 index d59dd39983c78..0000000000000 --- a/tests/crashes/130310.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: rust-lang/rust#130310 - -use std::marker::PhantomData; - -#[repr(C)] -struct A { - a: *const A>, - p: PhantomData, -} - -extern "C" { - fn f(a: *const A<()>); -} - -fn main() {} diff --git a/tests/crashes/147719.rs b/tests/crashes/147719.rs new file mode 100644 index 0000000000000..d9595b8ead565 --- /dev/null +++ b/tests/crashes/147719.rs @@ -0,0 +1,16 @@ +//@ known-bug: #147719 +//@ edition: 2024 +trait NodeImpl {} +struct Wrap(F, P); +impl Wrap { + fn new(_: F) -> Self { + loop {} + } +} +trait Arg {} +impl NodeImpl for Wrap where A: Arg {} +impl NodeImpl for Wrap where F: Fn(&(), A) -> Fut {} +fn trigger_ice() { + let _: &dyn NodeImpl = &Wrap::<_, (i128,)>::new(async |_: &(), i128| 0); +} +fn main() {} diff --git a/tests/crashes/148511.rs b/tests/crashes/148511.rs new file mode 100644 index 0000000000000..69203ebeb2d21 --- /dev/null +++ b/tests/crashes/148511.rs @@ -0,0 +1,42 @@ +//@ known-bug: #148511 +//@ edition: 2021 +use std::any::Any; + +fn main() { + use_service(make_service()); + let _future = async {}; +} + +fn make_service() -> impl FooService, Response: Body> {} + +fn use_service(_service: S) +where + S: FooService, + ::Data: Any, +{ +} + +trait Service { + type Response: Body; +} + +impl Service for T { + type Response = (); +} + +trait FooService: Service {} + +impl FooService for T +where + T: Service, + Resp: Body, +{ +} + +trait Body { + type Data; +} + +impl Body for T { + type Data = (); +} diff --git a/tests/crashes/148629.rs b/tests/crashes/148629.rs new file mode 100644 index 0000000000000..926b4cc75c952 --- /dev/null +++ b/tests/crashes/148629.rs @@ -0,0 +1,13 @@ +//@ known-bug: #148629 +#![feature(with_negative_coherence)] +trait Foo { + type AssociatedType; +} + +impl Foo for [(); N] {} + +pub struct Happy; + +impl Foo for Happy {} + +impl Foo for Happy where <[(); N] as Foo>::AssociatedType: Clone {} diff --git a/tests/crashes/148630.rs b/tests/crashes/148630.rs new file mode 100644 index 0000000000000..7b857bbb408a1 --- /dev/null +++ b/tests/crashes/148630.rs @@ -0,0 +1,13 @@ +//@ known-bug: #148630 +#![feature(unboxed_closures)] + +trait Tr {} +trait Foo { + fn foo() -> impl Sized + where + for<'a> <() as FnOnce<&'a i32>>::Output: Tr, + { + } +} + +fn main() {} diff --git a/tests/crashes/148632.rs b/tests/crashes/148632.rs new file mode 100644 index 0000000000000..c77ff5e8d7954 --- /dev/null +++ b/tests/crashes/148632.rs @@ -0,0 +1,10 @@ +//@ known-bug: #148632 +trait D {} + +trait Project { + const SELF: dyn D; +} + +fn main() { + let _: &dyn Project; +} diff --git a/tests/crashes/148890.rs b/tests/crashes/148890.rs new file mode 100644 index 0000000000000..6e78ca6717f15 --- /dev/null +++ b/tests/crashes/148890.rs @@ -0,0 +1,6 @@ +//@ known-bug: #148890 +impl std::ops::Neg for u128 {} + +fn foo(-128..=127: u128) {} + +fn main() {} diff --git a/tests/crashes/148891.rs b/tests/crashes/148891.rs new file mode 100644 index 0000000000000..75534440d4902 --- /dev/null +++ b/tests/crashes/148891.rs @@ -0,0 +1,14 @@ +//@ known-bug: #148891 +macro_rules! values { + ($inner:ty) => { + #[derive(Debug)] + pub enum TokenKind { + #[cfg(test)] + STRING ([u8; $inner]), + } + }; +} + +values!(String); + +pub fn main() {} diff --git a/tests/crashes/149162.rs b/tests/crashes/149162.rs new file mode 100644 index 0000000000000..33532fe365ba4 --- /dev/null +++ b/tests/crashes/149162.rs @@ -0,0 +1,30 @@ +//@ known-bug: #149162 +use std::marker::PhantomData; +pub trait ViewArgument { + type Params<'a>; +} + +impl ViewArgument for () { + type Params<'a> = (); +} + +pub trait View {} + +pub fn buttons() -> Option { + Some(()).map(|()| text_button(|()| {})) +} +pub fn text_button( + _: impl Fn(::Params<'_>), +) -> Button { + Button { + callback: || (), + phantom: PhantomData, + } +} +pub struct Button { + pub callback: F, + pub phantom: PhantomData, +} + +impl View for Button<(), F> {} +fn main() {} diff --git a/tests/crashes/149703.rs b/tests/crashes/149703.rs new file mode 100644 index 0000000000000..40d78484307a6 --- /dev/null +++ b/tests/crashes/149703.rs @@ -0,0 +1,12 @@ +//@ known-bug: #149703 +#![feature(const_trait_impl)] +trait Z { + type Assoc; +} +struct A; +impl Z for T { + type Assoc = (); +} +impl From<::Assoc> for T {} + +fn main() {} diff --git a/tests/crashes/149748.rs b/tests/crashes/149748.rs new file mode 100644 index 0000000000000..99e4a6ae15b80 --- /dev/null +++ b/tests/crashes/149748.rs @@ -0,0 +1,13 @@ +//@ known-bug: #149748 +//@ edition: 2024 +//@ compile-flags: -Zmir-enable-passes=+Inline -Zmir-enable-passes=+ReferencePropagation -Zlint-mir + +#![feature(gen_blocks)] +gen fn foo(z: i32) -> i32 { + yield z; + z; +} +pub fn main() { + let mut iter = foo(3); + assert_eq!(iter.next(), Some(3)) +} diff --git a/tests/crashes/150040.rs b/tests/crashes/150040.rs new file mode 100644 index 0000000000000..bf5b7bbf9b536 --- /dev/null +++ b/tests/crashes/150040.rs @@ -0,0 +1,7 @@ +//@ known-bug: #150040 + +fn main() { + let [(ref a, b), x]; + a = ""; + b = 5; +} diff --git a/tests/crashes/150049.rs b/tests/crashes/150049.rs new file mode 100644 index 0000000000000..fa39785779c0f --- /dev/null +++ b/tests/crashes/150049.rs @@ -0,0 +1,12 @@ +//@ known-bug: #150049 +#![feature(min_generic_const_args)] +#![feature(inherent_associated_types)] +struct Foo<'a> { + x: &'a (), +} + +impl<'a> Foo<'a> { + fn foo(_: [u8; Foo::X]) { std::mem::transmute([4]) } +} + +fn main() {} diff --git a/tests/crashes/150128.rs b/tests/crashes/150128.rs new file mode 100644 index 0000000000000..28372ee7935f7 --- /dev/null +++ b/tests/crashes/150128.rs @@ -0,0 +1,8 @@ +//@ known-bug: #150128 +fn main() { + match 0 { + _ => || (), + _ => || (), + _ => (|| ()) as unsafe fn, + }; +} diff --git a/tests/crashes/150296.rs b/tests/crashes/150296.rs new file mode 100644 index 0000000000000..f7d5a54ca9b65 --- /dev/null +++ b/tests/crashes/150296.rs @@ -0,0 +1,14 @@ +//@ known-bug: #150296 +#[derive(PartialEq)] +pub struct Thing; + +impl Thing { + const A: Self = Thing; +} + +fn broken(x: Thing) { + match x { + >::A => {} + _ => {} + } +} diff --git a/tests/crashes/150387.rs b/tests/crashes/150387.rs new file mode 100644 index 0000000000000..b29ce143fb4ea --- /dev/null +++ b/tests/crashes/150387.rs @@ -0,0 +1,13 @@ +//@ known-bug: #150387 +#![feature(min_specialization)] +#![allow(dead_code)] + +struct Thing(T) where [T]: Sized; + +impl Drop for Thing where [T]: Sized { + default fn drop(&mut self) {} +} +impl Drop for Thing where [T]: Sized { + fn drop(&mut self) {} +} +fn main() {} diff --git a/tests/crashes/150403.rs b/tests/crashes/150403.rs new file mode 100644 index 0000000000000..bc00fc32135fc --- /dev/null +++ b/tests/crashes/150403.rs @@ -0,0 +1,26 @@ +//@ known-bug: #150403 +#![feature(non_lifetime_binders)] + +trait A { + type GAT: A; + fn foo(self, t: T) -> Self::GAT + where + Self: Sized; +} + +trait B: A where + for Self::GAT: B, +{ + fn bar(self) -> Self::GAT + where + Self: Sized; + + fn baz(self, t: T) -> Self::GAT + where + Self: Sized, + { + self.foo(t).bar() + } +} + +fn main() {} diff --git a/tests/crashes/150517.rs b/tests/crashes/150517.rs new file mode 100644 index 0000000000000..de14628547d28 --- /dev/null +++ b/tests/crashes/150517.rs @@ -0,0 +1,15 @@ +//@ known-bug: #150517 +trait Stream { + type Item; + fn next(self) -> (); +} +impl Stream for &'a () {} +impl<'a, A> Stream for <&A as Stream>::Item {} +trait StreamExt { + fn f(self) -> () + where + for<'b> &'b A: Stream, + { + self.next() + } +} diff --git a/tests/crashes/150545.rs b/tests/crashes/150545.rs new file mode 100644 index 0000000000000..04e084919d4f2 --- /dev/null +++ b/tests/crashes/150545.rs @@ -0,0 +1,8 @@ +//@ known-bug: #150545 +#![feature(non_lifetime_binders)] +trait Foo: for Bar { + type Item; + fn next(self) -> Self::Item; +} +trait Bar {} +fn main() {} diff --git a/tests/crashes/150749.rs b/tests/crashes/150749.rs new file mode 100644 index 0000000000000..572dc67c27511 --- /dev/null +++ b/tests/crashes/150749.rs @@ -0,0 +1,12 @@ +//@ known-bug: #150749 +#![feature(min_generic_const_args)] + +trait CollectArray { + fn inner_array(); +} +impl CollectArray for () { + fn inner_array() { + let temp_ptr: [(); Self]; + } +} +fn main() {} diff --git a/tests/crashes/150969.rs b/tests/crashes/150969.rs new file mode 100644 index 0000000000000..daab977bce193 --- /dev/null +++ b/tests/crashes/150969.rs @@ -0,0 +1,7 @@ +//@ known-bug: #150969 +#![feature(generic_const_exprs)] +#![feature(min_generic_const_args)] + +fn pass_enum { + pass_enum::<{None}> +} diff --git a/tests/crashes/151069.rs b/tests/crashes/151069.rs new file mode 100644 index 0000000000000..ae6f4da5c6c70 --- /dev/null +++ b/tests/crashes/151069.rs @@ -0,0 +1,16 @@ +//@ known-bug: #151069 +trait Trait { + type Assoc2; +} +struct Bar; +impl Trait for Bar +where + ::Assoc2: Trait, +{ + type Assoc2 = (); +} +struct Foo { + field: ::Assoc2, +} +static FOO2: &Foo = 0; +fn main() {} diff --git a/tests/crashes/152205.rs b/tests/crashes/152205.rs new file mode 100644 index 0000000000000..d6e5a92df5d7a --- /dev/null +++ b/tests/crashes/152205.rs @@ -0,0 +1,20 @@ +//@ known-bug: #152205 +#![deny(rust_2021_incompatible_closure_captures)] +struct Foo; +struct S; +impl Drop for S { + fn drop(&mut self) {} +} +struct U(::Assoc); +fn test_precise_analysis_long_path(u: U) { + let _ = || { + let _x = u.0.0; + }; +} +trait NewTrait { + type Assoc; +} +impl NewTrait for Foo { + type Assoc = (S,); +} +fn main(){} diff --git a/tests/crashes/152295.rs b/tests/crashes/152295.rs new file mode 100644 index 0000000000000..85e23f0f41037 --- /dev/null +++ b/tests/crashes/152295.rs @@ -0,0 +1,11 @@ +//@ known-bug: #152295 +#![feature(type_alias_impl_trait)] +type Tait = impl Sized; +trait Foo: Bar {} +trait Bar { + fn bar(&self); +} +fn test_correct2(x: &dyn Foo) { + x.bar(); +} +fn main() {} diff --git a/tests/crashes/154556.rs b/tests/crashes/154556.rs new file mode 100644 index 0000000000000..d858588c11ec2 --- /dev/null +++ b/tests/crashes/154556.rs @@ -0,0 +1,25 @@ +//@ known-bug: #154556 +#![feature(generic_const_exprs)] + +pub trait Foo { + // Has to take self or a reference to self to ICE. + fn eq(self); +} + +pub trait Bar { + const NUMBER: usize; +} + +impl Foo for T +where + [(); T::NUMBER]: Sized, // Bound required for ICE +{ + fn eq(self) {} +} + +// As long as the method called below has the same name as the one defined in Foo, the ICE happens. +fn baz() { + ().eq(&()); +} + +fn main() {} diff --git a/tests/crashes/154964.rs b/tests/crashes/154964.rs new file mode 100644 index 0000000000000..d44700f5a92b3 --- /dev/null +++ b/tests/crashes/154964.rs @@ -0,0 +1,9 @@ +//@ known-bug: #154964 +//@ edition: 2021 +//@ compile-flags: -Zprint-type-sizes +fn main() { + |foo: Foo<_>| async { foo.bar }; +} +struct Foo { + bar: (), +} diff --git a/tests/crashes/157568.rs b/tests/crashes/157568.rs new file mode 100644 index 0000000000000..627bfbadf6bc4 --- /dev/null +++ b/tests/crashes/157568.rs @@ -0,0 +1,10 @@ +//@ known-bug: #157568 +//@ compile-flags: -Zpolonius +#![warn(rust_2024_compatibility)] +pub struct F; +impl std::fmt::Debug for F { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("F").finish_non_exhaustive() + } +} +fn main() {} diff --git a/tests/debuginfo/lexical-scope-in-match.rs b/tests/debuginfo/lexical-scope-in-match.rs index f91a623112e3b..fda7223760f0b 100644 --- a/tests/debuginfo/lexical-scope-in-match.rs +++ b/tests/debuginfo/lexical-scope-in-match.rs @@ -1,6 +1,7 @@ //@ compile-flags:-g //@ disable-gdb-pretty-printers //@ ignore-backends: gcc +//@ min-lldb-version: 2100 // === GDB TESTS =================================================================================== diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index 14a14ffe377f2..4a028248d6633 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -2,7 +2,7 @@ | User Type Annotations | 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. }], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [AliasConst(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index 14a14ffe377f2..4a028248d6633 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -2,7 +2,7 @@ | User Type Annotations | 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. }], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [AliasConst(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/pre-codegen/unaligned.rs b/tests/mir-opt/pre-codegen/unaligned.rs new file mode 100644 index 0000000000000..4aeb10c261e21 --- /dev/null +++ b/tests/mir-opt/pre-codegen/unaligned.rs @@ -0,0 +1,39 @@ +//@ compile-flags: -O -Zmir-opt-level=2 +//@ ignore-std-debug-assertions (there's one in `ptr::read`) + +#![crate_type = "lib"] + +// EMIT_MIR unaligned.unaligned_copy_manual.runtime-optimized.after.mir +pub unsafe fn unaligned_copy_manual(src: *const u128, dst: *mut u128) { + #[repr(packed)] + struct Packed(T); + + // CHECK-LABEL: fn unaligned_copy_manual(_1: *const u128, _2: *mut u128) -> () + // CHECK: [[SRCU:_.+]] = copy _1 as *const unaligned_copy_manual::Packed (PtrToPtr); + // CHECK: [[DSTU:_.+]] = copy _2 as *mut unaligned_copy_manual::Packed (PtrToPtr); + // CHECK: [[TEMP:_.+]] = copy ((*[[SRCU]]).0: u128); + // ((*[[DSTU]]).0: u128) = move [[TEMP]]; + let src = src.cast::>(); + let dst = dst.cast::>(); + unsafe { (*dst).0 = (*src).0 }; +} + +// EMIT_MIR unaligned.unaligned_copy_generic.runtime-optimized.after.mir +pub unsafe fn unaligned_copy_generic(src: *const T, dst: *mut T) { + // CHECK-LABEL: fn unaligned_copy_generic( + // CHECK: debug src => _1; + // CHECK: debug dst => _2; + // CHECK: debug val => [[VAL:_.+]]; + // CHECK: [[SRC_P:_.+]] = copy _1 as *const {{.+}}::Packed (PtrToPtr); + // CHECK: [[PACKED1:_.+]] = copy (*[[SRC_P]]); + // CHECK: [[VAL]] = copy [[PACKED1]] as T (Transmute); + // CHECK: [[DST_P:_.+]] = copy _2 as *mut {{.+}}::Packed (PtrToPtr); + // CHECK: [[PACKED2:_.+]] = {{.+}}::Packed::(copy [[VAL]]); + // CHECK: (*[[DST_P]]) = copy [[PACKED2]]; + // CHECK-NOT: copy_nonoverlapping + // CHECK-NOT: drop + unsafe { + let val = std::ptr::read_unaligned(src); + std::ptr::write_unaligned(dst, val); + } +} diff --git a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir new file mode 100644 index 0000000000000..4d0cfa6a4dfa3 --- /dev/null +++ b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir @@ -0,0 +1,57 @@ +// MIR for `unaligned_copy_generic` after runtime-optimized + +fn unaligned_copy_generic(_1: *const T, _2: *mut T) -> () { + debug src => _1; + debug dst => _2; + let mut _0: (); + let _5: T; + scope 1 { + debug val => _5; + scope 8 (inlined #[track_caller] write_unaligned::) { + let _6: *mut std::ptr::Packed; + scope 9 { + let _7: std::ptr::Packed; + scope 10 { + scope 12 (inlined #[track_caller] std::ptr::write::>) { + } + } + } + scope 11 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 2 (inlined #[track_caller] read_unaligned::) { + let _3: *const std::ptr::Packed; + scope 3 { + let _4: std::ptr::Packed; + scope 4 { + scope 7 (inlined transmute_neo::, T>) { + } + } + scope 6 (inlined #[track_caller] std::ptr::read::>) { + } + } + scope 5 (inlined std::ptr::const_ptr::::cast::>) { + } + } + + bb0: { + StorageLive(_5); + StorageLive(_3); + _3 = copy _1 as *const std::ptr::Packed (PtrToPtr); + StorageLive(_4); + _4 = copy (*_3); + _5 = copy _4 as T (Transmute); + StorageDead(_4); + StorageDead(_3); + StorageLive(_6); + _6 = copy _2 as *mut std::ptr::Packed (PtrToPtr); + StorageLive(_7); + _7 = std::ptr::Packed::(copy _5); + (*_6) = copy _7; + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir new file mode 100644 index 0000000000000..450df935a0fb3 --- /dev/null +++ b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir @@ -0,0 +1,34 @@ +// MIR for `unaligned_copy_manual` after runtime-optimized + +fn unaligned_copy_manual(_1: *const u128, _2: *mut u128) -> () { + debug src => _1; + debug dst => _2; + let mut _0: (); + let _3: *const unaligned_copy_manual::Packed; + let mut _5: u128; + scope 1 { + debug src => _3; + let _4: *mut unaligned_copy_manual::Packed; + scope 2 { + debug dst => _4; + } + scope 4 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + scope 3 (inlined std::ptr::const_ptr::::cast::>) { + } + + bb0: { + StorageLive(_3); + _3 = copy _1 as *const unaligned_copy_manual::Packed (PtrToPtr); + StorageLive(_4); + _4 = copy _2 as *mut unaligned_copy_manual::Packed (PtrToPtr); + StorageLive(_5); + _5 = copy ((*_3).0: u128); + ((*_4).0: u128) = move _5; + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + return; + } +} diff --git a/tests/pretty/auxiliary/to-reuse-functions.rs b/tests/pretty/delegation/auxiliary/to-reuse-functions.rs similarity index 100% rename from tests/pretty/auxiliary/to-reuse-functions.rs rename to tests/pretty/delegation/auxiliary/to-reuse-functions.rs diff --git a/tests/pretty/delegation.rs b/tests/pretty/delegation/delegation.rs similarity index 100% rename from tests/pretty/delegation.rs rename to tests/pretty/delegation/delegation.rs diff --git a/tests/pretty/delegation/generics.pp b/tests/pretty/delegation/generics.pp new file mode 100644 index 0000000000000..f5429c3c26a75 --- /dev/null +++ b/tests/pretty/delegation/generics.pp @@ -0,0 +1,131 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:generics.pp + +#![allow(incomplete_features)] +#![attr = Feature([fn_delegation#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; + +mod free_to_trait { + trait Trait<'a, XX, Y, T = (), const N: usize = 2> { + fn method(&self, t: (T, A, B), slice: &'_ [usize; N]) { } + fn r#static(t: (T, A, B), slice: &'_ [usize; N]) { } + } + + struct X; + impl Trait<'_, XX, Y, T, N> for X { } + + // When infer is specified for default parameter the generic param is generated. + #[attr = Inline(Hint)] + fn foo<'a, Self, XX, Y, T, const N: _, A, B>(self: _, arg1: _, arg2: _) + -> _ where + 'a:'a { + >::method::(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_foo<'a, Self, XX, Y, T, const N: _, A, B>(arg0: _, arg1: _) -> _ + where + 'a:'a { + >::r#static::(arg0, arg1) + } + + // When default params are omitted they are not generated but used in signature inheritance. + #[attr = Inline(Hint)] + fn bar<'a, Self, XX, Y, A, B>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { + >::method::(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_bar<'a, Self, XX, Y, A, B>(arg0: _, arg1: _) -> _ where + 'a:'a { >::r#static::(arg0, arg1) } + + // Check with user specified args in child: + // When infer is specified for default parameter the generic param is generated. + #[attr = Inline(Hint)] + fn foo1<'a, Self, XX, Y, T, const N: _, B>(self: _, arg1: _, arg2: _) -> _ + where + 'a:'a { + >::method::<(), B>(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_foo1<'a, Self, XX, Y, T, const N: _, B>(arg0: _, arg1: _) -> _ + where + 'a:'a { + >::r#static::<(), B>(arg0, arg1) + } + + // When default params are omitted they are not generated but used in signature inheritance. + #[attr = Inline(Hint)] + fn bar1<'a, Self, XX, Y, A>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { + >::method::(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_bar1<'a, Self, XX, Y, A>(arg0: _, arg1: _) -> _ where + 'a:'a { >::r#static::(arg0, arg1) } + + // Check with explicit self type. + #[attr = Inline(Hint)] + fn foo2<'a, XX, Y, T, const N: _, A, B>(self: _, arg1: _, arg2: _) -> _ + where + 'a:'a { + >::method::(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_foo2<'a, XX, Y, T, const N: _, A, B>(arg0: _, arg1: _) -> _ + where + 'a:'a { + >::r#static::(arg0, arg1) + } + + #[attr = Inline(Hint)] + fn bar2<'a, XX, Y, A, B>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { >::method::(self, arg1, arg2) } + #[attr = Inline(Hint)] + fn static_bar2<'a, XX, Y, A, B>(arg0: _, arg1: _) -> _ where + 'a:'a { >::r#static::(arg0, arg1) } + + #[attr = Inline(Hint)] + fn foo3<'a, XX, Y, T, const N: _, B>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { + >::method::<(), B>(self, arg1, arg2) + } + #[attr = Inline(Hint)] + fn static_foo3<'a, XX, Y, T, const N: _, B>(arg0: _, arg1: _) -> _ where + 'a:'a { + >::r#static::<(), B>(arg0, arg1) + } + + #[attr = Inline(Hint)] + fn bar3<'a, XX, Y, A>(self: _, arg1: _, arg2: _) -> _ where + 'a:'a { >::method::(self, arg1, arg2) } + #[attr = Inline(Hint)] + fn static_bar3<'a, XX, Y, A>(arg0: _, arg1: _) -> _ where + 'a:'a { >::r#static::(arg0, arg1) } +} + +mod trait_impl_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &'_ [usize; N]) { } + fn bar(&self, t: (T, T, T), slice: &'_ [usize; N]) { } + } + + struct S; + impl Trait<'_, X, Y> for S { } + + struct W(S); + impl Trait<'_, X, Y> for W { + // Generics of both methods match generics of their signature + // functions in `Trait` declaration, no matter specified infers. + #[attr = Inline(Hint)] + fn foo(self: _, arg1: _, arg2: _) + -> _ { Trait::<'static, X, Y>::foo(self.0, arg1, arg2) } + #[attr = Inline(Hint)] + fn bar(self: _, arg1: _, arg2: _) + -> _ { Trait::<'static, X, Y, _, _>::foo(self.0, arg1, arg2) } + } +} + +fn main() { } diff --git a/tests/pretty/delegation/generics.rs b/tests/pretty/delegation/generics.rs new file mode 100644 index 0000000000000..7491bc663393a --- /dev/null +++ b/tests/pretty/delegation/generics.rs @@ -0,0 +1,66 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:generics.pp + +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +mod free_to_trait { + trait Trait<'a, XX, Y, T = (), const N: usize = 2> { + fn method(&self, t: (T, A, B), slice: &[usize; N]) {} + fn r#static(t: (T, A, B), slice: &[usize; N]) {} + } + + struct X; + impl Trait<'_, XX, Y, T, N> for X {} + + // When infer is specified for default parameter the generic param is generated. + reuse Trait::<'_, _, _, _, _>::method as foo; + reuse Trait::<'_, _, _, _, _>::r#static as static_foo; + + // When default params are omitted they are not generated but used in signature inheritance. + reuse Trait::<'_, _, _>::method as bar; + reuse Trait::<'_, _, _>::r#static as static_bar; + + // Check with user specified args in child: + // When infer is specified for default parameter the generic param is generated. + reuse Trait::<'_, _, _, _, _>::method::<(), _> as foo1; + reuse Trait::<'_, _, _, _, _>::r#static::<(), _> as static_foo1; + + // When default params are omitted they are not generated but used in signature inheritance. + reuse Trait::<'_, _, _>::method::<_, ()> as bar1; + reuse Trait::<'_, _, _>::r#static::<_, ()> as static_bar1; + + // Check with explicit self type. + reuse >::method as foo2; + reuse >::r#static as static_foo2; + + reuse >::method as bar2; + reuse >::r#static as static_bar2; + + reuse >::method::<(), _> as foo3; + reuse >::r#static::<(), _> as static_foo3; + + reuse >::method::<_, ()> as bar3; + reuse >::r#static::<_, ()> as static_bar3; +} + +mod trait_impl_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + } + + struct S; + impl Trait<'_, X, Y> for S {} + + struct W(S); + impl Trait<'_, X, Y> for W { + // Generics of both methods match generics of their signature + // functions in `Trait` declaration, no matter specified infers. + reuse Trait::<'static, X, Y>::foo { self.0 } + reuse Trait::<'static, X, Y, _, _>::foo as bar { self.0 } + } +} + +fn main() {} diff --git a/tests/pretty/delegation-impl-reuse.pp b/tests/pretty/delegation/impl-reuse.pp similarity index 94% rename from tests/pretty/delegation-impl-reuse.pp rename to tests/pretty/delegation/impl-reuse.pp index 6c6c8a594fc8e..2097b23e5c524 100644 --- a/tests/pretty/delegation-impl-reuse.pp +++ b/tests/pretty/delegation/impl-reuse.pp @@ -2,7 +2,7 @@ #![no_std] //@ pretty-compare-only //@ pretty-mode:expanded -//@ pp-exact:delegation-impl-reuse.pp +//@ pp-exact:impl-reuse.pp #![allow(incomplete_features)] #![feature(fn_delegation)] diff --git a/tests/pretty/delegation-impl-reuse.rs b/tests/pretty/delegation/impl-reuse.rs similarity index 87% rename from tests/pretty/delegation-impl-reuse.rs rename to tests/pretty/delegation/impl-reuse.rs index 9265ea18a76f0..3e813bf10d01e 100644 --- a/tests/pretty/delegation-impl-reuse.rs +++ b/tests/pretty/delegation/impl-reuse.rs @@ -1,6 +1,6 @@ //@ pretty-compare-only //@ pretty-mode:expanded -//@ pp-exact:delegation-impl-reuse.pp +//@ pp-exact:impl-reuse.pp #![allow(incomplete_features)] #![feature(fn_delegation)] diff --git a/tests/pretty/delegation-inherit-attributes.pp b/tests/pretty/delegation/inherit-attributes.pp similarity index 98% rename from tests/pretty/delegation-inherit-attributes.pp rename to tests/pretty/delegation/inherit-attributes.pp index e29f76089256d..a45eb5a1c5f38 100644 --- a/tests/pretty/delegation-inherit-attributes.pp +++ b/tests/pretty/delegation/inherit-attributes.pp @@ -2,7 +2,7 @@ //@ aux-crate:to_reuse_functions=to-reuse-functions.rs //@ pretty-mode:hir //@ pretty-compare-only -//@ pp-exact:delegation-inherit-attributes.pp +//@ pp-exact:inherit-attributes.pp #![allow(incomplete_features)] #![attr = Feature([fn_delegation#0])] diff --git a/tests/pretty/delegation-inherit-attributes.rs b/tests/pretty/delegation/inherit-attributes.rs similarity index 97% rename from tests/pretty/delegation-inherit-attributes.rs rename to tests/pretty/delegation/inherit-attributes.rs index 581294d472a33..538fb7cd7d75a 100644 --- a/tests/pretty/delegation-inherit-attributes.rs +++ b/tests/pretty/delegation/inherit-attributes.rs @@ -2,7 +2,7 @@ //@ aux-crate:to_reuse_functions=to-reuse-functions.rs //@ pretty-mode:hir //@ pretty-compare-only -//@ pp-exact:delegation-inherit-attributes.pp +//@ pp-exact:inherit-attributes.pp #![allow(incomplete_features)] #![feature(fn_delegation)] diff --git a/tests/pretty/delegation-inline-attribute.pp b/tests/pretty/delegation/inline-attribute.pp similarity index 98% rename from tests/pretty/delegation-inline-attribute.pp rename to tests/pretty/delegation/inline-attribute.pp index 8cf2d98eeb412..361eb56f558f4 100644 --- a/tests/pretty/delegation-inline-attribute.pp +++ b/tests/pretty/delegation/inline-attribute.pp @@ -1,6 +1,6 @@ //@ pretty-compare-only //@ pretty-mode:hir -//@ pp-exact:delegation-inline-attribute.pp +//@ pp-exact:inline-attribute.pp #![allow(incomplete_features)] #![attr = Feature([fn_delegation#0])] diff --git a/tests/pretty/delegation-inline-attribute.rs b/tests/pretty/delegation/inline-attribute.rs similarity index 98% rename from tests/pretty/delegation-inline-attribute.rs rename to tests/pretty/delegation/inline-attribute.rs index c79f68f8942d2..f390bf64b24a7 100644 --- a/tests/pretty/delegation-inline-attribute.rs +++ b/tests/pretty/delegation/inline-attribute.rs @@ -1,6 +1,6 @@ //@ pretty-compare-only //@ pretty-mode:hir -//@ pp-exact:delegation-inline-attribute.pp +//@ pp-exact:inline-attribute.pp #![allow(incomplete_features)] #![feature(fn_delegation)] diff --git a/tests/pretty/delegation/self-mapping-output.pp b/tests/pretty/delegation/self-mapping-output.pp new file mode 100644 index 0000000000000..84e98d6e97b06 --- /dev/null +++ b/tests/pretty/delegation/self-mapping-output.pp @@ -0,0 +1,44 @@ +#![attr = Feature([fn_delegation#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:self-mapping-output.pp + + +trait Trait { + fn method(&self) + -> Self; + fn r#static() + -> Self; + fn raw_S(&self) -> S { S } +} + +struct S; +impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } +} + +struct W(S); +impl Trait for W { + #[attr = Inline(Hint)] + fn method(self: _) -> _ { Self { 0: Trait::method(self.0) } } + #[attr = Inline(Hint)] + fn r#static() -> _ { Trait::r#static() } + //~^ WARN: function cannot return without recursing [unconditional_recursion] + #[attr = Inline(Hint)] + fn raw_S(self: _) -> _ { Trait::raw_S(self.0) } +} + +impl W { + #[attr = Inline(Hint)] + fn method(self: _) -> _ { Self { 0: Trait::method(self.0) } } + #[attr = Inline(Hint)] + fn r#static() -> _ { Trait::r#static() } + #[attr = Inline(Hint)] + fn raw_S(self: _) -> _ { Trait::raw_S(self.0) } +} + +fn main() { } diff --git a/tests/pretty/delegation/self-mapping-output.rs b/tests/pretty/delegation/self-mapping-output.rs new file mode 100644 index 0000000000000..11699d710e081 --- /dev/null +++ b/tests/pretty/delegation/self-mapping-output.rs @@ -0,0 +1,33 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:self-mapping-output.pp + +#![feature(fn_delegation)] + +trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } +} + +struct S; +impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } +} + +struct W(S); +impl Trait for W { + reuse Trait::method { self.0 } + reuse Trait::r#static; + //~^ WARN: function cannot return without recursing [unconditional_recursion] + reuse Trait::raw_S { self.0 } +} + +impl W { + reuse Trait::method { self.0 } + reuse Trait::r#static; + reuse Trait::raw_S { self.0 } +} + +fn main() {} diff --git a/tests/pretty/delegation-self-rename.pp b/tests/pretty/delegation/self-rename.pp similarity index 79% rename from tests/pretty/delegation-self-rename.pp rename to tests/pretty/delegation/self-rename.pp index 7f7afc403607b..661a0ab5490eb 100644 --- a/tests/pretty/delegation-self-rename.pp +++ b/tests/pretty/delegation/self-rename.pp @@ -4,7 +4,7 @@ use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir -//@ pp-exact:delegation-self-rename.pp +//@ pp-exact:self-rename.pp trait Trait<'a, A, const B: bool> { @@ -19,15 +19,18 @@ #[attr = Inline(Hint)] fn foo<'a, Self, A, const B: _, const B2: _, T, U, impl FnOnce() -> usize>(self: _, arg1: _) -> _ where - 'a:'a { Trait::<'a, A, B>::foo::(self, arg1) } + 'a:'a { >::foo::(self, arg1) } #[attr = Inline(Hint)] fn bar usize>(self: _, arg1: _) - -> _ { Trait::<'static, (), true>::foo::(self, arg1) } + -> + _ { + >::foo::(self, arg1) +} #[attr = Inline(Hint)] fn foo2<'a, This, A, const B: _, const B2: _, T, U, impl FnOnce() -> usize>(arg0: _, arg1: _) -> _ where - 'a:'a { foo::(arg0, arg1) } + 'a:'a { foo::<'a, This, A, B, B2, T, U>(arg0, arg1) } #[attr = Inline(Hint)] fn bar2 usize>(arg0: _, arg1: _) -> _ { bar::(arg0, arg1) } @@ -36,7 +39,7 @@ #[attr = Inline(Hint)] fn foo3<'a, This, A, const B: _, const B2: _, T, U, impl FnOnce() -> usize>(arg0: _, arg1: _) -> _ where - 'a:'a { foo2::(arg0, arg1) } + 'a:'a { foo2::<'a, This, A, B, B2, T, U>(arg0, arg1) } #[attr = Inline(Hint)] fn bar3 usize>(arg0: _, arg1: _) -> _ { bar2::(arg0, arg1) } @@ -47,7 +50,7 @@ #[attr = Inline(Hint)] fn foo4<'a, This, A, const B: _, const B2: _, T, U, impl FnOnce() -> usize>(arg0: _, arg1: _) -> _ where - 'a:'a { <() as Trait2>::foo3::(arg0, arg1) } + 'a:'a { <() as Trait2>::foo3::<'a, This, A, B, B2, T, U>(arg0, arg1) } #[attr = Inline(Hint)] fn bar4 usize>(arg0: _, arg1: _) -> _ { <() as Trait2>::bar3::(arg0, arg1) } diff --git a/tests/pretty/delegation-self-rename.rs b/tests/pretty/delegation/self-rename.rs similarity index 93% rename from tests/pretty/delegation-self-rename.rs rename to tests/pretty/delegation/self-rename.rs index 9054bb2b89571..c04980508cb85 100644 --- a/tests/pretty/delegation-self-rename.rs +++ b/tests/pretty/delegation/self-rename.rs @@ -1,6 +1,6 @@ //@ pretty-compare-only //@ pretty-mode:hir -//@ pp-exact:delegation-self-rename.pp +//@ pp-exact:self-rename.pp #![feature(fn_delegation)] diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index 96b8ade1f1b16..7be68faccc714 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -6,6 +6,8 @@ //@ needs-target-std //@ ignore-android: FIXME(#142855) //@ ignore-sgx: (x86 machine code cannot be directly executed) +//@ ignore-pauthtest: (it requires non-trivial compilation of c sources, and only supports dynamic +// linking, ignore the test). use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; diff --git a/tests/run-make/cdylib-export-c-library-symbols/rmake.rs b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs index b71ff7bd6d8d7..286eb95c41561 100644 --- a/tests/run-make/cdylib-export-c-library-symbols/rmake.rs +++ b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs @@ -1,41 +1,54 @@ //@ ignore-nvptx64 //@ ignore-wasm //@ ignore-cross-compile -// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. -// Need to be resolved. -//@ ignore-windows -//@ ignore-apple // Reason: the compiled binary is executed use run_make_support::{ - build_native_static_lib, cc, dynamic_lib_name, is_aix, is_darwin, llvm_nm, rustc, + build_native_static_lib, cc, dynamic_lib_name, is_aix, is_darwin, is_windows, is_windows_msvc, + llvm_ar, llvm_nm, llvm_readobj, rfs, rustc, static_lib_name, }; fn main() { - cc().input("foo.c").arg("-c").out_exe("foo.o").run(); - build_native_static_lib("foo"); + let obj_file = if is_windows_msvc() { "foo.obj" } else { "foo.o" }; + cc().input("foo.c").arg("-c").arg("-fno-lto").out_exe(obj_file).run(); + llvm_ar().obj_to_ar().output_input(&static_lib_name("foo"), obj_file).run(); rustc().input("foo.rs").arg("-lstatic=foo").crate_type("cdylib").run(); - let out = llvm_nm() - .input(dynamic_lib_name("foo")) - .run() - .assert_stdout_not_contains_regex("T *my_function"); + if is_darwin() { + llvm_nm().input(dynamic_lib_name("foo")).run().assert_stdout_not_contains("T _my_function"); + } else if is_windows() { + llvm_readobj() + .arg("--coff-exports") + .input(dynamic_lib_name("foo")) + .run() + .assert_stdout_not_contains("my_function"); + } else { + llvm_nm().input(dynamic_lib_name("foo")).run().assert_stdout_not_contains("T my_function"); + } + + rfs::remove_file(dynamic_lib_name("foo")); rustc().input("foo_export.rs").arg("-lstatic:+export-symbols=foo").crate_type("cdylib").run(); if is_darwin() { - let out = llvm_nm() + llvm_nm() .input(dynamic_lib_name("foo_export")) .run() .assert_stdout_contains("T _my_function"); + } else if is_windows() { + llvm_readobj() + .arg("--coff-exports") + .input(dynamic_lib_name("foo_export")) + .run() + .assert_stdout_contains("my_function"); } else if is_aix() { - let out = llvm_nm() + llvm_nm() .input(dynamic_lib_name("foo_export")) .run() .assert_stdout_contains("T .my_function"); } else { - let out = llvm_nm() + llvm_nm() .input(dynamic_lib_name("foo_export")) .run() .assert_stdout_contains("T my_function"); diff --git a/tests/run-make/dll-weak-definition/rmake.rs b/tests/run-make/dll-weak-definition/rmake.rs new file mode 100644 index 0000000000000..1b05bdecc2cdf --- /dev/null +++ b/tests/run-make/dll-weak-definition/rmake.rs @@ -0,0 +1,23 @@ +// Regression test for MSVC link.exe failing to export weak definitions from dlls. +// See https://github.com/rust-lang/rust/pull/158294 + +//@ only-msvc +//@ needs-rust-lld + +use run_make_support::{dynamic_lib_name, llvm_readobj, rustc}; + +fn test_with_linker(linker: &str) { + rustc().input("weak.rs").linker(linker).run(); + + llvm_readobj() + .arg("--coff-exports") + .input(dynamic_lib_name("weak")) + .run() + .assert_stdout_contains("Name: weak_function") + .assert_stdout_contains("Name: WEAK_STATIC"); +} + +fn main() { + test_with_linker("link"); + test_with_linker("rust-lld"); +} diff --git a/tests/run-make/dll-weak-definition/weak.rs b/tests/run-make/dll-weak-definition/weak.rs new file mode 100644 index 0000000000000..93b19ad627910 --- /dev/null +++ b/tests/run-make/dll-weak-definition/weak.rs @@ -0,0 +1,10 @@ +#![feature(linkage)] +#![crate_type = "cdylib"] + +#[linkage = "weak"] +#[no_mangle] +pub fn weak_function() {} + +#[linkage = "weak"] +#[no_mangle] +pub static WEAK_STATIC: u8 = 42; diff --git a/tests/run-make/pauth-quicksort-c-driver/main.c b/tests/run-make/pauth-quicksort-c-driver/main.c new file mode 100644 index 0000000000000..58f8d149cc446 --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/main.c @@ -0,0 +1,45 @@ +#include +#include + +#define NUM_ELEMS 5 + +void quickSort(void *Base, size_t N, size_t Size, + int (*Cmp)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif + +int cmpI32Ascending(const void *LHS, const void *RHS) { + int32_t x = *(const int32_t *)LHS; + int32_t y = *(const int32_t *)RHS; + + if (x < y) + return -1; + else if (x > y) + return 1; + else + return 0; +} + +int main() { + int32_t Data[NUM_ELEMS] = {4, 2, 5, 3, 1}; + + printf("Before sorting: "); + for (int i = 0; i < NUM_ELEMS; i++) + printf("%d ", Data[i]); + printf("\n"); + + quickSort(Data, NUM_ELEMS, sizeof(int32_t), cmpI32Ascending); + + printf("After sorting: "); + for (int i = 0; i < NUM_ELEMS; i++) + printf("%d ", Data[i]); + printf("\n"); + + for (size_t i = 1; i < NUM_ELEMS; i++) + if (Data[i - 1] > Data[i]) + return 42; + + return 0; +} diff --git a/tests/run-make/pauth-quicksort-c-driver/quicksort.rs b/tests/run-make/pauth-quicksort-c-driver/quicksort.rs new file mode 100644 index 0000000000000..ac73dced2e1fc --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/quicksort.rs @@ -0,0 +1,66 @@ +use std::mem::size_of; +use std::os::raw::{c_int, c_void}; +use std::ptr; + +unsafe fn swap_i32(lhs: *mut i32, rhs: *mut i32) { + ptr::swap(lhs, rhs); +} + +unsafe fn partition( + arr: *mut i32, + low: isize, + high: isize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) -> isize { + let pivot = arr.offset(low); + let mut i = low; + let mut j = high; + + while i < j { + while i <= high - 1 && cmp(arr.offset(i) as *const c_void, pivot as *const c_void) <= 0 { + i += 1; + } + + while j >= low + 1 && cmp(arr.offset(j) as *const c_void, pivot as *const c_void) > 0 { + j -= 1; + } + + if i < j { + swap_i32(arr.offset(i), arr.offset(j)); + } + } + + swap_i32(arr.offset(low), arr.offset(j)); + j +} + +unsafe fn quicksort_rec( + arr: *mut i32, + low: isize, + high: isize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + if low < high { + let part = partition(arr, low, high, cmp); + quicksort_rec(arr, low, part - 1, cmp); + quicksort_rec(arr, part + 1, high, cmp); + } +} + +#[no_mangle] +pub extern "C" fn quickSort( + base: *mut c_void, + n: usize, + size: usize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + if size != size_of::() { + std::process::abort(); + } + + if n > 1 { + unsafe { + quicksort_rec(base as *mut i32, 0, (n as isize) - 1, cmp); + } + } +} diff --git a/tests/run-make/pauth-quicksort-c-driver/rmake.rs b/tests/run-make/pauth-quicksort-c-driver/rmake.rs new file mode 100644 index 0000000000000..3cf978a2f6da5 --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/rmake.rs @@ -0,0 +1,42 @@ +// Test compilation flow using custom pauth-enabled toolchain and signing extern "C" function +// pointers used from within rust. The test assumes that pointer-authentication-enabled `clang` is +// available on the path. In this test rust is the driver - providing the data and the comparison +// function; while c - provides the implementation of quicksort algorithm and is the user of the +// data and comparator. + +//@ only-pauthtest + +use run_make_support::{cc, env_var, rfs, run, run_fail, rustc}; + +fn main() { + // Use CC and CC_DEFAULT_FLAGS env variables to set up linker for rustc. This results in the + // same command as cc(). The CC env variable corresponds to cc field in the config toml file. + // This field is required to point to a clang family compiler on aarch64-unknown-linux-pauthtest + // target. + let rust_lib_name = "rust_quicksort"; + rustc() + .target("aarch64-unknown-linux-pauthtest") + .crate_type("cdylib") + .input("quicksort.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .crate_name(rust_lib_name) + .run(); + + let exe_name = "main"; + cc().out_exe(exe_name) + .input("main.c") + .args(&[ + "-march=armv8.3-a+pauth", + "-target", + "aarch64-unknown-linux-pauthtest", + &format!("-l{}", rust_lib_name), + ]) + .library_search_path(".") + .run(); + + run(exe_name); + + rfs::remove_file(format!("{}{rust_lib_name}.{}", "lib", "so")); + run_fail(exe_name); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/main.rs b/tests/run-make/pauth-quicksort-rust-driver/main.rs new file mode 100644 index 0000000000000..37700e0ddc169 --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/main.rs @@ -0,0 +1,43 @@ +use std::os::raw::{c_int, c_void}; + +#[link(name = "quicksort")] +extern "C" { + fn quickSort( + base: *mut c_void, + n: usize, + size: usize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, + ); +} + +extern "C" fn cmp_i32_ascending(a: *const c_void, b: *const c_void) -> c_int { + unsafe { + let x = *(a as *const i32); + let y = *(b as *const i32); + + if x < y { + -1 + } else if x > y { + 1 + } else { + 0 + } + } +} + +fn main() { + let mut data: [i32; 5] = [4, 2, 5, 3, 1]; + println!("Before sorting: {:?}", data); + + unsafe { + quickSort( + data.as_mut_ptr() as *mut c_void, + data.len(), + std::mem::size_of::(), + cmp_i32_ascending, + ); + } + + println!("After sorting: {:?}", data); + assert!(data.windows(2).all(|w| w[0] <= w[1])); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/quicksort.c b/tests/run-make/pauth-quicksort-rust-driver/quicksort.c new file mode 100644 index 0000000000000..029435d562bb6 --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/quicksort.c @@ -0,0 +1,50 @@ +#include +#include +#include + +void swap(void *A, void *B, size_t Size) { + unsigned char Tmp[Size]; + memcpy(Tmp, A, Size); + memcpy(A, B, Size); + memcpy(B, Tmp, Size); +} + +int partition(void *Base, int Low, int High, size_t Size, + int (*Cmp)(const void *, const void *)) { + char *Arr = (char *)Base; + + void *Pivot = Arr + Low * Size; + int i = Low; + int j = High; + + while (i < j) { + while (i <= High - 1 && Cmp(Arr + i * Size, Pivot) <= 0) + i++; + + while (j >= Low + 1 && Cmp(Arr + j * Size, Pivot) > 0) + j--; + + if (i < j) + swap(Arr + i * Size, Arr + j * Size, Size); + } + + swap(Arr + Low * Size, Arr + j * Size, Size); + return j; +} + +void quickSortRec(void *Base, int Low, int High, size_t Size, + int (*Cmp)(const void *, const void *)) { + if (Low < High) { + int Part = partition(Base, Low, High, Size, Cmp); + quickSortRec(Base, Low, Part - 1, Size, Cmp); + quickSortRec(Base, Part + 1, High, Size, Cmp); + } +} + +void quickSort(void *Base, size_t N, size_t Size, + int (*Cmp)(const void *, const void *)) { + if (Size != sizeof(int32_t)) + abort(); + if (N > 1) + quickSortRec(Base, 0, (int)N - 1, Size, Cmp); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/rmake.rs b/tests/run-make/pauth-quicksort-rust-driver/rmake.rs new file mode 100644 index 0000000000000..e77081d70039c --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/rmake.rs @@ -0,0 +1,34 @@ +// Test compilation flow using custom pauth-enabled toolchain and signing extern "C" function +// pointers used from within rust. The test assumes that pointer-authentication-enabled `clang` is +// available on the path. +// In this test rust is the driver - providing the data and the comparison function; while c - +// provides the implementation of quicksort algorithm and is the user of the data and comparator. + +//@ only-pauthtest + +use run_make_support::{cc, env_var, rfs, run, run_fail, rustc}; + +fn main() { + let input = "quicksort"; + let input_name = format!("{input}.c"); + let lib_name = format!("{}{input}.{}", "lib", "so"); + cc().out_exe(&lib_name) + .input(&input_name) + .args(&["-target", "aarch64-unknown-linux-pauthtest", "-march=armv8.3-a+pauth", "-shared"]) + .run(); + + // Use CC and CC_DEFAULT_FLAGS env variables to set up linker for rustc. This results in the + // same command as cc(). The CC env variable corresponds to cc field in the config toml file. + // This field is required to point to a clang family compiler on aarch64-unknown-linux-pauthtest + // target. + rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .run(); + run("main"); + + rfs::remove_file(&lib_name); + run_fail("main"); +} diff --git a/tests/run-make/pauth-static-link-warning/helper.c b/tests/run-make/pauth-static-link-warning/helper.c new file mode 100644 index 0000000000000..fb2bfd5cb990e --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/helper.c @@ -0,0 +1 @@ +int helper_function() { return 42; } diff --git a/tests/run-make/pauth-static-link-warning/main.rs b/tests/run-make/pauth-static-link-warning/main.rs new file mode 100644 index 0000000000000..0323db8838559 --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/main.rs @@ -0,0 +1,10 @@ +#[link(name = "helper", kind = "static")] +extern "C" { + fn helper_function() -> i32; +} + +fn main() { + unsafe { + assert!(42 == helper_function()); + } +} diff --git a/tests/run-make/pauth-static-link-warning/main_cmd_line.rs b/tests/run-make/pauth-static-link-warning/main_cmd_line.rs new file mode 100644 index 0000000000000..a0edd66ce9910 --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/main_cmd_line.rs @@ -0,0 +1,9 @@ +extern "C" { + fn helper_function() -> i32; +} + +fn main() { + unsafe { + assert!(42 == helper_function()); + } +} diff --git a/tests/run-make/pauth-static-link-warning/rmake.rs b/tests/run-make/pauth-static-link-warning/rmake.rs new file mode 100644 index 0000000000000..8d4fb18fe33f9 --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/rmake.rs @@ -0,0 +1,46 @@ +// Make sure that for `aarch64-unknown-linux-pauthtest` compiler emits warning when static +// libraries are linked. Test both foreign module linked from #[link] directive and command line +// invocations. + +//@ only-pauthtest +// ignore-tidy-linelength + +use run_make_support::{cc, env_var, regex, run, rustc}; + +fn main() { + let input = "helper"; + let input_name = format!("{input}.c"); + let lib_name = format!("{}{input}.{}", "lib", "a"); + // Build a static library + cc().out_exe(&lib_name) + .input(&input_name) + .args(&["-target", "aarch64-unknown-linux-pauthtest", "-march=armv8.3-a+pauth", "-c"]) + .run(); + + // Check against foreign module warning: #[link(name = "helper", kind = "static")] + let stderr_foreign_module = rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .arg("-L.") + .run() + .stderr_utf8(); + run("main"); + let re_foreign_moule = regex::Regex::new( r"(?s)warning: library `helper`.*linked statically.*aarch64-unknown-linux-pauthtest.*requires dynamic linking.*using dynamic linking instead").unwrap(); + assert!(re_foreign_moule.is_match(&stderr_foreign_module)); + + // Check against command line warning: -lstatic=helper + let stderr_command_line = rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main_cmd_line.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .arg("-L.") + .arg("-lstatic=helper") + .run() + .stderr_utf8(); + run("main_cmd_line"); + let re_cmd_line = regex::Regex::new( r"(?s)warning: static linking of `helper`.*is not supported on.*aarch64-unknown-linux-pauthtest.*using dynamic linking instead").unwrap(); + assert!(re_cmd_line.is_match(&stderr_command_line)); +} diff --git a/tests/run-make/rustdoc-test-builder/rmake.rs b/tests/run-make/rustdoc-test-builder/rmake.rs index 17d40c68fd922..5520426e16fa8 100644 --- a/tests/run-make/rustdoc-test-builder/rmake.rs +++ b/tests/run-make/rustdoc-test-builder/rmake.rs @@ -27,6 +27,7 @@ fn main() { // so only exercise the success path when the target can run on the host. if target().contains("wasm") || target().contains("sgx") + || target().contains("pauthtest") || std::env::var_os("REMOTE_TEST_CLIENT").is_some() { return; diff --git a/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs b/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs new file mode 100644 index 0000000000000..0294308d199f2 --- /dev/null +++ b/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs @@ -0,0 +1,54 @@ +//! Regression test for . +//! +//! The prebuilt `libcompiler_builtins` rlib bundled in the wasm sysroot must +//! contain wasm object files — never host ELF/Mach-O/COFF. Bootstrap could +//! previously pick the host C toolchain for compiler-rt fallbacks on wasm +//! targets and silently embed host objects into the wasm sysroot +//! (fixed in rust-lang/rust#137457). + +//@ only-wasm32 + +use run_make_support::object::read::Object; +use run_make_support::object::read::archive::ArchiveFile; +use run_make_support::object::{self, Architecture}; +use run_make_support::{has_extension, has_prefix, rfs, rustc, shallow_find_files}; + +fn main() { + let libdir = rustc().print("target-libdir").run().stdout_utf8(); + let libdir = libdir.trim(); + + let rlibs = shallow_find_files(libdir, |path| { + has_prefix(path, "libcompiler_builtins") && has_extension(path, "rlib") + }); + assert!(!rlibs.is_empty(), "no libcompiler_builtins rlib found in {libdir}"); + + let data = rfs::read(&rlibs[0]); + let archive = ArchiveFile::parse(&*data).unwrap(); + + let mut checked = 0usize; + for member in archive.members() { + let member = member.unwrap(); + let name = std::str::from_utf8(member.name()).unwrap_or(""); + if name.ends_with(".rmeta") || name.ends_with(".rmeta-link") { + continue; + } + let obj_data = member.data(&*data).unwrap(); + let obj = object::File::parse(obj_data).unwrap_or_else(|e| { + panic!("failed to parse member `{name}` in compiler_builtins rlib: {e}") + }); + let arch = obj.architecture(); + assert!( + matches!(arch, Architecture::Wasm32 | Architecture::Wasm64), + "object `{name}` in compiler_builtins rlib has architecture {arch:?}, \ + expected wasm — see rust-lang/rust#132802", + ); + checked += 1; + } + + assert!( + checked > 0, + "no object members found in compiler_builtins rlib at {} — \ + archive should always contain object files", + rlibs[0].display(), + ); +} diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs index ea0b6faf037c5..f6504830a5263 100644 --- a/tests/run-make/wasm-panic-small/rmake.rs +++ b/tests/run-make/wasm-panic-small/rmake.rs @@ -24,5 +24,5 @@ fn test(cfg: &str) { let bytes = rfs::read("foo.wasm"); println!("{}", bytes.len()); - assert!(bytes.len() < 40_000, "bytes len was: {}", bytes.len()); + assert!(bytes.len() < 45_000, "bytes len was: {}", bytes.len()); } diff --git a/tests/rustdoc-gui/setting-go-to-only-result.goml b/tests/rustdoc-gui/setting-go-to-only-result.goml index 72c1e2bf59ca4..f94f45fe102ef 100644 --- a/tests/rustdoc-gui/setting-go-to-only-result.goml +++ b/tests/rustdoc-gui/setting-go-to-only-result.goml @@ -33,16 +33,18 @@ assert-local-storage: {"rustdoc-go-to-only-result": "true"} go-to: "file://" + |DOC_PATH| + "/lib2/index.html" // We enter it into the search. +click: "#search-button" +wait-for: "#alternative-display .search-input" write-into: (".search-input", "HasALongTraitWithParams") wait-for-document-property: {"title": "HasALongTraitWithParams in lib2 - Rust"} -assert-window-property: ({"location": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH) +assert-window-property: ({"location"."pathname": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH) // Regression test for . // If "go-to-only-result" is enabled and you go back to history, it should not lead you back to the // page result again automatically. history-go-back: wait-for-document-property: {"title": "lib2 - Rust"} -assert-window-property: ({"location": "/lib2/index.html"}, ENDS_WITH) +assert-window-property: ({"location"."pathname": "/lib2/index.html"}, ENDS_WITH) // We try again to see if it goes to the only result go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=HasALongTraitWithParams" @@ -69,3 +71,43 @@ call-function: ("check-setting", { "storage_value": "false", "setting_attribute_value": "false", }) + +define-function: ( + "search-multiple-results", + [location], + block { + // We wait for the search to be done. + wait-for: ".search-form" + // We ensure the page didn't change. + assert-window-property: {"location"."pathname": |location|} + // We ensure that there is more than 0 results. + wait-for-text: ("#search-tabs > button:nth-child(1) .count", "(2)", CONTAINS) + wait-for-text: ("#search-tabs > button:nth-child(2) .count", "(1)", CONTAINS) + wait-for-text: ("#search-tabs > button:nth-child(3) .count", "(0)", CONTAINS) + }, +) + +// This is a regression test to ensure that it works when the setting is switched in-between. It +// could be reproduced by: +// 1. Doing a search (with "in parameters" or "in return types" having exactly one result) +// 2. Enabling the setting to true. +// 3. Reloading the page. + +// First we go to a different page. +go-to: "file://" + |DOC_PATH| + "/lib2/index.html" + +// The setting should be disabled. +assert-local-storage-false: {"rustdoc-go-to-only-result": "true"} +store-window-property: {"location"."pathname": location} +click: "#search-button" +wait-for: "#alternative-display .search-input" +write-into: (".search-input", "Whitespace") +call-function: ("search-multiple-results", {"location": |location|}) + +// We now toggle the setting and reload the page. +call-function: ("open-settings-menu", {}) +click: "#go-to-only-result" +assert-local-storage: {"rustdoc-go-to-only-result": "true"} + +reload: +call-function: ("search-multiple-results", {"location": |location|}) diff --git a/tests/rustdoc-html/doc-cfg/all-targets.rs b/tests/rustdoc-html/doc-cfg/all-targets.rs index 605a27a7d8927..d5a8be83bc1d3 100644 --- a/tests/rustdoc-html/doc-cfg/all-targets.rs +++ b/tests/rustdoc-html/doc-cfg/all-targets.rs @@ -3,9 +3,9 @@ //@ has all_targets/fn.foo.html \ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ // 'Available on target_env=fake_env or Catalyst or GNU or Managarm C Library \ -// or MSVC or musl or Neutrino 7.0 or Neutrino 7.1 or Neutrino 7.1 with io-sock \ -// or Neutrino 8.0 or Newlib or OpenHarmony or relibc or SGX or Simulator or \ -// uClibc or V5 or WASIp1 or WASIp2 or WASIp3 only.' +// or MSVC or musl or Newlib or OpenHarmony or QNX SDP 7.0 or QNX SDP 7.1 or \ +// QNX SDP 7.1 with io-sock or relibc or SGX or Simulator or uClibc or V5 or WASIp1 \ +// or WASIp2 or WASIp3 only.' #[doc(cfg(any( target_env = "gnu", target_env = "macabi", @@ -16,7 +16,6 @@ target_env = "nto70", target_env = "nto71", target_env = "nto71_iosock", - target_env = "nto80", target_env = "ohos", target_env = "relibc", target_env = "sgx", @@ -81,7 +80,7 @@ pub fn bar() {} // and HelenOS and Hermit and Horizon and illumos and iOS and L4Re and Linux \ // and LynxOS-178 and macOS and Managarm and Motor OS and NetBSD and NuttX \ // and OpenBSD and Play Station 1 and Play Station Portable and Play Station Vita \ -// and QNX Neutrino and QuRT and Redox OS and RTEMS OS and Solaris and \ +// and QNX SDP 7.x and QNX SDP 8.0+ and QuRT and Redox OS and RTEMS OS and Solaris and \ // SOLID ASP3 and TEEOS and Trusty and tvOS and UEFI and VEXos and visionOS \ // and VxWorks and WASI and watchOS and Windows and Xous and zero knowledge \ // Virtual Machine only.' @@ -112,6 +111,7 @@ pub fn bar() {} target_os = "netbsd", target_os = "none", target_os = "nto", + target_os = "qnx", target_os = "nuttx", target_os = "openbsd", target_os = "psp", diff --git a/tests/rustdoc-html/cfg-bool.rs b/tests/rustdoc-html/doc-cfg/cfg-bool.rs similarity index 100% rename from tests/rustdoc-html/cfg-bool.rs rename to tests/rustdoc-html/doc-cfg/cfg-bool.rs diff --git a/tests/rustdoc-html/doc-cfg/decl-macro.rs b/tests/rustdoc-html/doc-cfg/decl-macro.rs index e97da8647a6ab..ce4cd36d1367e 100644 --- a/tests/rustdoc-html/doc-cfg/decl-macro.rs +++ b/tests/rustdoc-html/doc-cfg/decl-macro.rs @@ -45,7 +45,7 @@ pub mod auto_cfg_disabled { } #[cfg(feature = "routing")] -#[doc(auto_cfg(hide(feature = "routing")))] +#[doc(auto_cfg(hide(feature, values("routing"))))] pub mod auto_cfg_hidden { //@ count 'foo/macro.hidden_cfg_macro.html' '//*[@class="stab portability"]' 0 #[macro_export] @@ -55,9 +55,9 @@ pub mod auto_cfg_hidden { } #[cfg(feature = "routing")] -#[doc(auto_cfg(hide(feature = "routing")))] +#[doc(auto_cfg(hide(feature, values("routing"))))] pub mod auto_cfg_shown { - #[doc(auto_cfg(show(feature = "routing")))] + #[doc(auto_cfg(show(feature, values("routing"))))] pub mod inner { //@ has 'foo/macro.shown_cfg_macro.html' '//*[@class="stab portability"]' 'Available on crate feature routing only.' #[macro_export] diff --git a/tests/rustdoc-html/doc_auto_cfg.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg-2.rs similarity index 90% rename from tests/rustdoc-html/doc_auto_cfg.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg-2.rs index a1903e1a0ca3d..a33c5830157fb 100644 --- a/tests/rustdoc-html/doc_auto_cfg.rs +++ b/tests/rustdoc-html/doc-cfg/doc-auto-cfg-2.rs @@ -2,7 +2,7 @@ #![crate_name = "foo"] #![feature(doc_cfg)] -#![doc(auto_cfg(hide(feature = "hidden")))] +#![doc(auto_cfg(hide(feature, values("hidden"))))] //@ has 'foo/index.html' //@ !has - '//*[@class="stab portability"]' 'Non-moustache' @@ -17,18 +17,18 @@ pub mod m { pub struct A; //@ has 'foo/m/inner/index.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' - #[doc(auto_cfg(show(feature = "hidden")))] + #[doc(auto_cfg(show(feature, values("hidden"))))] pub mod inner { //@ has 'foo/m/inner/struct.B.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' pub struct B; //@ count 'foo/m/inner/struct.A.html' '//*[@class="stab portability"]' 0 - #[doc(auto_cfg(hide(feature = "hidden")))] + #[doc(auto_cfg(hide(feature, values("hidden"))))] pub struct A; } //@ has 'foo/m/struct.B.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' - #[doc(auto_cfg(show(feature = "hidden")))] + #[doc(auto_cfg(show(feature, values("hidden"))))] pub struct B; } @@ -61,7 +61,7 @@ pub mod n { // Should re-enable `auto_cfg` and make `moustache` listed. //@ has 'foo/n/struct.Z.html' '//*[@class="stab portability"]' \ // 'Available on non-crate feature moustache only.' - #[doc(auto_cfg(hide(feature = "hidden")))] + #[doc(auto_cfg(hide(feature, values("hidden"))))] pub struct Z; } diff --git a/tests/rustdoc-html/doc-auto-cfg-public-in-private.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg-public-in-private.rs similarity index 100% rename from tests/rustdoc-html/doc-auto-cfg-public-in-private.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg-public-in-private.rs diff --git a/tests/rustdoc-html/doc-auto-cfg.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg.rs similarity index 100% rename from tests/rustdoc-html/doc-auto-cfg.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg.rs diff --git a/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs b/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs index e919206d3a478..72c33d209c7a1 100644 --- a/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs +++ b/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs @@ -1,7 +1,7 @@ #![crate_name = "oud"] #![feature(doc_cfg)] -#![doc(auto_cfg(hide(feature = "solecism")))] +#![doc(auto_cfg(hide(feature, values("solecism"))))] //@ has 'oud/struct.Solecism.html' //@ count - '//*[@class="stab portability"]' 0 diff --git a/tests/rustdoc-html/doc_auto_cfg_reexports.rs b/tests/rustdoc-html/doc-cfg/doc_auto_cfg_reexports.rs similarity index 100% rename from tests/rustdoc-html/doc_auto_cfg_reexports.rs rename to tests/rustdoc-html/doc-cfg/doc_auto_cfg_reexports.rs diff --git a/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs b/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs new file mode 100644 index 0000000000000..d3842b4b9591c --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs @@ -0,0 +1,26 @@ +// This test ensures that `auto_cfg(hide(key))` does not hide `key = value` and that +// `auto_cfg(hide(key, values(none())))` does the same. + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +#![doc(auto_cfg(hide(meow)))] +#![doc(auto_cfg(hide(another_meow, values(none()))))] + +//@ has foo/fn.foo.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-another_meow' +#[cfg(not(meow))] +#[cfg(not(another_meow))] +#[cfg(not(blob))] +pub fn foo() {} + +//@ has foo/fn.bar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-another_meow=lol' +#[cfg(not(meow = "lol"))] +#[cfg(not(another_meow = "lol"))] +#[cfg(not(blob = "lol"))] +pub fn bar() {} diff --git a/tests/rustdoc-html/doc-cfg/hide-inheritance.rs b/tests/rustdoc-html/doc-cfg/hide-inheritance.rs new file mode 100644 index 0000000000000..3f0e2ffca2136 --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/hide-inheritance.rs @@ -0,0 +1,179 @@ +// This test ensures that using `auto_cfg(show(key))` works correctly. + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +#![doc(auto_cfg(hide(meow)))] +#![doc(auto_cfg(hide(meow, values("lol"))))] + +//@ has foo/fn.foo.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' +#[cfg(not(meow))] +#[cfg(not(blob))] +pub fn foo() {} + +//@ has foo/fn.bar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' +#[cfg(not(meow = "lol"))] +#[cfg(not(blob = "lol"))] +pub fn bar() {} + +//@ has foo/fn.babar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' +#[cfg(not(meow = "lola"))] +#[cfg(not(blob = "lola"))] +pub fn babar() {} + +pub mod sub { + // We show again `meow`, however `meow="lol"` should still be hidden. + #![doc(auto_cfg(show(meow)))] + + //@ has foo/sub/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +pub mod sub2 { + // We show again `meow = "lol`, however `meow` should still be hidden. + #![doc(auto_cfg(show(meow, values("lol"))))] + + //@ has foo/sub2/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub2/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub2/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +pub mod sub3 { + // We show again `meow = "lol`, but by using `any()` this time. + #![doc(auto_cfg(show(meow, values(any()))))] + + //@ has foo/sub3/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub3/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub3/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +// This test the mix of values and `none()`. +#[doc(auto_cfg( + hide(bla, values(none(), "tic")), + hide(alb, values(none())), +))] +pub mod sub4 { + //@ has foo/sub4/fn.foo.html + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=tic' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + #[cfg(not(bla))] + #[cfg(not(bla = "tic"))] + #[cfg(not(alb))] + pub fn foo() {} + + //@ has foo/sub4/fn.foo2.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=tic' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + #[doc(auto_cfg( + show(bla, values(none(), "tic")), + show(alb, values(none())), + ))] + #[cfg(not(bla))] + #[cfg(not(bla = "tic"))] + #[cfg(not(alb))] + pub fn foo2() {} +} + +// This test the mix of `any()` and values. +#[doc(auto_cfg( + hide(alb, values(any())), + hide(bla, values(any())), + show(bla, values("top")), +))] +pub mod sub5 { + //@ has foo/sub5/fn.foo.html + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo() {} + + //@ has foo/sub5/fn.foo2.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[doc(auto_cfg( + show(alb, values(none())), + hide(bla, values("top")), + ))] + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo2() {} + + //@ has foo/sub5/fn.foo3.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[doc(auto_cfg( + show(alb, values(any())), + show(bla, values(any())), + hide(bla, values(none(), "top")), + ))] + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo3() {} +} diff --git a/tests/rustdoc-html/doc-cfg/sort.rs b/tests/rustdoc-html/doc-cfg/sort.rs index 3aa79b7836271..2c54e86be67c7 100644 --- a/tests/rustdoc-html/doc-cfg/sort.rs +++ b/tests/rustdoc-html/doc-cfg/sort.rs @@ -9,7 +9,7 @@ // Tests that OS targets are sorted alphabetically. //@ has 'foo/fn.foo.html' //@ has - '//*[@class="stab portability"]' 'Available on Android or Apple or Cygwin \ -// or DragonFly BSD or FreeBSD or Linux or NetBSD or OpenBSD or QNX Neutrino only.' +// or DragonFly BSD or FreeBSD or Linux or NetBSD or OpenBSD only.' #[doc(cfg(any( target_os = "android", target_os = "linux", @@ -17,7 +17,6 @@ target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "nto", target_vendor = "apple", target_os = "cygwin" )))] @@ -27,7 +26,7 @@ pub fn foo() {} // Tests that targets are sorted alphabetically just like explicit `doc(cfg)`. //@ has 'foo/fn.bar.html' //@ has - '//*[@class="stab portability"]' 'Available on Android or Apple or Cygwin \ -// or DragonFly BSD or FreeBSD or Linux or NetBSD or OpenBSD or QNX Neutrino only.' +// or DragonFly BSD or FreeBSD or Linux or NetBSD or OpenBSD only.' #[cfg(any( target_os = "android", target_os = "linux", @@ -35,7 +34,6 @@ pub fn foo() {} target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "nto", target_vendor = "apple", target_os = "cygwin" ))] diff --git a/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs b/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs index fbd96cc46d800..4329d8e06dfc5 100644 --- a/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs +++ b/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs @@ -3,7 +3,7 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide( - target_pointer_width = "64", + target_pointer_width, values("64"), )))] #![crate_name = "foo"] @@ -36,7 +36,7 @@ pub struct X; //@count - '//*[@id="impl-Trait-for-X"]' 1 //@count - '//*[@id="impl-Trait-for-X"]/*[@class="item-info"]' 0 #[doc(cfg(any(target_pointer_width = "64", target_arch = "wasm32")))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp { impl super::Trait for super::X { fn f(&self) {} } } @@ -64,7 +64,7 @@ pub struct Y; //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]' 1 //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]/*[@class="item-info"]' 0 #[doc(cfg(any(target_pointer_width = "64", target_arch = "wasm32")))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp4 { impl super::Y { pub fn plain_auto() {} } } diff --git a/tests/rustdoc-html/doc-cfg/trait-impls.rs b/tests/rustdoc-html/doc-cfg/trait-impls.rs index 581d171123d00..9ea6490ae8e21 100644 --- a/tests/rustdoc-html/doc-cfg/trait-impls.rs +++ b/tests/rustdoc-html/doc-cfg/trait-impls.rs @@ -3,7 +3,7 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide( - target_pointer_width = "64", + target_pointer_width, values("64"), )))] #![crate_name = "foo"] @@ -36,7 +36,7 @@ pub struct X; //@count - '//*[@id="impl-Trait-for-X"]' 1 //@count - '//*[@id="impl-Trait-for-X"]/*[@class="item-info"]' 0 #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp { impl super::Trait for super::X { fn f(&self) {} } } @@ -64,7 +64,7 @@ pub struct Y; //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]' 1 //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]/*[@class="item-info"]' 0 #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp4 { impl super::Y { pub fn plain_auto() {} } } diff --git a/tests/rustdoc-html/impl/impl-fundamental-nesting.rs b/tests/rustdoc-html/impl/impl-fundamental-nesting.rs new file mode 100644 index 0000000000000..25aed33e53514 --- /dev/null +++ b/tests/rustdoc-html/impl/impl-fundamental-nesting.rs @@ -0,0 +1,58 @@ +// Followup to https://github.com/rust-lang/rust/issues/92940 and impl-box.rs. +// +// Show traits implemented on fundamental types that wrap local ones: nested edition. + +#![crate_name = "foo"] + +use std::pin::Pin; + +pub struct Local; + +//@ has 'foo/struct.Local.html' + +// Nested fundamental + foreign Self. +//@ has '-' '//*[@id="impl-From%3CBox%3CLocal%3E%3E-for-String"]' 'impl From> for String' +impl From> for String { + fn from(_: Box) -> String { + String::new() + } +} + +// Also test with Pin. +//@ has '-' '//*[@id="impl-From%3CPin%3CLocal%3E%3E-for-u8"]' 'impl From> for u8' +impl From> for u8 { + fn from(_: Pin) -> u8 { + 0 + } +} + +// Reference to a fundamental wrapper. +//@ has '-' '//*[@id="impl-From%3C%26Box%3CLocal%3E%3E-for-u16"]' "impl<'a> From<&'a Box> for u16" +impl<'a> From<&'a Box> for u16 { + fn from(_: &'a Box) -> u16 { + 0 + } +} + +// Nested two levels deep in Self. +//@ has '-' '//*[@id="impl-From%3Cu32%3E-for-Box%3CBox%3CLocal%3E%3E"]' 'impl From for Box>' +impl From for Box> { + fn from(_: u32) -> Box> { + Box::new(Box::new(Local)) + } +} + +// Mixed fundamental wrappers in Self. +//@ has '-' '//*[@id="impl-From%3Cu64%3E-for-Pin%3CBox%3CLocal%3E%3E"]' 'impl From for Pin>' +impl From for Pin> { + fn from(_: u64) -> Pin> { + Pin::new(Box::new(Local)) + } +} + +// A non-fundamental wrapper must not show up on Local's page, but it should still be listed on the +// trait's own page. +pub trait Marker {} +//@ has 'foo/trait.Marker.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec' +//@ !has 'foo/struct.Local.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec' +impl Marker for Vec {} diff --git a/tests/rustdoc-html/reexport/auxiliary/inline-foreign-no_inline.rs b/tests/rustdoc-html/reexport/auxiliary/inline-foreign-no_inline.rs new file mode 100644 index 0000000000000..e27ad8d7f4e80 --- /dev/null +++ b/tests/rustdoc-html/reexport/auxiliary/inline-foreign-no_inline.rs @@ -0,0 +1,9 @@ +#[doc(no_inline)] +pub use crate::{ + future::{Future, FutureExt as _}, +}; + +mod future { + pub struct Future; + pub trait FutureExt {} +} diff --git a/tests/rustdoc-html/reexport/inline-foreign-no_inline.rs b/tests/rustdoc-html/reexport/inline-foreign-no_inline.rs new file mode 100644 index 0000000000000..329a36cc9d264 --- /dev/null +++ b/tests/rustdoc-html/reexport/inline-foreign-no_inline.rs @@ -0,0 +1,22 @@ +// This test ensures that an inlined foreign item with `#[doc(no_inline)]` is +// not inlined. +// This is a regression test for . + +//@ aux-build: inline-foreign-no_inline.rs + +#![crate_name = "foo"] + +extern crate inline_foreign_no_inline; + +// Since we cannot inline `inline_foreign_no_inline` because it has `no_inline`, there +// should have no other items than "Module". +//@ has 'foo/index.html' +//@ count - '//*[@id="main-content"]/h2' 1 +//@ has - '//*[@id="main-content"]/h2' 'Module' +//@ has - '//*[@id="main-content"]/dl[@class="item-table"]/dt' 'dep' + +//@ has 'foo/dep/index.html' +//@ has - '//*[@class="item-table reexports"]/dt' 'pub use dep::Future;' +//@ has - '//*[@class="item-table reexports"]/dt' 'pub use dep::FutureExt as _;' +#[doc(inline)] +pub use inline_foreign_no_inline as dep; diff --git a/tests/rustdoc-html/reexport/inline-no_inline.rs b/tests/rustdoc-html/reexport/inline-no_inline.rs new file mode 100644 index 0000000000000..af483181a487c --- /dev/null +++ b/tests/rustdoc-html/reexport/inline-no_inline.rs @@ -0,0 +1,21 @@ +// This test checks that inlining a `no_inline` is done correctly. + +#![crate_name = "foo"] + +//@ has 'foo/index.html' +// There should be `Re-exports` and `Structs` +//@ count - '//*[@id="main-content"]/h2' 2 +//@ has - '//*[@id="main-content"]/h2[@class="section-header"]' 'Re-exports' +//@ has - '//*[@id="main-content"]/h2[@class="section-header"]' 'Structs' + +//@ has - '//*[@id="main-content"]/dl[@class="item-table reexports"]/dt' 'pub use self::bar::A;' +//@ has - '//*[@id="main-content"]/dl[@class="item-table"]/dt' 'X' + +mod bar { + pub struct A; +} + +#[doc(no_inline)] +pub use self::bar::A; +#[doc(inline)] +pub use self::A as X; diff --git a/tests/rustdoc-html/type-const-free-in-array.rs b/tests/rustdoc-html/type-const-free-in-array.rs new file mode 100644 index 0000000000000..bea46596caa58 --- /dev/null +++ b/tests/rustdoc-html/type-const-free-in-array.rs @@ -0,0 +1,11 @@ +#![crate_name = "foo"] +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +type const N: usize = 2; + +//@ has 'foo/trait.CollectArray.html' +//@ has - '//pre[@class="rust item-decl"]/code' '[A; N]' +pub trait CollectArray { + fn inner_array(&mut self) -> [A; N]; +} diff --git a/tests/rustdoc-html/type-const-inherent-with-body.rs b/tests/rustdoc-html/type-const-inherent-with-body.rs new file mode 100644 index 0000000000000..e9771b1f606c1 --- /dev/null +++ b/tests/rustdoc-html/type-const-inherent-with-body.rs @@ -0,0 +1,15 @@ +#![crate_name = "foo"] +#![feature(min_generic_const_args, inherent_associated_types)] +#![expect(incomplete_features)] + +pub struct Foo; + +impl Foo { + type const LEN: usize = 4; +} + +//@ has 'foo/fn.mk_array.html' +//@ has - '//pre[@class="rust item-decl"]/code' '[u8; Foo::LEN]' +pub fn mk_array() -> [u8; Foo::LEN] { + [0u8; Foo::LEN] +} diff --git a/tests/rustdoc-json/attrs/stability/const.rs b/tests/rustdoc-json/attrs/stability/const.rs new file mode 100644 index 0000000000000..eccbc89d54b1c --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/const.rs @@ -0,0 +1,55 @@ +#![feature(staged_api)] + +//@ is "$.index[?(@.name=='non_const_function')].const_stability" null +#[stable(feature = "non_const_function_feature", since = "0.9.0")] +pub fn non_const_function() {} + +//@ set stable_const_fn = "$.index[?(@.name=='stable_const_fn')].id" +//@ is "$.index[?(@.name=='stable_const_fn')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='stable_const_fn')].stability.feature" '"stable_const_fn_feature"' +//@ is "$.index[?(@.name=='stable_const_fn')].stability.since" '"1.0.0"' +//@ is "$.index[?(@.name=='stable_const_fn')].const_stability.level" '"stable"' +//@ is "$.index[?(@.name=='stable_const_fn')].const_stability.feature" '"stable_const_fn_const_feature"' +//@ is "$.index[?(@.name=='stable_const_fn')].const_stability.since" '"1.1.0"' +//@ is "$.index[?(@.name=='stable_const_fn')].attrs" [] +#[stable(feature = "stable_const_fn_feature", since = "1.0.0")] +#[rustc_const_stable(feature = "stable_const_fn_const_feature", since = "1.1.0")] +pub const fn stable_const_fn() {} + +//@ is "$.index[?(@.name=='const_unstable_fn')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='const_unstable_fn')].stability.feature" '"const_unstable_fn_feature"' +//@ is "$.index[?(@.name=='const_unstable_fn')].stability.since" '"2.0.0"' +//@ is "$.index[?(@.name=='const_unstable_fn')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.name=='const_unstable_fn')].const_stability.feature" '"const_unstable_fn_const_feature"' +//@ !has "$.index[?(@.name=='const_unstable_fn')].const_stability.since" +//@ is "$.index[?(@.name=='const_unstable_fn')].attrs" [] +#[stable(feature = "const_unstable_fn_feature", since = "2.0.0")] +#[rustc_const_unstable(feature = "const_unstable_fn_const_feature", issue = "none")] +pub const fn const_unstable_fn() {} + +// Even when the item itself is unstable, if a separate const-stability attribute is present, +// that's a distinct fact possibly associated with a different feature gate. +// It should therefore be exposed on its own, instead of being collapsed into regular stability. +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].stability.level" '"unstable"' +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].stability.feature" '"unstable_fn_with_explicit_const_gate_feature"' +//@ !has "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].stability.since" +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].const_stability.feature" '"explicit_const_gate_on_unstable_fn"' +//@ !has "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].const_stability.since" +//@ is "$.index[?(@.name=='unstable_fn_with_explicit_const_gate')].attrs" [] +#[unstable(feature = "unstable_fn_with_explicit_const_gate_feature", issue = "none")] +#[rustc_const_unstable(feature = "explicit_const_gate_on_unstable_fn", issue = "none")] +pub const fn unstable_fn_with_explicit_const_gate() {} + +// `lookup_const_stability` synthesizes a const-unstable record for this item from its regular +// instability. Rustdoc JSON filters that out because there is no separate const feature gate. +//@ is "$.index[?(@.name=='unstable_const_fn_without_const_gate')].stability.level" '"unstable"' +//@ is "$.index[?(@.name=='unstable_const_fn_without_const_gate')].stability.feature" '"unstable_const_fn_without_const_gate_feature"' +//@ is "$.index[?(@.name=='unstable_const_fn_without_const_gate')].const_stability" null +#[unstable(feature = "unstable_const_fn_without_const_gate_feature", issue = "none")] +pub const fn unstable_const_fn_without_const_gate() {} + +// The `Use` item describes the re-export. It doesn't have `const_stability` of its own. +//@ is "$.index[?(@.inner.use.name=='stable_const_fn_reexport')].const_stability" null +//@ is "$.index[?(@.inner.use.name=='stable_const_fn_reexport')].inner.use.id" $stable_const_fn +pub use stable_const_fn as stable_const_fn_reexport; diff --git a/tests/rustdoc-json/attrs/stability/const_traits.rs b/tests/rustdoc-json/attrs/stability/const_traits.rs new file mode 100644 index 0000000000000..3308a2bdbd05b --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/const_traits.rs @@ -0,0 +1,159 @@ +#![feature(staged_api, const_trait_impl)] + +#[stable(feature = "const_trait_target_feature", since = "1.0.0")] +pub struct ConstTraitTarget; + +#[stable(feature = "const_stable_trait_target_feature", since = "1.0.0")] +pub struct ConstStableTraitTarget; + +#[stable(feature = "inherent_const_method_target_feature", since = "1.1.0")] +pub struct InherentConstMethodTarget; + +impl InherentConstMethodTarget { + // This item is plain unstable, not const-unstable specifically. + // Rustdoc JSON should populate `stability` only, not both it and `const_stability`. + // Duplicating the info would just add noise to the JSON file. + //@ is "$.index[?(@.name=='unstable_inherent_const_method_without_const_gate')].stability.level" '"unstable"' + //@ is "$.index[?(@.name=='unstable_inherent_const_method_without_const_gate')].stability.feature" '"unstable_inherent_const_method_without_const_gate"' + //@ is "$.index[?(@.name=='unstable_inherent_const_method_without_const_gate')].const_stability" null + #[unstable(feature = "unstable_inherent_const_method_without_const_gate", issue = "none")] + pub const fn unstable_inherent_const_method_without_const_gate(&self) {} +} + +//@ is "$.index[?(@.name=='ConstTrait')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='ConstTrait')].stability.feature" '"const_trait_feature"' +//@ is "$.index[?(@.name=='ConstTrait')].stability.since" '"2.0.0"' +//@ is "$.index[?(@.name=='ConstTrait')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.name=='ConstTrait')].const_stability.feature" '"const_trait_const_feature"' +//@ !has "$.index[?(@.name=='ConstTrait')].const_stability.since" +//@ is "$.index[?(@.name=='ConstTrait')].attrs" [] +#[stable(feature = "const_trait_feature", since = "2.0.0")] +#[rustc_const_unstable(feature = "const_trait_const_feature", issue = "none")] +pub const trait ConstTrait { + // This item is *not* usable in a const context, because its parent is const-unstable. + //@ is "$.index[?(@.docs=='assoc type inside const trait')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.docs=='assoc type inside const trait')].const_stability.feature" '"const_trait_const_feature"' + //@ !has "$.index[?(@.docs=='assoc type inside const trait')].const_stability.since" + /// assoc type inside const trait + #[stable(feature = "trait_assoc_type_feature", since = "2.1.0")] + type TraitAssocType; + + // This item is also *not* usable in a const context, because its parent is const-unstable. + //@ is "$.index[?(@.docs=='method inside const trait')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.docs=='method inside const trait')].const_stability.feature" '"const_trait_const_feature"' + //@ !has "$.index[?(@.docs=='method inside const trait')].const_stability.since" + /// method inside const trait + #[stable(feature = "trait_method_feature", since = "2.2.0")] + fn trait_method(&self); + + // An associated const can't be const-unstable. + // It is usable in a const context *regardless* of the fact its parent is const-unstable. + // See the UI test in `tests/ui/traits/const-traits/associated-const-stability.rs` for proof. + //@ is "$.index[?(@.docs=='assoc const inside const trait')].const_stability" null + /// assoc const inside const trait + #[stable(feature = "trait_assoc_const_feature", since = "2.3.0")] + const TRAIT_ASSOC_CONST: usize; +} + +//@ is "$.index[?(@.name=='ConstTraitWithUnstableMethod')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='ConstTraitWithUnstableMethod')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.name=='ConstTraitWithUnstableMethod')].const_stability.feature" '"const_trait_with_unstable_method_const_feature"' +#[stable(feature = "const_trait_with_unstable_method_feature", since = "2.4.0")] +#[rustc_const_unstable(feature = "const_trait_with_unstable_method_const_feature", issue = "none")] +pub const trait ConstTraitWithUnstableMethod { + // The method has its own regular instability, but no separate const-stability attribute. + // Even if it became stable, the parent's const-instability would still dominate and + // keep it const-unstable. Rustdoc JSON should expose the inherited const-instability. + //@ is "$.index[?(@.name=='unstable_method_without_const_gate')].stability.level" '"unstable"' + //@ is "$.index[?(@.name=='unstable_method_without_const_gate')].stability.feature" '"unstable_method_without_const_gate"' + //@ is "$.index[?(@.name=='unstable_method_without_const_gate')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.name=='unstable_method_without_const_gate')].const_stability.feature" '"const_trait_with_unstable_method_const_feature"' + #[unstable(feature = "unstable_method_without_const_gate", issue = "none")] + fn unstable_method_without_const_gate(&self); +} + +//@ is "$.index[?(@.docs=='const trait impl')].stability.level" '"stable"' +//@ is "$.index[?(@.docs=='const trait impl')].stability.feature" '"const_trait_impl_feature"' +//@ is "$.index[?(@.docs=='const trait impl')].stability.since" '"3.0.0"' +//@ is "$.index[?(@.docs=='const trait impl')].const_stability.level" '"unstable"' +//@ is "$.index[?(@.docs=='const trait impl')].const_stability.feature" '"const_trait_impl_const_feature"' +//@ !has "$.index[?(@.docs=='const trait impl')].const_stability.since" +/// const trait impl +#[stable(feature = "const_trait_impl_feature", since = "3.0.0")] +#[rustc_const_unstable(feature = "const_trait_impl_const_feature", issue = "none")] +const impl ConstTrait for ConstTraitTarget { + // This item is *not* usable in a const context, because its parent is const-unstable. + //@ is "$.index[?(@.docs=='assoc type inside const trait impl')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.docs=='assoc type inside const trait impl')].const_stability.feature" '"const_trait_impl_const_feature"' + //@ !has "$.index[?(@.docs=='assoc type inside const trait impl')].const_stability.since" + /// assoc type inside const trait impl + type TraitAssocType = usize; + + // This item is also *not* usable in a const context, because its parent is const-unstable. + //@ is "$.index[?(@.docs=='method inside const trait impl')].const_stability.level" '"unstable"' + //@ is "$.index[?(@.docs=='method inside const trait impl')].const_stability.feature" '"const_trait_impl_const_feature"' + //@ !has "$.index[?(@.docs=='method inside const trait impl')].const_stability.since" + /// method inside const trait impl + fn trait_method(&self) {} + + // An associated const can't be const-unstable. + // It is usable in a const context *regardless* of the fact its parent is const-unstable. + // See the UI test in `tests/ui/traits/const-traits/associated-const-stability.rs` for proof. + //@ is "$.index[?(@.docs=='assoc const inside const trait impl')].const_stability" null + /// assoc const inside const trait impl + const TRAIT_ASSOC_CONST: usize = 0; +} + +// The `const trait` is const-stable. We indicate that *once* on the trait, but since +// there are no special cases here (unlike in the const-unstable case with associated consts) +// we do not duplicate the const-stability to all the associated items so as not to bloat the JSON. +//@ is "$.index[?(@.name=='ConstStableTrait')].stability.level" '"stable"' +//@ is "$.index[?(@.name=='ConstStableTrait')].stability.feature" '"const_stable_trait_feature"' +//@ is "$.index[?(@.name=='ConstStableTrait')].stability.since" '"4.0.0"' +//@ is "$.index[?(@.name=='ConstStableTrait')].const_stability.level" '"stable"' +//@ is "$.index[?(@.name=='ConstStableTrait')].const_stability.since" '"4.0.0"' +//@ is "$.index[?(@.name=='ConstStableTrait')].const_stability.feature" '"const_stable_trait_const_feature"' +#[stable(feature = "const_stable_trait_feature", since = "4.0.0")] +#[rustc_const_stable(feature = "const_stable_trait_const_feature", since = "4.0.0")] +pub const trait ConstStableTrait { + //@ is "$.index[?(@.docs=='assoc type inside const-stable trait')].const_stability" null + /// assoc type inside const-stable trait + #[stable(feature = "stable_trait_assoc_type_feature", since = "4.1.0")] + type StableTraitAssocType; + + //@ is "$.index[?(@.docs=='method inside const-stable trait')].const_stability" null + /// method inside const-stable trait + #[stable(feature = "stable_trait_method_feature", since = "4.2.0")] + fn stable_trait_method(&self); + + //@ is "$.index[?(@.docs=='assoc const inside const-stable trait')].const_stability" null + /// assoc const inside const-stable trait + #[stable(feature = "stable_trait_assoc_const_feature", since = "4.3.0")] + const STABLE_TRAIT_ASSOC_CONST: usize; +} + +// The `const impl` is const-stable. We indicate that *once* on the impl itself, but since +// there are no special cases here (unlike in the const-unstable case with associated consts) +// we do not duplicate the const-stability to all the associated items so as not to bloat the JSON. +//@ is "$.index[?(@.docs=='const-stable trait impl')].stability.level" '"stable"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].stability.feature" '"const_stable_trait_impl_feature"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].stability.since" '"5.0.0"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].const_stability.level" '"stable"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].const_stability.since" '"5.0.0"' +//@ is "$.index[?(@.docs=='const-stable trait impl')].const_stability.feature" '"const_stable_trait_impl_const_feature"' +/// const-stable trait impl +#[stable(feature = "const_stable_trait_impl_feature", since = "5.0.0")] +#[rustc_const_stable(feature = "const_stable_trait_impl_const_feature", since = "5.0.0")] +const impl ConstStableTrait for ConstStableTraitTarget { + //@ is "$.index[?(@.docs=='assoc type inside const-stable trait impl')].const_stability" null + /// assoc type inside const-stable trait impl + type StableTraitAssocType = usize; + + //@ is "$.index[?(@.docs=='method inside const-stable trait impl')].const_stability" null + /// method inside const-stable trait impl + fn stable_trait_method(&self) {} + + //@ is "$.index[?(@.docs=='assoc const inside const-stable trait impl')].const_stability" null + /// assoc const inside const-stable trait impl + const STABLE_TRAIT_ASSOC_CONST: usize = 1; +} diff --git a/tests/rustdoc-json/attrs/stability/default_body.rs b/tests/rustdoc-json/attrs/stability/default_body.rs new file mode 100644 index 0000000000000..5a70e47d6c505 --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/default_body.rs @@ -0,0 +1,76 @@ +#![feature(staged_api, rustc_attrs, associated_type_defaults)] + +#[stable(feature = "default_body_trait_feature", since = "1.0.0")] +pub trait TraitWithDefaults { + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.default_unstable.feature" '"method_default_body_feature"' + //@ is "$.index[?(@.docs=='method with unstable default body')].attrs" [] + /// method with unstable default body + #[stable(feature = "default_body_method_feature", since = "1.1.0")] + #[rustc_default_body_unstable(feature = "method_default_body_feature", issue = "none")] + fn method_with_unstable_default() {} + + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.has_body" false + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.default_unstable" null + /// required method without default body + #[stable(feature = "required_method_feature", since = "1.2.0")] + fn required_method(); + + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.default_unstable" null + /// method with stable default body + #[stable(feature = "stable_default_method_feature", since = "1.3.0")] + fn method_with_stable_default() {} + + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.value" '"0"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.default_unstable.feature" '"assoc_const_default_value_feature"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].attrs" [] + /// associated constant with unstable default value + #[stable(feature = "assoc_const_with_unstable_default_feature", since = "1.4.0")] + #[rustc_default_body_unstable(feature = "assoc_const_default_value_feature", issue = "none")] + const UNSTABLE_DEFAULT_CONST: usize = 0; + + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.value" null + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.default_unstable" null + /// required associated constant + #[stable(feature = "required_assoc_const_feature", since = "1.5.0")] + const REQUIRED_CONST: usize; + + //@ is "$.index[?(@.docs=='associated type with unstable default type')].inner.assoc_type.default_unstable.feature" '"assoc_type_default_type_feature"' + //@ is "$.index[?(@.docs=='associated type with unstable default type')].attrs" [] + /// associated type with unstable default type + #[stable(feature = "assoc_type_with_unstable_default_feature", since = "1.6.0")] + #[rustc_default_body_unstable(feature = "assoc_type_default_type_feature", issue = "none")] + type UnstableDefaultType = usize; + + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.type" null + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.default_unstable" null + /// required associated type + #[stable(feature = "required_assoc_type_feature", since = "1.7.0")] + type RequiredType; +} + +#[stable(feature = "default_body_impl_target_feature", since = "2.0.0")] +pub struct ImplTarget; + +// Impl items provide their own definitions, so they do not use the trait's unstable defaults. +#[stable(feature = "default_body_impl_feature", since = "2.1.0")] +impl TraitWithDefaults for ImplTarget { + //@ is "$.index[?(@.docs=='impl override for unstable default body')].inner.function.default_unstable" null + /// impl override for unstable default body + fn method_with_unstable_default() {} + + fn required_method() {} + + //@ is "$.index[?(@.docs=='impl override for unstable default value')].inner.assoc_const.default_unstable" null + /// impl override for unstable default value + const UNSTABLE_DEFAULT_CONST: usize = 1; + + const REQUIRED_CONST: usize = 2; + + //@ is "$.index[?(@.docs=='impl override for unstable default type')].inner.assoc_type.default_unstable" null + /// impl override for unstable default type + type UnstableDefaultType = u8; + + type RequiredType = (); +} diff --git a/tests/rustdoc-json/doc_cfg.rs b/tests/rustdoc-json/doc_cfg.rs new file mode 100644 index 0000000000000..0033968305bd0 --- /dev/null +++ b/tests/rustdoc-json/doc_cfg.rs @@ -0,0 +1,5 @@ +#![feature(doc_cfg)] + +//@ is "$.index[?(@.name=='f')].attrs" '[{"other": "#[doc(auto_cfg(hide(bar, values(none())), hide(blob, values(\"a\", \"14\"))))]"}]' +#[doc(auto_cfg(hide(bar, values(none())), hide(blob, values("a", "14")),))] +pub fn f() {} diff --git a/tests/rustdoc-json/impls/fundamental_nesting.rs b/tests/rustdoc-json/impls/fundamental_nesting.rs new file mode 100644 index 0000000000000..a6aefb94f961f --- /dev/null +++ b/tests/rustdoc-json/impls/fundamental_nesting.rs @@ -0,0 +1,57 @@ +// Companion to tests/rustdoc-html/impl/impl-fundamental-nesting.rs. +// +// Show traits implemented on fundamental types that wrap local ones: nested edition. + +use std::pin::Pin; + +pub struct Local; + +// Nested fundamental + foreign Self. +/// from box local +impl From> for String { + fn from(_: Box) -> String { + String::new() + } +} +//@ set from_box_local = "$.index[?(@.docs=='from box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_box_local + +// Reference to a fundamental wrapper. +/// from ref box local +impl<'a> From<&'a Box> for u16 { + fn from(_: &'a Box) -> u16 { + 0 + } +} +//@ set from_ref_box_local = "$.index[?(@.docs=='from ref box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_ref_box_local + +// Nested two levels deep in Self. +/// u32 for box box local +impl From for Box> { + fn from(_: u32) -> Box> { + Box::new(Box::new(Local)) + } +} +//@ set u32_for_box_box_local = "$.index[?(@.docs=='u32 for box box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u32_for_box_box_local + +// Mixed fundamental wrappers in Self. +/// u64 for pin box local +impl From for Pin> { + fn from(_: u64) -> Pin> { + Pin::new(Box::new(Local)) + } +} +//@ set u64_for_pin_box_local = "$.index[?(@.docs=='u64 for pin box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u64_for_pin_box_local + +// A non-fundamental wrapper must not associate the impl with Local, but the impl must still be +// listed on the trait itself. +pub trait Marker {} + +/// marker for vec local +impl Marker for Vec {} +//@ set marker_for_vec_local = "$.index[?(@.docs=='marker for vec local')].id" +//@ !has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $marker_for_vec_local +//@ has "$.index[?(@.name=='Marker')].inner.trait.implementations[*]" $marker_for_vec_local diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.rs b/tests/rustdoc-ui/cfg-hide-show-conflict.rs deleted file mode 100644 index 8e98b95c85bb9..0000000000000 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![feature(doc_cfg)] -#![doc(auto_cfg(hide(target_os = "linux")))] -#![doc(auto_cfg(show(windows, target_os = "linux")))] //~ ERROR diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr index 22231e82cd7bf..b43b8104cdc12 100644 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr +++ b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr @@ -1,14 +1,14 @@ error: same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item - --> $DIR/cfg-hide-show-conflict.rs:3:31 + --> $DIR/cfg-hide-show-conflict.rs:3:55 | -LL | #![doc(auto_cfg(show(windows, target_os = "linux")))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![doc(auto_cfg(show(windows), show(target_os, values("linux"))))] + | ^^^^^^^ | note: first change was here - --> $DIR/cfg-hide-show-conflict.rs:2:22 + --> $DIR/cfg-hide-show-conflict.rs:2:40 | -LL | #![doc(auto_cfg(hide(target_os = "linux")))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![doc(auto_cfg(hide(target_os, values("linux"))))] + | ^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doc-cfg-2.rs b/tests/rustdoc-ui/doc-cfg-2.rs index f615e96bbc6b5..ba9e22e755203 100644 --- a/tests/rustdoc-ui/doc-cfg-2.rs +++ b/tests/rustdoc-ui/doc-cfg-2.rs @@ -13,7 +13,5 @@ // Shouldn't lint #[doc(auto_cfg(hide(windows)))] #[doc(auto_cfg(hide(feature = "windows")))] -//~^ WARN unexpected `cfg` condition name: `feature` -#[doc(auto_cfg(hide(foo)))] -//~^ WARN unexpected `cfg` condition name: `foo` +//~^ ERROR `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg-2.stderr b/tests/rustdoc-ui/doc-cfg-2.stderr index 164e755de8ad7..aeb842c338c6b 100644 --- a/tests/rustdoc-ui/doc-cfg-2.stderr +++ b/tests/rustdoc-ui/doc-cfg-2.stderr @@ -30,19 +30,19 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:8:21 | LL | #[doc(auto_cfg(hide(true)))] | ^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:9:21 | LL | #[doc(auto_cfg(hide(42)))] | ^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:10:21 | LL | #[doc(auto_cfg(hide("a")))] @@ -60,23 +60,11 @@ error: expected boolean for `#[doc(auto_cfg = ...)]` LL | #[doc(auto_cfg = "a")] | ^^^ -warning: unexpected `cfg` condition name: `feature` +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:15:21 | LL | #[doc(auto_cfg(hide(feature = "windows")))] | ^^^^^^^^^^^^^^^^^^^ - | - = help: to expect this configuration use `--check-cfg=cfg(feature, values("windows"))` - = note: see for more information about checking conditional configuration - -warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:17:21 - | -LL | #[doc(auto_cfg(hide(foo)))] - | ^^^ - | - = help: to expect this configuration use `--check-cfg=cfg(foo)` - = note: see for more information about checking conditional configuration -error: aborting due to 6 previous errors; 4 warnings emitted +error: aborting due to 7 previous errors; 2 warnings emitted diff --git a/tests/rustdoc-ui/doc-cfg-3.rs b/tests/rustdoc-ui/doc-cfg-3.rs new file mode 100644 index 0000000000000..39ae86086c882 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-3.rs @@ -0,0 +1,7 @@ +// Checks that you cannot have `any()` and values at the same time. + +#![deny(invalid_doc_attributes)] +#![feature(doc_cfg)] + +#![doc(auto_cfg(hide(target_os, values(any(), "linux"))))] //~ ERROR +#![doc(auto_cfg(hide(target_os, values("linux", any()))))] //~ ERROR diff --git a/tests/rustdoc-ui/doc-cfg-3.stderr b/tests/rustdoc-ui/doc-cfg-3.stderr new file mode 100644 index 0000000000000..b9cd74388ecd9 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-3.stderr @@ -0,0 +1,22 @@ +error: `any()` was used when other values were provided + --> $DIR/doc-cfg-3.rs:6:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(any(), "linux"))))] + | ^^^^^ ------- value declared here + | +note: the lint level is defined here + --> $DIR/doc-cfg-3.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `any()` was used when other values were provided + --> $DIR/doc-cfg-3.rs:7:49 + | +LL | #![doc(auto_cfg(hide(target_os, values("linux", any()))))] + | ------- ^^^^^ + | | + | value declared here + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/doc-cfg-4.rs b/tests/rustdoc-ui/doc-cfg-4.rs new file mode 100644 index 0000000000000..75d0e30f76af9 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-4.rs @@ -0,0 +1,7 @@ +// Checks that you cannot have any item inside `any()` and `none()`. + +#![deny(invalid_doc_attributes)] +#![feature(doc_cfg)] + +#![doc(auto_cfg(hide(target_os, values(any("linux")))))] //~ ERROR +#![doc(auto_cfg(hide(target_os, values(none("linux")))))] //~ ERROR diff --git a/tests/rustdoc-ui/doc-cfg-4.stderr b/tests/rustdoc-ui/doc-cfg-4.stderr new file mode 100644 index 0000000000000..5de5e3b806594 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-4.stderr @@ -0,0 +1,20 @@ +error: `#![doc(auto_cfg(any(...)))]` only accepts identifiers or `values(...)` + --> $DIR/doc-cfg-4.rs:6:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(any("linux")))))] + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/doc-cfg-4.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `#![doc(auto_cfg(none(...)))]` only accepts identifiers or `values(...)` + --> $DIR/doc-cfg-4.rs:7:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(none("linux")))))] + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index abaea97192808..5f8b5a29a4402 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -6,5 +6,5 @@ //~| ERROR #[doc(cfg())] //~ ERROR #[doc(cfg(foo, bar))] //~ ERROR -#[doc(auto_cfg(hide(foo::bar)))] +#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index f3af95528b2d2..35d14f913b725 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -62,6 +62,15 @@ LL - #[doc(cfg(foo, bar))] LL + #[doc(cfg(any(foo, bar)))] | -error: aborting due to 4 previous errors +error[E0565]: malformed `doc` attribute input + --> $DIR/doc-cfg.rs:9:1 + | +LL | #[doc(auto_cfg(hide(foo::bar)))] + | ^^^^^^^^^^^^^^^^^^^^--------^^^^ + | | + | expected a valid identifier here + +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0805`. +Some errors have detailed explanations: E0565, E0805. +For more information about an error, try `rustc --explain E0565`. diff --git a/tests/rustdoc-ui/ice-bug-report-url.rs b/tests/rustdoc-ui/ice-bug-report-url.rs index 2e384fa1be623..5aa82c438fa36 100644 --- a/tests/rustdoc-ui/ice-bug-report-url.rs +++ b/tests/rustdoc-ui/ice-bug-report-url.rs @@ -4,7 +4,7 @@ //@ normalize-stderr: "note: compiler flags.*\n\n" -> "" //@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" -//@ normalize-stderr: "thread.*panicked at compiler.*" -> "" +//@ normalize-stderr: "thread 'rustc'.*panicked.*:\n.*\n" -> "" //@ normalize-stderr: " +\d{1,}: .*\n" -> "" //@ normalize-stderr: " + at .*\n" -> "" //@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" diff --git a/tests/rustdoc-ui/ice-bug-report-url.stderr b/tests/rustdoc-ui/ice-bug-report-url.stderr index bc46b226a703e..01dac0433e630 100644 --- a/tests/rustdoc-ui/ice-bug-report-url.stderr +++ b/tests/rustdoc-ui/ice-bug-report-url.stderr @@ -5,8 +5,6 @@ LL | fn wrong() | ^ expected one of `->`, `where`, or `{` - -aborting due to `-Z treat-err-as-bug=1` stack backtrace: error: the compiler unexpectedly panicked. This is a bug diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs b/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs new file mode 100644 index 0000000000000..f1ee59e64c93c --- /dev/null +++ b/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs @@ -0,0 +1,4 @@ +#![feature(doc_cfg)] + +#![deny(invalid_doc_attributes)] +#![doc(auto_cfg(hide(not(windows))))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr new file mode 100644 index 0000000000000..68503ebf132fa --- /dev/null +++ b/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr @@ -0,0 +1,11 @@ +error[E0565]: malformed `doc` attribute input + --> $DIR/doc_cfg_hide-2.rs:4:1 + | +LL | #![doc(auto_cfg(hide(not(windows))))] + | ^^^^^^^^^^^^^^^^^^^^^---^^^^^^^^^^^^^ + | | + | expected a valid identifier here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0565`. diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index 6c190f9befac8..28c0c4a89cebd 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -2,4 +2,3 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide = "test"))] //~ ERROR #![doc(auto_cfg(hide))] //~ ERROR -#![doc(auto_cfg(hide(not(windows))))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index a5ec8fdf5d34e..4b984feb3d5a1 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -16,11 +16,5 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items LL | #![doc(auto_cfg(hide))] | ^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc_cfg_hide.rs:5:22 - | -LL | #![doc(auto_cfg(hide(not(windows))))] - | ^^^^^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/const-trait-bound.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-trait-bound.rs new file mode 100644 index 0000000000000..858e38b08fcd1 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-trait-bound.rs @@ -0,0 +1,14 @@ +// We used to ICE here while trying to synthesize auto trait impls. +//@ check-pass + +#![feature(const_default)] +#![feature(const_trait_impl)] + +pub struct Inner(T); + +pub struct Outer(Inner); + +impl Unpin for Inner +where + T: const std::default::Default, +{} diff --git a/tests/ui/README.md b/tests/ui/README.md index 6ffef1a1cd699..d342f393d0550 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1351,6 +1351,10 @@ Generic collection of tests for suggestions, when no more specific directories a **FIXME**: Some overlap with `tests/ui/did_you_mean/`, that directory should probably be moved under here. +## `tests/ui/supertrait-shadowing/` + +Tests for supertrait item shadowing (RFC 3624). + ## `tests/ui/svh/`: Strict Version Hash Tests on the *Strict Version Hash* (SVH, also known as the "crate hash"). diff --git a/tests/ui/abi/bad-custom.rs b/tests/ui/abi/bad-custom.rs index 7c881134ccb4c..10e34fa19648a 100644 --- a/tests/ui/abi/bad-custom.rs +++ b/tests/ui/abi/bad-custom.rs @@ -100,7 +100,7 @@ async unsafe extern "custom" fn no_async_fn() { } fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() { - //~^ ERROR expected a `Fn()` closure, found `unsafe extern "custom" fn()` + //~^ ERROR expected an `Fn()` closure, found `unsafe extern "custom" fn()` f } diff --git a/tests/ui/abi/bad-custom.stderr b/tests/ui/abi/bad-custom.stderr index d7c9cae35c7b3..46da91d80c993 100644 --- a/tests/ui/abi/bad-custom.stderr +++ b/tests/ui/abi/bad-custom.stderr @@ -184,7 +184,7 @@ LL + #[unsafe(naked)] LL | async unsafe extern "custom" fn no_async_fn() { | -error[E0277]: expected a `Fn()` closure, found `unsafe extern "custom" fn()` +error[E0277]: expected an `Fn()` closure, found `unsafe extern "custom" fn()` --> $DIR/bad-custom.rs:102:64 | LL | fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() { diff --git a/tests/ui/issues/issue-26997.rs b/tests/ui/abi/extern/extern-c-method-return-struct.rs similarity index 75% rename from tests/ui/issues/issue-26997.rs rename to tests/ui/abi/extern/extern-c-method-return-struct.rs index 5441dc68bae6c..679f0b37758ab 100644 --- a/tests/ui/issues/issue-26997.rs +++ b/tests/ui/abi/extern/extern-c-method-return-struct.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ build-pass + #![allow(dead_code)] pub struct Foo { x: isize, diff --git a/tests/ui/array-slice-vec/eval-array-expr.rs b/tests/ui/array-slice-vec/eval-array-expr.rs new file mode 100644 index 0000000000000..1238ffe696e96 --- /dev/null +++ b/tests/ui/array-slice-vec/eval-array-expr.rs @@ -0,0 +1,12 @@ +//! Regression test for . +//! Check expr in [expr; N] is always being evaluated. +//! +//! This used to trigger an LLVM assertion during compilation +//@ run-fail +//@ error-pattern:panic evaluated +//@ needs-subprocess + +#[allow(unused_variables)] +fn main() { + let x = [panic!("panic evaluated"); 2]; +} diff --git a/tests/ui/issues/issue-23354.rs b/tests/ui/array-slice-vec/eval-empty-array-expr.rs similarity index 54% rename from tests/ui/issues/issue-23354.rs rename to tests/ui/array-slice-vec/eval-empty-array-expr.rs index eff5f3d27147a..92e1c689436ab 100644 --- a/tests/ui/issues/issue-23354.rs +++ b/tests/ui/array-slice-vec/eval-empty-array-expr.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! Check expr in [expr; N] is always being evaluated. //@ run-fail //@ error-pattern:panic evaluated //@ needs-subprocess diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr index c67c913d2a60d..28ac455c06441 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr @@ -1,38 +1,259 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:52:26 + | +LL | asm!("/* {} */", in(vreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:55:26 + | +LL | asm!("/* {} */", out(vreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:57:26 + | +LL | asm!("/* {} */", in(vreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:60:26 + | +LL | asm!("/* {} */", out(vreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:65:26 + | +LL | asm!("/* {} */", in(xreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:68:26 + | +LL | asm!("/* {} */", out(xreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:70:26 + | +LL | asm!("/* {} */", in(xreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:73:26 + | +LL | asm!("/* {} */", out(xreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:77:31 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:82:31 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:18 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:32 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:52:35 + | +LL | asm!("/* {} */", in(vreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:57:35 + | +LL | asm!("/* {} */", in(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:60:36 + | +LL | asm!("/* {} */", out(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:65:35 + | +LL | asm!("/* {} */", in(xreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:70:35 + | +LL | asm!("/* {} */", in(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:73:36 + | +LL | asm!("/* {} */", out(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:77:42 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:82:42 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:29 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:43 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: aborting due to 28 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr index 99c071919acfa..1ee1b86989c4d 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr @@ -1,62 +1,295 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:52:26 + | +LL | asm!("/* {} */", in(vreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:55:26 + | +LL | asm!("/* {} */", out(vreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:57:26 + | +LL | asm!("/* {} */", in(vreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:60:26 + | +LL | asm!("/* {} */", out(vreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:65:26 + | +LL | asm!("/* {} */", in(xreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:68:26 + | +LL | asm!("/* {} */", out(xreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:70:26 + | +LL | asm!("/* {} */", in(xreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:73:26 + | +LL | asm!("/* {} */", out(xreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:77:31 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:82:31 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:18 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:32 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:40:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:42:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:44:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:46:26 + --> $DIR/bad-reg.rs:48:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ -error: aborting due to 10 previous errors +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:52:35 + | +LL | asm!("/* {} */", in(vreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:57:35 + | +LL | asm!("/* {} */", in(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:60:36 + | +LL | asm!("/* {} */", out(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:65:35 + | +LL | asm!("/* {} */", in(xreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:70:35 + | +LL | asm!("/* {} */", in(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:73:36 + | +LL | asm!("/* {} */", out(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:77:18 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^ + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:77:42 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:82:18 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^ + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:82:42 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:29 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:43 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: aborting due to 34 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index c67c913d2a60d..97462d6dc5f0b 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,38 +1,62 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: register `$vr0` conflicts with register `$f0` + --> $DIR/bad-reg.rs:77:31 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ----------- ^^^^^^^^^^^^ register `$vr0` + | | + | register `$f0` + +error: register `$xr0` conflicts with register `$f0` + --> $DIR/bad-reg.rs:82:31 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ----------- ^^^^^^^^^^^^ register `$xr0` + | | + | register `$f0` + +error: register `$xr0` conflicts with register `$vr0` + --> $DIR/bad-reg.rs:87:32 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ------------ ^^^^^^^^^^^^ register `$xr0` + | | + | register `$vr0` + +error: aborting due to 9 previous errors diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index 99c071919acfa..1ee1b86989c4d 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,62 +1,295 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:52:26 + | +LL | asm!("/* {} */", in(vreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:55:26 + | +LL | asm!("/* {} */", out(vreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:57:26 + | +LL | asm!("/* {} */", in(vreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:60:26 + | +LL | asm!("/* {} */", out(vreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:65:26 + | +LL | asm!("/* {} */", in(xreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:68:26 + | +LL | asm!("/* {} */", out(xreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:70:26 + | +LL | asm!("/* {} */", in(xreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:73:26 + | +LL | asm!("/* {} */", out(xreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:77:31 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:82:31 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:18 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:32 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:40:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:42:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:44:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:46:26 + --> $DIR/bad-reg.rs:48:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ -error: aborting due to 10 previous errors +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:52:35 + | +LL | asm!("/* {} */", in(vreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:57:35 + | +LL | asm!("/* {} */", in(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:60:36 + | +LL | asm!("/* {} */", out(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:65:35 + | +LL | asm!("/* {} */", in(xreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:70:35 + | +LL | asm!("/* {} */", in(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:73:36 + | +LL | asm!("/* {} */", out(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:77:18 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^ + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:77:42 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:82:18 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^ + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:82:42 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:29 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:43 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` 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: aborting due to 34 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index 7d28884966d46..6bda28eb10fde 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -4,12 +4,14 @@ //@[loongarch32_ilp32d] needs-llvm-components: loongarch //@[loongarch32_ilp32s] compile-flags: --target loongarch32-unknown-none-softfloat //@[loongarch32_ilp32s] needs-llvm-components: loongarch -//@[loongarch64_lp64d] compile-flags: --target loongarch64-unknown-linux-gnu +//@[loongarch64_lp64d] compile-flags: --target loongarch64-unknown-linux-gnu -Ctarget-feature=+lasx //@[loongarch64_lp64d] needs-llvm-components: loongarch //@[loongarch64_lp64s] compile-flags: --target loongarch64-unknown-none-softfloat //@[loongarch64_lp64s] needs-llvm-components: loongarch //@ ignore-backends: gcc +#![cfg_attr(loongarch64_lp64d, feature(asm_experimental_reg))] + #![crate_type = "lib"] #![feature(no_core)] #![no_core] @@ -45,5 +47,48 @@ fn f() { //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("/* {} */", out(freg) d); //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + + asm!("", out("$vr0") _); // ok + asm!("/* {} */", in(vreg) f); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f32` cannot be used with this register class in stable + asm!("/* {} */", out(vreg) _); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + asm!("/* {} */", in(vreg) d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + asm!("/* {} */", out(vreg) d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + + asm!("", out("$xr0") _); // ok + asm!("/* {} */", in(xreg) f); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f32` cannot be used with this register class in stable + asm!("/* {} */", out(xreg) _); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + asm!("/* {} */", in(xreg) d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + asm!("/* {} */", out(xreg) d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + + asm!("", in("$f0") f, in("$vr0") d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch64_lp64s]~| ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + //[loongarch64_lp64d]~^^^^ ERROR register `$vr0` conflicts with register `$f0` + asm!("", in("$f0") f, in("$xr0") d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch64_lp64s]~| ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + //[loongarch64_lp64d]~^^^^ ERROR register `$xr0` conflicts with register `$f0` + asm!("", in("$vr0") f, in("$xr0") d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f32` cannot be used with this register class in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + //[loongarch64_lp64d]~^^^^^ ERROR register `$xr0` conflicts with register `$vr0` } } diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 9bb7b28b4424e..0048fa5075db2 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -57,6 +57,12 @@ error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` | LL | asm!("{}", in(reg) foo => bar); | ^^ expected one of 7 possible tokens + | +help: you might have meant to write a "greater than or equal to" comparison + | +LL - asm!("{}", in(reg) foo => bar); +LL + asm!("{}", in(reg) foo >= bar); + | error: expected a path for argument to `sym` --> $DIR/parse-error.rs:32:24 diff --git a/tests/ui/associated-inherent-types/next-solver-opaque-inherent-fn-ptr-issue-155204.stderr b/tests/ui/associated-inherent-types/next-solver-opaque-inherent-fn-ptr-issue-155204.stderr index a21ed19756477..f5861f46398c3 100644 --- a/tests/ui/associated-inherent-types/next-solver-opaque-inherent-fn-ptr-issue-155204.stderr +++ b/tests/ui/associated-inherent-types/next-solver-opaque-inherent-fn-ptr-issue-155204.stderr @@ -56,7 +56,7 @@ LL | fn ret(&self) -> Self::AssocType { | --------------- expected `Windows fn(&'a ())>::AssocType` because of return type LL | LL | () - | ^^ types differ + | ^^ expected opaque type, found `()` | = note: expected opaque type `Windows fn(&'a ())>::AssocType` found unit type `()` diff --git a/tests/ui/associated-inherent-types/next-solver-opaque-inherent-no-ice.stderr b/tests/ui/associated-inherent-types/next-solver-opaque-inherent-no-ice.stderr index 3c9892d7a98ec..087431033c0ec 100644 --- a/tests/ui/associated-inherent-types/next-solver-opaque-inherent-no-ice.stderr +++ b/tests/ui/associated-inherent-types/next-solver-opaque-inherent-no-ice.stderr @@ -49,7 +49,7 @@ LL | type ImplTrait = impl Clone; LL | fn f() -> Self::ImplTrait { | --------------- expected `Foo::ImplTrait` because of return type LL | () - | ^^ types differ + | ^^ expected opaque type, found `()` | = note: expected opaque type `Foo::ImplTrait` found unit type `()` diff --git a/tests/ui/associated-inherent-types/normalization-overflow.next.stderr b/tests/ui/associated-inherent-types/normalization-overflow.next.stderr index 376a071fdb248..69d087b0c7040 100644 --- a/tests/ui/associated-inherent-types/normalization-overflow.next.stderr +++ b/tests/ui/associated-inherent-types/normalization-overflow.next.stderr @@ -1,9 +1,9 @@ -error[E0271]: type mismatch resolving `_ == T::This` +error[E0275]: overflow evaluating the requirement `T::This == _` --> $DIR/normalization-overflow.rs:14:5 | LL | type This = Self::This; - | ^^^^^^^^^ types differ + | ^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-inherent-types/normalization-overflow.rs b/tests/ui/associated-inherent-types/normalization-overflow.rs index da3e83c2c5997..7fcea9cb67029 100644 --- a/tests/ui/associated-inherent-types/normalization-overflow.rs +++ b/tests/ui/associated-inherent-types/normalization-overflow.rs @@ -13,7 +13,7 @@ struct T; impl T { type This = Self::This; //[current]~^ ERROR: overflow evaluating associated type `T::This` - //[next]~^^ ERROR: type mismatch resolving `_ == T::This` + //[next]~^^ ERROR: overflow evaluating the requirement `T::This == _` } fn main() {} diff --git a/tests/ui/issues/issue-22036.rs b/tests/ui/associated-types/assoc-type-projection-in-default-method.rs similarity index 87% rename from tests/ui/issues/issue-22036.rs rename to tests/ui/associated-types/assoc-type-projection-in-default-method.rs index befdf7ead06d2..1886d6384b412 100644 --- a/tests/ui/issues/issue-22036.rs +++ b/tests/ui/associated-types/assoc-type-projection-in-default-method.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-pass trait DigitCollection: Sized { diff --git a/tests/ui/issues/issue-22356.rs b/tests/ui/associated-types/associated-type-projection-alias-in-field.rs similarity index 88% rename from tests/ui/issues/issue-22356.rs rename to tests/ui/associated-types/associated-type-projection-alias-in-field.rs index b7c5c2a59327e..de7350b108831 100644 --- a/tests/ui/issues/issue-22356.rs +++ b/tests/ui/associated-types/associated-type-projection-alias-in-field.rs @@ -1,3 +1,5 @@ +//! Regression test for . + //@ check-pass #![allow(type_alias_bounds)] diff --git a/tests/ui/associated-types/associated-type-unconstrained-lifetime.rs b/tests/ui/associated-types/associated-type-unconstrained-lifetime.rs new file mode 100644 index 0000000000000..a4b278392b6d6 --- /dev/null +++ b/tests/ui/associated-types/associated-type-unconstrained-lifetime.rs @@ -0,0 +1,32 @@ +//! Regression test for . +//! Test that associated type's inner state cannot be observed past +//! borrow end via saved reference. +//! +//! This was possible as trait implementation with unconstrained lifetime +//! allowed to use any lifetime for associated type, which introduced +//! soundness holes. +//! +//! Fixed by prohibiting use of unconstrained lifetimes on associated types +//! in . +//! +//! Related . + +fn crash_please() { + let mut iter = Newtype(Some(Box::new(0))); + let saved = iter.next().unwrap(); + println!("{}", saved); + iter.0 = None; + println!("{}", saved); +} + +struct Newtype(Option>); + +impl<'a> Iterator for Newtype { //~ ERROR E0207 + type Item = &'a Box; + + fn next(&mut self) -> Option<&Box> { + self.0.as_ref() + } +} + +fn main() { } diff --git a/tests/ui/issues/issue-22886.stderr b/tests/ui/associated-types/associated-type-unconstrained-lifetime.stderr similarity index 89% rename from tests/ui/issues/issue-22886.stderr rename to tests/ui/associated-types/associated-type-unconstrained-lifetime.stderr index aebbdf8a2ec12..108465bf6f932 100644 --- a/tests/ui/issues/issue-22886.stderr +++ b/tests/ui/associated-types/associated-type-unconstrained-lifetime.stderr @@ -1,5 +1,5 @@ error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-22886.rs:13:6 + --> $DIR/associated-type-unconstrained-lifetime.rs:24:6 | LL | impl<'a> Iterator for Newtype { | ^^ unconstrained lifetime parameter diff --git a/tests/ui/issues/issue-28550.rs b/tests/ui/associated-types/nested-fnonce-output-projection.rs similarity index 81% rename from tests/ui/issues/issue-28550.rs rename to tests/ui/associated-types/nested-fnonce-output-projection.rs index 31c7057d06f8c..aa03ca10430ac 100644 --- a/tests/ui/issues/issue-28550.rs +++ b/tests/ui/associated-types/nested-fnonce-output-projection.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + struct AT,T>(F::Output); struct BT,T>(A); diff --git a/tests/ui/associated-types/normalization-ice-issue-149746.rs b/tests/ui/associated-types/normalization-ice-issue-149746.rs index 8932d321b7e5b..29661092b5751 100644 --- a/tests/ui/associated-types/normalization-ice-issue-149746.rs +++ b/tests/ui/associated-types/normalization-ice-issue-149746.rs @@ -4,14 +4,14 @@ trait Owner { type Ty; } impl Owner for () { type Ty = T; } pub struct Warns { _significant_drop: <() as Owner>::Ty, - //~^ ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` field: String, } pub fn test(w: Warns) { - //~^ ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` _ = || w.field - //~^ ERROR expected a `FnMut()` closure, found `T` - //~| ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` + //~| ERROR expected an `FnMut()` closure, found `T` //~| WARN: changes to closure capture in Rust 2021 will affect drop order } fn main() {} diff --git a/tests/ui/associated-types/normalization-ice-issue-149746.stderr b/tests/ui/associated-types/normalization-ice-issue-149746.stderr index 7f983d6e14ae2..ae6f6bbbb52c9 100644 --- a/tests/ui/associated-types/normalization-ice-issue-149746.stderr +++ b/tests/ui/associated-types/normalization-ice-issue-149746.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:6:24 | LL | _significant_drop: <() as Owner>::Ty, @@ -35,7 +35,7 @@ help: add a dummy let to cause `w` to be fully captured LL | _ = || { let _ = &w; w.field } | +++++++++++++ + -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:12:9 | LL | _ = || w.field @@ -48,7 +48,7 @@ note: required by a bound in `Owner::Ty` LL | trait Owner { type Ty; } | ^^^^^^^ required by this bound in `Owner::Ty` -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:10:16 | LL | pub fn test(w: Warns) { @@ -61,7 +61,7 @@ note: required by a bound in `Owner::Ty` LL | trait Owner { type Ty; } | ^^^^^^^ required by this bound in `Owner::Ty` -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:12:9 | LL | _ = || w.field diff --git a/tests/ui/associated-types/normalize-assoc-types.rs b/tests/ui/associated-types/normalize-assoc-types.rs new file mode 100644 index 0000000000000..c90fef9554af8 --- /dev/null +++ b/tests/ui/associated-types/normalize-assoc-types.rs @@ -0,0 +1,14 @@ +//! Regression test for . +//! Test transitive analysis for associated types. Collected types +//! should be normalized and new obligations generated. + +//@ run-pass +#![allow(unused_imports)] + +use std::borrow::{ToOwned, Cow}; + +fn assert_send(_: T) {} + +fn main() { + assert_send(Cow::Borrowed("foo")); +} diff --git a/tests/ui/issues/issue-28828.rs b/tests/ui/associated-types/projection-as-type-alias.rs similarity index 60% rename from tests/ui/issues/issue-28828.rs rename to tests/ui/associated-types/projection-as-type-alias.rs index b5d7385cf28f0..b3e01be818e1f 100644 --- a/tests/ui/issues/issue-28828.rs +++ b/tests/ui/associated-types/projection-as-type-alias.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! This failed to compile as associated types aliases were not normalized. //@ run-pass + pub trait Foo { type Out; } diff --git a/tests/ui/issues/issue-28344.rs b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs similarity index 74% rename from tests/ui/issues/issue-28344.rs rename to tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs index 883a8397e5a9e..2c892fae7b930 100644 --- a/tests/ui/issues/issue-28344.rs +++ b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Test we don't ICE on item resolution when associated type is omitted. //@ edition:2015 + use std::ops::BitXor; fn main() { diff --git a/tests/ui/issues/issue-28344.stderr b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr similarity index 88% rename from tests/ui/issues/issue-28344.stderr rename to tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr index 0dcb585033d07..b942ad8d26556 100644 --- a/tests/ui/issues/issue-28344.stderr +++ b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-28344.rs:5:17 + --> $DIR/resolve-method-with-missing-assoc-type.rs:8:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^^ @@ -13,7 +13,7 @@ LL | let x: u8 = ::bitor(0 as u8, 0 as u8); | ++++ + error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified - --> $DIR/issue-28344.rs:5:17 + --> $DIR/resolve-method-with-missing-assoc-type.rs:8:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^^ @@ -24,7 +24,7 @@ LL | let x: u8 = BitXor::::bitor(0 as u8, 0 as u8); | +++++++++++++++++++++++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-28344.rs:10:13 + --> $DIR/resolve-method-with-missing-assoc-type.rs:13:13 | LL | let g = BitXor::bitor; | ^^^^^^ @@ -37,7 +37,7 @@ LL | let g = ::bitor; | ++++ + error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified - --> $DIR/issue-28344.rs:10:13 + --> $DIR/resolve-method-with-missing-assoc-type.rs:13:13 | LL | let g = BitXor::bitor; | ^^^^^^ diff --git a/tests/ui/issues/issue-22258.rs b/tests/ui/associated-types/ret-associated-type.rs similarity index 68% rename from tests/ui/issues/issue-22258.rs rename to tests/ui/associated-types/ret-associated-type.rs index a1eb8ba14ca98..0da4dfbe73c80 100644 --- a/tests/ui/issues/issue-22258.rs +++ b/tests/ui/associated-types/ret-associated-type.rs @@ -1,3 +1,5 @@ +//! Regression test for . + //@ run-pass use std::ops::Add; diff --git a/tests/ui/issues/issue-22673.rs b/tests/ui/associated-types/supertrait-bound-references-own-associated-type.rs similarity index 52% rename from tests/ui/issues/issue-22673.rs rename to tests/ui/associated-types/supertrait-bound-references-own-associated-type.rs index 019c45dbadc8a..7d410fd4d6127 100644 --- a/tests/ui/issues/issue-22673.rs +++ b/tests/ui/associated-types/supertrait-bound-references-own-associated-type.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ check-pass trait Expr: PartialEq { diff --git a/tests/ui/assumptions_on_binders/resolved-region-var-max-universe.rs b/tests/ui/assumptions_on_binders/resolved-region-var-max-universe.rs new file mode 100644 index 0000000000000..46c9aacdd93d9 --- /dev/null +++ b/tests/ui/assumptions_on_binders/resolved-region-var-max-universe.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +// Regression test for an ICE in the `MaxUniverse` region visitor. When computing +// the max universe of a region constraint, a `ReVar` term could already have been +// unified with another region. `universe_of_lt` returns `None` for such a resolved +// variable, so the visitor used to `unwrap()` `None` and panic. +// +// The missing `T` in `check` is intentional. It makes HIR ty lowering emit an +// error while still leaving behind the region constraint that used to ICE. + +#![feature(min_generic_const_args, inherent_associated_types, generic_const_items)] + +struct Parent<'a> { + a: &'a str, +} + +impl<'a> Parent<'a> { + type const CT: usize = 0; +} + +fn check() +where + [(); Parent::CT::]:, + //~^ ERROR cannot find type `T` in this scope +{ +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/resolved-region-var-max-universe.stderr b/tests/ui/assumptions_on_binders/resolved-region-var-max-universe.stderr new file mode 100644 index 0000000000000..e77a0994fc2db --- /dev/null +++ b/tests/ui/assumptions_on_binders/resolved-region-var-max-universe.stderr @@ -0,0 +1,14 @@ +error[E0425]: cannot find type `T` in this scope + --> $DIR/resolved-region-var-max-universe.rs:23:23 + | +LL | [(); Parent::CT::]:, + | ^ not found in this scope + | +help: you might be missing a type parameter + | +LL | fn check() + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/async-await/async-fn/impl-header.rs b/tests/ui/async-await/async-fn/impl-header.rs index 9af5f1f42a957..349a875d4a6c4 100644 --- a/tests/ui/async-await/async-fn/impl-header.rs +++ b/tests/ui/async-await/async-fn/impl-header.rs @@ -6,7 +6,7 @@ impl async Fn<()> for F {} //~^ ERROR `async` trait implementations are unsupported //~| ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `Fn` are experimental -//~| ERROR expected a `FnMut()` closure, found `F` +//~| ERROR expected an `FnMut()` closure, found `F` //~| ERROR not all trait items implemented, missing: `call` fn main() {} diff --git a/tests/ui/async-await/async-fn/impl-header.stderr b/tests/ui/async-await/async-fn/impl-header.stderr index d0cf0d822f254..d1e3f884d02b3 100644 --- a/tests/ui/async-await/async-fn/impl-header.stderr +++ b/tests/ui/async-await/async-fn/impl-header.stderr @@ -30,7 +30,7 @@ LL | impl async Fn<()> for F {} | = help: implement the missing item: `fn call(&self, _: ()) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut()` closure, found `F` +error[E0277]: expected an `FnMut()` closure, found `F` --> $DIR/impl-header.rs:5:23 | LL | impl async Fn<()> for F {} diff --git a/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr new file mode 100644 index 0000000000000..5a4c99acd0d43 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr @@ -0,0 +1,13 @@ +error: higher-ranked lifetime error + --> $DIR/higher-ranked-auto-trait-18.rs:69:5 + | +LL | / require_send(async { +LL | | let r = Receiver::> { _value: None, _not_sync: PhantomData }; +LL | | let _ = r.await; +LL | | }); + | |______^ + | + = note: could not prove `{async block@$DIR/higher-ranked-auto-trait-18.rs:69:18: 69:23}: Send` + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-18.rs b/tests/ui/async-await/higher-ranked-auto-trait-18.rs new file mode 100644 index 0000000000000..3839fd86cfeba --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-18.rs @@ -0,0 +1,79 @@ +// Repro for a bug where `PhantomData<*mut ()>` + `unsafe impl Send` fails to prove +// `Send` for an async block when the type parameter is a trait object (`dyn Trait`). +// +// The static assertion `assert_send::>>()` succeeds, +// but the same type captured across an `.await` in an async block does not. +// This is because MIR erases the `'static` lifetime from `dyn MyTrait + 'static`, +// and the auto-trait analysis cannot recover the `T: 'static` bound without +// higher-ranked assumptions. +// +// Using `PhantomData>` instead of `PhantomData<*mut ()>` avoids the +// issue because `Cell<()>` is natively `Send`, so no `unsafe impl` (with its +// `T: 'static` bound) is needed. +// +// See . +//@ edition: 2021 +//@ revisions: assumptions no_assumptions +//@[assumptions] compile-flags: -Zhigher-ranked-assumptions +//@[assumptions] check-pass +//@[no_assumptions] known-bug: #110338 + +use std::cell::Cell; +use std::future::Future; +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + +// --- PhantomData<*mut ()> version: needs `unsafe impl Send` --- + +struct Receiver { + _value: Option, + _not_sync: PhantomData<*mut ()>, +} + +unsafe impl Send for Receiver {} + +impl Future for Receiver { + type Output = T; + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +// --- PhantomData> version: auto-derived Send works --- + +struct ReceiverCell { + _value: Option, + _not_sync: PhantomData>, +} + +impl Future for ReceiverCell { + type Output = T; + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +trait MyTrait: Send {} + +fn require_send(_f: F) {} + +fn main() { + // PhantomData<*mut ()> + concrete type: always works. + require_send(async { + let r = Receiver:: { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); + + // PhantomData<*mut ()> + dyn Trait: fails without higher-ranked assumptions. + require_send(async { + let r = Receiver::> { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); + + // PhantomData> + dyn Trait: always works (auto-derived Send). + require_send(async { + let r = ReceiverCell::> { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); +} diff --git a/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs b/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs index f93a2aae5a1e9..6ea36dee947c1 100644 --- a/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs +++ b/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs @@ -18,6 +18,7 @@ target_os = "linux", target_os = "netbsd", target_os = "nto", + target_os = "qnx", target_os = "openbsd", target_os = "fuchsia", target_os = "managarm", diff --git a/tests/ui/attributes/dump-preds.stderr b/tests/ui/attributes/dump-preds.stderr index b4be76450c6a9..f5ca9f2b9110a 100644 --- a/tests/ui/attributes/dump-preds.stderr +++ b/tests/ui/attributes/dump-preds.stderr @@ -33,7 +33,7 @@ error: rustc_dump_item_bounds LL | type Assoc: std::ops::Deref | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(..) }, args: [Alias(Alias { kind: Projection { def_id: DefId(..) }, args: [Self/#0, T/#1, P/#2], .. })], .. }, Term::Ty(())), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(..) }, args: [Alias(No, Alias { kind: Projection { def_id: DefId(..) }, args: [Self/#0, T/#1, P/#2], .. })], .. }, Term::Ty(())), bound_vars: [] } = note: Binder { value: TraitPredicate(<>::Assoc

as std::ops::Deref>, polarity:Positive), bound_vars: [] } = note: Binder { value: TraitPredicate(<>::Assoc

as std::marker::Sized>, polarity:Positive), bound_vars: [] } diff --git a/tests/ui/attributes/invalid-crate-type.rs b/tests/ui/attributes/invalid-crate-type.rs index c233e7d5e3db8..aea75f4cfad90 100644 --- a/tests/ui/attributes/invalid-crate-type.rs +++ b/tests/ui/attributes/invalid-crate-type.rs @@ -1,5 +1,8 @@ // regression test for issue 11256 #![crate_type="foo"] //~ ERROR invalid `crate_type` value +//~| NOTE `#[deny(unknown_crate_types)]` on by default +//~| HELP did you mean +//~| SUGGESTION lib // Tests for suggestions (#53958) @@ -43,6 +46,52 @@ //~| HELP did you mean //~| SUGGESTION cdylib +// substring matching tests +#![crate_type="binary"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION bin + +#![crate_type="library"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION lib + +#![crate_type="rustlib"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION rlib + +#![crate_type="dynamiclib"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION dylib + +#![crate_type="dylibrary"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION dylib + +#![crate_type="cdynamiclib"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION cdylib + +#![crate_type="cdylibrary"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION cdylib + +#![crate_type="staticlibrary"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION staticlib + +#![crate_type="procedural-macro"] +//~^ ERROR invalid `crate_type` value +//~| HELP did you mean +//~| SUGGESTION proc-macro + fn main() { return } diff --git a/tests/ui/attributes/invalid-crate-type.stderr b/tests/ui/attributes/invalid-crate-type.stderr index 3eae04678b4a7..2c7663b10925e 100644 --- a/tests/ui/attributes/invalid-crate-type.stderr +++ b/tests/ui/attributes/invalid-crate-type.stderr @@ -2,57 +2,111 @@ error: invalid `crate_type` value --> $DIR/invalid-crate-type.rs:2:15 | LL | #![crate_type="foo"] - | ^^^^^ + | ^^^^^ help: did you mean: `"lib"` | = note: `#[deny(unknown_crate_types)]` on by default error: invalid `crate_type` value - --> $DIR/invalid-crate-type.rs:6:15 + --> $DIR/invalid-crate-type.rs:9:15 | LL | #![crate_type="statoclib"] | ^^^^^^^^^^^ help: did you mean: `"staticlib"` error: invalid `crate_type` value - --> $DIR/invalid-crate-type.rs:11:15 + --> $DIR/invalid-crate-type.rs:14:15 | LL | #![crate_type="procmacro"] | ^^^^^^^^^^^ help: did you mean: `"proc-macro"` error: invalid `crate_type` value - --> $DIR/invalid-crate-type.rs:16:15 + --> $DIR/invalid-crate-type.rs:19:15 | LL | #![crate_type="static-lib"] | ^^^^^^^^^^^^ help: did you mean: `"staticlib"` error: invalid `crate_type` value - --> $DIR/invalid-crate-type.rs:21:15 + --> $DIR/invalid-crate-type.rs:24:15 | LL | #![crate_type="drylib"] | ^^^^^^^^ help: did you mean: `"dylib"` error: invalid `crate_type` value - --> $DIR/invalid-crate-type.rs:26:15 + --> $DIR/invalid-crate-type.rs:29:15 | LL | #![crate_type="dlib"] | ^^^^^^ help: did you mean: `"lib"` error: invalid `crate_type` value - --> $DIR/invalid-crate-type.rs:31:15 + --> $DIR/invalid-crate-type.rs:34:15 | LL | #![crate_type="lob"] | ^^^^^ help: did you mean: `"lib"` error: invalid `crate_type` value - --> $DIR/invalid-crate-type.rs:36:15 + --> $DIR/invalid-crate-type.rs:39:15 | LL | #![crate_type="bon"] | ^^^^^ help: did you mean: `"bin"` error: invalid `crate_type` value - --> $DIR/invalid-crate-type.rs:41:15 + --> $DIR/invalid-crate-type.rs:44:15 | LL | #![crate_type="cdalib"] | ^^^^^^^^ help: did you mean: `"cdylib"` -error: aborting due to 9 previous errors +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:50:15 + | +LL | #![crate_type="binary"] + | ^^^^^^^^ help: did you mean: `"bin"` + +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:55:15 + | +LL | #![crate_type="library"] + | ^^^^^^^^^ help: did you mean: `"lib"` + +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:60:15 + | +LL | #![crate_type="rustlib"] + | ^^^^^^^^^ help: did you mean: `"rlib"` + +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:65:15 + | +LL | #![crate_type="dynamiclib"] + | ^^^^^^^^^^^^ help: did you mean: `"dylib"` + +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:70:15 + | +LL | #![crate_type="dylibrary"] + | ^^^^^^^^^^^ help: did you mean: `"dylib"` + +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:75:15 + | +LL | #![crate_type="cdynamiclib"] + | ^^^^^^^^^^^^^ help: did you mean: `"cdylib"` + +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:80:15 + | +LL | #![crate_type="cdylibrary"] + | ^^^^^^^^^^^^ help: did you mean: `"cdylib"` + +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:85:15 + | +LL | #![crate_type="staticlibrary"] + | ^^^^^^^^^^^^^^^ help: did you mean: `"staticlib"` + +error: invalid `crate_type` value + --> $DIR/invalid-crate-type.rs:90:15 + | +LL | #![crate_type="procedural-macro"] + | ^^^^^^^^^^^^^^^^^^ help: did you mean: `"proc-macro"` + +error: aborting due to 18 previous errors diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 7d8bd3700d4bb..96012d8df936b 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -550,8 +550,8 @@ LL | #[patchable_function_entry] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0565]: malformed `coroutine` attribute input --> $DIR/malformed-attrs.rs:118:5 diff --git a/tests/ui/attributes/may_dangle.rs b/tests/ui/attributes/may_dangle.rs index 209ba0e88ad18..401efce88851c 100644 --- a/tests/ui/attributes/may_dangle.rs +++ b/tests/ui/attributes/may_dangle.rs @@ -12,7 +12,7 @@ unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> { //~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl unsafe impl<'a, T, #[may_dangle] const N: usize> Drop for Implee1<'a, T, N> { - //~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl + //~^ ERROR attribute cannot be used on fn drop(&mut self) {} } @@ -39,15 +39,15 @@ mod fake { } } -#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl +#[may_dangle] //~ ERROR attribute cannot be used on struct Dangling; -#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl +#[may_dangle] //~ ERROR attribute cannot be used on impl NotDrop for () { } -#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl +#[may_dangle] //~ ERROR attribute cannot be used on fn main() { - #[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl + #[may_dangle] //~ ERROR attribute cannot be used on let () = (); } diff --git a/tests/ui/attributes/may_dangle.stderr b/tests/ui/attributes/may_dangle.stderr index dc24f847f712f..596c44aa958e1 100644 --- a/tests/ui/attributes/may_dangle.stderr +++ b/tests/ui/attributes/may_dangle.stderr @@ -1,44 +1,54 @@ -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl - --> $DIR/may_dangle.rs:8:13 - | -LL | unsafe impl<#[may_dangle] 'a, T, const N: usize> NotDrop for Implee1<'a, T, N> {} - | ^^^^^^^^^^^^^ - -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl - --> $DIR/may_dangle.rs:11:17 - | -LL | unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> {} - | ^^^^^^^^^^^^^ - -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on const parameters --> $DIR/may_dangle.rs:14:20 | LL | unsafe impl<'a, T, #[may_dangle] const N: usize> Drop for Implee1<'a, T, N> { | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on structs --> $DIR/may_dangle.rs:42:1 | LL | #[may_dangle] | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on trait impl blocks --> $DIR/may_dangle.rs:45:1 | LL | #[may_dangle] | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on functions --> $DIR/may_dangle.rs:49:1 | LL | #[may_dangle] | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters -error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl +error: `#[may_dangle]` attribute cannot be used on statements --> $DIR/may_dangle.rs:51:5 | LL | #[may_dangle] | ^^^^^^^^^^^^^ + | + = help: `#[may_dangle]` can be applied to lifetime parameters and type parameters + +error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl + --> $DIR/may_dangle.rs:8:13 + | +LL | unsafe impl<#[may_dangle] 'a, T, const N: usize> NotDrop for Implee1<'a, T, N> {} + | ^^^^^^^^^^^^^ + +error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl + --> $DIR/may_dangle.rs:11:17 + | +LL | unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> {} + | ^^^^^^^^^^^^^ error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl --> $DIR/may_dangle.rs:36:17 diff --git a/tests/ui/issues/issue-21763.rs b/tests/ui/auto-traits/hashmap-send-requires-send-contents.rs similarity index 68% rename from tests/ui/issues/issue-21763.rs rename to tests/ui/auto-traits/hashmap-send-requires-send-contents.rs index 5f236a25e9944..b543929642f3f 100644 --- a/tests/ui/issues/issue-21763.rs +++ b/tests/ui/auto-traits/hashmap-send-requires-send-contents.rs @@ -1,4 +1,5 @@ -// Regression test for HashMap only impl'ing Send/Sync if its contents do +//! Regression test for . +//! Test HashMap only impl Send/Sync if its contents do //@ normalize-stderr: "(?:[A-Za-z]:[/\\]|/).*[\\/]hashbrown\S+" -> "$$HASHBROWN_SRC_LOCATION" diff --git a/tests/ui/issues/issue-21763.stderr b/tests/ui/auto-traits/hashmap-send-requires-send-contents.stderr similarity index 89% rename from tests/ui/issues/issue-21763.stderr rename to tests/ui/auto-traits/hashmap-send-requires-send-contents.stderr index 135b705eeeff2..db37db925b00a 100644 --- a/tests/ui/issues/issue-21763.stderr +++ b/tests/ui/auto-traits/hashmap-send-requires-send-contents.stderr @@ -1,5 +1,5 @@ error[E0277]: `Rc<()>` cannot be sent between threads safely - --> $DIR/issue-21763.rs:11:11 + --> $DIR/hashmap-send-requires-send-contents.rs:12:11 | LL | foo::, Rc<()>>>(); | ^^^^^^^^^^^^^^^^^^^^^^^ `Rc<()>` cannot be sent between threads safely @@ -12,7 +12,7 @@ note: required because it appears within the type `hashbrown::map::HashMap, Rc<()>>` --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL note: required by a bound in `foo` - --> $DIR/issue-21763.rs:8:11 + --> $DIR/hashmap-send-requires-send-contents.rs:9:11 | LL | fn foo() {} | ^^^^ required by this bound in `foo` diff --git a/tests/ui/issues/issue-21922.rs b/tests/ui/binop/add-ref-operand-method-lookup.rs similarity index 56% rename from tests/ui/issues/issue-21922.rs rename to tests/ui/binop/add-ref-operand-method-lookup.rs index 6404ab8b7aa0f..b4573fc7658b6 100644 --- a/tests/ui/issues/issue-21922.rs +++ b/tests/ui/binop/add-ref-operand-method-lookup.rs @@ -1,3 +1,10 @@ +//! Regression test for . +//! +//! Ensure Add works with all value/reference operand combinations, +//! both via the + operator and ufcs. +//! +//! Originally method lookup failed only for x + &y. + //@ run-pass use std::ops::Add; fn show(z: i32) { diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr index a997fbee1f2a0..ad8802bf1370f 100644 --- a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr @@ -12,7 +12,7 @@ note: an implementation of `PartialEq` might be missing for `T1` LL | struct T1; | ^^^^^^^^^ must implement `PartialEq` note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` @@ -34,7 +34,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate @@ -58,7 +58,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` diff --git a/tests/ui/issues/issue-27054-primitive-binary-ops.rs b/tests/ui/binop/binop-mutate-lhs-in-rhs.rs similarity index 52% rename from tests/ui/issues/issue-27054-primitive-binary-ops.rs rename to tests/ui/binop/binop-mutate-lhs-in-rhs.rs index 96b0edf838ebf..d3e8166050513 100644 --- a/tests/ui/issues/issue-27054-primitive-binary-ops.rs +++ b/tests/ui/binop/binop-mutate-lhs-in-rhs.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + fn main() { let x = &mut 1; assert_eq!(*x + { *x=2; 1 }, 2); diff --git a/tests/ui/issues/issue-27949.rs b/tests/ui/binop/binop-subtyping-lifetimes.rs similarity index 67% rename from tests/ui/issues/issue-27949.rs rename to tests/ui/binop/binop-subtyping-lifetimes.rs index e10c60085072f..6456768fed939 100644 --- a/tests/ui/issues/issue-27949.rs +++ b/tests/ui/binop/binop-subtyping-lifetimes.rs @@ -1,10 +1,11 @@ +//! Regression test for . +//! +//! At one time, the `==` operator (and other binary operators) did not +//! support subtyping during type checking, and would therefore require +//! LHS and RHS to be exactly identical--i.e. to have the same lifetimes. +//! +//! This was fixed in 1a7fb7dc78439a704f024609ce3dc0beb1386552. //@ run-pass -// -// At one time, the `==` operator (and other binary operators) did not -// support subtyping during type checking, and would therefore require -// LHS and RHS to be exactly identical--i.e. to have the same lifetimes. -// -// This was fixed in 1a7fb7dc78439a704f024609ce3dc0beb1386552. #[derive(Copy, Clone)] struct Input<'a> { diff --git a/tests/ui/issues/issue-21634.rs b/tests/ui/binop/infer-rhs-type.rs similarity index 85% rename from tests/ui/issues/issue-21634.rs rename to tests/ui/binop/infer-rhs-type.rs index 475a33eca58ec..41ad829141c63 100644 --- a/tests/ui/issues/issue-21634.rs +++ b/tests/ui/binop/infer-rhs-type.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-pass #[cfg(any(not(target_arch = "x86"), target_feature = "sse2"))] diff --git a/tests/ui/issues/issue-27889.rs b/tests/ui/borrowck/enum-variants-share-field-name.rs similarity index 62% rename from tests/ui/issues/issue-27889.rs rename to tests/ui/borrowck/enum-variants-share-field-name.rs index 8737f03db1a59..d357d805ea22a 100644 --- a/tests/ui/issues/issue-27889.rs +++ b/tests/ui/borrowck/enum-variants-share-field-name.rs @@ -1,8 +1,9 @@ +//! Regression test for . +//! Test that a field can have the same name in different variants +//! of an enum, and borrowck won't treat them as the same value. //@ check-pass #![allow(unused_assignments)] #![allow(unused_variables)] -// Test that a field can have the same name in different variants -// of an enum pub enum Foo { X { foo: u32 }, diff --git a/tests/ui/issues/issue-25497.rs b/tests/ui/box/match-box-deref-codegen.rs similarity index 60% rename from tests/ui/issues/issue-25497.rs rename to tests/ui/box/match-box-deref-codegen.rs index 58dcf416dbebb..30f07c0fd8990 100644 --- a/tests/ui/issues/issue-25497.rs +++ b/tests/ui/box/match-box-deref-codegen.rs @@ -1,4 +1,8 @@ +//! Regression test for . +//! Test box deref in match arm doesn't generate invalid LLVM IR. +//! Related . //@ run-pass + #[derive(Clone, Debug, PartialEq)] enum Expression { Dummy, diff --git a/tests/ui/issues/issue-23024.rs b/tests/ui/cast/dyn-any-to-fn-with-missing-generics.rs similarity index 79% rename from tests/ui/issues/issue-23024.rs rename to tests/ui/cast/dyn-any-to-fn-with-missing-generics.rs index 1b072dd7b69c0..504fe096c4204 100644 --- a/tests/ui/issues/issue-23024.rs +++ b/tests/ui/cast/dyn-any-to-fn-with-missing-generics.rs @@ -1,3 +1,4 @@ +//! Regression test for . use std::any::Any; fn main() diff --git a/tests/ui/issues/issue-23024.stderr b/tests/ui/cast/dyn-any-to-fn-with-missing-generics.stderr similarity index 90% rename from tests/ui/issues/issue-23024.stderr rename to tests/ui/cast/dyn-any-to-fn-with-missing-generics.stderr index 51db0414f3a39..53aa609321431 100644 --- a/tests/ui/issues/issue-23024.stderr +++ b/tests/ui/cast/dyn-any-to-fn-with-missing-generics.stderr @@ -1,5 +1,5 @@ error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change - --> $DIR/issue-23024.rs:8:39 + --> $DIR/dyn-any-to-fn-with-missing-generics.rs:9:39 | LL | println!("{:?}",(vfnfer[0] as dyn Fn)(3)); | ^^ help: use parenthetical notation instead: `Fn() -> ()` @@ -9,7 +9,7 @@ LL | println!("{:?}",(vfnfer[0] as dyn Fn)(3)); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0107]: missing generics for trait `Fn` - --> $DIR/issue-23024.rs:8:39 + --> $DIR/dyn-any-to-fn-with-missing-generics.rs:9:39 | LL | println!("{:?}",(vfnfer[0] as dyn Fn)(3)); | ^^ expected 1 generic argument diff --git a/tests/ui/issues/issue-22034.rs b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs similarity index 51% rename from tests/ui/issues/issue-22034.rs rename to tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs index 3631c2368fe82..d966781f90d9f 100644 --- a/tests/ui/issues/issue-22034.rs +++ b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs @@ -1,7 +1,9 @@ +//! Regression test for . + fn main() { let ptr: *mut () = core::ptr::null_mut(); let _: &mut dyn Fn() = unsafe { &mut *(ptr as *mut dyn Fn()) - //~^ ERROR expected a `Fn()` closure, found `()` + //~^ ERROR expected an `Fn()` closure, found `()` }; } diff --git a/tests/ui/issues/issue-22034.stderr b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr similarity index 81% rename from tests/ui/issues/issue-22034.stderr rename to tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr index 68202085e77db..a830ddc4468ff 100644 --- a/tests/ui/issues/issue-22034.stderr +++ b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr @@ -1,5 +1,5 @@ -error[E0277]: expected a `Fn()` closure, found `()` - --> $DIR/issue-22034.rs:4:16 +error[E0277]: expected an `Fn()` closure, found `()` + --> $DIR/raw-ptr-to-dyn-fn-raw-ptr.rs:6:16 | LL | &mut *(ptr as *mut dyn Fn()) | ^^^ expected an `Fn()` closure, found `()` diff --git a/tests/ui/check-cfg/cfg-crate-features.stderr b/tests/ui/check-cfg/cfg-crate-features.stderr index 242883995488e..9bf3cef403159 100644 --- a/tests/ui/check-cfg/cfg-crate-features.stderr +++ b/tests/ui/check-cfg/cfg-crate-features.stderr @@ -24,7 +24,7 @@ warning: unexpected `cfg` condition value: `does_not_exist` LL | #![cfg(not(target(os = "does_not_exist")))] | ^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `qurt`, `redox`, `rtems`, `solaris`, and `solid_asp3` and 14 more + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `qnx`, `qurt`, `redox`, `rtems`, and `solaris` and 15 more = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 47ed9891a1a6c..5c797783e48b8 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `pauthtest`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `p3`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` + = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `ohos`, `p1`, `p2`, `p3`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -210,7 +210,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `qurt`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `qnx`, `qurt`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -283,7 +283,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `qurt`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `qnx`, `qurt`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: 29 warnings emitted diff --git a/tests/ui/closures/closure-expected.rs b/tests/ui/closures/closure-expected.rs index d730bcd1faf51..4fc1b3a1fd006 100644 --- a/tests/ui/closures/closure-expected.rs +++ b/tests/ui/closures/closure-expected.rs @@ -1,5 +1,5 @@ fn main() { let x = Some(1); let y = x.or_else(4); - //~^ ERROR expected a `FnOnce()` closure, found `{integer}` + //~^ ERROR expected an `FnOnce()` closure, found `{integer}` } diff --git a/tests/ui/closures/closure-expected.stderr b/tests/ui/closures/closure-expected.stderr index 53a93e1e84dd0..490988f247cdd 100644 --- a/tests/ui/closures/closure-expected.stderr +++ b/tests/ui/closures/closure-expected.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/closure-expected.rs:3:23 | LL | let y = x.or_else(4); diff --git a/tests/ui/closures/coerce-unsafe-to-closure.stderr b/tests/ui/closures/coerce-unsafe-to-closure.stderr index 013b9009da406..2bf2739be52d6 100644 --- a/tests/ui/closures/coerce-unsafe-to-closure.stderr +++ b/tests/ui/closures/coerce-unsafe-to-closure.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&str)` closure, found `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` +error[E0277]: expected an `FnOnce(&str)` closure, found `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` --> $DIR/coerce-unsafe-to-closure.rs:2:44 | LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); diff --git a/tests/ui/codegen/huge-stacks.rs b/tests/ui/codegen/huge-stacks.rs index e0f2cd2cb05d6..9aee3937c7534 100644 --- a/tests/ui/codegen/huge-stacks.rs +++ b/tests/ui/codegen/huge-stacks.rs @@ -5,6 +5,7 @@ //@ min-llvm-version: 22 // Regression test for https://github.com/rust-lang/rust/issues/83060 +// Verifies a program is not miscompiled if it includes a 4GB array on the stack fn func() { const CAP: usize = std::u32::MAX as usize; @@ -17,7 +18,7 @@ fn main() { std::thread::Builder::new() .stack_size(5 * 1024 * 1024 * 1024) .spawn(func) - .unwrap() + .expect("huge-stacks.rs requires 5GB RAM to run") .join() .unwrap(); } diff --git a/tests/ui/issues/issue-26468.rs b/tests/ui/codegen/match-enum-range-metadata-misopt.rs similarity index 75% rename from tests/ui/issues/issue-26468.rs rename to tests/ui/codegen/match-enum-range-metadata-misopt.rs index 2103b354977f8..a087ef8850b2c 100644 --- a/tests/ui/issues/issue-26468.rs +++ b/tests/ui/codegen/match-enum-range-metadata-misopt.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Range bug in LLVM misoptimized second `if let` inside `for _ in 0..1` away. //@ run-pass + #![allow(dead_code)] enum FooMode { diff --git a/tests/ui/issues/issue-27105.rs b/tests/ui/coercion/infer-coerce-unsized-trait-object.rs similarity index 80% rename from tests/ui/issues/issue-27105.rs rename to tests/ui/coercion/infer-coerce-unsized-trait-object.rs index 3308f3e8e010d..dfb9ce361f481 100644 --- a/tests/ui/issues/issue-27105.rs +++ b/tests/ui/coercion/infer-coerce-unsized-trait-object.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ check-pass + use std::cell::RefCell; use std::rc::Rc; diff --git a/tests/ui/const-generics/gca/non-type-equality-fail.stderr b/tests/ui/const-generics/gca/non-type-equality-fail.stderr index 796efdcea93cc..05d90f5632728 100644 --- a/tests/ui/const-generics/gca/non-type-equality-fail.stderr +++ b/tests/ui/const-generics/gca/non-type-equality-fail.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | let _: Struct<{ as Trait>::PROJECTED_A }> = | -------------------------------------------------------- expected due to this LL | Struct::<{ as Trait>::PROJECTED_B }>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected ` as Trait>::PROJECTED_A`, found ` as Trait>::PROJECTED_B` | = note: expected struct `Struct< as Trait>::PROJECTED_A>` found struct `Struct< as Trait>::PROJECTED_B>` @@ -13,7 +13,7 @@ error[E0308]: mismatched types --> $DIR/non-type-equality-fail.rs:37:41 | LL | let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_B }>; - | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `::PROJECTED_A`, found `::PROJECTED_B` | | | expected due to this | diff --git a/tests/ui/const-generics/issues/issue-100313.rs b/tests/ui/const-generics/issues/issue-100313.rs index 3eed2e71ae859..6a70a9f81e925 100644 --- a/tests/ui/const-generics/issues/issue-100313.rs +++ b/tests/ui/const-generics/issues/issue-100313.rs @@ -1,5 +1,5 @@ //@ dont-require-annotations: NOTE -//@ ignore-parallel-frontend different alloc ids + #![allow(incomplete_features)] #![feature(adt_const_params, unsized_const_params)] @@ -15,7 +15,7 @@ impl T { const _: () = { let x = T::<{ &true }>; - x.set_false(); //~ ERROR writing to ALLOC0 which is read-only + x.set_false(); //~ ERROR writing to ALLOC$ID which is read-only }; fn main() {} diff --git a/tests/ui/const-generics/issues/issue-100313.stderr b/tests/ui/const-generics/issues/issue-100313.stderr index d1a0249728ebd..17bad92d8dfa5 100644 --- a/tests/ui/const-generics/issues/issue-100313.stderr +++ b/tests/ui/const-generics/issues/issue-100313.stderr @@ -1,4 +1,4 @@ -error[E0080]: writing to ALLOC0 which is read-only +error[E0080]: writing to ALLOC$ID which is read-only --> $DIR/issue-100313.rs:18:5 | LL | x.set_false(); diff --git a/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs new file mode 100644 index 0000000000000..c509c52951197 --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs @@ -0,0 +1,13 @@ +// Regression test for issue #155834 + +#![expect(incomplete_features)] +#![feature(min_generic_const_args)] + +trait Trait {} + +impl<'t> Trait for [(); N] {} +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for implementations + +fn N(arg: impl Trait) {} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr new file mode 100644 index 0000000000000..a5caa077c7620 --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr @@ -0,0 +1,9 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/bad-impl-trait-with-apit.rs:8:25 + | +LL | impl<'t> Trait for [(); N] {} + | ^ not allowed in type signatures + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs b/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs index a7e46ad877e72..b3dfef1cb26ca 100644 --- a/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs +++ b/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs @@ -5,6 +5,6 @@ #![expect(incomplete_features)] type const A: u8 = A; -//~^ ERROR overflow normalizing the unevaluated constant `A` +//~^ ERROR overflow normalizing the const alias `A` fn main() {} diff --git a/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr b/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr index 47653dd1896f7..07b4ca43eb77b 100644 --- a/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr +++ b/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow normalizing the unevaluated constant `A` +error[E0275]: overflow normalizing the const alias `A` --> $DIR/cyclic-type-const-151251.rs:7:1 | LL | type const A: u8 = A; diff --git a/tests/ui/const-generics/mgca/double-inline-const.rs b/tests/ui/const-generics/mgca/double-inline-const.rs new file mode 100644 index 0000000000000..9bfbd1819c695 --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.rs @@ -0,0 +1,11 @@ +#![feature(min_generic_const_args)] + +struct S; + +impl S { + const Q: usize = 2; + fn foo(_: S<{ const { const { Self::Q } } }>) {} + //~^ ERROR generic `Self` types are currently not permitted in anonymous constants +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/double-inline-const.stderr b/tests/ui/const-generics/mgca/double-inline-const.stderr new file mode 100644 index 0000000000000..bc73e3d93575e --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.stderr @@ -0,0 +1,15 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/double-inline-const.rs:7:35 + | +LL | fn foo(_: S<{ const { const { Self::Q } } }>) {} + | ^^^^ + | +note: not a concrete type + --> $DIR/double-inline-const.rs:5:22 + | +LL | impl S { + | ^^^^ + = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/free-const-recursive.rs b/tests/ui/const-generics/mgca/free-const-recursive.rs index 6ace061baa3e9..1bbdc36e168b6 100644 --- a/tests/ui/const-generics/mgca/free-const-recursive.rs +++ b/tests/ui/const-generics/mgca/free-const-recursive.rs @@ -5,8 +5,8 @@ #![expect(incomplete_features)] type const A: () = A; -//~^ ERROR type mismatch resolving `A == _` -//~| ERROR the constant `A` is not of type `()` +//~^ ERROR: overflow evaluating the requirement `A == _` +//~| ERROR: overflow evaluating the requirement `the constant `A` has type `()`` fn main() { A; diff --git a/tests/ui/const-generics/mgca/free-const-recursive.stderr b/tests/ui/const-generics/mgca/free-const-recursive.stderr index 405091904418b..aeb2bd4b22ddf 100644 --- a/tests/ui/const-generics/mgca/free-const-recursive.stderr +++ b/tests/ui/const-generics/mgca/free-const-recursive.stderr @@ -1,15 +1,15 @@ -error[E0271]: type mismatch resolving `A == _` +error[E0275]: overflow evaluating the requirement `A == _` --> $DIR/free-const-recursive.rs:7:1 | LL | type const A: () = A; - | ^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^ -error: the constant `A` is not of type `()` +error[E0275]: overflow evaluating the requirement `the constant `A` has type `()`` --> $DIR/free-const-recursive.rs:7:1 | LL | type const A: () = A; - | ^^^^^^^^^^^^^^^^ expected `()`, found a different `()` + | ^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/const-generics/mgca/projection-const-recursive.rs b/tests/ui/const-generics/mgca/projection-const-recursive.rs index 330a182549a6c..a2f54fbfca0e3 100644 --- a/tests/ui/const-generics/mgca/projection-const-recursive.rs +++ b/tests/ui/const-generics/mgca/projection-const-recursive.rs @@ -10,8 +10,8 @@ trait Trait { impl Trait for () { type const A: () = <() as Trait>::A; - //~^ ERROR type mismatch resolving `<() as Trait>::A == _` - //~| ERROR the constant `<() as Trait>::A` is not of type `()` + //~^ ERROR: overflow evaluating the requirement `<() as Trait>::A == _` + //~| ERROR: overflow evaluating the requirement `the constant `<() as Trait>::A` has type `()`` } fn main() { diff --git a/tests/ui/const-generics/mgca/projection-const-recursive.stderr b/tests/ui/const-generics/mgca/projection-const-recursive.stderr index 8f6ca9e4b7d24..a102e82426fca 100644 --- a/tests/ui/const-generics/mgca/projection-const-recursive.stderr +++ b/tests/ui/const-generics/mgca/projection-const-recursive.stderr @@ -1,15 +1,15 @@ -error[E0271]: type mismatch resolving `<() as Trait>::A == _` +error[E0275]: overflow evaluating the requirement `<() as Trait>::A == _` --> $DIR/projection-const-recursive.rs:12:5 | LL | type const A: () = <() as Trait>::A; - | ^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^ -error: the constant `<() as Trait>::A` is not of type `()` +error[E0275]: overflow evaluating the requirement `the constant `<() as Trait>::A` has type `()`` --> $DIR/projection-const-recursive.rs:12:5 | LL | type const A: () = <() as Trait>::A; - | ^^^^^^^^^^^^^^^^ expected `()`, found a different `()` + | ^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs new file mode 100644 index 0000000000000..53cf964b8f5a5 --- /dev/null +++ b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs @@ -0,0 +1,12 @@ +#![feature(min_generic_const_args)] + +trait A {} +trait Trait {} + +impl A<[usize; fn_item]> for () {} +//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for implementations + +fn fn_item(_: impl Trait) {} +//~^ ERROR: type provided when a constant was expected + +fn main() {} diff --git a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr new file mode 100644 index 0000000000000..a68c0a1cb409d --- /dev/null +++ b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr @@ -0,0 +1,21 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/synth-gen-arg-ice-158152.rs:6:16 + | +LL | impl A<[usize; fn_item]> for () {} + | ^^^^^^^ not allowed in type signatures + +error[E0747]: type provided when a constant was expected + --> $DIR/synth-gen-arg-ice-158152.rs:9:26 + | +LL | fn fn_item(_: impl Trait) {} + | ^^^^^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | fn fn_item(_: impl Trait<{ usize }>) {} + | + + + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0121, E0747. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs index 4aecd30e86bbb..70b955589d3e7 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs @@ -2,7 +2,7 @@ #![expect(incomplete_features)] struct Y { - stuff: [u8; { ([1, 2], 3, [4, 5]) }], //~ ERROR expected `usize`, found const tuple + stuff: [u8; { ([1, 2], 3, [4, 5]) }], //~ ERROR expected `usize`, found `([1, 2], 3, [4, 5])` } fn main() {} diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr index 468bf703d90d4..aa54a5ae77048 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr @@ -1,4 +1,4 @@ -error: expected `usize`, found const tuple +error: expected `usize`, found `([1, 2], 3, [4, 5])` --> $DIR/tuple_expr_arg_bad-issue-151048.rs:5:19 | LL | stuff: [u8; { ([1, 2], 3, [4, 5]) }], diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.rs index 95acd66074f6f..608a4af353a5e 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.rs @@ -2,7 +2,7 @@ #![expect(incomplete_features)] pub fn takes_nested_tuple() { - takes_nested_tuple::<{ () }> //~ ERROR expected `u32`, found const tuple + takes_nested_tuple::<{ () }> //~ ERROR expected `u32`, found `()` } fn main() {} diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.stderr index dfbd294951fdc..d8314a6b6a57e 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.stderr +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.stderr @@ -1,4 +1,4 @@ -error: expected `u32`, found const tuple +error: expected `u32`, found `()` --> $DIR/tuple_expr_arg_mismatch_type.rs:5:28 | LL | takes_nested_tuple::<{ () }> diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.rs new file mode 100644 index 0000000000000..f844dae5c8220 --- /dev/null +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.rs @@ -0,0 +1,13 @@ +#![feature(min_generic_const_args, adt_const_params)] + +// Regression test for an ICE in privacy checking while walking the `T` qself +// of `T::ASSOC` inside a tuple const argument. + +fn takes_tuple() {} + +fn generic_caller() { + takes_tuple::<{ (N, T::ASSOC) }>; + //~^ ERROR expected `()`, found `(N, T::ASSOC)` +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.stderr new file mode 100644 index 0000000000000..3af04db14b60b --- /dev/null +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.stderr @@ -0,0 +1,8 @@ +error: expected `()`, found `(N, T::ASSOC)` + --> $DIR/tuple_expr_arg_unbounded_assoc_const.rs:9:21 + | +LL | takes_tuple::<{ (N, T::ASSOC) }>; + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/type_const-recursive.rs b/tests/ui/const-generics/mgca/type_const-recursive.rs index ebaab51bbc3b6..0791325d603be 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.rs +++ b/tests/ui/const-generics/mgca/type_const-recursive.rs @@ -3,6 +3,6 @@ type const A: u8 = A; -//~^ ERROR: overflow normalizing the unevaluated constant `A` [E0275] +//~^ ERROR: overflow normalizing the const alias `A` [E0275] fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-recursive.stderr b/tests/ui/const-generics/mgca/type_const-recursive.stderr index d21ccb22bc90b..f9e0965dc5412 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.stderr +++ b/tests/ui/const-generics/mgca/type_const-recursive.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow normalizing the unevaluated constant `A` +error[E0275]: overflow normalizing the const alias `A` --> $DIR/type_const-recursive.rs:5:1 | LL | type const A: u8 = A; diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr index ec39cc27efb41..31a2accccd7fd 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr @@ -46,7 +46,7 @@ note: expected because of the type of the const parameter LL | fn get_flag() -> Option { | ^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: reading memory at ALLOC0[0x0..0x4], but memory is uninitialized at [0x1..0x4], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x4], but memory is uninitialized at [0x1..0x4], and this operation requires initialized memory --> $DIR/invalid-patterns.rs:40:32 | LL | get_flag::(); @@ -78,7 +78,7 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character 42 │ B } -error[E0080]: reading memory at ALLOC1[0x0..0x4], but memory is uninitialized at [0x1..0x4], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x4], but memory is uninitialized at [0x1..0x4], and this operation requires initialized memory --> $DIR/invalid-patterns.rs:44:58 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr index ec39cc27efb41..31a2accccd7fd 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr @@ -46,7 +46,7 @@ note: expected because of the type of the const parameter LL | fn get_flag() -> Option { | ^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: reading memory at ALLOC0[0x0..0x4], but memory is uninitialized at [0x1..0x4], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x4], but memory is uninitialized at [0x1..0x4], and this operation requires initialized memory --> $DIR/invalid-patterns.rs:40:32 | LL | get_flag::(); @@ -78,7 +78,7 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character 42 │ B } -error[E0080]: reading memory at ALLOC1[0x0..0x4], but memory is uninitialized at [0x1..0x4], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x4], but memory is uninitialized at [0x1..0x4], and this operation requires initialized memory --> $DIR/invalid-patterns.rs:44:58 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.rs b/tests/ui/const-generics/min_const_generics/invalid-patterns.rs index 847b51abb6af5..85f019adf6649 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.rs +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.rs @@ -1,6 +1,6 @@ //@ stderr-per-bitwidth //@ dont-require-annotations: NOTE -//@ ignore-parallel-frontend different alloc ids + use std::mem::transmute; fn get_flag() -> Option { diff --git a/tests/ui/const-ptr/forbidden_slices.rs b/tests/ui/const-ptr/forbidden_slices.rs index b7c02e6735b4e..fcb0dccf750e3 100644 --- a/tests/ui/const-ptr/forbidden_slices.rs +++ b/tests/ui/const-ptr/forbidden_slices.rs @@ -1,7 +1,7 @@ // Strip out raw byte dumps to make comparison platform-independent: //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" -//@ ignore-parallel-frontend different alloc ids + #![feature( slice_from_ptr_range, const_slice_from_ptr_range, diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index fb74be3bb52f6..e23b4e5b1aa75 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -28,7 +28,7 @@ LL | pub static S2: &[u32] = unsafe { from_raw_parts(&D0, 2) }; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered uninitialized memory, but expected an integer @@ -39,7 +39,7 @@ LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) } | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered a pointer, but expected an integer @@ -52,7 +52,7 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[bool]: at .[0], encountered 0x11, but expected a boolean @@ -63,7 +63,7 @@ LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[u16]: at .[1], encountered uninitialized memory, but expected an integer @@ -74,7 +74,7 @@ LL | pub static S7: &[u16] = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[u64]: encountered a dangling reference (going beyond the bounds of its allocation) @@ -85,7 +85,7 @@ LL | pub static S8: &[u64] = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[u32]: encountered a null reference @@ -105,7 +105,7 @@ error[E0080]: evaluation panicked: assertion failed: 0 < pointee_size && pointee LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `R1` failed here -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC10 which is only 4 bytes from the end of the allocation +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC$ID which is only 4 bytes from the end of the allocation --> $DIR/forbidden_slices.rs:54:25 | LL | from_ptr_range(ptr..ptr.add(2)) // errors inside libcore @@ -119,7 +119,7 @@ LL | pub static R4: &[u8] = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered a pointer, but expected an integer @@ -132,7 +132,7 @@ LL | pub static R5: &[u8] = unsafe { = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[bool]: at .[0], encountered 0x11, but expected a boolean @@ -143,7 +143,7 @@ LL | pub static R6: &[bool] = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[u16]: encountered an unaligned reference (required 2 byte alignment but found 1) @@ -154,10 +154,10 @@ LL | pub static R7: &[u16] = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC11+0x1 which is only 7 bytes from the end of the allocation +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC$ID+0x1 which is only 7 bytes from the end of the allocation --> $DIR/forbidden_slices.rs:79:25 | LL | from_ptr_range(ptr..ptr.add(1)) diff --git a/tests/ui/const-ptr/out_of_bounds_read.rs b/tests/ui/const-ptr/out_of_bounds_read.rs index 51accc657b81f..b09978badde5f 100644 --- a/tests/ui/const-ptr/out_of_bounds_read.rs +++ b/tests/ui/const-ptr/out_of_bounds_read.rs @@ -1,6 +1,6 @@ fn main() { use std::ptr; -//@ ignore-parallel-frontend different alloc ids + const DATA: [u32; 1] = [42]; const PAST_END_PTR: *const u32 = unsafe { DATA.as_ptr().add(1) }; diff --git a/tests/ui/const-ptr/out_of_bounds_read.stderr b/tests/ui/const-ptr/out_of_bounds_read.stderr index a98765b15f267..8465d69db28e7 100644 --- a/tests/ui/const-ptr/out_of_bounds_read.stderr +++ b/tests/ui/const-ptr/out_of_bounds_read.stderr @@ -1,16 +1,16 @@ -error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes +error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC$ID+0x4 which is at or beyond the end of the allocation of size 4 bytes --> $DIR/out_of_bounds_read.rs:8:33 | LL | const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) }; | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `main::_READ` failed here -error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes +error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC$ID+0x4 which is at or beyond the end of the allocation of size 4 bytes --> $DIR/out_of_bounds_read.rs:10:39 | LL | const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() }; | ^^^^^^^^^^^^^^^^^^^ evaluation of `main::_CONST_READ` failed here -error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes +error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC$ID+0x4 which is at or beyond the end of the allocation of size 4 bytes --> $DIR/out_of_bounds_read.rs:12:37 | LL | const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() }; diff --git a/tests/ui/consts/const-compare-bytes-ub.rs b/tests/ui/consts/const-compare-bytes-ub.rs index f30507858f209..7e3df92a2bf5a 100644 --- a/tests/ui/consts/const-compare-bytes-ub.rs +++ b/tests/ui/consts/const-compare-bytes-ub.rs @@ -1,5 +1,5 @@ //@ check-fail -//@ ignore-parallel-frontend different alloc ids + #![feature(core_intrinsics, const_cmp)] use std::intrinsics::compare_bytes; use std::mem::MaybeUninit; diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr index 770a55cc7267d..94098c1e26407 100644 --- a/tests/ui/consts/const-compare-bytes-ub.stderr +++ b/tests/ui/consts/const-compare-bytes-ub.stderr @@ -16,19 +16,19 @@ error[E0080]: memory access failed: attempting to access 1 byte, but got 0x1[noa LL | compare_bytes(1 as *const u8, 2 as *const u8, 1) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `main::DANGLING_PTR_NON_ZERO_LENGTH` failed here -error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC0 which is only 3 bytes from the end of the allocation +error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC$ID which is only 3 bytes from the end of the allocation --> $DIR/const-compare-bytes-ub.rs:21:9 | LL | compare_bytes([1, 2, 3].as_ptr(), [1, 2, 3, 4].as_ptr(), 4) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `main::LHS_OUT_OF_BOUNDS` failed here -error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC1 which is only 3 bytes from the end of the allocation +error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC$ID which is only 3 bytes from the end of the allocation --> $DIR/const-compare-bytes-ub.rs:25:9 | LL | compare_bytes([1, 2, 3, 4].as_ptr(), [1, 2, 3].as_ptr(), 4) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `main::RHS_OUT_OF_BOUNDS` failed here -error[E0080]: reading memory at ALLOC2[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory --> $DIR/const-compare-bytes-ub.rs:29:9 | LL | compare_bytes(MaybeUninit::uninit().as_ptr(), [1].as_ptr(), 1) @@ -38,7 +38,7 @@ LL | compare_bytes(MaybeUninit::uninit().as_ptr(), [1].as_ptr(), 1) __ │ ░ } -error[E0080]: reading memory at ALLOC3[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory --> $DIR/const-compare-bytes-ub.rs:33:9 | LL | compare_bytes([1].as_ptr(), MaybeUninit::uninit().as_ptr(), 1) diff --git a/tests/ui/consts/const-err-enum-discriminant.32bit.stderr b/tests/ui/consts/const-err-enum-discriminant.32bit.stderr index cc786108f648b..864364139594a 100644 --- a/tests/ui/consts/const-err-enum-discriminant.32bit.stderr +++ b/tests/ui/consts/const-err-enum-discriminant.32bit.stderr @@ -1,4 +1,4 @@ -error[E0080]: reading memory at ALLOC0[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory --> $DIR/const-err-enum-discriminant.rs:10:21 | LL | Boo = [unsafe { Foo { b: () }.a }; 4][3], diff --git a/tests/ui/consts/const-err-enum-discriminant.64bit.stderr b/tests/ui/consts/const-err-enum-discriminant.64bit.stderr index 1d32851aac176..9212299bffbd4 100644 --- a/tests/ui/consts/const-err-enum-discriminant.64bit.stderr +++ b/tests/ui/consts/const-err-enum-discriminant.64bit.stderr @@ -1,4 +1,4 @@ -error[E0080]: reading memory at ALLOC0[0x0..0x8], but memory is uninitialized at [0x0..0x8], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x8], but memory is uninitialized at [0x0..0x8], and this operation requires initialized memory --> $DIR/const-err-enum-discriminant.rs:10:21 | LL | Boo = [unsafe { Foo { b: () }.a }; 4][3], diff --git a/tests/ui/consts/const-err-enum-discriminant.rs b/tests/ui/consts/const-err-enum-discriminant.rs index f6142707f9223..5567460058434 100644 --- a/tests/ui/consts/const-err-enum-discriminant.rs +++ b/tests/ui/consts/const-err-enum-discriminant.rs @@ -1,5 +1,5 @@ //@ stderr-per-bitwidth -//@ ignore-parallel-frontend different alloc ids + #[derive(Copy, Clone)] union Foo { a: isize, diff --git a/tests/ui/consts/const-eval/c-variadic-fail.rs b/tests/ui/consts/const-eval/c-variadic-fail.rs index 063e1af88cc7c..410fa273f98f8 100644 --- a/tests/ui/consts/const-eval/c-variadic-fail.rs +++ b/tests/ui/consts/const-eval/c-variadic-fail.rs @@ -1,5 +1,5 @@ //@ build-fail -//@ ignore-parallel-frontend different alloc ids + #![feature(c_variadic)] #![feature(const_c_variadic)] #![feature(const_trait_impl)] @@ -119,6 +119,15 @@ unsafe fn read_cast_pointer() { //~^ ERROR requested `*const u8` is incompatible with next argument of type `usize` } +unsafe fn read_cast_lifetime() { + // The types are equal up to free lifetimes. + const { read_as::<*const &'static i32>(std::ptr::dangling::<&i32>()) }; + + // Bound lifetimes do matter. + const { read_as::<*const fn(&'static ())>(std::ptr::dangling:: fn(&'a ())>()) }; + //~^ ERROR va_arg type mismatch: requested `*const fn(&())` is incompatible with next argument of type `*const for<'a> fn(&'a ())` +} + fn use_after_free() { const unsafe extern "C" fn helper(ap: ...) -> [u8; size_of::()] { unsafe { std::mem::transmute(ap) } @@ -129,7 +138,7 @@ fn use_after_free() { let ap = helper(1, 2, 3); let mut ap = std::mem::transmute::<_, VaList>(ap); ap.next_arg::(); - //~^ ERROR memory access failed: ALLOC0 has been freed, so this pointer is dangling [E0080] + //~^ ERROR memory access failed: ALLOC$ID has been freed, so this pointer is dangling [E0080] } }; } @@ -151,7 +160,7 @@ fn manual_copy_drop() { } const { unsafe { helper(1, 2, 3) } }; - //~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080] + //~^ ERROR using ALLOC$ID as variable argument list pointer but it does not point to a variable argument list [E0080] } fn manual_copy_forget() { @@ -167,7 +176,7 @@ fn manual_copy_forget() { } const { unsafe { helper(1, 2, 3) } }; - //~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080] + //~^ ERROR using ALLOC$ID as variable argument list pointer but it does not point to a variable argument list [E0080] } fn manual_copy_read() { @@ -180,7 +189,7 @@ fn manual_copy_read() { } const { unsafe { helper(1, 2, 3) } }; - //~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080] + //~^ ERROR using ALLOC$ID as variable argument list pointer but it does not point to a variable argument list [E0080] } fn drop_of_invalid() { @@ -196,6 +205,7 @@ fn main() { read_too_many(); read_cast_numeric(); read_cast_pointer(); + read_cast_lifetime(); manual_copy_read(); manual_copy_drop(); manual_copy_forget(); diff --git a/tests/ui/consts/const-eval/c-variadic-fail.stderr b/tests/ui/consts/const-eval/c-variadic-fail.stderr index a184a3ab435e7..e32edfb3ea453 100644 --- a/tests/ui/consts/const-eval/c-variadic-fail.stderr +++ b/tests/ui/consts/const-eval/c-variadic-fail.stderr @@ -390,8 +390,36 @@ LL | const { read_as::<*const u8>(1usize) }; | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: memory access failed: ALLOC0 has been freed, so this pointer is dangling - --> $DIR/c-variadic-fail.rs:131:13 +error[E0080]: va_arg type mismatch: requested `*const fn(&())` is incompatible with next argument of type `*const for<'a> fn(&'a ())` + --> $DIR/c-variadic-fail.rs:127:13 + | +LL | const { read_as::<*const fn(&'static ())>(std::ptr::dangling:: fn(&'a ())>()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast_lifetime::{constant#1}` failed inside this call + | +note: inside `read_as::<*const fn(&())>` + --> $DIR/c-variadic-fail.rs:37:5 + | +LL | ap.next_arg::() + | ^^^^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::next_arg::<*const fn(&())>` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:127:5 + | +LL | const { read_as::<*const fn(&'static ())>(std::ptr::dangling:: fn(&'a ())>()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:127:5 + | +LL | const { read_as::<*const fn(&'static ())>(std::ptr::dangling:: fn(&'a ())>()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: memory access failed: ALLOC$ID has been freed, so this pointer is dangling + --> $DIR/c-variadic-fail.rs:140:13 | LL | ap.next_arg::(); | ^^^^^^^^^^^^^^^^^^^^ evaluation of `use_after_free::{constant#0}` failed inside this call @@ -400,7 +428,7 @@ note: inside `VaList::<'_>::next_arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:127:5 + --> $DIR/c-variadic-fail.rs:136:5 | LL | / const { LL | | unsafe { @@ -411,7 +439,7 @@ LL | | }; | |_____^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:127:5 + --> $DIR/c-variadic-fail.rs:136:5 | LL | / const { LL | | unsafe { @@ -423,14 +451,14 @@ LL | | }; | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: using ALLOC1 as variable argument list pointer but it does not point to a variable argument list - --> $DIR/c-variadic-fail.rs:153:22 +error[E0080]: using ALLOC$ID as variable argument list pointer but it does not point to a variable argument list + --> $DIR/c-variadic-fail.rs:162:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_drop::{constant#0}` failed inside this call | note: inside `manual_copy_drop::helper` - --> $DIR/c-variadic-fail.rs:150:9 + --> $DIR/c-variadic-fail.rs:159:9 | LL | drop(ap); | ^^^^^^^^ @@ -442,27 +470,27 @@ note: inside ` as Drop>::drop` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:153:5 + --> $DIR/c-variadic-fail.rs:162:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:153:5 + --> $DIR/c-variadic-fail.rs:162:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: using ALLOC2 as variable argument list pointer but it does not point to a variable argument list - --> $DIR/c-variadic-fail.rs:169:22 +error[E0080]: using ALLOC$ID as variable argument list pointer but it does not point to a variable argument list + --> $DIR/c-variadic-fail.rs:178:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_forget::{constant#0}` failed inside this call | note: inside `manual_copy_forget::helper` - --> $DIR/c-variadic-fail.rs:166:9 + --> $DIR/c-variadic-fail.rs:175:9 | LL | drop(ap); | ^^^^^^^^ @@ -474,27 +502,27 @@ note: inside ` as Drop>::drop` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:169:5 + --> $DIR/c-variadic-fail.rs:178:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:169:5 + --> $DIR/c-variadic-fail.rs:178:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: using ALLOC3 as variable argument list pointer but it does not point to a variable argument list - --> $DIR/c-variadic-fail.rs:182:22 +error[E0080]: using ALLOC$ID as variable argument list pointer but it does not point to a variable argument list + --> $DIR/c-variadic-fail.rs:191:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_read::{constant#0}` failed inside this call | note: inside `manual_copy_read::helper` - --> $DIR/c-variadic-fail.rs:179:17 + --> $DIR/c-variadic-fail.rs:188:17 | LL | let _ = ap.next_arg::(); | ^^^^^^^^^^^^^^^^^^^^ @@ -502,13 +530,13 @@ note: inside `VaList::<'_>::next_arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:182:5 + --> $DIR/c-variadic-fail.rs:191:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:182:5 + --> $DIR/c-variadic-fail.rs:191:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -516,7 +544,7 @@ LL | const { unsafe { helper(1, 2, 3) } }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got null pointer - --> $DIR/c-variadic-fail.rs:190:5 + --> $DIR/c-variadic-fail.rs:199:5 | LL | } | ^ evaluation of `drop_of_invalid::{constant#0}` failed inside this call @@ -527,7 +555,7 @@ note: inside ` as Drop>::drop` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:187:5 + --> $DIR/c-variadic-fail.rs:196:5 | LL | / const { LL | | let mut invalid: MaybeUninit = MaybeUninit::zeroed(); @@ -536,7 +564,7 @@ LL | | } | |_____^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:187:5 + --> $DIR/c-variadic-fail.rs:196:5 | LL | / const { LL | | let mut invalid: MaybeUninit = MaybeUninit::zeroed(); @@ -546,6 +574,6 @@ LL | | } | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr b/tests/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr index 60f6ef0f64b1c..f68ef6f96988f 100644 --- a/tests/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr +++ b/tests/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr @@ -43,14 +43,14 @@ LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uin = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error[E0080]: reading memory at ALLOC2[0x0..0x10], but memory is uninitialized at [0x8..0x10], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x10], but memory is uninitialized at [0x8..0x10], and this operation requires initialized memory --> $DIR/const-pointer-values-in-various-types.rs:42:47 | LL | const I32_REF_U128_UNION: u128 = unsafe { Nonsense { int_32_ref: &3 }.uint_128 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `main::I32_REF_U128_UNION` failed here | = note: the raw bytes of the constant (size: 16, align: 16) { - ╾ALLOC0╼ __ __ __ __ __ __ __ __ │ ╾──────╼░░░░░░░░ + ╾ALLOC$ID╼ __ __ __ __ __ __ __ __ │ ╾──────╼░░░░░░░░ } error[E0080]: unable to turn pointer into integer @@ -89,14 +89,14 @@ LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error[E0080]: reading memory at ALLOC3[0x0..0x10], but memory is uninitialized at [0x8..0x10], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x10], but memory is uninitialized at [0x8..0x10], and this operation requires initialized memory --> $DIR/const-pointer-values-in-various-types.rs:57:47 | LL | const I32_REF_I128_UNION: i128 = unsafe { Nonsense { int_32_ref: &3 }.int_128 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `main::I32_REF_I128_UNION` failed here | = note: the raw bytes of the constant (size: 16, align: 16) { - ╾ALLOC1╼ __ __ __ __ __ __ __ __ │ ╾──────╼░░░░░░░░ + ╾ALLOC$ID╼ __ __ __ __ __ __ __ __ │ ╾──────╼░░░░░░░░ } error[E0080]: unable to turn pointer into integer diff --git a/tests/ui/consts/const-eval/const-pointer-values-in-various-types.rs b/tests/ui/consts/const-eval/const-pointer-values-in-various-types.rs index c6aae902c8bdb..5720d6ea91ed5 100644 --- a/tests/ui/consts/const-eval/const-pointer-values-in-various-types.rs +++ b/tests/ui/consts/const-eval/const-pointer-values-in-various-types.rs @@ -1,7 +1,7 @@ //@ only-x86_64 //@ stderr-per-bitwidth //@ dont-require-annotations: NOTE -//@ ignore-parallel-frontend different alloc ids + #[repr(C)] union Nonsense { u: usize, diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr index f8d6cc145fca8..2d0e2e5d41839 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr @@ -6,7 +6,7 @@ LL | const BAR: &i32 = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC0╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr index f7b652f7e43b1..e0138e4b3aebe 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr @@ -6,7 +6,7 @@ LL | const BAR: &i32 = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC0╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs index 3ef18a5f43695..c54115de2045d 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs @@ -3,7 +3,7 @@ #![feature(core_intrinsics)] #![feature(const_heap)] use std::intrinsics; -//@ ignore-parallel-frontend different alloc ids + const BAR: &i32 = unsafe { //~ ERROR: uninitialized memory // Make the pointer immutable to avoid errors related to mutable pointers in constants. &*(intrinsics::const_make_global(intrinsics::const_allocate(4, 4)) as *const i32) diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs index a377ee35d92b2..80cf3ffef11a5 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs @@ -1,10 +1,11 @@ #![feature(core_intrinsics)] #![feature(const_heap)] -//@ ignore-parallel-frontend different alloc ids + // Strip out raw byte dumps to make comparison platform-independent: //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" use std::intrinsics; diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr index 0942924464091..e9b7f99ee6d99 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr @@ -1,16 +1,16 @@ error[E0080]: constructing invalid value of type &u8: encountered a dangling reference (use-after-free) - --> $DIR/dealloc_intrinsic_dangling.rs:11:1 + --> $DIR/dealloc_intrinsic_dangling.rs:12:1 | LL | const _X: &'static u8 = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } -error[E0080]: memory access failed: ALLOC1 has been freed, so this pointer is dangling - --> $DIR/dealloc_intrinsic_dangling.rs:22:5 +error[E0080]: memory access failed: ALLOC$ID has been freed, so this pointer is dangling + --> $DIR/dealloc_intrinsic_dangling.rs:23:5 | LL | *reference | ^^^^^^^^^^ evaluation of `_Y` failed here diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.rs b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.rs index bc9227b31f807..5b7cd039b9baa 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.rs +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.rs @@ -1,6 +1,6 @@ #![feature(core_intrinsics)] #![feature(const_heap)] -//@ ignore-parallel-frontend different alloc ids + use std::intrinsics; const _X: () = unsafe { diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.stderr b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.stderr index 803b772615f24..eb73857143877 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.stderr +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.stderr @@ -1,4 +1,4 @@ -error[E0080]: memory access failed: ALLOC0 has been freed, so this pointer is dangling +error[E0080]: memory access failed: ALLOC$ID has been freed, so this pointer is dangling --> $DIR/dealloc_intrinsic_duplicate.rs:9:5 | LL | intrinsics::const_deallocate(ptr, 4, 4); diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.rs b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.rs index 8800188e9036c..75c3601f2166e 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.rs +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.rs @@ -1,6 +1,6 @@ #![feature(core_intrinsics)] #![feature(const_heap)] -//@ ignore-parallel-frontend different alloc ids + use std::intrinsics; const _X: () = unsafe { diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.stderr b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.stderr index 2d61f06ac5c62..cc7f14d6ddb35 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.stderr +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.stderr @@ -1,16 +1,16 @@ -error[E0080]: incorrect layout on deallocation: ALLOC0 has size 4 and alignment 4, but gave size 4 and alignment 2 +error[E0080]: incorrect layout on deallocation: ALLOC$ID has size 4 and alignment 4, but gave size 4 and alignment 2 --> $DIR/dealloc_intrinsic_incorrect_layout.rs:8:5 | LL | intrinsics::const_deallocate(ptr, 4, 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_X` failed here -error[E0080]: incorrect layout on deallocation: ALLOC1 has size 4 and alignment 4, but gave size 2 and alignment 4 +error[E0080]: incorrect layout on deallocation: ALLOC$ID has size 4 and alignment 4, but gave size 2 and alignment 4 --> $DIR/dealloc_intrinsic_incorrect_layout.rs:13:5 | LL | intrinsics::const_deallocate(ptr, 2, 4); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_Y` failed here -error[E0080]: incorrect layout on deallocation: ALLOC2 has size 4 and alignment 4, but gave size 3 and alignment 4 +error[E0080]: incorrect layout on deallocation: ALLOC$ID has size 4 and alignment 4, but gave size 3 and alignment 4 --> $DIR/dealloc_intrinsic_incorrect_layout.rs:19:5 | LL | intrinsics::const_deallocate(ptr, 3, 4); diff --git a/tests/ui/consts/const-eval/heap/make-global-dangling.rs b/tests/ui/consts/const-eval/heap/make-global-dangling.rs index 12b9ca7a6980f..f4c5929bc416d 100644 --- a/tests/ui/consts/const-eval/heap/make-global-dangling.rs +++ b/tests/ui/consts/const-eval/heap/make-global-dangling.rs @@ -1,5 +1,5 @@ // Ensure that we can't call `const_make_global` on dangling pointers. -//@ ignore-parallel-frontend different alloc ids + #![feature(core_intrinsics)] #![feature(const_heap)] diff --git a/tests/ui/consts/const-eval/heap/make-global-other.rs b/tests/ui/consts/const-eval/heap/make-global-other.rs index 9250f7a55be41..1a4ec29af1767 100644 --- a/tests/ui/consts/const-eval/heap/make-global-other.rs +++ b/tests/ui/consts/const-eval/heap/make-global-other.rs @@ -1,5 +1,5 @@ // Ensure that we can't call `const_make_global` on pointers not in the current interpreter. -//@ ignore-parallel-frontend different alloc ids + #![feature(core_intrinsics)] #![feature(const_heap)] @@ -9,7 +9,7 @@ const X: &i32 = &0; const Y: &i32 = unsafe { &*(intrinsics::const_make_global(X as *const i32 as *mut u8) as *const i32) - //~^ error: pointer passed to `const_make_global` does not point to a heap allocation: ALLOC0 + //~^ error: pointer passed to `const_make_global` does not point to a heap allocation: ALLOC$ID }; fn main() {} diff --git a/tests/ui/consts/const-eval/heap/make-global-other.stderr b/tests/ui/consts/const-eval/heap/make-global-other.stderr index ed0d768cf379f..f97c25bdc4957 100644 --- a/tests/ui/consts/const-eval/heap/make-global-other.stderr +++ b/tests/ui/consts/const-eval/heap/make-global-other.stderr @@ -1,4 +1,4 @@ -error[E0080]: pointer passed to `const_make_global` does not point to a heap allocation: ALLOC0 +error[E0080]: pointer passed to `const_make_global` does not point to a heap allocation: ALLOC$ID --> $DIR/make-global-other.rs:11:8 | LL | &*(intrinsics::const_make_global(X as *const i32 as *mut u8) as *const i32) diff --git a/tests/ui/consts/const-eval/heap/make-global-twice.rs b/tests/ui/consts/const-eval/heap/make-global-twice.rs index a4d01170812cc..38769ef4d199a 100644 --- a/tests/ui/consts/const-eval/heap/make-global-twice.rs +++ b/tests/ui/consts/const-eval/heap/make-global-twice.rs @@ -1,5 +1,5 @@ // Ensure that we can't call `const_make_global` twice. -//@ ignore-parallel-frontend different alloc ids + #![feature(core_intrinsics)] #![feature(const_heap)] @@ -11,7 +11,7 @@ const Y: &i32 = unsafe { *i = 20; intrinsics::const_make_global(ptr); intrinsics::const_make_global(ptr); - //~^ error: attempting to call `const_make_global` twice on the same allocation ALLOC0 + //~^ error: attempting to call `const_make_global` twice on the same allocation ALLOC$ID &*i }; diff --git a/tests/ui/consts/const-eval/heap/make-global-twice.stderr b/tests/ui/consts/const-eval/heap/make-global-twice.stderr index 95cdb9694d8b9..a499299d5297f 100644 --- a/tests/ui/consts/const-eval/heap/make-global-twice.stderr +++ b/tests/ui/consts/const-eval/heap/make-global-twice.stderr @@ -1,4 +1,4 @@ -error[E0080]: attempting to call `const_make_global` twice on the same allocation ALLOC0 +error[E0080]: attempting to call `const_make_global` twice on the same allocation ALLOC$ID --> $DIR/make-global-twice.rs:13:5 | LL | intrinsics::const_make_global(ptr); diff --git a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs index c405f73160481..77c12f3dbe0f9 100644 --- a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs +++ b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs @@ -2,13 +2,13 @@ #![feature(core_intrinsics)] #![feature(const_heap)] use std::intrinsics; -//@ ignore-parallel-frontend different alloc ids + const A: &u8 = unsafe { let ptr = intrinsics::const_allocate(1, 1); *ptr = 1; let ptr: *const u8 = intrinsics::const_make_global(ptr); *(ptr as *mut u8) = 2; - //~^ error: writing to ALLOC0 which is read-only + //~^ error: writing to ALLOC$ID which is read-only &*ptr }; diff --git a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr index 0e88ea77d1ca0..8e56ff1cbb7c6 100644 --- a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr @@ -1,4 +1,4 @@ -error[E0080]: writing to ALLOC0 which is read-only +error[E0080]: writing to ALLOC$ID which is read-only --> $DIR/ptr_made_global_mutated.rs:10:5 | LL | *(ptr as *mut u8) = 2; diff --git a/tests/ui/consts/const-eval/issue-49296.rs b/tests/ui/consts/const-eval/issue-49296.rs index 35797fbf248bf..a427b642899a5 100644 --- a/tests/ui/consts/const-eval/issue-49296.rs +++ b/tests/ui/consts/const-eval/issue-49296.rs @@ -1,5 +1,5 @@ // issue-49296: Unsafe shenigans in constants can result in missing errors -//@ ignore-parallel-frontend different alloc ids + use std::mem::transmute; const fn wat(x: u64) -> &'static u64 { diff --git a/tests/ui/consts/const-eval/issue-49296.stderr b/tests/ui/consts/const-eval/issue-49296.stderr index 9da3e3d6d30b1..64e892e61af47 100644 --- a/tests/ui/consts/const-eval/issue-49296.stderr +++ b/tests/ui/consts/const-eval/issue-49296.stderr @@ -1,4 +1,4 @@ -error[E0080]: memory access failed: ALLOC0 has been freed, so this pointer is dangling +error[E0080]: memory access failed: ALLOC$ID has been freed, so this pointer is dangling --> $DIR/issue-49296.rs:9:16 | LL | const X: u64 = *wat(42); diff --git a/tests/ui/consts/const-eval/ptr_fragments_mixed.rs b/tests/ui/consts/const-eval/ptr_fragments_mixed.rs index 471d464055960..24169eac4780d 100644 --- a/tests/ui/consts/const-eval/ptr_fragments_mixed.rs +++ b/tests/ui/consts/const-eval/ptr_fragments_mixed.rs @@ -1,6 +1,6 @@ //! This mixes fragments from different pointers, in a way that we should not accept. //! See . -//@ ignore-parallel-frontend different alloc ids + static A: u8 = 123; static B: u8 = 123; diff --git a/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr b/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr index 608f09d24bf42..a9be41f10d410 100644 --- a/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr +++ b/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr @@ -1,4 +1,4 @@ -error[E0080]: unable to read parts of a pointer from memory at ALLOC0 +error[E0080]: unable to read parts of a pointer from memory at ALLOC$ID --> $DIR/ptr_fragments_mixed.rs:19:9 | LL | y @@ -7,7 +7,7 @@ LL | y = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error[E0080]: unable to read parts of a pointer from memory at ALLOC1 +error[E0080]: unable to read parts of a pointer from memory at ALLOC$ID --> $DIR/ptr_fragments_mixed.rs:33:9 | LL | y diff --git a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr index 4493067e9a8bb..37c715e003e8f 100644 --- a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr @@ -1,5 +1,5 @@ error[E0080]: constructing invalid value of type Enum: at ., encountered 0x00000001, but expected a valid enum tag - --> $DIR/raw-bytes.rs:24:1 + --> $DIR/raw-bytes.rs:23:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -10,7 +10,7 @@ LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; } error[E0080]: constructing invalid value of type Enum2: at ., encountered 0x00000000, but expected a valid enum tag - --> $DIR/raw-bytes.rs:32:1 + --> $DIR/raw-bytes.rs:31:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -21,7 +21,7 @@ LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type UninhDiscriminant: at ., encountered an uninhabited enum variant - --> $DIR/raw-bytes.rs:46:1 + --> $DIR/raw-bytes.rs:45:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -32,7 +32,7 @@ LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: constructing invalid value of type UninhDiscriminant: at ., encountered 0x03, but expected a valid enum tag - --> $DIR/raw-bytes.rs:48:1 + --> $DIR/raw-bytes.rs:47:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -43,7 +43,7 @@ LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: constructing invalid value of type Option<(char, char)>: at ..0.1, encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) - --> $DIR/raw-bytes.rs:54:1 + --> $DIR/raw-bytes.rs:53:1 | LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -54,7 +54,7 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran } error[E0080]: constructing invalid value of type NonNull: at .pointer, encountered 0, but expected something greater or equal to 1 - --> $DIR/raw-bytes.rs:59:1 + --> $DIR/raw-bytes.rs:58:1 | LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -65,7 +65,7 @@ LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type NonZero: at .0.0, encountered 0, but expected something greater or equal to 1 - --> $DIR/raw-bytes.rs:62:1 + --> $DIR/raw-bytes.rs:61:1 | LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -76,7 +76,7 @@ LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; } error[E0080]: constructing invalid value of type NonZero: at .0.0, encountered 0, but expected something greater or equal to 1 - --> $DIR/raw-bytes.rs:64:1 + --> $DIR/raw-bytes.rs:63:1 | LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -87,7 +87,7 @@ LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type RestrictedRange1: at .0, encountered 42, but expected something in the range 10..=30 - --> $DIR/raw-bytes.rs:68:1 + --> $DIR/raw-bytes.rs:67:1 | LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(mem::transmute(42_u32)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -98,7 +98,7 @@ LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(mem::transmu } error[E0080]: constructing invalid value of type RestrictedRange2: at .0, encountered 20, but expected something less or equal to 10, or greater or equal to 30 - --> $DIR/raw-bytes.rs:72:1 + --> $DIR/raw-bytes.rs:71:1 | LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(mem::transmute(20_i32)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -109,40 +109,40 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(mem::transmu } error[E0080]: constructing invalid value of type NonNull: at .pointer, encountered 0, but expected something greater or equal to 1 - --> $DIR/raw-bytes.rs:75:1 + --> $DIR/raw-bytes.rs:74:1 | LL | const NULL_FAT_PTR: NonNull = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - 00 00 00 00 ╾ALLOC_ID╼ │ ....╾──╼ + 00 00 00 00 ╾ALLOC$ID╼ │ ....╾──╼ } error[E0080]: constructing invalid value of type &u16: encountered an unaligned reference (required 2 byte alignment but found 1) - --> $DIR/raw-bytes.rs:82:1 + --> $DIR/raw-bytes.rs:81:1 | LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC_ID╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } error[E0080]: constructing invalid value of type Box: encountered an unaligned box (required 2 byte alignment but found 1) - --> $DIR/raw-bytes.rs:85:1 + --> $DIR/raw-bytes.rs:84:1 | LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC_ID╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } error[E0080]: constructing invalid value of type &u16: encountered a null reference - --> $DIR/raw-bytes.rs:88:1 + --> $DIR/raw-bytes.rs:87:1 | LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -153,7 +153,7 @@ LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type Box: encountered a null box - --> $DIR/raw-bytes.rs:91:1 + --> $DIR/raw-bytes.rs:90:1 | LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -164,7 +164,7 @@ LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type &u8: encountered a dangling reference (0x539[noalloc] has no provenance) - --> $DIR/raw-bytes.rs:94:1 + --> $DIR/raw-bytes.rs:93:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -175,7 +175,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; } error[E0080]: constructing invalid value of type Box: encountered a dangling box (0x539[noalloc] has no provenance) - --> $DIR/raw-bytes.rs:97:1 + --> $DIR/raw-bytes.rs:96:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -186,7 +186,7 @@ LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; } error[E0080]: constructing invalid value of type fn(): encountered null pointer, but expected a function pointer - --> $DIR/raw-bytes.rs:100:1 + --> $DIR/raw-bytes.rs:99:1 | LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -197,7 +197,7 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type fn(): encountered 0xd[noalloc], but expected a function pointer - --> $DIR/raw-bytes.rs:102:1 + --> $DIR/raw-bytes.rs:101:1 | LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -207,19 +207,19 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; 0d 00 00 00 │ .... } -error[E0080]: constructing invalid value of type fn(): encountered ALLOC3, but expected a function pointer - --> $DIR/raw-bytes.rs:104:1 +error[E0080]: constructing invalid value of type fn(): encountered ALLOC$ID, but expected a function pointer + --> $DIR/raw-bytes.rs:103:1 | LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC_ID╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } error[E0080]: constructing invalid value of type &Bar: encountered a reference pointing to uninhabited type Bar - --> $DIR/raw-bytes.rs:110:1 + --> $DIR/raw-bytes.rs:109:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -230,62 +230,62 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; } error[E0080]: constructing invalid value of type &str: encountered a dangling reference (going beyond the bounds of its allocation) - --> $DIR/raw-bytes.rs:134:1 + --> $DIR/raw-bytes.rs:133:1 | LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ e7 03 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ e7 03 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type (&str,): at .0, encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/raw-bytes.rs:136:1 + --> $DIR/raw-bytes.rs:135:1 | LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ ff ff ff ff │ ╾──╼.... + ╾ALLOC$ID╼ ff ff ff ff │ ╾──╼.... } error[E0080]: constructing invalid value of type &MyStr: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/raw-bytes.rs:138:1 + --> $DIR/raw-bytes.rs:137:1 | LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ ff ff ff ff │ ╾──╼.... + ╾ALLOC$ID╼ ff ff ff ff │ ╾──╼.... } error[E0080]: constructing invalid value of type &str: at ., encountered uninitialized memory, but expected a string - --> $DIR/raw-bytes.rs:141:1 + --> $DIR/raw-bytes.rs:140:1 | LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 01 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &MyStr: at ..0, encountered uninitialized memory, but expected a string - --> $DIR/raw-bytes.rs:143:1 + --> $DIR/raw-bytes.rs:142:1 | LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 01 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &MyStr: at ..0, encountered a pointer, but expected a string - --> $DIR/raw-bytes.rs:145:1 + --> $DIR/raw-bytes.rs:144:1 | LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -294,172 +294,172 @@ LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _> = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 01 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[u8]: encountered a dangling reference (going beyond the bounds of its allocation) - --> $DIR/raw-bytes.rs:149:1 + --> $DIR/raw-bytes.rs:148:1 | LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ e7 03 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ e7 03 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[u32]: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/raw-bytes.rs:151:1 + --> $DIR/raw-bytes.rs:150:1 | LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ ff ff ff 7f │ ╾──╼.... + ╾ALLOC$ID╼ ff ff ff 7f │ ╾──╼.... } error[E0080]: constructing invalid value of type Box<[u8]>: encountered a dangling box (going beyond the bounds of its allocation) - --> $DIR/raw-bytes.rs:154:1 + --> $DIR/raw-bytes.rs:153:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ e7 03 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ e7 03 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[bool; 1]: at .[0], encountered 0x03, but expected a boolean - --> $DIR/raw-bytes.rs:157:1 + --> $DIR/raw-bytes.rs:156:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC_ID╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:157:40 + --> $DIR/raw-bytes.rs:156:40 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value of type &MySlice<[bool; 1]>: at ..0, encountered 0x03, but expected a boolean - --> $DIR/raw-bytes.rs:161:1 + --> $DIR/raw-bytes.rs:160:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC_ID╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:161:42 + --> $DIR/raw-bytes.rs:160:42 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value of type &MySlice<[bool; 1]>: at ..1[0], encountered 0x03, but expected a boolean - --> $DIR/raw-bytes.rs:164:1 + --> $DIR/raw-bytes.rs:163:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC_ID╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:164:42 + --> $DIR/raw-bytes.rs:163:42 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC17, but expected a vtable pointer - --> $DIR/raw-bytes.rs:168:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/raw-bytes.rs:167:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC19, but expected a vtable pointer - --> $DIR/raw-bytes.rs:171:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/raw-bytes.rs:170:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered 0x4[noalloc], but expected a vtable pointer - --> $DIR/raw-bytes.rs:174:1 + --> $DIR/raw-bytes.rs:173:1 | LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 04 00 00 00 │ ╾──╼.... } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC22, but expected a vtable pointer - --> $DIR/raw-bytes.rs:176:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/raw-bytes.rs:175:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } error[E0080]: constructing invalid value of type &dyn Trait: at .., encountered 0x03, but expected a boolean - --> $DIR/raw-bytes.rs:179:1 + --> $DIR/raw-bytes.rs:178:1 | LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } error[E0080]: constructing invalid value of type *const dyn Trait: encountered null pointer, but expected a vtable pointer - --> $DIR/raw-bytes.rs:182:1 + --> $DIR/raw-bytes.rs:181:1 | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 00 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 00 00 00 00 │ ╾──╼.... } -error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC27, but expected a vtable pointer - --> $DIR/raw-bytes.rs:184:1 +error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/raw-bytes.rs:183:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } error[E0080]: constructing invalid value of type &[!; 1]: encountered a reference pointing to uninhabited type [!; 1] - --> $DIR/raw-bytes.rs:188:1 + --> $DIR/raw-bytes.rs:187:1 | LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -470,7 +470,7 @@ LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; } error[E0080]: constructing invalid value of type &[!]: at .[0], encountered a value of the never type `!` - --> $DIR/raw-bytes.rs:189:1 + --> $DIR/raw-bytes.rs:188:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -481,7 +481,7 @@ LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; } error[E0080]: constructing invalid value of type &[!]: at .[0], encountered a value of the never type `!` - --> $DIR/raw-bytes.rs:190:1 + --> $DIR/raw-bytes.rs:189:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -492,18 +492,18 @@ LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered uninitialized memory, but expected an integer - --> $DIR/raw-bytes.rs:193:1 + --> $DIR/raw-bytes.rs:192:1 | LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 01 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered a pointer, but expected an integer - --> $DIR/raw-bytes.rs:196:1 + --> $DIR/raw-bytes.rs:195:1 | LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem::size_of::<&u32>()) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -512,44 +512,44 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem: = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 04 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[bool]: at .[0], encountered 0x11, but expected a boolean - --> $DIR/raw-bytes.rs:199:1 + --> $DIR/raw-bytes.rs:198:1 | LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 04 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[u16]: at .[1], encountered uninitialized memory, but expected an integer - --> $DIR/raw-bytes.rs:203:1 + --> $DIR/raw-bytes.rs:202:1 | LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID+0x2╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 04 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered uninitialized memory, but expected an integer - --> $DIR/raw-bytes.rs:210:1 + --> $DIR/raw-bytes.rs:209:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 01 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered a pointer, but expected an integer - --> $DIR/raw-bytes.rs:215:1 + --> $DIR/raw-bytes.rs:214:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -558,18 +558,18 @@ LL | pub static R5: &[u8] = unsafe { = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 04 00 00 00 │ ╾──╼.... } error[E0080]: constructing invalid value of type &[bool]: at .[0], encountered 0x11, but expected a boolean - --> $DIR/raw-bytes.rs:220:1 + --> $DIR/raw-bytes.rs:219:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC$ID╼ 04 00 00 00 │ ╾──╼.... } error: aborting due to 50 previous errors diff --git a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr index 07deb955d24e7..b74aee7980aa4 100644 --- a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr @@ -1,5 +1,5 @@ error[E0080]: constructing invalid value of type Enum: at ., encountered 0x0000000000000001, but expected a valid enum tag - --> $DIR/raw-bytes.rs:24:1 + --> $DIR/raw-bytes.rs:23:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -10,7 +10,7 @@ LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; } error[E0080]: constructing invalid value of type Enum2: at ., encountered 0x0000000000000000, but expected a valid enum tag - --> $DIR/raw-bytes.rs:32:1 + --> $DIR/raw-bytes.rs:31:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -21,7 +21,7 @@ LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type UninhDiscriminant: at ., encountered an uninhabited enum variant - --> $DIR/raw-bytes.rs:46:1 + --> $DIR/raw-bytes.rs:45:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -32,7 +32,7 @@ LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: constructing invalid value of type UninhDiscriminant: at ., encountered 0x03, but expected a valid enum tag - --> $DIR/raw-bytes.rs:48:1 + --> $DIR/raw-bytes.rs:47:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -43,7 +43,7 @@ LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: constructing invalid value of type Option<(char, char)>: at ..0.1, encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) - --> $DIR/raw-bytes.rs:54:1 + --> $DIR/raw-bytes.rs:53:1 | LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -54,7 +54,7 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran } error[E0080]: constructing invalid value of type NonNull: at .pointer, encountered 0, but expected something greater or equal to 1 - --> $DIR/raw-bytes.rs:59:1 + --> $DIR/raw-bytes.rs:58:1 | LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -65,7 +65,7 @@ LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type NonZero: at .0.0, encountered 0, but expected something greater or equal to 1 - --> $DIR/raw-bytes.rs:62:1 + --> $DIR/raw-bytes.rs:61:1 | LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -76,7 +76,7 @@ LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; } error[E0080]: constructing invalid value of type NonZero: at .0.0, encountered 0, but expected something greater or equal to 1 - --> $DIR/raw-bytes.rs:64:1 + --> $DIR/raw-bytes.rs:63:1 | LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -87,7 +87,7 @@ LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type RestrictedRange1: at .0, encountered 42, but expected something in the range 10..=30 - --> $DIR/raw-bytes.rs:68:1 + --> $DIR/raw-bytes.rs:67:1 | LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(mem::transmute(42_u32)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -98,7 +98,7 @@ LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(mem::transmu } error[E0080]: constructing invalid value of type RestrictedRange2: at .0, encountered 20, but expected something less or equal to 10, or greater or equal to 30 - --> $DIR/raw-bytes.rs:72:1 + --> $DIR/raw-bytes.rs:71:1 | LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(mem::transmute(20_i32)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -109,40 +109,40 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(mem::transmu } error[E0080]: constructing invalid value of type NonNull: at .pointer, encountered 0, but expected something greater or equal to 1 - --> $DIR/raw-bytes.rs:75:1 + --> $DIR/raw-bytes.rs:74:1 | LL | const NULL_FAT_PTR: NonNull = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - 00 00 00 00 00 00 00 00 ╾ALLOC_ID╼ │ ........╾──────╼ + 00 00 00 00 00 00 00 00 ╾ALLOC$ID╼ │ ........╾──────╼ } error[E0080]: constructing invalid value of type &u16: encountered an unaligned reference (required 2 byte alignment but found 1) - --> $DIR/raw-bytes.rs:82:1 + --> $DIR/raw-bytes.rs:81:1 | LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC_ID╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } error[E0080]: constructing invalid value of type Box: encountered an unaligned box (required 2 byte alignment but found 1) - --> $DIR/raw-bytes.rs:85:1 + --> $DIR/raw-bytes.rs:84:1 | LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC_ID╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } error[E0080]: constructing invalid value of type &u16: encountered a null reference - --> $DIR/raw-bytes.rs:88:1 + --> $DIR/raw-bytes.rs:87:1 | LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -153,7 +153,7 @@ LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type Box: encountered a null box - --> $DIR/raw-bytes.rs:91:1 + --> $DIR/raw-bytes.rs:90:1 | LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -164,7 +164,7 @@ LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type &u8: encountered a dangling reference (0x539[noalloc] has no provenance) - --> $DIR/raw-bytes.rs:94:1 + --> $DIR/raw-bytes.rs:93:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -175,7 +175,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; } error[E0080]: constructing invalid value of type Box: encountered a dangling box (0x539[noalloc] has no provenance) - --> $DIR/raw-bytes.rs:97:1 + --> $DIR/raw-bytes.rs:96:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -186,7 +186,7 @@ LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; } error[E0080]: constructing invalid value of type fn(): encountered null pointer, but expected a function pointer - --> $DIR/raw-bytes.rs:100:1 + --> $DIR/raw-bytes.rs:99:1 | LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -197,7 +197,7 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type fn(): encountered 0xd[noalloc], but expected a function pointer - --> $DIR/raw-bytes.rs:102:1 + --> $DIR/raw-bytes.rs:101:1 | LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -207,19 +207,19 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; 0d 00 00 00 00 00 00 00 │ ........ } -error[E0080]: constructing invalid value of type fn(): encountered ALLOC3, but expected a function pointer - --> $DIR/raw-bytes.rs:104:1 +error[E0080]: constructing invalid value of type fn(): encountered ALLOC$ID, but expected a function pointer + --> $DIR/raw-bytes.rs:103:1 | LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC_ID╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } error[E0080]: constructing invalid value of type &Bar: encountered a reference pointing to uninhabited type Bar - --> $DIR/raw-bytes.rs:110:1 + --> $DIR/raw-bytes.rs:109:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -230,62 +230,62 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; } error[E0080]: constructing invalid value of type &str: encountered a dangling reference (going beyond the bounds of its allocation) - --> $DIR/raw-bytes.rs:134:1 + --> $DIR/raw-bytes.rs:133:1 | LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type (&str,): at .0, encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/raw-bytes.rs:136:1 + --> $DIR/raw-bytes.rs:135:1 | LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ + ╾ALLOC$ID╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ } error[E0080]: constructing invalid value of type &MyStr: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/raw-bytes.rs:138:1 + --> $DIR/raw-bytes.rs:137:1 | LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ + ╾ALLOC$ID╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ } error[E0080]: constructing invalid value of type &str: at ., encountered uninitialized memory, but expected a string - --> $DIR/raw-bytes.rs:141:1 + --> $DIR/raw-bytes.rs:140:1 | LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &MyStr: at ..0, encountered uninitialized memory, but expected a string - --> $DIR/raw-bytes.rs:143:1 + --> $DIR/raw-bytes.rs:142:1 | LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &MyStr: at ..0, encountered a pointer, but expected a string - --> $DIR/raw-bytes.rs:145:1 + --> $DIR/raw-bytes.rs:144:1 | LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -294,172 +294,172 @@ LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _> = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[u8]: encountered a dangling reference (going beyond the bounds of its allocation) - --> $DIR/raw-bytes.rs:149:1 + --> $DIR/raw-bytes.rs:148:1 | LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[u32]: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/raw-bytes.rs:151:1 + --> $DIR/raw-bytes.rs:150:1 | LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ ff ff ff ff ff ff ff 7f │ ╾──────╼........ + ╾ALLOC$ID╼ ff ff ff ff ff ff ff 7f │ ╾──────╼........ } error[E0080]: constructing invalid value of type Box<[u8]>: encountered a dangling box (going beyond the bounds of its allocation) - --> $DIR/raw-bytes.rs:154:1 + --> $DIR/raw-bytes.rs:153:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[bool; 1]: at .[0], encountered 0x03, but expected a boolean - --> $DIR/raw-bytes.rs:157:1 + --> $DIR/raw-bytes.rs:156:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC_ID╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:157:40 + --> $DIR/raw-bytes.rs:156:40 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value of type &MySlice<[bool; 1]>: at ..0, encountered 0x03, but expected a boolean - --> $DIR/raw-bytes.rs:161:1 + --> $DIR/raw-bytes.rs:160:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC_ID╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:161:42 + --> $DIR/raw-bytes.rs:160:42 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value of type &MySlice<[bool; 1]>: at ..1[0], encountered 0x03, but expected a boolean - --> $DIR/raw-bytes.rs:164:1 + --> $DIR/raw-bytes.rs:163:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC_ID╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:164:42 + --> $DIR/raw-bytes.rs:163:42 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC17, but expected a vtable pointer - --> $DIR/raw-bytes.rs:168:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/raw-bytes.rs:167:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC19, but expected a vtable pointer - --> $DIR/raw-bytes.rs:171:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/raw-bytes.rs:170:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered 0x4[noalloc], but expected a vtable pointer - --> $DIR/raw-bytes.rs:174:1 + --> $DIR/raw-bytes.rs:173:1 | LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC22, but expected a vtable pointer - --> $DIR/raw-bytes.rs:176:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/raw-bytes.rs:175:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } error[E0080]: constructing invalid value of type &dyn Trait: at .., encountered 0x03, but expected a boolean - --> $DIR/raw-bytes.rs:179:1 + --> $DIR/raw-bytes.rs:178:1 | LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } error[E0080]: constructing invalid value of type *const dyn Trait: encountered null pointer, but expected a vtable pointer - --> $DIR/raw-bytes.rs:182:1 + --> $DIR/raw-bytes.rs:181:1 | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........ } -error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC27, but expected a vtable pointer - --> $DIR/raw-bytes.rs:184:1 +error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/raw-bytes.rs:183:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } error[E0080]: constructing invalid value of type &[!; 1]: encountered a reference pointing to uninhabited type [!; 1] - --> $DIR/raw-bytes.rs:188:1 + --> $DIR/raw-bytes.rs:187:1 | LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -470,7 +470,7 @@ LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; } error[E0080]: constructing invalid value of type &[!]: at .[0], encountered a value of the never type `!` - --> $DIR/raw-bytes.rs:189:1 + --> $DIR/raw-bytes.rs:188:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -481,7 +481,7 @@ LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; } error[E0080]: constructing invalid value of type &[!]: at .[0], encountered a value of the never type `!` - --> $DIR/raw-bytes.rs:190:1 + --> $DIR/raw-bytes.rs:189:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; | ^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -492,18 +492,18 @@ LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered uninitialized memory, but expected an integer - --> $DIR/raw-bytes.rs:193:1 + --> $DIR/raw-bytes.rs:192:1 | LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered a pointer, but expected an integer - --> $DIR/raw-bytes.rs:196:1 + --> $DIR/raw-bytes.rs:195:1 | LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem::size_of::<&u32>()) }; | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -512,44 +512,44 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem: = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[bool]: at .[0], encountered 0x11, but expected a boolean - --> $DIR/raw-bytes.rs:199:1 + --> $DIR/raw-bytes.rs:198:1 | LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[u16]: at .[1], encountered uninitialized memory, but expected an integer - --> $DIR/raw-bytes.rs:203:1 + --> $DIR/raw-bytes.rs:202:1 | LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID+0x2╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered uninitialized memory, but expected an integer - --> $DIR/raw-bytes.rs:210:1 + --> $DIR/raw-bytes.rs:209:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered a pointer, but expected an integer - --> $DIR/raw-bytes.rs:215:1 + --> $DIR/raw-bytes.rs:214:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -558,18 +558,18 @@ LL | pub static R5: &[u8] = unsafe { = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: constructing invalid value of type &[bool]: at .[0], encountered 0x11, but expected a boolean - --> $DIR/raw-bytes.rs:220:1 + --> $DIR/raw-bytes.rs:219:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC_ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC$ID╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } error: aborting due to 50 previous errors diff --git a/tests/ui/consts/const-eval/raw-bytes.rs b/tests/ui/consts/const-eval/raw-bytes.rs index acaf8c8310aab..5c8253ee2b41f 100644 --- a/tests/ui/consts/const-eval/raw-bytes.rs +++ b/tests/ui/consts/const-eval/raw-bytes.rs @@ -1,9 +1,8 @@ //@ stderr-per-bitwidth //@ ignore-endian-big // ignore-tidy-linelength -//@ normalize-stderr: "╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼" -> "╾ALLOC_ID$1╼" //@ dont-require-annotations: NOTE -//@ ignore-parallel-frontend different alloc ids + #![allow(invalid_value, unnecessary_transmutes)] #![feature(never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] #![feature(pattern_types, pattern_type_macro)] diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.rs b/tests/ui/consts/const-eval/raw-pointer-ub.rs index 0998617b5b71d..df7bc2fe4fb18 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.rs +++ b/tests/ui/consts/const-eval/raw-pointer-ub.rs @@ -4,7 +4,7 @@ const MISALIGNED_LOAD: () = unsafe { let _val = *ptr; //~NOTE: failed here //~^ERROR: based on pointer with alignment 1, but alignment 4 is required }; -//@ ignore-parallel-frontend different alloc ids + const MISALIGNED_STORE: () = unsafe { let mut mem = [0u32; 8]; let ptr = mem.as_mut_ptr().byte_add(1); diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.stderr b/tests/ui/consts/const-eval/raw-pointer-ub.stderr index 00af20a722dae..5769f46022ff1 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.stderr +++ b/tests/ui/consts/const-eval/raw-pointer-ub.stderr @@ -25,7 +25,7 @@ error[E0080]: accessing memory based on pointer with alignment 4, but alignment LL | let _val = (*ptr).0; | ^^^^^^^^ evaluation of `MISALIGNED_FIELD` failed here -error[E0080]: memory access failed: attempting to access 8 bytes, but got ALLOC0 which is only 4 bytes from the end of the allocation +error[E0080]: memory access failed: attempting to access 8 bytes, but got ALLOC$ID which is only 4 bytes from the end of the allocation --> $DIR/raw-pointer-ub.rs:40:16 | LL | let _val = *ptr; diff --git a/tests/ui/consts/const-eval/read_partial_ptr.rs b/tests/ui/consts/const-eval/read_partial_ptr.rs index 5248f92a92db4..bccef9c0bc6ca 100644 --- a/tests/ui/consts/const-eval/read_partial_ptr.rs +++ b/tests/ui/consts/const-eval/read_partial_ptr.rs @@ -1,5 +1,5 @@ //! Ensure we error when trying to load from a pointer whose provenance has been messed with. -//@ ignore-parallel-frontend different alloc ids + const PARTIAL_OVERWRITE: () = { let mut p = &42; // Overwrite one byte with a no-provenance value. diff --git a/tests/ui/consts/const-eval/read_partial_ptr.stderr b/tests/ui/consts/const-eval/read_partial_ptr.stderr index 196606c77a37f..c2d220b2e1e21 100644 --- a/tests/ui/consts/const-eval/read_partial_ptr.stderr +++ b/tests/ui/consts/const-eval/read_partial_ptr.stderr @@ -1,4 +1,4 @@ -error[E0080]: unable to read parts of a pointer from memory at ALLOC0 +error[E0080]: unable to read parts of a pointer from memory at ALLOC$ID --> $DIR/read_partial_ptr.rs:10:13 | LL | let x = *p; @@ -7,7 +7,7 @@ LL | let x = *p; = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error[E0080]: unable to read parts of a pointer from memory at ALLOC1 +error[E0080]: unable to read parts of a pointer from memory at ALLOC$ID --> $DIR/read_partial_ptr.rs:23:13 | LL | let x = *p; @@ -16,7 +16,7 @@ LL | let x = *p; = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error[E0080]: unable to read parts of a pointer from memory at ALLOC2 +error[E0080]: unable to read parts of a pointer from memory at ALLOC$ID --> $DIR/read_partial_ptr.rs:34:13 | LL | let x = *p; @@ -25,7 +25,7 @@ LL | let x = *p; = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error[E0080]: unable to read parts of a pointer from memory at ALLOC3 +error[E0080]: unable to read parts of a pointer from memory at ALLOC$ID --> $DIR/read_partial_ptr.rs:46:13 | LL | let x = *p; diff --git a/tests/ui/consts/const-eval/ub-enum-overwrite.rs b/tests/ui/consts/const-eval/ub-enum-overwrite.rs index a1bd49f53acea..005f3c78c1d7d 100644 --- a/tests/ui/consts/const-eval/ub-enum-overwrite.rs +++ b/tests/ui/consts/const-eval/ub-enum-overwrite.rs @@ -2,7 +2,7 @@ enum E { A(u8), B, } -//@ ignore-parallel-frontend different alloc ids + const _: u8 = { let mut e = E::A(1); let p = if let E::A(x) = &mut e { x as *mut u8 } else { unreachable!() }; diff --git a/tests/ui/consts/const-eval/ub-enum-overwrite.stderr b/tests/ui/consts/const-eval/ub-enum-overwrite.stderr index 2fd01b67c4928..bda771bd38e88 100644 --- a/tests/ui/consts/const-eval/ub-enum-overwrite.stderr +++ b/tests/ui/consts/const-eval/ub-enum-overwrite.stderr @@ -1,4 +1,4 @@ -error[E0080]: reading memory at ALLOC0[0x1..0x2], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x1..0x2], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory --> $DIR/ub-enum-overwrite.rs:11:14 | LL | unsafe { *p } diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 8feb78e0b11e8..cea8b6b6403c4 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -4,7 +4,7 @@ //@ normalize-stderr: "0x0+" -> "0x0" //@ normalize-stderr: "0x[0-9](\.\.|\])" -> "0x%$1" //@ dont-require-annotations: NOTE -//@ ignore-parallel-frontend different alloc ids + #![feature(never_type)] #![allow(invalid_value, unnecessary_transmutes)] diff --git a/tests/ui/consts/const-eval/ub-enum.stderr b/tests/ui/consts/const-eval/ub-enum.stderr index bb2c58796b1c4..2fbc115fe4f6e 100644 --- a/tests/ui/consts/const-eval/ub-enum.stderr +++ b/tests/ui/consts/const-eval/ub-enum.stderr @@ -56,7 +56,7 @@ LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error[E0080]: reading memory at ALLOC0[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory --> $DIR/ub-enum.rs:62:41 | LL | const BAD_ENUM2_UNDEF: Enum2 = unsafe { MaybeUninit { uninit: () }.init }; diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr b/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr index 2d2d97da97879..1efd30818b2e5 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr @@ -1,4 +1,4 @@ -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC1, but expected a vtable pointer +error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:18:1 | LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait = @@ -6,10 +6,10 @@ LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC0╼ ╾ALLOC1╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC3, but expected a vtable pointer +error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:22:1 | LL | const INVALID_VTABLE_SIZE: &dyn Trait = @@ -17,10 +17,10 @@ LL | const INVALID_VTABLE_SIZE: &dyn Trait = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC2╼ ╾ALLOC3╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC5, but expected a vtable pointer +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:31:1 | LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> = @@ -28,10 +28,10 @@ LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC4╼ ╾ALLOC5╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC7, but expected a vtable pointer +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:35:1 | LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> = @@ -39,10 +39,10 @@ LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC6╼ ╾ALLOC7╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC9, but expected a vtable pointer +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:40:1 | LL | const INVALID_VTABLE_UB: W<&dyn Trait> = @@ -50,7 +50,7 @@ LL | const INVALID_VTABLE_UB: W<&dyn Trait> = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC8╼ ╾ALLOC9╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } error[E0080]: constructing invalid value of type Wide<'_>: at .1, encountered a dangling reference (going beyond the bounds of its allocation) @@ -61,7 +61,7 @@ LL | const G: Wide = unsafe { Transmute { t: FOO }.u }; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC10╼ ╾ALLOC11╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } error: aborting due to 6 previous errors diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr b/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr index 4c8a1802317b2..bc26c93513964 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr @@ -1,4 +1,4 @@ -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC1, but expected a vtable pointer +error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:18:1 | LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait = @@ -6,10 +6,10 @@ LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC0╼ ╾ALLOC1╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC3, but expected a vtable pointer +error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:22:1 | LL | const INVALID_VTABLE_SIZE: &dyn Trait = @@ -17,10 +17,10 @@ LL | const INVALID_VTABLE_SIZE: &dyn Trait = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC2╼ ╾ALLOC3╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC5, but expected a vtable pointer +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:31:1 | LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> = @@ -28,10 +28,10 @@ LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC4╼ ╾ALLOC5╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC7, but expected a vtable pointer +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:35:1 | LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> = @@ -39,10 +39,10 @@ LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC6╼ ╾ALLOC7╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC9, but expected a vtable pointer +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:40:1 | LL | const INVALID_VTABLE_UB: W<&dyn Trait> = @@ -50,7 +50,7 @@ LL | const INVALID_VTABLE_UB: W<&dyn Trait> = | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC8╼ ╾ALLOC9╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } error[E0080]: constructing invalid value of type Wide<'_>: at .1, encountered a dangling reference (going beyond the bounds of its allocation) @@ -61,7 +61,7 @@ LL | const G: Wide = unsafe { Transmute { t: FOO }.u }; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC10╼ ╾ALLOC11╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } error: aborting due to 6 previous errors diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.rs b/tests/ui/consts/const-eval/ub-incorrect-vtable.rs index 09129012e083c..4185b0261b296 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.rs +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.rs @@ -12,7 +12,7 @@ //@ stderr-per-bitwidth //@ dont-require-annotations: NOTE -//@ ignore-parallel-frontend different alloc ids + trait Trait {} const INVALID_VTABLE_ALIGNMENT: &dyn Trait = diff --git a/tests/ui/consts/const-eval/ub-nonnull.rs b/tests/ui/consts/const-eval/ub-nonnull.rs index b6aca0684b84d..679de8b0cf101 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.rs +++ b/tests/ui/consts/const-eval/ub-nonnull.rs @@ -1,8 +1,10 @@ // Strip out raw byte dumps to make comparison platform-independent: //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" +//@ normalize-stderr: "[0-9a-f][0-9a-f]( [0-9a-f][0-9a-f]){3,7} ╾ALLOC\$ID╼" -> "HEX_DUMP ╾ALLOC$$ID╼ │ ╾─╼" //@ dont-require-annotations: NOTE -//@ ignore-parallel-frontend different alloc ids + #![allow(invalid_value)] // make sure we cannot allow away the errors tested here #![feature(rustc_attrs, ptr_metadata)] diff --git a/tests/ui/consts/const-eval/ub-nonnull.stderr b/tests/ui/consts/const-eval/ub-nonnull.stderr index 5bd23944ed5be..81f2ff3ca5fa4 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.stderr +++ b/tests/ui/consts/const-eval/ub-nonnull.stderr @@ -1,5 +1,5 @@ error[E0080]: constructing invalid value of type NonNull: at .pointer, encountered 0, but expected something greater or equal to 1 - --> $DIR/ub-nonnull.rs:16:1 + --> $DIR/ub-nonnull.rs:18:1 | LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -9,14 +9,14 @@ LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; HEX_DUMP } -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 255 bytes, but got ALLOC2 which is only 1 byte from the end of the allocation - --> $DIR/ub-nonnull.rs:22:29 +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 255 bytes, but got ALLOC$ID which is only 1 byte from the end of the allocation + --> $DIR/ub-nonnull.rs:24:29 | LL | let out_of_bounds_ptr = &ptr[255]; | ^^^^^^^^^ evaluation of `OUT_OF_BOUNDS_PTR` failed here error[E0080]: constructing invalid value of type NonZero: at .0.0, encountered 0, but expected something greater or equal to 1 - --> $DIR/ub-nonnull.rs:26:1 + --> $DIR/ub-nonnull.rs:28:1 | LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -27,7 +27,7 @@ LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; } error[E0080]: constructing invalid value of type NonZero: at .0.0, encountered 0, but expected something greater or equal to 1 - --> $DIR/ub-nonnull.rs:28:1 + --> $DIR/ub-nonnull.rs:30:1 | LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -37,8 +37,8 @@ LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC3[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory - --> $DIR/ub-nonnull.rs:36:38 +error[E0080]: reading memory at ALLOC$ID[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory + --> $DIR/ub-nonnull.rs:38:38 | LL | const UNINIT: NonZero = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINIT` failed here @@ -48,25 +48,25 @@ LL | const UNINIT: NonZero = unsafe { MaybeUninit { uninit: () }.init }; } error[E0080]: constructing invalid value of type NonNull: at .pointer, encountered 0, but expected something greater or equal to 1 - --> $DIR/ub-nonnull.rs:39:1 + --> $DIR/ub-nonnull.rs:41:1 | LL | const NULL_FAT_PTR: NonNull = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + HEX_DUMP ╾ALLOC$ID╼ │ ╾─╼ │ ╾─╼ } error[E0080]: constructing invalid value of type NonNull<()>: at .pointer, encountered a maybe-null pointer, but expected something that is definitely non-zero - --> $DIR/ub-nonnull.rs:47:1 + --> $DIR/ub-nonnull.rs:49:1 | LL | const MAYBE_NULL_PTR: NonNull<()> = unsafe { mem::transmute((&raw const S).wrapping_add(4)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error: aborting due to 7 previous errors diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.rs b/tests/ui/consts/const-eval/ub-ref-ptr.rs index 1f92e8edec2ca..dedfd3a676f87 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.rs +++ b/tests/ui/consts/const-eval/ub-ref-ptr.rs @@ -4,9 +4,11 @@ //@ normalize-stderr: "([0-9a-f][0-9a-f] |__ |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ dont-require-annotations: NOTE //@ normalize-stderr: "0x[0-9](\.\.|\])" -> "0x%$1" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" + #![feature(pattern_types, pattern_type_macro)] #![allow(invalid_value)] -//@ ignore-parallel-frontend different alloc ids + use std::{mem, pat::pattern_type}; #[repr(C)] diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr index 1087d89f6389d..f1f219243c385 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr @@ -1,27 +1,27 @@ error[E0080]: constructing invalid value of type &u16: encountered an unaligned reference (required 2 byte alignment but found 1) - --> $DIR/ub-ref-ptr.rs:18:1 + --> $DIR/ub-ref-ptr.rs:20:1 | LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type Box: encountered an unaligned box (required 2 byte alignment but found 1) - --> $DIR/ub-ref-ptr.rs:21:1 + --> $DIR/ub-ref-ptr.rs:23:1 | LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type &u16: encountered a null reference - --> $DIR/ub-ref-ptr.rs:24:1 + --> $DIR/ub-ref-ptr.rs:26:1 | LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -32,7 +32,7 @@ LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type Box: encountered a null box - --> $DIR/ub-ref-ptr.rs:27:1 + --> $DIR/ub-ref-ptr.rs:29:1 | LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -43,18 +43,18 @@ LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; } error[E0080]: constructing invalid value of type Box<()>: encountered a maybe-null box - --> $DIR/ub-ref-ptr.rs:30:1 + --> $DIR/ub-ref-ptr.rs:32:1 | LL | const MAYBE_NULL_BOX: Box<()> = unsafe { mem::transmute({ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: unable to turn pointer into integer - --> $DIR/ub-ref-ptr.rs:39:1 + --> $DIR/ub-ref-ptr.rs:41:1 | LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE` failed here @@ -63,7 +63,7 @@ LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: unable to turn pointer into integer - --> $DIR/ub-ref-ptr.rs:42:39 + --> $DIR/ub-ref-ptr.rs:44:39 | LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE_SLICE` failed here @@ -72,13 +72,13 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported note: erroneous constant encountered - --> $DIR/ub-ref-ptr.rs:42:38 + --> $DIR/ub-ref-ptr.rs:44:38 | LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: unable to turn pointer into integer - --> $DIR/ub-ref-ptr.rs:45:86 + --> $DIR/ub-ref-ptr.rs:47:86 | LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; | ^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE_BOX_SLICE` failed here @@ -87,13 +87,13 @@ LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[us = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported note: erroneous constant encountered - --> $DIR/ub-ref-ptr.rs:45:85 + --> $DIR/ub-ref-ptr.rs:47:85 | LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; | ^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value of type &u8: encountered a dangling reference (0x539[noalloc] has no provenance) - --> $DIR/ub-ref-ptr.rs:48:1 + --> $DIR/ub-ref-ptr.rs:50:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -104,7 +104,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; } error[E0080]: constructing invalid value of type Box: encountered a dangling box (0x539[noalloc] has no provenance) - --> $DIR/ub-ref-ptr.rs:51:1 + --> $DIR/ub-ref-ptr.rs:53:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -114,8 +114,8 @@ LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC5[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory - --> $DIR/ub-ref-ptr.rs:54:41 +error[E0080]: reading memory at ALLOC$ID[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory + --> $DIR/ub-ref-ptr.rs:56:41 | LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINIT_PTR` failed here @@ -125,7 +125,7 @@ LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; } error[E0080]: constructing invalid value of type fn(): encountered null pointer, but expected a function pointer - --> $DIR/ub-ref-ptr.rs:57:1 + --> $DIR/ub-ref-ptr.rs:59:1 | LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -135,8 +135,8 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC6[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory - --> $DIR/ub-ref-ptr.rs:59:38 +error[E0080]: reading memory at ALLOC$ID[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory + --> $DIR/ub-ref-ptr.rs:61:38 | LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINIT_FN_PTR` failed here @@ -146,7 +146,7 @@ LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; } error[E0080]: constructing invalid value of type fn(): encountered 0xd[noalloc], but expected a function pointer - --> $DIR/ub-ref-ptr.rs:61:1 + --> $DIR/ub-ref-ptr.rs:63:1 | LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -156,30 +156,30 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; HEX_DUMP } -error[E0080]: constructing invalid value of type fn(): encountered ALLOC3, but expected a function pointer - --> $DIR/ub-ref-ptr.rs:63:1 +error[E0080]: constructing invalid value of type fn(): encountered ALLOC$ID, but expected a function pointer + --> $DIR/ub-ref-ptr.rs:65:1 | LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } -error[E0080]: constructing invalid value of type fn(): encountered ALLOC4+0xa, but expected a function pointer - --> $DIR/ub-ref-ptr.rs:65:1 +error[E0080]: constructing invalid value of type fn(): encountered ALLOC$ID+0xa, but expected a function pointer + --> $DIR/ub-ref-ptr.rs:67:1 | LL | const MAYBE_NULL_FN_PTR: fn() = unsafe { mem::transmute({ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: accessing memory based on pointer with alignment 1, but alignment 4 is required - --> $DIR/ub-ref-ptr.rs:75:5 + --> $DIR/ub-ref-ptr.rs:77:5 | LL | ptr.read(); | ^^^^^^^^^^ evaluation of `UNALIGNED_READ` failed here diff --git a/tests/ui/consts/const-eval/ub-upvars.32bit.stderr b/tests/ui/consts/const-eval/ub-upvars.32bit.stderr index 2e5797c65b35d..e0c3afadd6fca 100644 --- a/tests/ui/consts/const-eval/ub-upvars.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-upvars.32bit.stderr @@ -6,7 +6,7 @@ LL | const BAD_UPVAR: &dyn FnOnce() = &{ | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC0╼ ╾ALLOC1╼ │ ╾──╼╾──╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/ub-upvars.64bit.stderr b/tests/ui/consts/const-eval/ub-upvars.64bit.stderr index c45ad7856e6ea..7ce7467d77441 100644 --- a/tests/ui/consts/const-eval/ub-upvars.64bit.stderr +++ b/tests/ui/consts/const-eval/ub-upvars.64bit.stderr @@ -6,7 +6,7 @@ LL | const BAD_UPVAR: &dyn FnOnce() = &{ | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC0╼ ╾ALLOC1╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/ub-upvars.rs b/tests/ui/consts/const-eval/ub-upvars.rs index 7270d90bf7927..c5bf074ec4626 100644 --- a/tests/ui/consts/const-eval/ub-upvars.rs +++ b/tests/ui/consts/const-eval/ub-upvars.rs @@ -1,7 +1,7 @@ //@ edition:2015..2021 //@ stderr-per-bitwidth #![allow(invalid_value)] // make sure we cannot allow away the errors tested here -//@ ignore-parallel-frontend different alloc ids + use std::mem; const BAD_UPVAR: &dyn FnOnce() = &{ //~ ERROR null reference diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.rs b/tests/ui/consts/const-eval/ub-wide-ptr.rs index 6297d2f3d3af3..a5c0b6f628e9c 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.rs +++ b/tests/ui/consts/const-eval/ub-wide-ptr.rs @@ -3,13 +3,14 @@ #![feature(ptr_metadata)] use std::{ptr, mem}; -//@ ignore-parallel-frontend different alloc ids + // Strip out raw byte dumps to make comparison platform-independent: //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |__ |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "offset \d+" -> "offset N" //@ normalize-stderr: "size \d+" -> "size N" //@ normalize-stderr: "0x[0-9](\.\.|\])" -> "0x%$1" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" //@ dont-require-annotations: NOTE /// A newtype wrapper to prevent MIR generation from inserting reborrows that would affect the error @@ -137,7 +138,6 @@ const RAW_TRAIT_OBJ_CONTENT_INVALID: *const dyn Trait = unsafe { mem::transmute: // Officially blessed way to get the vtable const DYN_METADATA: ptr::DynMetadata = ptr::metadata::(ptr::null::()); - static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe { mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) //~^^ ERROR null pointer diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.stderr b/tests/ui/consts/const-eval/ub-wide-ptr.stderr index 9603710e4fd8c..b442a43c6276f 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-wide-ptr.stderr @@ -1,27 +1,27 @@ error[E0080]: constructing invalid value of type &str: encountered a dangling reference (going beyond the bounds of its allocation) - --> $DIR/ub-wide-ptr.rs:40:1 + --> $DIR/ub-wide-ptr.rs:41:1 | LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type (&str,): at .0, encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/ub-wide-ptr.rs:42:1 + --> $DIR/ub-wide-ptr.rs:43:1 | LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: unable to turn pointer into integer - --> $DIR/ub-wide-ptr.rs:45:1 + --> $DIR/ub-wide-ptr.rs:46:1 | LL | const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `STR_LENGTH_PTR` failed here @@ -30,7 +30,7 @@ LL | const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: unable to turn pointer into integer - --> $DIR/ub-wide-ptr.rs:48:1 + --> $DIR/ub-wide-ptr.rs:49:1 | LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `MY_STR_LENGTH_PTR` failed here @@ -39,40 +39,40 @@ LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: constructing invalid value of type &MyStr: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/ub-wide-ptr.rs:50:1 + --> $DIR/ub-wide-ptr.rs:51:1 | LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &str: at ., encountered uninitialized memory, but expected a string - --> $DIR/ub-wide-ptr.rs:54:1 + --> $DIR/ub-wide-ptr.rs:55:1 | LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &MyStr: at ..0, encountered uninitialized memory, but expected a string - --> $DIR/ub-wide-ptr.rs:57:1 + --> $DIR/ub-wide-ptr.rs:58:1 | LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } -error[E0080]: reading memory at ALLOC32[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory - --> $DIR/ub-wide-ptr.rs:64:1 +error[E0080]: reading memory at ALLOC$ID[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory + --> $DIR/ub-wide-ptr.rs:65:1 | LL | const SLICE_LENGTH_UNINIT: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `SLICE_LENGTH_UNINIT` failed here @@ -82,29 +82,29 @@ LL | const SLICE_LENGTH_UNINIT: &[u8] = unsafe { } error[E0080]: constructing invalid value of type &[u8]: encountered a dangling reference (going beyond the bounds of its allocation) - --> $DIR/ub-wide-ptr.rs:70:1 + --> $DIR/ub-wide-ptr.rs:71:1 | LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: constructing invalid value of type &[u32]: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/ub-wide-ptr.rs:73:1 + --> $DIR/ub-wide-ptr.rs:74:1 | LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: unable to turn pointer into integer - --> $DIR/ub-wide-ptr.rs:76:1 + --> $DIR/ub-wide-ptr.rs:77:1 | LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `SLICE_LENGTH_PTR` failed here @@ -113,18 +113,18 @@ LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: constructing invalid value of type Box<[u8]>: encountered a dangling box (going beyond the bounds of its allocation) - --> $DIR/ub-wide-ptr.rs:79:1 + --> $DIR/ub-wide-ptr.rs:80:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } error[E0080]: unable to turn pointer into integer - --> $DIR/ub-wide-ptr.rs:82:1 + --> $DIR/ub-wide-ptr.rs:83:1 | LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `SLICE_LENGTH_PTR_BOX` failed here @@ -133,58 +133,58 @@ LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3) = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: constructing invalid value of type &[bool; 1]: at .[0], encountered 0x03, but expected a boolean - --> $DIR/ub-wide-ptr.rs:86:1 + --> $DIR/ub-wide-ptr.rs:87:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } note: erroneous constant encountered - --> $DIR/ub-wide-ptr.rs:86:40 + --> $DIR/ub-wide-ptr.rs:87:40 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value of type &MySlice<[bool; 1]>: at ..0, encountered 0x03, but expected a boolean - --> $DIR/ub-wide-ptr.rs:92:1 + --> $DIR/ub-wide-ptr.rs:93:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } note: erroneous constant encountered - --> $DIR/ub-wide-ptr.rs:92:42 + --> $DIR/ub-wide-ptr.rs:93:42 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value of type &MySlice<[bool; 1]>: at ..1[0], encountered 0x03, but expected a boolean - --> $DIR/ub-wide-ptr.rs:95:1 + --> $DIR/ub-wide-ptr.rs:96:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } note: erroneous constant encountered - --> $DIR/ub-wide-ptr.rs:95:42 + --> $DIR/ub-wide-ptr.rs:96:42 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: reading memory at ALLOC33[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory - --> $DIR/ub-wide-ptr.rs:102:1 +error[E0080]: reading memory at ALLOC$ID[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory + --> $DIR/ub-wide-ptr.rs:103:1 | LL | const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_SLICE_LENGTH_UNINIT` failed here @@ -193,114 +193,114 @@ LL | const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { HEX_DUMP } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC12, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:110:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/ub-wide-ptr.rs:111:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC14, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:113:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/ub-wide-ptr.rs:114:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered 0x4[noalloc], but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:116:1 + --> $DIR/ub-wide-ptr.rs:117:1 | LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC17, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:118:1 +error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/ub-wide-ptr.rs:119:1 | LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC19, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:120:1 +error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/ub-wide-ptr.rs:121:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC21, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:122:1 +error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/ub-wide-ptr.rs:123:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } -error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC23, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:124:1 +error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/ub-wide-ptr.rs:125:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type &dyn Trait: at .., encountered 0x03, but expected a boolean - --> $DIR/ub-wide-ptr.rs:128:1 + --> $DIR/ub-wide-ptr.rs:129:1 | LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type *const dyn Trait: encountered null pointer, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:132:1 + --> $DIR/ub-wide-ptr.rs:133:1 | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } -error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC28, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:134:1 +error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC$ID, but expected a vtable pointer + --> $DIR/ub-wide-ptr.rs:135:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type *const dyn Trait: encountered null pointer, but expected a vtable pointer @@ -311,10 +311,10 @@ LL | static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ HEX_DUMP } -error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC31, but expected a vtable pointer +error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-wide-ptr.rs:145:1 | LL | static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = unsafe { @@ -322,7 +322,7 @@ LL | static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = uns | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } error: aborting due to 29 previous errors diff --git a/tests/ui/consts/const-eval/union-const-eval-field.rs b/tests/ui/consts/const-eval/union-const-eval-field.rs index 92d056b3b1ef8..2c9061a7a50f8 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.rs +++ b/tests/ui/consts/const-eval/union-const-eval-field.rs @@ -1,7 +1,7 @@ //@ dont-require-annotations: NOTE //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([[:xdigit:]]{2}\s){4}(__\s){4}\s+│\s+([?|\.]){4}\W{4}" -> "HEX_DUMP" -//@ ignore-parallel-frontend different alloc ids + type Field1 = i32; type Field2 = f32; type Field3 = i64; diff --git a/tests/ui/consts/const-eval/union-const-eval-field.stderr b/tests/ui/consts/const-eval/union-const-eval-field.stderr index 3b7e5508d56c4..e86e1fa22a7e5 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.stderr +++ b/tests/ui/consts/const-eval/union-const-eval-field.stderr @@ -1,4 +1,4 @@ -error[E0080]: reading memory at ALLOC0[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory --> $DIR/union-const-eval-field.rs:30:37 | LL | const FIELD3: Field3 = unsafe { UNION.field3 }; diff --git a/tests/ui/consts/const-eval/union-ice.rs b/tests/ui/consts/const-eval/union-ice.rs index 055a130cf0e89..7a4909e8ce281 100644 --- a/tests/ui/consts/const-eval/union-ice.rs +++ b/tests/ui/consts/const-eval/union-ice.rs @@ -1,5 +1,5 @@ //@ only-x86_64 -//@ ignore-parallel-frontend different alloc ids + type Field1 = i32; type Field3 = i64; diff --git a/tests/ui/consts/const-eval/union-ice.stderr b/tests/ui/consts/const-eval/union-ice.stderr index 0506be63ea6f1..6a4927e42d240 100644 --- a/tests/ui/consts/const-eval/union-ice.stderr +++ b/tests/ui/consts/const-eval/union-ice.stderr @@ -1,4 +1,4 @@ -error[E0080]: reading memory at ALLOC0[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory --> $DIR/union-ice.rs:14:33 | LL | const FIELD3: Field3 = unsafe { UNION.field3 }; @@ -8,7 +8,7 @@ LL | const FIELD3: Field3 = unsafe { UNION.field3 }; 00 00 80 3f __ __ __ __ │ ...?░░░░ } -error[E0080]: reading memory at ALLOC1[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory --> $DIR/union-ice.rs:19:17 | LL | b: unsafe { UNION.field3 }, @@ -18,7 +18,7 @@ LL | b: unsafe { UNION.field3 }, 00 00 80 3f __ __ __ __ │ ...?░░░░ } -error[E0080]: reading memory at ALLOC2[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory --> $DIR/union-ice.rs:31:18 | LL | unsafe { UNION.field3 }, diff --git a/tests/ui/consts/const-eval/union-ub.32bit.stderr b/tests/ui/consts/const-eval/union-ub.32bit.stderr index 95960d1c77dc7..071a6ae99183f 100644 --- a/tests/ui/consts/const-eval/union-ub.32bit.stderr +++ b/tests/ui/consts/const-eval/union-ub.32bit.stderr @@ -9,7 +9,7 @@ LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool }; 2a │ * } -error[E0080]: reading memory at ALLOC0[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory --> $DIR/union-ub.rs:35:36 | LL | const UNINIT_BOOL: bool = unsafe { DummyUnion { unit: () }.bool }; diff --git a/tests/ui/consts/const-eval/union-ub.64bit.stderr b/tests/ui/consts/const-eval/union-ub.64bit.stderr index 95960d1c77dc7..071a6ae99183f 100644 --- a/tests/ui/consts/const-eval/union-ub.64bit.stderr +++ b/tests/ui/consts/const-eval/union-ub.64bit.stderr @@ -9,7 +9,7 @@ LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool }; 2a │ * } -error[E0080]: reading memory at ALLOC0[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory --> $DIR/union-ub.rs:35:36 | LL | const UNINIT_BOOL: bool = unsafe { DummyUnion { unit: () }.bool }; diff --git a/tests/ui/consts/const-eval/union-ub.rs b/tests/ui/consts/const-eval/union-ub.rs index 8de54f5749f35..0fa5d31285604 100644 --- a/tests/ui/consts/const-eval/union-ub.rs +++ b/tests/ui/consts/const-eval/union-ub.rs @@ -1,6 +1,6 @@ //@ stderr-per-bitwidth //@ dont-require-annotations: NOTE -//@ ignore-parallel-frontend different alloc ids + #[repr(C)] union DummyUnion { unit: (), diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs index c620c2cf9fb8c..ebf1f88eb8e72 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs @@ -1,6 +1,7 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "( 0x[0-9a-f][0-9a-f] │)? ([0-9a-f][0-9a-f] |__ |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> " HEX_DUMP" //@ normalize-stderr: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" //@ dont-require-annotations: NOTE use std::cell::UnsafeCell; diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr index 9b289f4306b8f..ad19f78ef831b 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr @@ -1,5 +1,5 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed - --> $DIR/mut_ref_in_final.rs:15:21 + --> $DIR/mut_ref_in_final.rs:16:21 | LL | const B: *mut i32 = &mut 4; | ^^^^^^ this mutable borrow refers to such a temporary @@ -9,7 +9,7 @@ LL | const B: *mut i32 = &mut 4; = help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed - --> $DIR/mut_ref_in_final.rs:21:35 + --> $DIR/mut_ref_in_final.rs:22:35 | LL | const B3: Option<&mut i32> = Some(&mut 42); | ^^^^^^^ this mutable borrow refers to such a temporary @@ -19,7 +19,7 @@ LL | const B3: Option<&mut i32> = Some(&mut 42); = help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:24:42 + --> $DIR/mut_ref_in_final.rs:25:42 | LL | const B4: Option<&mut i32> = helper(&mut 42); | ------------^^- @@ -29,29 +29,29 @@ LL | const B4: Option<&mut i32> = helper(&mut 42); | using this value as a constant requires that borrow lasts for `'static` error[E0080]: constructing invalid value of type &mut u16: encountered mutable reference or box pointing to read-only memory - --> $DIR/mut_ref_in_final.rs:27:1 + --> $DIR/mut_ref_in_final.rs:28:1 | LL | const IMMUT_MUT_REF: &mut u16 = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type &mut u16: encountered mutable reference or box pointing to read-only memory - --> $DIR/mut_ref_in_final.rs:29:1 + --> $DIR/mut_ref_in_final.rs:30:1 | LL | static IMMUT_MUT_REF_STATIC: &mut u16 = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:52:65 + --> $DIR/mut_ref_in_final.rs:53:65 | LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- @@ -61,7 +61,7 @@ LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:55:67 + --> $DIR/mut_ref_in_final.rs:56:67 | LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- @@ -71,7 +71,7 @@ LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | using this value as a static requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:58:71 + --> $DIR/mut_ref_in_final.rs:59:71 | LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- @@ -81,7 +81,7 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | using this value as a static requires that borrow lasts for `'static` error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed - --> $DIR/mut_ref_in_final.rs:71:53 + --> $DIR/mut_ref_in_final.rs:72:53 | LL | static RAW_MUT_CAST_S: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; | ^^^^^^^ this mutable borrow refers to such a temporary @@ -91,7 +91,7 @@ LL | static RAW_MUT_CAST_S: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *c = help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed - --> $DIR/mut_ref_in_final.rs:73:54 + --> $DIR/mut_ref_in_final.rs:74:54 | LL | static RAW_MUT_COERCE_S: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^ this mutable borrow refers to such a temporary @@ -101,7 +101,7 @@ LL | static RAW_MUT_COERCE_S: SyncPtr = SyncPtr { x: &mut 0 }; = help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed - --> $DIR/mut_ref_in_final.rs:75:52 + --> $DIR/mut_ref_in_final.rs:76:52 | LL | const RAW_MUT_CAST_C: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; | ^^^^^^^ this mutable borrow refers to such a temporary @@ -111,7 +111,7 @@ LL | const RAW_MUT_CAST_C: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *co = help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed - --> $DIR/mut_ref_in_final.rs:77:53 + --> $DIR/mut_ref_in_final.rs:78:53 | LL | const RAW_MUT_COERCE_C: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^ this mutable borrow refers to such a temporary @@ -121,7 +121,7 @@ LL | const RAW_MUT_COERCE_C: SyncPtr = SyncPtr { x: &mut 0 }; = help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` error[E0080]: constructing invalid value of type Option<&mut i32>: at ..0, encountered a dangling reference (0x2a[noalloc] has no provenance) - --> $DIR/mut_ref_in_final.rs:86:5 + --> $DIR/mut_ref_in_final.rs:87:5 | LL | const INT2PTR: Option<&mut i32> = helper_int2ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -132,7 +132,7 @@ LL | const INT2PTR: Option<&mut i32> = helper_int2ptr(); } error[E0080]: constructing invalid value of type Option<&mut i32>: at ..0, encountered a dangling reference (0x2a[noalloc] has no provenance) - --> $DIR/mut_ref_in_final.rs:87:5 + --> $DIR/mut_ref_in_final.rs:88:5 | LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value @@ -143,25 +143,25 @@ LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); } error[E0080]: constructing invalid value of type Option<&mut i32>: at ..0, encountered a dangling reference (use-after-free) - --> $DIR/mut_ref_in_final.rs:93:5 + --> $DIR/mut_ref_in_final.rs:94:5 | LL | const DANGLING: Option<&mut i32> = helper_dangling(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type Option<&mut i32>: at ..0, encountered a dangling reference (use-after-free) - --> $DIR/mut_ref_in_final.rs:94:5 + --> $DIR/mut_ref_in_final.rs:95:5 | LL | static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error: aborting due to 16 previous errors diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.rs b/tests/ui/consts/const_refs_to_static_fail_invalid.rs index 229b9fdcc6027..0db8c059d5392 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.rs +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.rs @@ -1,5 +1,6 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" //@ dont-require-annotations: NOTE #![allow(static_mut_refs)] diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr index b0150fc596669..1bd031f163483 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr @@ -1,16 +1,16 @@ error[E0080]: constructing invalid value of type &bool: at ., encountered 0x0a, but expected a boolean - --> $DIR/const_refs_to_static_fail_invalid.rs:10:5 + --> $DIR/const_refs_to_static_fail_invalid.rs:11:5 | LL | const C: &bool = unsafe { std::mem::transmute(&S) }; | ^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error: constant extern_::C cannot be used as pattern - --> $DIR/const_refs_to_static_fail_invalid.rs:29:9 + --> $DIR/const_refs_to_static_fail_invalid.rs:30:9 | LL | C => {} | ^ @@ -18,7 +18,7 @@ LL | C => {} = note: constants that reference mutable or external memory cannot be used as patterns error: constant mutable::C cannot be used as pattern - --> $DIR/const_refs_to_static_fail_invalid.rs:42:9 + --> $DIR/const_refs_to_static_fail_invalid.rs:43:9 | LL | C => {} | ^ diff --git a/tests/ui/consts/const_transmute_type_id7.rs b/tests/ui/consts/const_transmute_type_id7.rs index 6863b88ca2798..1576f00a5af64 100644 --- a/tests/ui/consts/const_transmute_type_id7.rs +++ b/tests/ui/consts/const_transmute_type_id7.rs @@ -5,6 +5,8 @@ //@ normalize-stderr: "\[&\(\); \d\]" -> "ARRAY" //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ normalize-stderr: "(╾ALLOC\$ID╼ )+" -> "╾ALLOC$$IDs╼" +//@ normalize-stderr: "╾[╼╾─]*╼" -> "╾─╼" #![feature(const_trait_impl, const_cmp)] diff --git a/tests/ui/consts/const_transmute_type_id7.stderr b/tests/ui/consts/const_transmute_type_id7.stderr index a1c37f2b36feb..e96dee687afbf 100644 --- a/tests/ui/consts/const_transmute_type_id7.stderr +++ b/tests/ui/consts/const_transmute_type_id7.stderr @@ -1,12 +1,12 @@ error[E0080]: constructing invalid value of type ARRAY: at [0], encountered a maybe-null reference - --> $DIR/const_transmute_type_id7.rs:14:1 + --> $DIR/const_transmute_type_id7.rs:16:1 | LL | const A: [&(); 16 / size_of::<*const ()>()] = unsafe { transmute(TypeId::of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$IDs╼│ ╾─╼ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs index 5329e5bc242ed..480f1c5f460d0 100644 --- a/tests/ui/consts/copy-intrinsic.rs +++ b/tests/ui/consts/copy-intrinsic.rs @@ -1,6 +1,6 @@ // ignore-tidy-linelength #![feature(core_intrinsics)] -//@ ignore-parallel-frontend different alloc ids + use std::intrinsics::{copy, copy_nonoverlapping}; use std::mem; diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr index 41cf7e513572b..4c56304e4c157 100644 --- a/tests/ui/consts/copy-intrinsic.stderr +++ b/tests/ui/consts/copy-intrinsic.stderr @@ -4,7 +4,7 @@ error[E0080]: memory access failed: attempting to access 4 bytes, but got 0x100[ LL | copy_nonoverlapping(0x100 as *const i32, dangle, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `COPY_OOB_1` failed here -error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC0+0x28 which is at or beyond the end of the allocation of size 4 bytes +error[E0080]: memory access failed: attempting to access 4 bytes, but got ALLOC$ID+0x28 which is at or beyond the end of the allocation of size 4 bytes --> $DIR/copy-intrinsic.rs:28:5 | LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); diff --git a/tests/ui/consts/dangling-alloc-id-ice.rs b/tests/ui/consts/dangling-alloc-id-ice.rs index b77c5971b2bc1..1b10b1d78aa3a 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.rs +++ b/tests/ui/consts/dangling-alloc-id-ice.rs @@ -3,6 +3,7 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" union Foo<'a> { y: &'a (), diff --git a/tests/ui/consts/dangling-alloc-id-ice.stderr b/tests/ui/consts/dangling-alloc-id-ice.stderr index 5b92bef890acb..b6166f9b543f2 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.stderr +++ b/tests/ui/consts/dangling-alloc-id-ice.stderr @@ -1,12 +1,12 @@ error[E0080]: constructing invalid value of type &(): encountered a dangling reference (use-after-free) - --> $DIR/dangling-alloc-id-ice.rs:12:1 + --> $DIR/dangling-alloc-id-ice.rs:13:1 | LL | const FOO: &() = { | ^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/dangling-zst-ice-issue-126393.rs b/tests/ui/consts/dangling-zst-ice-issue-126393.rs index 6b667fdfd35de..52f97e0364b72 100644 --- a/tests/ui/consts/dangling-zst-ice-issue-126393.rs +++ b/tests/ui/consts/dangling-zst-ice-issue-126393.rs @@ -2,6 +2,7 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" pub struct Wrapper; pub static MAGIC_FFI_REF: &'static Wrapper = unsafe { diff --git a/tests/ui/consts/dangling-zst-ice-issue-126393.stderr b/tests/ui/consts/dangling-zst-ice-issue-126393.stderr index 0824afd862ad2..ba792740bbb89 100644 --- a/tests/ui/consts/dangling-zst-ice-issue-126393.stderr +++ b/tests/ui/consts/dangling-zst-ice-issue-126393.stderr @@ -1,12 +1,12 @@ error[E0080]: constructing invalid value of type &Wrapper: encountered a dangling reference (use-after-free) - --> $DIR/dangling-zst-ice-issue-126393.rs:7:1 + --> $DIR/dangling-zst-ice-issue-126393.rs:8:1 | LL | pub static MAGIC_FFI_REF: &'static Wrapper = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/interior-mut-const-via-union.32bit.stderr b/tests/ui/consts/interior-mut-const-via-union.32bit.stderr index 64a178b6913dc..e43b790fad472 100644 --- a/tests/ui/consts/interior-mut-const-via-union.32bit.stderr +++ b/tests/ui/consts/interior-mut-const-via-union.32bit.stderr @@ -6,7 +6,7 @@ LL | fn main() { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC0╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } note: erroneous constant encountered diff --git a/tests/ui/consts/interior-mut-const-via-union.64bit.stderr b/tests/ui/consts/interior-mut-const-via-union.64bit.stderr index f2301560faae7..5f0ec6aa2b959 100644 --- a/tests/ui/consts/interior-mut-const-via-union.64bit.stderr +++ b/tests/ui/consts/interior-mut-const-via-union.64bit.stderr @@ -6,7 +6,7 @@ LL | fn main() { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC0╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } note: erroneous constant encountered diff --git a/tests/ui/consts/interior-mut-const-via-union.rs b/tests/ui/consts/interior-mut-const-via-union.rs index cba3b36bd98a0..5e624671aee02 100644 --- a/tests/ui/consts/interior-mut-const-via-union.rs +++ b/tests/ui/consts/interior-mut-const-via-union.rs @@ -3,7 +3,7 @@ // //@ build-fail //@ stderr-per-bitwidth -//@ ignore-parallel-frontend different alloc ids + use std::cell::Cell; use std::mem::ManuallyDrop; diff --git a/tests/ui/consts/issue-63952.32bit.stderr b/tests/ui/consts/issue-63952.32bit.stderr index eb57a2f2ab5ea..61028b5b426b2 100644 --- a/tests/ui/consts/issue-63952.32bit.stderr +++ b/tests/ui/consts/issue-63952.32bit.stderr @@ -6,7 +6,7 @@ LL | const SLICE_WAY_TOO_LONG: &[u8] = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC0╼ ff ff ff ff │ ╾──╼.... + ╾ALLOC$ID╼ ff ff ff ff │ ╾──╼.... } error: aborting due to 1 previous error diff --git a/tests/ui/consts/issue-63952.64bit.stderr b/tests/ui/consts/issue-63952.64bit.stderr index da1d05eb5dc55..4ce50ec0da4dc 100644 --- a/tests/ui/consts/issue-63952.64bit.stderr +++ b/tests/ui/consts/issue-63952.64bit.stderr @@ -6,7 +6,7 @@ LL | const SLICE_WAY_TOO_LONG: &[u8] = unsafe { | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC0╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ + ╾ALLOC$ID╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/issue-63952.rs b/tests/ui/consts/issue-63952.rs index c0ee7a5dc1e7e..fce6013b4d310 100644 --- a/tests/ui/consts/issue-63952.rs +++ b/tests/ui/consts/issue-63952.rs @@ -1,6 +1,6 @@ // Regression test for #63952, shouldn't hang. //@ stderr-per-bitwidth -//@ ignore-parallel-frontend different alloc ids + #[repr(C)] #[derive(Copy, Clone)] struct SliceRepr { diff --git a/tests/ui/consts/issue-79690.64bit.stderr b/tests/ui/consts/issue-79690.64bit.stderr index c3f89d243335e..4e3df27f470a3 100644 --- a/tests/ui/consts/issue-79690.64bit.stderr +++ b/tests/ui/consts/issue-79690.64bit.stderr @@ -6,7 +6,7 @@ LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC0╼ ╾ALLOC1╼ │ ╾──────╼╾──────╼ + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ } error: aborting due to 1 previous error diff --git a/tests/ui/consts/issue-79690.rs b/tests/ui/consts/issue-79690.rs index 5e1e65cdb8d57..24e3220155d18 100644 --- a/tests/ui/consts/issue-79690.rs +++ b/tests/ui/consts/issue-79690.rs @@ -1,7 +1,7 @@ //@ ignore-32bit // This test gives a different error on 32-bit architectures. //@ stderr-per-bitwidth -//@ ignore-parallel-frontend different alloc ids + union Transmute { t: T, u: U, diff --git a/tests/ui/consts/miri_unleashed/mutable_references.rs b/tests/ui/consts/miri_unleashed/mutable_references.rs index 78f0cd520cb2b..300da408896e5 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references.rs +++ b/tests/ui/consts/miri_unleashed/mutable_references.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Zunleash-the-miri-inside-of-you //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" //@ dont-require-annotations: NOTE #![allow(static_mut_refs)] diff --git a/tests/ui/consts/miri_unleashed/mutable_references.stderr b/tests/ui/consts/miri_unleashed/mutable_references.stderr index 68c6dd1ed6004..3750b5354a982 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references.stderr +++ b/tests/ui/consts/miri_unleashed/mutable_references.stderr @@ -1,124 +1,124 @@ error[E0080]: constructing invalid value of type &&mut u32: at ., encountered mutable reference or box pointing to read-only memory - --> $DIR/mutable_references.rs:13:1 + --> $DIR/mutable_references.rs:14:1 | LL | static FOO: &&mut u32 = &&mut 42; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type &mut i32: encountered mutable reference or box pointing to read-only memory - --> $DIR/mutable_references.rs:15:1 + --> $DIR/mutable_references.rs:16:1 | LL | static OH_YES: &mut i32 = &mut 42; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error: encountered mutable pointer in final value of static - --> $DIR/mutable_references.rs:17:1 + --> $DIR/mutable_references.rs:18:1 | LL | static BAR: &mut () = &mut (); | ^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of static - --> $DIR/mutable_references.rs:22:1 + --> $DIR/mutable_references.rs:23:1 | LL | static BOO: &mut Foo<()> = &mut Foo(()); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: constructing invalid value of type &mut i32: encountered mutable reference or box pointing to read-only memory - --> $DIR/mutable_references.rs:25:1 + --> $DIR/mutable_references.rs:26:1 | LL | const BLUNT: &mut i32 = &mut 42; | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type Meh: at .x., encountered `UnsafeCell` in read-only memory - --> $DIR/mutable_references.rs:40:1 + --> $DIR/mutable_references.rs:41:1 | LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type Meh: at .x., encountered `UnsafeCell` in read-only memory - --> $DIR/mutable_references.rs:45:1 + --> $DIR/mutable_references.rs:46:1 | LL | const MUH: Meh = Meh { | ^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type &dyn Sync: at ...x, encountered `UnsafeCell` in read-only memory - --> $DIR/mutable_references.rs:56:1 + --> $DIR/mutable_references.rs:57:1 | LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constructing invalid value of type &mut i32: encountered mutable reference or box pointing to read-only memory - --> $DIR/mutable_references.rs:62:1 + --> $DIR/mutable_references.rs:63:1 | LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error[E0080]: constant accesses mutable global memory - --> $DIR/mutable_references.rs:73:43 + --> $DIR/mutable_references.rs:74:43 | LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; | ^^^^^^^^^^^^^ evaluation of `POINTS_TO_MUTABLE2` failed here error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:76:1 + --> $DIR/mutable_references.rs:77:1 | LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:79:1 + --> $DIR/mutable_references.rs:80:1 | LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:99:1 + --> $DIR/mutable_references.rs:100:1 | LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x: &mut 42 as *mut _ as *const _ }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:102:1 + --> $DIR/mutable_references.rs:103:1 | LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item - --> $DIR/mutable_references.rs:109:5 + --> $DIR/mutable_references.rs:110:5 | LL | static OH_YES: &mut i32 = &mut 42; | ----------------------- this `static` cannot be written to @@ -129,72 +129,72 @@ LL | *OH_YES = 99; warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:13:26 + --> $DIR/mutable_references.rs:14:26 | LL | static FOO: &&mut u32 = &&mut 42; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:15:27 + --> $DIR/mutable_references.rs:16:27 | LL | static OH_YES: &mut i32 = &mut 42; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:17:23 + --> $DIR/mutable_references.rs:18:23 | LL | static BAR: &mut () = &mut (); | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:22:28 + --> $DIR/mutable_references.rs:23:28 | LL | static BOO: &mut Foo<()> = &mut Foo(()); | ^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:25:25 + --> $DIR/mutable_references.rs:26:25 | LL | const BLUNT: &mut i32 = &mut 42; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:40:28 + --> $DIR/mutable_references.rs:41:28 | LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:47:8 + --> $DIR/mutable_references.rs:48:8 | LL | x: &UnsafeCell::new(42), | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:56:27 + --> $DIR/mutable_references.rs:57:27 | LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:76:45 + --> $DIR/mutable_references.rs:77:45 | LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:79:46 + --> $DIR/mutable_references.rs:80:46 | LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:84:47 + --> $DIR/mutable_references.rs:85:47 | LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:96:51 + --> $DIR/mutable_references.rs:97:51 | LL | const RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; | ^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:99:49 + --> $DIR/mutable_references.rs:100:49 | LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x: &mut 42 as *mut _ as *const _ }; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:102:51 + --> $DIR/mutable_references.rs:103:51 | LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^ diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr b/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr index ab21696729100..f6061d9f5d794 100644 --- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr @@ -6,7 +6,7 @@ LL | static REF: &AtomicI32 = &AtomicI32::new(42); | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC0╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } error[E0080]: constructing invalid value of type &mut i32: encountered mutable reference or box pointing to read-only memory @@ -17,7 +17,7 @@ LL | static REFMUT: &mut i32 = &mut 0; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC1╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } error[E0080]: constructing invalid value of type &Atomic: at ..v, encountered `UnsafeCell` in read-only memory @@ -28,7 +28,7 @@ LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC2╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } error[E0080]: constructing invalid value of type &mut i32: encountered mutable reference or box pointing to read-only memory @@ -39,7 +39,7 @@ LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC3╼ │ ╾──╼ + ╾ALLOC$ID╼ │ ╾──╼ } error: encountered mutable pointer in final value of static diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr b/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr index 673c1b3c0b6e2..fa63b920be535 100644 --- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr @@ -6,7 +6,7 @@ LL | static REF: &AtomicI32 = &AtomicI32::new(42); | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC0╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } error[E0080]: constructing invalid value of type &mut i32: encountered mutable reference or box pointing to read-only memory @@ -17,7 +17,7 @@ LL | static REFMUT: &mut i32 = &mut 0; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC1╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } error[E0080]: constructing invalid value of type &Atomic: at ..v, encountered `UnsafeCell` in read-only memory @@ -28,7 +28,7 @@ LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC2╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } error[E0080]: constructing invalid value of type &mut i32: encountered mutable reference or box pointing to read-only memory @@ -39,7 +39,7 @@ LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC3╼ │ ╾──────╼ + ╾ALLOC$ID╼ │ ╾──────╼ } error: encountered mutable pointer in final value of static diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs b/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs index 27eb9a6c7c344..0e87442f6a698 100644 --- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs @@ -1,6 +1,6 @@ //@ stderr-per-bitwidth //@ compile-flags: -Zunleash-the-miri-inside-of-you -//@ ignore-parallel-frontend different alloc ids + // All "inner" allocations that come with a `static` are interned immutably. This means it is // crucial that we do not accept any form of (interior) mutability there. use std::sync::atomic::*; diff --git a/tests/ui/consts/missing_span_in_backtrace.rs b/tests/ui/consts/missing_span_in_backtrace.rs index 897b37b3e65ae..b679493eb08eb 100644 --- a/tests/ui/consts/missing_span_in_backtrace.rs +++ b/tests/ui/consts/missing_span_in_backtrace.rs @@ -1,7 +1,7 @@ //! Check what happens when the error occurs inside a std function that we can't print the span of. //@ ignore-backends: gcc //@ compile-flags: -Z ui-testing=no --diagnostic-width=80 -//@ ignore-parallel-frontend different alloc ids + use std::{ mem::{self, MaybeUninit}, ptr, diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index b86911e7057b2..ccba98050e79a 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -1,4 +1,4 @@ -error[E0080]: memory access failed: attempting to access 1 byte, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes +error[E0080]: memory access failed: attempting to access 1 byte, but got ALLOC$ID+0x4 which is at or beyond the end of the allocation of size 4 bytes --> $DIR/missing_span_in_backtrace.rs:16:9 | 16 | / ... ptr::swap_nonoverlapping( diff --git a/tests/ui/consts/offset_ub.rs b/tests/ui/consts/offset_ub.rs index c9c34e19e4dd6..98a50156a9487 100644 --- a/tests/ui/consts/offset_ub.rs +++ b/tests/ui/consts/offset_ub.rs @@ -1,5 +1,5 @@ use std::ptr; -//@ ignore-parallel-frontend different alloc ids + //@ normalize-stderr: "0xf+" -> "0xf..f" //@ normalize-stderr: "0x7f+" -> "0x7f..f" //@ normalize-stderr: "\d+ bytes" -> "$$BYTES bytes" diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index f40d342758360..c3bfca27925a2 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -1,16 +1,16 @@ -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC0 which is at the beginning of the allocation +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC$ID which is at the beginning of the allocation --> $DIR/offset_ub.rs:8:46 | LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `BEFORE_START` failed here -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC1 which is only 1 byte from the end of the allocation +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC$ID which is only 1 byte from the end of the allocation --> $DIR/offset_ub.rs:9:43 | LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `AFTER_END` failed here -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC2 which is only $BYTES bytes from the end of the allocation +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC$ID which is only $BYTES bytes from the end of the allocation --> $DIR/offset_ub.rs:10:45 | LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; @@ -40,13 +40,13 @@ error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNDERFLOW_ADDRESS_SPACE` failed here -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC3-0x2 which points to before the beginning of the allocation +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC$ID-0x2 which points to before the beginning of the allocation --> $DIR/offset_ub.rs:16:49 | LL | ...nst u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `NEGATIVE_OFFSET` failed here -error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes +error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got ALLOC$ID which is at or beyond the end of the allocation of size $BYTES bytes --> $DIR/offset_ub.rs:18:50 | LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; diff --git a/tests/ui/contracts/empty-ensures.rs b/tests/ui/contracts/empty-ensures.rs index 0a52391117861..79e57df6eb984 100644 --- a/tests/ui/contracts/empty-ensures.rs +++ b/tests/ui/contracts/empty-ensures.rs @@ -6,7 +6,7 @@ extern crate core; use core::contracts::ensures; #[ensures()] -//~^ ERROR expected a `Fn(&_)` closure, found `()` [E0277] +//~^ ERROR expected an `Fn(&_)` closure, found `()` [E0277] fn foo(x: u32) -> u32 { x * 2 } diff --git a/tests/ui/contracts/empty-ensures.stderr b/tests/ui/contracts/empty-ensures.stderr index 6a19d234b52c2..b87f709eeb7a4 100644 --- a/tests/ui/contracts/empty-ensures.stderr +++ b/tests/ui/contracts/empty-ensures.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&_)` closure, found `()` +error[E0277]: expected an `Fn(&_)` closure, found `()` --> $DIR/empty-ensures.rs:8:1 | LL | #[ensures()] diff --git a/tests/ui/coroutine/delayed-obligations-emit.next.stderr b/tests/ui/coroutine/delayed-obligations-emit.next.stderr index 3a3663398c9a7..af7dfc75963c2 100644 --- a/tests/ui/coroutine/delayed-obligations-emit.next.stderr +++ b/tests/ui/coroutine/delayed-obligations-emit.next.stderr @@ -4,6 +4,7 @@ error[E0275]: overflow evaluating the requirement `{async block@$DIR/delayed-obl LL | spawn(async { build_dependencies().await }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`delayed_obligations_emit`) note: required by a bound in `spawn` --> $DIR/delayed-obligations-emit.rs:31:13 | diff --git a/tests/ui/delegation/generics/free-fn-to-trait-infer.rs b/tests/ui/delegation/generics/free-fn-to-trait-infer.rs deleted file mode 100644 index 0a0665b752681..0000000000000 --- a/tests/ui/delegation/generics/free-fn-to-trait-infer.rs +++ /dev/null @@ -1,19 +0,0 @@ -//@ compile-flags: -Z deduplicate-diagnostics=yes - -#![feature(fn_delegation)] - -trait Trait { - fn foo(&self, _: U, _: T) {} -} - -impl Trait for u8 {} - -reuse Trait::<_>::foo:: as generic_arguments1; -//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions -reuse >::foo as generic_arguments2; -//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions -reuse <_ as Trait<_>>::foo as generic_arguments3; -//~^ 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 - -fn main() {} diff --git a/tests/ui/delegation/generics/free-fn-to-trait-infer.stderr b/tests/ui/delegation/generics/free-fn-to-trait-infer.stderr deleted file mode 100644 index f1e9231cc4050..0000000000000 --- a/tests/ui/delegation/generics/free-fn-to-trait-infer.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/free-fn-to-trait-infer.rs:11:15 - | -LL | reuse Trait::<_>::foo:: as generic_arguments1; - | ^ not allowed in type signatures - -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/free-fn-to-trait-infer.rs:13:20 - | -LL | reuse >::foo as generic_arguments2; - | ^ not allowed in type signatures - -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/free-fn-to-trait-infer.rs:15:8 - | -LL | reuse <_ as Trait<_>>::foo as generic_arguments3; - | ^ not allowed in type signatures - -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/free-fn-to-trait-infer.rs:15:19 - | -LL | reuse <_ as Trait<_>>::foo as generic_arguments3; - | ^ not allowed in type signatures - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/delegation/generics/free-to-trait-static-reuse.rs b/tests/ui/delegation/generics/free-to-trait-static-reuse.rs index e3d0bdbc7e9b0..3f52209ded317 100644 --- a/tests/ui/delegation/generics/free-to-trait-static-reuse.rs +++ b/tests/ui/delegation/generics/free-to-trait-static-reuse.rs @@ -20,17 +20,9 @@ reuse ::static_method as bar { self + 1 } reuse ::static_method::<'static, Vec, false> as bar2; reuse Trait::static_method as error { self - 123 } -//~^ ERROR: type annotations needed -//~| ERROR: delegation self type is not specified reuse Trait::<'static, i32, 123>::static_method as error2; -//~^ ERROR: type annotations needed -//~| ERROR: delegation self type is not specified reuse Trait::<'static, i32, 123>::static_method::<'static, String, false> as error3; -//~^ ERROR: type annotations needed -//~| ERROR: delegation self type is not specified reuse Trait::static_method::<'static, Vec, false> as error4 { self + 4 } -//~^ ERROR: type annotations needed -//~| ERROR: delegation self type is not specified reuse ::static_method as error5; //~^ ERROR: the trait bound `String: Trait<'a, T, X>` is not satisfied diff --git a/tests/ui/delegation/generics/free-to-trait-static-reuse.stderr b/tests/ui/delegation/generics/free-to-trait-static-reuse.stderr index 8d87b9376acd4..04f5e60044cdc 100644 --- a/tests/ui/delegation/generics/free-to-trait-static-reuse.stderr +++ b/tests/ui/delegation/generics/free-to-trait-static-reuse.stderr @@ -1,43 +1,11 @@ -error: delegation self type is not specified - --> $DIR/free-to-trait-static-reuse.rs:22:14 - | -LL | reuse Trait::static_method as error { self - 123 } - | ^^^^^^^^^^^^^ - | - = help: consider explicitly specifying self type: `reuse ::function` - -error: delegation self type is not specified - --> $DIR/free-to-trait-static-reuse.rs:25:35 - | -LL | reuse Trait::<'static, i32, 123>::static_method as error2; - | ^^^^^^^^^^^^^ - | - = help: consider explicitly specifying self type: `reuse ::function` - -error: delegation self type is not specified - --> $DIR/free-to-trait-static-reuse.rs:28:35 - | -LL | reuse Trait::<'static, i32, 123>::static_method::<'static, String, false> as error3; - | ^^^^^^^^^^^^^ - | - = help: consider explicitly specifying self type: `reuse ::function` - -error: delegation self type is not specified - --> $DIR/free-to-trait-static-reuse.rs:31:14 - | -LL | reuse Trait::static_method::<'static, Vec, false> as error4 { self + 4 } - | ^^^^^^^^^^^^^ - | - = help: consider explicitly specifying self type: `reuse ::function` - error[E0277]: the trait bound `Struct: Bound` is not satisfied - --> $DIR/free-to-trait-static-reuse.rs:39:49 + --> $DIR/free-to-trait-static-reuse.rs:31:49 | LL | impl<'a, T, const X: usize> Trait<'a, T, X> for Struct {} | ^^^^^^ unsatisfied trait bound | help: the trait `Bound` is not implemented for `Struct` - --> $DIR/free-to-trait-static-reuse.rs:38:1 + --> $DIR/free-to-trait-static-reuse.rs:30:1 | LL | struct Struct; | ^^^^^^^^^^^^^ @@ -55,68 +23,8 @@ LL | where LL | Self: Bound, | ^^^^^^^^ required by this bound in `Trait` -error[E0283]: type annotations needed - --> $DIR/free-to-trait-static-reuse.rs:22:14 - | -LL | reuse Trait::static_method as error { self - 123 } - | ^^^^^^^^^^^^^ cannot infer type - | -note: multiple `impl`s satisfying `_: Trait<'a, T, X>` found - --> $DIR/free-to-trait-static-reuse.rs:14:1 - | -LL | impl<'a, T, const X: usize> Trait<'a, T, X> for usize {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | impl<'a, T, const X: usize> Trait<'a, T, X> for Struct {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0283]: type annotations needed - --> $DIR/free-to-trait-static-reuse.rs:25:35 - | -LL | reuse Trait::<'static, i32, 123>::static_method as error2; - | ^^^^^^^^^^^^^ cannot infer type - | -note: multiple `impl`s satisfying `_: Trait<'static, i32, 123>` found - --> $DIR/free-to-trait-static-reuse.rs:14:1 - | -LL | impl<'a, T, const X: usize> Trait<'a, T, X> for usize {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | impl<'a, T, const X: usize> Trait<'a, T, X> for Struct {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0283]: type annotations needed - --> $DIR/free-to-trait-static-reuse.rs:28:35 - | -LL | reuse Trait::<'static, i32, 123>::static_method::<'static, String, false> as error3; - | ^^^^^^^^^^^^^ cannot infer type - | -note: multiple `impl`s satisfying `_: Trait<'static, i32, 123>` found - --> $DIR/free-to-trait-static-reuse.rs:14:1 - | -LL | impl<'a, T, const X: usize> Trait<'a, T, X> for usize {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | impl<'a, T, const X: usize> Trait<'a, T, X> for Struct {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0283]: type annotations needed - --> $DIR/free-to-trait-static-reuse.rs:31:14 - | -LL | reuse Trait::static_method::<'static, Vec, false> as error4 { self + 4 } - | ^^^^^^^^^^^^^ cannot infer type - | -note: multiple `impl`s satisfying `_: Trait<'a, T, X>` found - --> $DIR/free-to-trait-static-reuse.rs:14:1 - | -LL | impl<'a, T, const X: usize> Trait<'a, T, X> for usize {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | impl<'a, T, const X: usize> Trait<'a, T, X> for Struct {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0277]: the trait bound `String: Trait<'a, T, X>` is not satisfied - --> $DIR/free-to-trait-static-reuse.rs:35:8 + --> $DIR/free-to-trait-static-reuse.rs:27:8 | LL | reuse ::static_method as error5; | ^^^^^^ the trait `Trait<'a, T, X>` is not implemented for `String` @@ -131,13 +39,13 @@ LL | impl<'a, T, const X: usize> Trait<'a, T, X> for Struct {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Struct` error[E0277]: the trait bound `Struct: Bound` is not satisfied - --> $DIR/free-to-trait-static-reuse.rs:42:8 + --> $DIR/free-to-trait-static-reuse.rs:34:8 | LL | reuse ::static_method as error6; | ^^^^^^ unsatisfied trait bound | help: the trait `Bound` is not implemented for `Struct` - --> $DIR/free-to-trait-static-reuse.rs:38:1 + --> $DIR/free-to-trait-static-reuse.rs:30:1 | LL | struct Struct; | ^^^^^^^^^^^^^ @@ -155,7 +63,6 @@ LL | { LL | fn static_method<'c: 'c, U, const B: bool>(x: usize) {} | ------------- required by a bound in this associated function -error: aborting due to 11 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0277, E0283. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/generics/generics-gen-args-errors.rs b/tests/ui/delegation/generics/generics-gen-args-errors.rs index 4e1ac0a5e4176..6a8d1674fe184 100644 --- a/tests/ui/delegation/generics/generics-gen-args-errors.rs +++ b/tests/ui/delegation/generics/generics-gen-args-errors.rs @@ -44,8 +44,6 @@ mod test_2 { fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} reuse foo::<> as bar1; - //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions - //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature reuse foo:: as bar2; //~^ ERROR: function takes 3 generic arguments but 2 generic arguments were supplied @@ -54,9 +52,9 @@ mod test_2 { reuse foo::<'static, _, 'asdasd, 'static, 'static, 'static, _> as bar3; //~^ ERROR: use of undeclared lifetime name `'asdasd` - //~| ERROR: function takes 2 lifetime arguments but 5 lifetime arguments were supplied - //~| ERROR: function takes 3 generic arguments but 2 generic arguments were supplied - //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: function takes 2 lifetime arguments but 6 lifetime arguments were supplied + //~| ERROR: function takes 3 generic arguments but 1 generic argument was supplied + //~| ERROR: wrong infer used: expected '_, found: _ reuse foo:: as bar4; //~^ ERROR: cannot find type `asdasd` in this scope @@ -131,12 +129,13 @@ mod test_3 { //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; - //~^ ERROR: trait takes 3 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR: trait takes 2 generic arguments but 5 generic arguments were supplied + //~^ ERROR: trait takes 3 lifetime arguments but 2 lifetime arguments were supplied + //~| ERROR: trait takes 2 generic arguments but 4 generic arguments were supplied //~| ERROR: method takes 2 generic arguments but 5 generic arguments were supplied //~| ERROR: method takes 1 lifetime argument but 0 lifetime arguments were supplied //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: wrong infer used: expected '_, found: _ } fn main() {} diff --git a/tests/ui/delegation/generics/generics-gen-args-errors.stderr b/tests/ui/delegation/generics/generics-gen-args-errors.stderr index 7aa1766dfef89..c08e23e6c016a 100644 --- a/tests/ui/delegation/generics/generics-gen-args-errors.stderr +++ b/tests/ui/delegation/generics/generics-gen-args-errors.stderr @@ -38,7 +38,7 @@ LL | reuse foo:: as xd; = note: nested items are independent from their parent item for everything except for privacy and name resolution error[E0261]: use of undeclared lifetime name `'asdasd` - --> $DIR/generics-gen-args-errors.rs:55:29 + --> $DIR/generics-gen-args-errors.rs:53:29 | LL | reuse foo::<'static, _, 'asdasd, 'static, 'static, 'static, _> as bar3; | ^^^^^^^ undeclared lifetime @@ -49,7 +49,7 @@ LL | reuse foo'asdasd, ::<'static, _, 'asdasd, 'static, 'static, 'static, _> | ++++++++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/generics-gen-args-errors.rs:77:50 + --> $DIR/generics-gen-args-errors.rs:75:50 | LL | reuse foo::<"asdasd", asd, "askdn", 'static, 'a> as bar7; | ^^ undeclared lifetime @@ -103,65 +103,77 @@ LL | fn check() { | +++++ error[E0425]: cannot find type `asdasd` in this scope - --> $DIR/generics-gen-args-errors.rs:61:39 + --> $DIR/generics-gen-args-errors.rs:59:39 | LL | reuse foo:: as bar4; | ^^^^^^ not found in this scope error[E0425]: cannot find type `asd` in this scope - --> $DIR/generics-gen-args-errors.rs:71:22 + --> $DIR/generics-gen-args-errors.rs:69:22 | LL | reuse foo::<1, 2,asd,String, { let x = 0; }> as bar6; | ^^^ not found in this scope error[E0425]: cannot find type `asd` in this scope - --> $DIR/generics-gen-args-errors.rs:77:27 + --> $DIR/generics-gen-args-errors.rs:75:27 | LL | reuse foo::<"asdasd", asd, "askdn", 'static, 'a> as bar7; | ^^^ not found in this scope error[E0425]: cannot find type `asd` in this scope - --> $DIR/generics-gen-args-errors.rs:94:19 + --> $DIR/generics-gen-args-errors.rs:92:19 | LL | reuse Trait::::foo as bar1; | ^^^ not found in this scope error[E0425]: cannot find type `asd` in this scope - --> $DIR/generics-gen-args-errors.rs:94:24 + --> $DIR/generics-gen-args-errors.rs:92:24 | LL | reuse Trait::::foo as bar1; | ^^^ not found in this scope error[E0425]: cannot find type `asd` in this scope - --> $DIR/generics-gen-args-errors.rs:94:29 + --> $DIR/generics-gen-args-errors.rs:92:29 | LL | reuse Trait::::foo as bar1; | ^^^ not found in this scope error[E0425]: cannot find type `asd` in this scope - --> $DIR/generics-gen-args-errors.rs:94:34 + --> $DIR/generics-gen-args-errors.rs:92:34 | LL | reuse Trait::::foo as bar1; | ^^^ not found in this scope error[E0425]: cannot find type `asd` in this scope - --> $DIR/generics-gen-args-errors.rs:94:39 + --> $DIR/generics-gen-args-errors.rs:92:39 | LL | reuse Trait::::foo as bar1; | ^^^ not found in this scope error[E0425]: cannot find type `asdasa` in this scope - --> $DIR/generics-gen-args-errors.rs:94:44 + --> $DIR/generics-gen-args-errors.rs:92:44 | LL | reuse Trait::::foo as bar1; | ^^^^^^ not found in this scope error[E0425]: cannot find type `DDDD` in this scope - --> $DIR/generics-gen-args-errors.rs:124:34 + --> $DIR/generics-gen-args-errors.rs:122:34 | LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | ^^^^ not found in this scope +error: wrong infer used: expected '_, found: _ + --> $DIR/generics-gen-args-errors.rs:53:26 + | +LL | reuse foo::<'static, _, 'asdasd, 'static, 'static, 'static, _> as bar3; + | ^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/generics-gen-args-errors.rs:131:33 + | +LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; + | ^ + error[E0107]: function takes 2 lifetime arguments but 0 lifetime arguments were supplied --> $DIR/generics-gen-args-errors.rs:33:15 | @@ -184,20 +196,8 @@ error: inferred lifetimes are not allowed in delegations as we need to inherit s LL | reuse foo:: as xd; | ^^^ -error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:46:11 - | -LL | reuse foo::<> as bar1; - | ^^^ - -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/generics-gen-args-errors.rs:46:11 - | -LL | reuse foo::<> as bar1; - | ^^^ not allowed in type signatures - error[E0107]: function takes 2 lifetime arguments but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:50:11 + --> $DIR/generics-gen-args-errors.rs:48:11 | LL | reuse foo:: as bar2; | ^^^ expected 2 lifetime arguments @@ -213,7 +213,7 @@ LL | reuse foo::<'a, 'b, String, String> as bar2; | +++++++ error[E0107]: function takes 3 generic arguments but 2 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:50:11 + --> $DIR/generics-gen-args-errors.rs:48:11 | LL | reuse foo:: as bar2; | ^^^ ------ ------ supplied 2 generic arguments @@ -231,16 +231,16 @@ LL | reuse foo:: as bar2; | +++ error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:50:11 + --> $DIR/generics-gen-args-errors.rs:48:11 | LL | reuse foo:: as bar2; | ^^^ -error[E0107]: function takes 2 lifetime arguments but 5 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:55:11 +error[E0107]: function takes 2 lifetime arguments but 6 lifetime arguments were supplied + --> $DIR/generics-gen-args-errors.rs:53:11 | LL | reuse foo::<'static, _, 'asdasd, 'static, 'static, 'static, _> as bar3; - | ^^^ --------------------------- help: remove the lifetime arguments + | ^^^------------------------------------------------- help: remove the lifetime arguments | | | expected 2 lifetime arguments | @@ -250,30 +250,24 @@ note: function defined here, with 2 lifetime parameters: `'a`, `'b` LL | fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} | ^^^ -- -- -error[E0107]: function takes 3 generic arguments but 2 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:55:11 +error[E0107]: function takes 3 generic arguments but 1 generic argument was supplied + --> $DIR/generics-gen-args-errors.rs:53:11 | LL | reuse foo::<'static, _, 'asdasd, 'static, 'static, 'static, _> as bar3; - | ^^^ expected 3 generic arguments ------- - supplied 2 generic arguments + | ^^^ expected 3 generic arguments - supplied 1 generic argument | note: function defined here, with 3 generic parameters: `T`, `U`, `N` --> $DIR/generics-gen-args-errors.rs:44:8 | LL | fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} | ^^^ - - -------------- -help: add missing generic argument +help: add missing generic arguments | -LL | reuse foo::<'static, _, 'asdasd, 'static, 'static, 'static, _, N> as bar3; - | +++ - -error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:55:11 - | -LL | reuse foo::<'static, _, 'asdasd, 'static, 'static, 'static, _> as bar3; - | ^^^ +LL | reuse foo::<'static, _, 'asdasd, 'static, 'static, 'static, _, U, N> as bar3; + | ++++++ error[E0107]: function takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/generics-gen-args-errors.rs:61:11 + --> $DIR/generics-gen-args-errors.rs:59:11 | LL | reuse foo:: as bar4; | ^^^ ------ supplied 1 lifetime argument @@ -291,13 +285,13 @@ LL | reuse foo:: as bar4; | +++++++++ error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:61:11 + --> $DIR/generics-gen-args-errors.rs:59:11 | LL | reuse foo:: as bar4; | ^^^ error[E0107]: function takes 2 lifetime arguments but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:66:11 + --> $DIR/generics-gen-args-errors.rs:64:11 | LL | reuse foo::<1, 2, _, 4, 5, _> as bar5; | ^^^ expected 2 lifetime arguments @@ -313,10 +307,10 @@ LL | reuse foo::<'a, 'b, 1, 2, _, 4, 5, _> as bar5; | +++++++ error[E0107]: function takes 3 generic arguments but 6 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:66:11 + --> $DIR/generics-gen-args-errors.rs:64:11 | LL | reuse foo::<1, 2, _, 4, 5, _> as bar5; - | ^^^ --------- help: remove the unnecessary generic arguments + | ^^^------------------- help: remove the unnecessary generic arguments | | | expected 3 generic arguments | @@ -327,13 +321,13 @@ LL | fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} | ^^^ - - -------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:66:11 + --> $DIR/generics-gen-args-errors.rs:64:11 | LL | reuse foo::<1, 2, _, 4, 5, _> as bar5; | ^^^ error[E0107]: function takes 2 lifetime arguments but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:71:11 + --> $DIR/generics-gen-args-errors.rs:69:11 | LL | reuse foo::<1, 2,asd,String, { let x = 0; }> as bar6; | ^^^ expected 2 lifetime arguments @@ -349,7 +343,7 @@ LL | reuse foo::<'a, 'b, 1, 2,asd,String, { let x = 0; }> as bar6; | +++++++ error[E0107]: function takes 3 generic arguments but 5 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:71:11 + --> $DIR/generics-gen-args-errors.rs:69:11 | LL | reuse foo::<1, 2,asd,String, { let x = 0; }> as bar6; | ^^^ ----------------------- help: remove the unnecessary generic arguments @@ -363,25 +357,25 @@ LL | fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} | ^^^ - - -------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:71:11 + --> $DIR/generics-gen-args-errors.rs:69:11 | LL | reuse foo::<1, 2,asd,String, { let x = 0; }> as bar6; | ^^^ error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:77:11 + --> $DIR/generics-gen-args-errors.rs:75:11 | LL | reuse foo::<"asdasd", asd, "askdn", 'static, 'a> as bar7; | ^^^ error[E0747]: constant provided when a type was expected - --> $DIR/generics-gen-args-errors.rs:77:17 + --> $DIR/generics-gen-args-errors.rs:75:17 | LL | reuse foo::<"asdasd", asd, "askdn", 'static, 'a> as bar7; | ^^^^^^^^ error[E0107]: function takes 2 lifetime arguments but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:83:11 + --> $DIR/generics-gen-args-errors.rs:81:11 | LL | reuse foo::<{}, {}, {}> as bar8; | ^^^ expected 2 lifetime arguments @@ -397,19 +391,19 @@ LL | reuse foo::<'a, 'b, {}, {}, {}> as bar8; | +++++++ error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:83:11 + --> $DIR/generics-gen-args-errors.rs:81:11 | LL | reuse foo::<{}, {}, {}> as bar8; | ^^^ error[E0107]: trait takes 3 lifetime arguments but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:94:11 + --> $DIR/generics-gen-args-errors.rs:92:11 | LL | reuse Trait::::foo as bar1; | ^^^^^ expected 3 lifetime arguments | note: trait defined here, with 3 lifetime parameters: `'b`, `'c`, `'a` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ -- -- -- @@ -419,7 +413,7 @@ LL | reuse Trait::<'b, 'c, 'a, asd, asd, asd, asd, asd, asdasa>::foo as bar1 | +++++++++++ error[E0107]: trait takes 2 generic arguments but 6 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:94:11 + --> $DIR/generics-gen-args-errors.rs:92:11 | LL | reuse Trait::::foo as bar1; | ^^^^^ ----------------------- help: remove the unnecessary generic arguments @@ -427,19 +421,19 @@ LL | reuse Trait::::foo as bar1; | expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `T`, `N` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ - -------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:94:11 + --> $DIR/generics-gen-args-errors.rs:92:11 | LL | reuse Trait::::foo as bar1; | ^^^^^ error[E0107]: trait takes 3 lifetime arguments but 2 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:105:11 + --> $DIR/generics-gen-args-errors.rs:103:11 | LL | reuse Trait::<'static, 'static>::foo as bar2; | ^^^^^ ------- ------- supplied 2 lifetime arguments @@ -447,7 +441,7 @@ LL | reuse Trait::<'static, 'static>::foo as bar2; | expected 3 lifetime arguments | note: trait defined here, with 3 lifetime parameters: `'b`, `'c`, `'a` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ -- -- -- @@ -457,25 +451,25 @@ LL | reuse Trait::<'static, 'static, 'static>::foo as bar2; | +++++++++ error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:105:11 + --> $DIR/generics-gen-args-errors.rs:103:11 | LL | reuse Trait::<'static, 'static>::foo as bar2; | ^^^^^ error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/generics-gen-args-errors.rs:105:11 + --> $DIR/generics-gen-args-errors.rs:103:11 | LL | reuse Trait::<'static, 'static>::foo as bar2; | ^^^^^ not allowed in type signatures error[E0107]: trait takes 3 lifetime arguments but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:109:11 + --> $DIR/generics-gen-args-errors.rs:107:11 | LL | reuse Trait::<1, 2, 3, 4, 5>::foo as bar3; | ^^^^^ expected 3 lifetime arguments | note: trait defined here, with 3 lifetime parameters: `'b`, `'c`, `'a` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ -- -- -- @@ -485,7 +479,7 @@ LL | reuse Trait::<'b, 'c, 'a, 1, 2, 3, 4, 5>::foo as bar3; | +++++++++++ error[E0107]: trait takes 2 generic arguments but 5 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:109:11 + --> $DIR/generics-gen-args-errors.rs:107:11 | LL | reuse Trait::<1, 2, 3, 4, 5>::foo as bar3; | ^^^^^ --------- help: remove the unnecessary generic arguments @@ -493,25 +487,25 @@ LL | reuse Trait::<1, 2, 3, 4, 5>::foo as bar3; | expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `T`, `N` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ - -------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:109:11 + --> $DIR/generics-gen-args-errors.rs:107:11 | LL | reuse Trait::<1, 2, 3, 4, 5>::foo as bar3; | ^^^^^ error[E0107]: trait takes 3 lifetime arguments but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:114:11 + --> $DIR/generics-gen-args-errors.rs:112:11 | LL | reuse Trait::<1, 2, true>::foo as bar4; | ^^^^^ expected 3 lifetime arguments | note: trait defined here, with 3 lifetime parameters: `'b`, `'c`, `'a` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ -- -- -- @@ -521,7 +515,7 @@ LL | reuse Trait::<'b, 'c, 'a, 1, 2, true>::foo as bar4; | +++++++++++ error[E0107]: trait takes 2 generic arguments but 3 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:114:11 + --> $DIR/generics-gen-args-errors.rs:112:11 | LL | reuse Trait::<1, 2, true>::foo as bar4; | ^^^^^ ------ help: remove the unnecessary generic argument @@ -529,19 +523,19 @@ LL | reuse Trait::<1, 2, true>::foo as bar4; | expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `T`, `N` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ - -------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:114:11 + --> $DIR/generics-gen-args-errors.rs:112:11 | LL | reuse Trait::<1, 2, true>::foo as bar4; | ^^^^^ error[E0107]: trait takes 3 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/generics-gen-args-errors.rs:119:11 + --> $DIR/generics-gen-args-errors.rs:117:11 | LL | reuse Trait::<'static>::foo as bar5; | ^^^^^ ------- supplied 1 lifetime argument @@ -549,7 +543,7 @@ LL | reuse Trait::<'static>::foo as bar5; | expected 3 lifetime arguments | note: trait defined here, with 3 lifetime parameters: `'b`, `'c`, `'a` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ -- -- -- @@ -559,19 +553,19 @@ LL | reuse Trait::<'static, 'static, 'static>::foo as bar5; | ++++++++++++++++++ error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:119:11 + --> $DIR/generics-gen-args-errors.rs:117:11 | LL | reuse Trait::<'static>::foo as bar5; | ^^^^^ error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/generics-gen-args-errors.rs:119:11 + --> $DIR/generics-gen-args-errors.rs:117:11 | LL | reuse Trait::<'static>::foo as bar5; | ^^^^^ not allowed in type signatures error[E0107]: trait takes 3 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/generics-gen-args-errors.rs:124:11 + --> $DIR/generics-gen-args-errors.rs:122:11 | LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | ^^^^^ - supplied 1 lifetime argument @@ -579,7 +573,7 @@ LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | expected 3 lifetime arguments | note: trait defined here, with 3 lifetime parameters: `'b`, `'c`, `'a` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ -- -- -- @@ -589,7 +583,7 @@ LL | reuse Trait::<1, 'static, 'static, 2, 'static, DDDD>::foo::<1, 2, 3, 4, | ++++++++++++++++++ error[E0107]: trait takes 2 generic arguments but 3 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:124:11 + --> $DIR/generics-gen-args-errors.rs:122:11 | LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | ^^^^^ --------------- help: remove the unnecessary generic argument @@ -597,25 +591,25 @@ LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `T`, `N` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ - -------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:124:11 + --> $DIR/generics-gen-args-errors.rs:122:11 | LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | ^^^^^ error[E0107]: method takes 1 lifetime argument but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:124:41 + --> $DIR/generics-gen-args-errors.rs:122:41 | LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | ^^^ expected 1 lifetime argument | note: method defined here, with 1 lifetime parameter: `'d` - --> $DIR/generics-gen-args-errors.rs:91:12 + --> $DIR/generics-gen-args-errors.rs:89:12 | LL | fn foo<'d: 'd, U, const M: bool>(self) {} | ^^^ -- @@ -625,7 +619,7 @@ LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<'d, 1, 2, 3, 4, 5, 6> as bar6 | +++ error[E0107]: method takes 2 generic arguments but 6 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:124:41 + --> $DIR/generics-gen-args-errors.rs:122:41 | LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | ^^^ ------------ help: remove the unnecessary generic arguments @@ -633,73 +627,73 @@ LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | expected 2 generic arguments | note: method defined here, with 2 generic parameters: `U`, `M` - --> $DIR/generics-gen-args-errors.rs:91:12 + --> $DIR/generics-gen-args-errors.rs:89:12 | LL | fn foo<'d: 'd, U, const M: bool>(self) {} | ^^^ - ------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:124:41 + --> $DIR/generics-gen-args-errors.rs:122:41 | LL | reuse Trait::<1, 2, 'static, DDDD>::foo::<1, 2, 3, 4, 5, 6> as bar6; | ^^^ -error[E0107]: trait takes 3 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/generics-gen-args-errors.rs:133:11 +error[E0107]: trait takes 3 lifetime arguments but 2 lifetime arguments were supplied + --> $DIR/generics-gen-args-errors.rs:131:11 | LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; - | ^^^^^ ----- supplied 1 lifetime argument + | ^^^^^ ----- ----- supplied 2 lifetime arguments | | | expected 3 lifetime arguments | note: trait defined here, with 3 lifetime parameters: `'b`, `'c`, `'a` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ -- -- -- -help: add missing lifetime arguments +help: add missing lifetime argument | -LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; - | ++++++++++++++++++ +LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; + | ++++ -error[E0107]: trait takes 2 generic arguments but 5 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:133:11 +error[E0107]: trait takes 2 generic arguments but 4 generic arguments were supplied + --> $DIR/generics-gen-args-errors.rs:131:11 | LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; - | ^^^^^ --- help: remove the unnecessary generic argument + | ^^^^^ ------------------------- help: remove the unnecessary generic arguments | | | expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `T`, `N` - --> $DIR/generics-gen-args-errors.rs:90:11 + --> $DIR/generics-gen-args-errors.rs:88:11 | LL | trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { | ^^^^^ - -------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:133:11 + --> $DIR/generics-gen-args-errors.rs:131:11 | LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; | ^^^^^ error[E0107]: method takes 1 lifetime argument but 0 lifetime arguments were supplied - --> $DIR/generics-gen-args-errors.rs:133:59 + --> $DIR/generics-gen-args-errors.rs:131:59 | LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; | ^^^ expected 1 lifetime argument | note: method defined here, with 1 lifetime parameter: `'d` - --> $DIR/generics-gen-args-errors.rs:91:12 + --> $DIR/generics-gen-args-errors.rs:89:12 | LL | fn foo<'d: 'd, U, const M: bool>(self) {} | ^^^ -- help: add missing lifetime argument | -LL | reuse Trait::::foo::<'d, 1, 2, 3, _, 6> as bar7; +LL | reuse Trait::::foo::<'a, 1, 2, 3, _, 6> as bar7; | +++ error[E0107]: method takes 2 generic arguments but 5 generic arguments were supplied - --> $DIR/generics-gen-args-errors.rs:133:59 + --> $DIR/generics-gen-args-errors.rs:131:59 | LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; | ^^^ --------- help: remove the unnecessary generic arguments @@ -707,13 +701,13 @@ LL | reuse Trait::::foo::<1, 2, 3, _, | expected 2 generic arguments | note: method defined here, with 2 generic parameters: `U`, `M` - --> $DIR/generics-gen-args-errors.rs:91:12 + --> $DIR/generics-gen-args-errors.rs:89:12 | LL | fn foo<'d: 'd, U, const M: bool>(self) {} | ^^^ - ------------- error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/generics-gen-args-errors.rs:133:59 + --> $DIR/generics-gen-args-errors.rs:131:59 | LL | reuse Trait::::foo::<1, 2, 3, _, 6> as bar7; | ^^^ @@ -789,12 +783,12 @@ LL | reuse foo:: as xd; | + + error[E0747]: constant provided when a type was expected - --> $DIR/generics-gen-args-errors.rs:83:17 + --> $DIR/generics-gen-args-errors.rs:81:17 | LL | reuse foo::<{}, {}, {}> as bar8; | ^^ -error: aborting due to 75 previous errors +error: aborting due to 74 previous errors Some errors have detailed explanations: E0107, E0121, E0261, E0401, E0423, E0425, E0747. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/delegation/generics/infer-defaults.rs b/tests/ui/delegation/generics/infer-defaults.rs new file mode 100644 index 0000000000000..c801eab6af083 --- /dev/null +++ b/tests/ui/delegation/generics/infer-defaults.rs @@ -0,0 +1,124 @@ +#![feature(fn_delegation)] + +mod free_to_trait { + trait Trait<'a, XX, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, A, B), slice: &[usize; N]) {} + } + + struct X; + impl Trait<'_, XX, Y, T, N> for X {} + + // When infer is specified for default parameter the generic param is generated. + reuse Trait::<'_, _, _, _, _>::foo as foo; + // When default params are omitted they are not generated but used in signature inheritance. + reuse Trait::<'_, _, _>::foo as bar; + + // Check with user specified args in child: + // When infer is specified for default parameter the generic param is generated. + reuse Trait::<'_, _, _, _, _>::foo::<(), _> as foo1; + // When default params are omitted they are not generated but used in signature inheritance. + reuse Trait::<'_, _, _>::foo::<_, ()> as bar1; + + // Check with explicit self type. + reuse >::foo as foo2; + reuse >::foo as bar2; + + reuse >::foo::<(), _> as foo3; + reuse >::foo::<_, ()> as bar3; + + fn check() { + foo::<'static, X, (), (), (), 1, (), ()>(&X, ((), (), ()), &[1]); + bar::<'static, X, (), (), (), ()>(&X, ((), (), ()), &[1, 2]); + + foo1::<'static, X, (), (), (), 2, ()>(&X, ((), (), ()), &[1, 2]); + bar1::<'static, X, (), (), ()>(&X, ((), (), ()), &[1, 2]); + + foo2::<'static, (), (), (), 3, (), ()>(&X, ((), (), ()), &[1, 2, 3]); + bar2::<'static, (), (), (), ()>(&X, ((), (), ()), &[1, 2]); + + foo3::<'static, (), (), (), 4, ()>(&X, ((), (), ()), &[1, 2, 3, 4]); + bar3::<'static, (), (), ()>(&X, ((), (), ()), &[1, 2]); + } +} + +mod trait_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + } + + trait Trait2<'a, X, Y>: Trait<'a, X, Y> { + // Default params are generated as usual generics as infers are specified. + reuse Trait::<'a, X, Y, _, _>::foo; + //~^ ERROR: the trait bound `Self: trait_to_trait::Trait<'a, X, Y, T, N>` is not satisfied + + // Default params are not generated, as they are not specified. + reuse Trait::<'a, X, Y>::foo as bar; + } + + impl Trait<'static, (), ()> for () {} + impl Trait2<'static, (), ()> for () {} + + fn check() { + Trait2::<'static, (), ()>::foo::<(), 1>(&(), ((), (), ()), &[1]); + Trait2::<'static, (), ()>::bar(&(), ((), (), ()), &[1, 2]); + } +} + +mod trait_impl_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + } + + struct S; + impl Trait<'_, X, Y> for S {} + + struct W(S); + impl Trait<'_, X, Y> for W { + // Generics of both methods match generics of their signature + // functions in `Trait` declaration, no matter specified infers. + reuse Trait::<'static, X, Y>::foo { self.0 } + reuse Trait::<'static, X, Y, _, _>::foo as bar { self.0 } + } + + fn check() { + W(S).foo(((), (), ()), &[1, 2]); + W(S).foo::<1, 2, 3>(((), (), ()), &[1, 2]); + //~^ ERROR: method takes 0 generic arguments but 3 generic arguments were supplied + + W(S).bar(((), (), ()), &[1]); + //~^ ERROR: mismatched types + W(S).bar::<((), ()), 0>(((), (), ()), &[1, 2]); + //~^ ERROR: method takes 0 generic arguments but 2 generic arguments were supplied + + W(S).bar(((), (), ()), &[1, 2]); + } +} + +mod inherent_impl_to_trait { + trait Trait<'a, X, Y, T = (), const N: usize = 2> { + fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + } + + struct S(T); + + impl> S { + // Default params are not generated, as they are not specified. + reuse Trait::<'static, (), ()>::foo { self.0 } + + // Default params are generated as usual generics as infers are specified. + reuse Trait::<'static, (), (), _, _>::foo as bar { self.0 } + //~^ ERROR: the trait bound `T: inherent_impl_to_trait::Trait<'static, (), (), T, N>` is not satisfied + } + + impl Trait<'static, (), ()> for () {} + + fn check() { + S(()).foo(((), (), ()), &[1, 2]); + S(()).bar(((), (), ()), &[1, 2]); + S(()).bar::((1, 2, 3), &[1, 2, 3, 4]); + } +} + +fn main() {} diff --git a/tests/ui/delegation/generics/infer-defaults.stderr b/tests/ui/delegation/generics/infer-defaults.stderr new file mode 100644 index 0000000000000..4d9695fc823e7 --- /dev/null +++ b/tests/ui/delegation/generics/infer-defaults.stderr @@ -0,0 +1,70 @@ +error[E0277]: the trait bound `Self: trait_to_trait::Trait<'a, X, Y, T, N>` is not satisfied + --> $DIR/infer-defaults.rs:52:40 + | +LL | reuse Trait::<'a, X, Y, _, _>::foo; + | ^^^ the trait `trait_to_trait::Trait<'a, X, Y, T, N>` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | reuse Trait::<'a, X, Y, _, _>::foo Self: trait_to_trait::Trait<'a, X, Y, T, N>; + | +++++++++++++++++++++++++++++++++++++++++++ + +error[E0107]: method takes 0 generic arguments but 3 generic arguments were supplied + --> $DIR/infer-defaults.rs:87:14 + | +LL | W(S).foo::<1, 2, 3>(((), (), ()), &[1, 2]); + | ^^^----------- help: remove the unnecessary generics + | | + | expected 0 generic arguments + | +note: method defined here, with 0 generic parameters + --> $DIR/infer-defaults.rs:70:12 + | +LL | fn foo(&self, t: (T, T, T), slice: &[usize; N]) {} + | ^^^ + +error[E0308]: mismatched types + --> $DIR/infer-defaults.rs:90:32 + | +LL | W(S).bar(((), (), ()), &[1]); + | --- ^^^^ expected an array with a size of 2, found one with a size of 1 + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/infer-defaults.rs:71:12 + | +LL | fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + | ^^^ ------------------ + +error[E0107]: method takes 0 generic arguments but 2 generic arguments were supplied + --> $DIR/infer-defaults.rs:92:14 + | +LL | W(S).bar::<((), ()), 0>(((), (), ()), &[1, 2]); + | ^^^--------------- help: remove the unnecessary generics + | | + | expected 0 generic arguments + | +note: method defined here, with 0 generic parameters + --> $DIR/infer-defaults.rs:71:12 + | +LL | fn bar(&self, t: (T, T, T), slice: &[usize; N]) {} + | ^^^ + +error[E0277]: the trait bound `T: inherent_impl_to_trait::Trait<'static, (), (), T, N>` is not satisfied + --> $DIR/infer-defaults.rs:111:60 + | +LL | reuse Trait::<'static, (), (), _, _>::foo as bar { self.0 } + | --- ^^^^^^ the trait `inherent_impl_to_trait::Trait<'static, (), (), T, N>` is not implemented for `T` + | | + | required by a bound introduced by this call + | +help: consider restricting type parameter `T` with trait `Trait` + | +LL | reuse Trait::<'static, (), (), _, _>::foo inherent_impl_to_trait::Trait<'static, (), (), T, N> as bar { self.0 } + | ++++++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0107, E0277, E0308. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/delegation/generics/infers.rs b/tests/ui/delegation/generics/infers.rs new file mode 100644 index 0000000000000..d9205f510a7b4 --- /dev/null +++ b/tests/ui/delegation/generics/infers.rs @@ -0,0 +1,356 @@ +//@ compile-flags: -Z deduplicate-diagnostics=yes + +#![feature(fn_delegation)] + +// Some interesting cases: +mod selected_tests { + mod different_infers { + fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + + // Should differentiate between lifetime and types/consts infers. + reuse foo::<_, '_, '_, '_> as bar; + //~^ ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + } + + mod self_type { + trait Trait<'a, X> { + fn method<'b: 'b, const M: usize>(&self) {} + fn r#static<'b, Y, const B: bool>() {} + } + + impl<'a, X> Trait<'a, X> for () {} + + reuse Trait::<'_, _>::method::<'_, _> as foo; + + reuse <_ as Trait<'_, _>>::method::<'_, _> as foo1; + reuse <() as Trait<'_, _>>::method::<'_, _> as foo2; + + reuse <_ as Trait<'_, _>>::r#static::<_, _> as foo3; + reuse <() as Trait<'_, _>>::r#static::<_, _> as foo4; + + reuse Trait::<'_, _>::r#static::<_, _> as foo5; + } + + mod late_bound_lifetimes { + fn foo<'a, 'b, 'c: 'c, 'd>(_: &'a &'b &'c &'d ()) {} + + // 'c corresponds to infer. + reuse foo::<'_> as foo1; + + // Only 'c is generated in desugaring, second infer remains just infer in call path. + reuse foo::<'_, '_> as foo2; + //~^ ERROR: function takes 1 lifetime argument but 2 lifetime arguments were supplied + + reuse foo as foo3; + reuse foo::<'static> as foo4; + } + + mod non_angle_bracketed_args { + fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + + reuse foo::('_, _, _, _) as bar; + //~^ ERROR: lifetimes must be followed by `+` to form a trait object type + //~| ERROR: at least one trait is required for an object type + //~| ERROR: parenthesized type parameters may only be used with a `Fn` trait [E0214] + //~| ERROR: function takes 1 lifetime argument but 0 lifetime arguments were supplied [E0107] + //~| ERROR: function takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + } +} + +// All other stuff: +mod legacy_tests { + trait Trait { + fn foo(&self, _: U, _: T) {} + } + + impl Trait for u8 {} + + reuse Trait::<_>::foo:: as generic_arguments1; + reuse >::foo as generic_arguments2; + reuse <_ as Trait<_>>::foo as generic_arguments3; +} + +mod free_to_free { + fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + + reuse foo::<> as foo1; + reuse foo::<'_, _, _, _> as foo2; + reuse foo::<'static, String, _, _> as foo3; + reuse foo::<'_, _, 123, _> as foo4; + + reuse foo::<'_, '_, '_, _, _, _,> as foo5; + //~^ ERROR: function takes 3 generic arguments but 5 generic arguments were supplied + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + //~^ ERROR: function takes 3 generic arguments but 6 generic arguments were supplied [E0107] + //~| ERROR: function takes 1 lifetime argument but 3 lifetime arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse foo::<_, '_, _, _> as foo7; + //~^ ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse foo::<'_, '_, '_, '_> as foo8; + //~^ ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse foo::<_> as foo9; + //~^ ERROR: function takes 3 generic arguments but 0 generic arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + + reuse foo::, _, _, ()> as foo10; + //~^ ERROR: function takes 1 lifetime argument but 0 lifetime arguments were supplied [E0107] + //~| ERROR: function takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: struct takes 0 lifetime arguments but 1 lifetime argument was supplied [E0107] + //~| ERROR: struct takes at least 1 generic argument but 0 generic arguments were supplied [E0107] + + reuse foo::, _, _, ()> as foo11; + //~^ ERROR: function takes 1 lifetime argument but 0 lifetime arguments were supplied [E0107] + //~| ERROR: function takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + + reuse foo::<'____, ___, _, ___> as foo12; + //~^ ERROR: use of undeclared lifetime name `'____` [E0261] + //~| ERROR: cannot find type `___` in this scope [E0425] + //~| ERROR: cannot find type `___` in this scope [E0425] + + reuse foo::<'_, Vec<_>, Vec>, _> as foo13; + //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + //~| ERROR: type provided when a constant was expected [E0747] + + reuse foo::<'_, unresolved_, _, _> as foo14; + //~^ ERROR: cannot find type `unresolved_` in this scope + + reuse foo::<_, _, _> as foo15; + //~^ ERROR: function takes 3 generic arguments but 2 generic arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ +} + +mod free_to_trait { + pub trait Trait<'a, 'b, X, const C: usize, Y> { + fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + } + + struct X; + impl<'a, 'b, Some, Params, X, const C: usize, Y> Trait<'a, 'b, X, C, Y> for X {} + //~^ ERROR: the type parameter `Some` is not constrained by the impl trait, self type, or predicates [E0207] + //~| ERROR: the type parameter `Params` is not constrained by the impl trait, self type, or predicates [E0207] + + mod child_only { + use super::*; + + reuse Trait::foo::<> as foo1; + reuse Trait::foo::<'_, _, _, _> as foo2; + reuse Trait::foo::<'static, String, _, _> as foo3; + reuse Trait::foo::<'_, _, 123, _> as foo4; + + reuse Trait::foo::<'_, '_, '_, _, _, _,> as foo5; + //~^ ERROR: method takes 3 generic arguments but 5 generic arguments were supplied + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + //~^ ERROR: method takes 3 generic arguments but 6 generic arguments were supplied [E0107] + //~| ERROR: method takes 1 lifetime argument but 3 lifetime arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::foo::<_, '_, _, _> as foo7; + //~^ ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::foo::<'_, '_, '_, '_> as foo8; + //~^ ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::foo::<_> as foo9; + //~^ ERROR: method takes 3 generic arguments but 0 generic arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + + reuse Trait::foo::, _, _, ()> as foo10; + //~^ ERROR: method takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: struct takes 0 lifetime arguments but 1 lifetime argument was supplied [E0107] + //~| ERROR: struct takes at least 1 generic argument but 0 generic arguments were supplied [E0107] + //~| ERROR: method takes 1 lifetime argument but 0 lifetime arguments were supplied + + reuse Trait::foo::, _, _, ()> as foo11; + //~^ ERROR: method takes 1 lifetime argument but 0 lifetime arguments were supplied [E0107] + //~| ERROR: method takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + + reuse Trait::foo::<'____, ___, _, ___> as foo12; + //~^ ERROR: use of undeclared lifetime name `'____` [E0261] + //~| ERROR: cannot find type `___` in this scope [E0425] + //~| ERROR: cannot find type `___` in this scope [E0425] + + reuse Trait::foo::<'_, Vec<_>, Vec>, _> as foo13; + //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + //~| ERROR: type provided when a constant was expected [E0747] + + reuse Trait::foo::<'_, unresolved_, _, _> as foo14; + //~^ ERROR: cannot find type `unresolved_` in this scope + + reuse Trait::foo::<_, _, _> as foo15; + //~^ ERROR: method takes 3 generic arguments but 2 generic arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + } + + mod parent_only { + use super::*; + + reuse Trait::<'_, 'static, _, _, _>::foo as foo1; + reuse Trait::<'_, '_, _, _, _>::foo as foo2; + + reuse Trait::<'_, (), _, '_, _>::foo as foo3; + //~^ ERROR: trait takes 2 lifetime arguments but 1 lifetime argument was supplied [E0107] + //~| ERROR: trait takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::<>::foo as foo4; + + reuse Trait::<_, _>::foo as foo5; + //~^ ERROR: trait takes 3 generic arguments but 0 generic arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected '_, found: _ + + reuse Trait::<'_, '_>::foo as foo6; + //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions + + reuse Trait::<'_, '_, Vec<_>, 123, Vec>>::foo as foo7; + //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + //~| ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + + reuse Trait::<'static, 'static, (), 123, ()>::foo as foo8; + reuse Trait::<'static, 'static, _, _, _>::foo as foo9; + + reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo as foo10; + //~^ ERROR: trait takes 3 generic arguments but 7 generic arguments were supplied + + reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_, '_>::foo as foo11; + //~^ ERROR: trait takes 2 lifetime arguments but 6 lifetime arguments were supplied + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::<'static, 'static, _>::foo as foo12; + //~^ ERROR: trait takes 3 generic arguments but 1 generic argument was supplied + } + + mod parent_and_child_random { + use super::*; + + reuse Trait::<'_, 'static, _, _, _>::foo::<> as foo1; + reuse Trait::<'_, '_, _, _, _>::foo::<'_, _, _, _> as foo2; + + reuse Trait::<'_, (), _, '_, _>::foo::<'static, String, _, _> as foo3; + //~^ ERROR: trait takes 2 lifetime arguments but 1 lifetime argument was supplied [E0107] + //~| ERROR: trait takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::<>::foo::<'_, _, 123, _> as foo4; + + reuse Trait::<_, _>::foo::<'_, '_, '_, _, _, _,> as foo5; + //~^ ERROR: trait takes 3 generic arguments but 0 generic arguments were supplied + //~| ERROR: method takes 3 generic arguments but 5 generic arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::<'_, '_>::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + //~| ERROR: method takes 3 generic arguments but 6 generic arguments were supplied [E0107] + //~| ERROR: method takes 1 lifetime argument but 3 lifetime arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::<'_, '_, Vec<_>, 123, Vec>>::foo::<_, '_, _, _> as foo7; + //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + //~| ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + //~| ERROR: wrong infer used: expected '_, found: _ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::<'static, 'static, (), 123, ()>::foo::<'_, '_, '_, '_> as foo8; + //~^ ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::<'static, 'static, _, _, _>::foo::<_> as foo9; + //~^ ERROR: method takes 3 generic arguments but 0 generic arguments were supplied + //~| ERROR: wrong infer used: expected '_, found: _ + + reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::, _, _, ()> as foo10; + //~^ ERROR: trait takes 3 generic arguments but 7 generic arguments were supplied [E0107] + //~| ERROR: method takes 1 lifetime argument but 0 lifetime arguments were supplied [E0107] + //~| ERROR: method takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: struct takes 0 lifetime arguments but 1 lifetime argument was supplied [E0107] + //~| ERROR: struct takes at least 1 generic argument but 0 generic arguments were supplied [E0107] + + reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + //~^ ERROR: trait takes 2 lifetime arguments but 5 lifetime arguments were supplied [E0107] + //~| ERROR: method takes 1 lifetime argument but 0 lifetime arguments were supplied [E0107] + //~| ERROR: method takes 3 generic arguments but 4 generic arguments were supplied [E0107] + //~| ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + //~| ERROR: the placeholder `_` is not allowed within types on item signatures for functions [E0121] + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + //~| ERROR: wrong infer used: expected _, found: '_ + + reuse Trait::<'static, 'static, _>::foo::<'____, ___, _, ___> as foo12; + //~^ ERROR: cannot find type `___` in this scope + //~| ERROR: cannot find type `___` in this scope + //~| ERROR: use of undeclared lifetime name `'____` + //~| ERROR: trait takes 3 generic arguments but 1 generic argument was supplied + } +} + +mod trait_impl_to_free { + pub trait Trait<'a, 'b, X, const C: usize, Y> { + fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self) {} + } + + struct S; + impl<'a, 'b, X, const C: usize, Y> Trait<'a, 'b, X, C, Y> for S {} + + mod to_reuse { + pub fn foo(_: ()) {} + } + + struct F1(S); + impl<'a, 'b, X, const C: usize, Y> Trait<'a, 'b, X, C, Y> for F1 { + reuse to_reuse::foo::<_, _, _> { self.0 } + //~^ ERROR: mismatched types + } + + struct F2(S); + impl<'a, 'b, X, const C: usize, Y> Trait<'a, 'b, X, C, Y> for F2 { + reuse to_reuse::foo { self.0 } + //~^ ERROR: mismatched types + //~| ERROR: function takes 0 lifetime arguments but 1 lifetime argument was supplied + } + + struct F3(S); + impl<'a, 'b, X, const C: usize, Y> Trait<'a, 'b, X, C, Y> for F3 { + reuse to_reuse::foo::<(), 123, ()> { self.0 } + //~^ ERROR: mismatched types + } +} + +fn main() {} diff --git a/tests/ui/delegation/generics/infers.stderr b/tests/ui/delegation/generics/infers.stderr new file mode 100644 index 0000000000000..1ab3ef232832e --- /dev/null +++ b/tests/ui/delegation/generics/infers.stderr @@ -0,0 +1,1296 @@ +error: lifetimes must be followed by `+` to form a trait object type + --> $DIR/infers.rs:54:21 + | +LL | reuse foo::('_, _, _, _) as bar; + | ^^ + | +help: consider adding a trait bound after the potential lifetime bound + | +LL | reuse foo::('_ + /* Trait */, _, _, _) as bar; + | +++++++++++++ + +error[E0261]: use of undeclared lifetime name `'____` + --> $DIR/infers.rs:123:17 + | +LL | reuse foo::<'____, ___, _, ___> as foo12; + | ^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'____` here + | +LL | reuse foo'____, ::<'____, ___, _, ___> as foo12; + | ++++++ + +error[E0261]: use of undeclared lifetime name `'____` + --> $DIR/infers.rs:195:28 + | +LL | reuse Trait::foo::<'____, ___, _, ___> as foo12; + | ^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'____` here + | +LL | reuse Trait::foo'____, ::<'____, ___, _, ___> as foo12; + | ++++++ + +error[E0261]: use of undeclared lifetime name `'____` + --> $DIR/infers.rs:316:51 + | +LL | reuse Trait::<'static, 'static, _>::foo::<'____, ___, _, ___> as foo12; + | ^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'____` here + | +LL | reuse Trait::<'static, 'static, _>::foo'____, ::<'____, ___, _, ___> as foo12; + | ++++++ + +error[E0425]: cannot find type `___` in this scope + --> $DIR/infers.rs:123:24 + | +LL | reuse foo::<'____, ___, _, ___> as foo12; + | ^^^ not found in this scope + +error[E0425]: cannot find type `___` in this scope + --> $DIR/infers.rs:123:32 + | +LL | reuse foo::<'____, ___, _, ___> as foo12; + | ^^^ not found in this scope + +error[E0425]: cannot find type `unresolved_` in this scope + --> $DIR/infers.rs:132:21 + | +LL | reuse foo::<'_, unresolved_, _, _> as foo14; + | ^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find type `___` in this scope + --> $DIR/infers.rs:195:35 + | +LL | reuse Trait::foo::<'____, ___, _, ___> as foo12; + | ^^^ not found in this scope + +error[E0425]: cannot find type `___` in this scope + --> $DIR/infers.rs:195:43 + | +LL | reuse Trait::foo::<'____, ___, _, ___> as foo12; + | ^^^ not found in this scope + +error[E0425]: cannot find type `unresolved_` in this scope + --> $DIR/infers.rs:204:32 + | +LL | reuse Trait::foo::<'_, unresolved_, _, _> as foo14; + | ^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find type `___` in this scope + --> $DIR/infers.rs:316:58 + | +LL | reuse Trait::<'static, 'static, _>::foo::<'____, ___, _, ___> as foo12; + | ^^^ not found in this scope + +error[E0425]: cannot find type `___` in this scope + --> $DIR/infers.rs:316:66 + | +LL | reuse Trait::<'static, 'static, _>::foo::<'____, ___, _, ___> as foo12; + | ^^^ not found in this scope + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:11:21 + | +LL | reuse foo::<_, '_, '_, '_> as bar; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:11:24 + | +LL | reuse foo::<_, '_, '_, '_> as bar; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:11:28 + | +LL | reuse foo::<_, '_, '_, '_> as bar; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:11:32 + | +LL | reuse foo::<_, '_, '_, '_> as bar; + | ^^ + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/infers.rs:54:15 + | +LL | reuse foo::('_, _, _, _) as bar; + | ^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses + | +help: use angle brackets instead + | +LL - reuse foo::('_, _, _, _) as bar; +LL + reuse foo::<'_, _, _, _> as bar; + | + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:86:21 + | +LL | reuse foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:86:25 + | +LL | reuse foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:91:17 + | +LL | reuse foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:91:26 + | +LL | reuse foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:97:17 + | +LL | reuse foo::<_, '_, _, _> as foo7; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:97:20 + | +LL | reuse foo::<_, '_, _, _> as foo7; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:101:21 + | +LL | reuse foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:101:25 + | +LL | reuse foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:101:29 + | +LL | reuse foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:106:17 + | +LL | reuse foo::<_> as foo9; + | ^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:135:17 + | +LL | reuse foo::<_, _, _> as foo15; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:158:32 + | +LL | reuse Trait::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:158:36 + | +LL | reuse Trait::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:163:28 + | +LL | reuse Trait::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:163:37 + | +LL | reuse Trait::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:169:28 + | +LL | reuse Trait::foo::<_, '_, _, _> as foo7; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:169:31 + | +LL | reuse Trait::foo::<_, '_, _, _> as foo7; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:173:32 + | +LL | reuse Trait::foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:173:36 + | +LL | reuse Trait::foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:173:40 + | +LL | reuse Trait::foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:178:28 + | +LL | reuse Trait::foo::<_> as foo9; + | ^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:207:28 + | +LL | reuse Trait::foo::<_, _, _> as foo15; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:218:34 + | +LL | reuse Trait::<'_, (), _, '_, _>::foo as foo3; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:226:23 + | +LL | reuse Trait::<_, _>::foo as foo5; + | ^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:226:26 + | +LL | reuse Trait::<_, _>::foo as foo5; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:244:41 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_, '_>::foo as foo11; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:244:44 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_, '_>::foo as foo11; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:244:48 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_, '_>::foo as foo11; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:260:34 + | +LL | reuse Trait::<'_, (), _, '_, _>::foo::<'static, String, _, _> as foo3; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:268:23 + | +LL | reuse Trait::<_, _>::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:268:26 + | +LL | reuse Trait::<_, _>::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:268:40 + | +LL | reuse Trait::<_, _>::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:268:44 + | +LL | reuse Trait::<_, _>::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:276:38 + | +LL | reuse Trait::<'_, '_>::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:276:47 + | +LL | reuse Trait::<'_, '_>::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:283:64 + | +LL | reuse Trait::<'_, '_, Vec<_>, 123, Vec>>::foo::<_, '_, _, _> as foo7; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:283:67 + | +LL | reuse Trait::<'_, '_, Vec<_>, 123, Vec>>::foo::<_, '_, _, _> as foo7; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:289:65 + | +LL | reuse Trait::<'static, 'static, (), 123, ()>::foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:289:69 + | +LL | reuse Trait::<'static, 'static, (), 123, ()>::foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:289:73 + | +LL | reuse Trait::<'static, 'static, (), 123, ()>::foo::<'_, '_, '_, '_> as foo8; + | ^^ + +error: wrong infer used: expected '_, found: _ + --> $DIR/infers.rs:294:57 + | +LL | reuse Trait::<'static, 'static, _, _, _>::foo::<_> as foo9; + | ^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:306:41 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:306:44 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + | ^^ + +error: wrong infer used: expected _, found: '_ + --> $DIR/infers.rs:306:48 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + | ^^ + +error[E0107]: function takes 1 lifetime argument but 2 lifetime arguments were supplied + --> $DIR/infers.rs:44:15 + | +LL | reuse foo::<'_, '_> as foo2; + | ^^^--------- help: remove the lifetime argument + | | + | expected 1 lifetime argument + | +note: function defined here, with 1 lifetime parameter: `'c` + --> $DIR/infers.rs:38:12 + | +LL | fn foo<'a, 'b, 'c: 'c, 'd>(_: &'a &'b &'c &'d ()) {} + | ^^^ -- + +error[E0107]: function takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/infers.rs:54:15 + | +LL | reuse foo::('_, _, _, _) as bar; + | ^^^ expected 1 lifetime argument + | +note: function defined here, with 1 lifetime parameter: `'b` + --> $DIR/infers.rs:52:12 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ -- +help: add missing lifetime argument + | +LL | reuse foo::('b, '_, _, _, _) as bar; + | +++ + +error[E0107]: function takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:54:15 + | +LL | reuse foo::('_, _, _, _) as bar; + | ^^^ --- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: function defined here, with 3 generic parameters: `X`, `M`, `Y` + --> $DIR/infers.rs:52:12 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ - -------------- - + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:54:15 + | +LL | reuse foo::('_, _, _, _) as bar; + | ^^^ + +error[E0224]: at least one trait is required for an object type + --> $DIR/infers.rs:54:21 + | +LL | reuse foo::('_, _, _, _) as bar; + | ^^ + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:54:25 + | +LL | reuse foo::('_, _, _, _) as bar; + | ^ not allowed in type signatures + +error[E0107]: function takes 3 generic arguments but 5 generic arguments were supplied + --> $DIR/infers.rs:86:11 + | +LL | reuse foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^^---------------------- help: remove the unnecessary generic arguments + | | + | expected 3 generic arguments + | +note: function defined here, with 3 generic parameters: `X`, `M`, `Y` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ - -------------- - + +error[E0107]: function takes 1 lifetime argument but 3 lifetime arguments were supplied + --> $DIR/infers.rs:91:11 + | +LL | reuse foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^^---------------------- help: remove the lifetime arguments + | | + | expected 1 lifetime argument + | +note: function defined here, with 1 lifetime parameter: `'b` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ -- + +error[E0107]: function takes 3 generic arguments but 6 generic arguments were supplied + --> $DIR/infers.rs:91:11 + | +LL | reuse foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^^------------------------------- help: remove the unnecessary generic arguments + | | + | expected 3 generic arguments + | +note: function defined here, with 3 generic parameters: `X`, `M`, `Y` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ - -------------- - + +error[E0107]: function takes 3 generic arguments but 0 generic arguments were supplied + --> $DIR/infers.rs:106:11 + | +LL | reuse foo::<_> as foo9; + | ^^^ expected 3 generic arguments + | +note: function defined here, with 3 generic parameters: `X`, `M`, `Y` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ - -------------- - +help: add missing generic arguments + | +LL | reuse fooX, M, Y::<_> as foo9; + | +++++++ + +error[E0107]: function takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/infers.rs:110:11 + | +LL | reuse foo::, _, _, ()> as foo10; + | ^^^ expected 1 lifetime argument + | +note: function defined here, with 1 lifetime parameter: `'b` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ -- +help: add missing lifetime argument + | +LL | reuse foo::<'b, Vec<'_>, _, _, ()> as foo10; + | +++ + +error[E0107]: function takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:110:11 + | +LL | reuse foo::, _, _, ()> as foo10; + | ^^^-------------------- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: function defined here, with 3 generic parameters: `X`, `M`, `Y` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ - -------------- - + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:110:11 + | +LL | reuse foo::, _, _, ()> as foo10; + | ^^^ + +error[E0107]: struct takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/infers.rs:110:17 + | +LL | reuse foo::, _, _, ()> as foo10; + | ^^^---- help: remove the unnecessary generics + | | + | expected 0 lifetime arguments + +error[E0107]: struct takes at least 1 generic argument but 0 generic arguments were supplied + --> $DIR/infers.rs:110:17 + | +LL | reuse foo::, _, _, ()> as foo10; + | ^^^ expected at least 1 generic argument + | +help: add missing generic argument + | +LL | reuse foo::, _, _, ()> as foo10; + | +++ + +error[E0107]: function takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/infers.rs:117:11 + | +LL | reuse foo::, _, _, ()> as foo11; + | ^^^ expected 1 lifetime argument + | +note: function defined here, with 1 lifetime parameter: `'b` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ -- +help: add missing lifetime argument + | +LL | reuse foo::<'b, Vec<_>, _, _, ()> as foo11; + | +++ + +error[E0107]: function takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:117:11 + | +LL | reuse foo::, _, _, ()> as foo11; + | ^^^------------------- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: function defined here, with 3 generic parameters: `X`, `M`, `Y` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ - -------------- - + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:117:11 + | +LL | reuse foo::, _, _, ()> as foo11; + | ^^^ + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:117:21 + | +LL | reuse foo::, _, _, ()> as foo11; + | ^ not allowed in type signatures + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:128:25 + | +LL | reuse foo::<'_, Vec<_>, Vec>, _> as foo13; + | ^ not allowed in type signatures + +error[E0747]: type provided when a constant was expected + --> $DIR/infers.rs:128:29 + | +LL | reuse foo::<'_, Vec<_>, Vec>, _> as foo13; + | ^^^^^^^^^^^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | reuse foo::<'_, Vec<_>, { Vec> }, _> as foo13; + | + + + +error[E0107]: function takes 3 generic arguments but 2 generic arguments were supplied + --> $DIR/infers.rs:135:11 + | +LL | reuse foo::<_, _, _> as foo15; + | ^^^ + | | + | expected 3 generic arguments + | supplied 2 generic arguments + | +note: function defined here, with 3 generic parameters: `X`, `M`, `Y` + --> $DIR/infers.rs:79:8 + | +LL | fn foo<'a, 'b: 'b, 'c, X, const M: usize, Y>(_: &'a &'b &'c ()) {} + | ^^^ - -------------- - +help: add missing generic argument + | +LL | reuse fooY::<_, _, _> as foo15; + | + + +error[E0207]: the type parameter `Some` is not constrained by the impl trait, self type, or predicates + --> $DIR/infers.rs:146:18 + | +LL | impl<'a, 'b, Some, Params, X, const C: usize, Y> Trait<'a, 'b, X, C, Y> for X {} + | ^^^^ unconstrained type parameter + +error[E0207]: the type parameter `Params` is not constrained by the impl trait, self type, or predicates + --> $DIR/infers.rs:146:24 + | +LL | impl<'a, 'b, Some, Params, X, const C: usize, Y> Trait<'a, 'b, X, C, Y> for X {} + | ^^^^^^ unconstrained type parameter + +error[E0107]: method takes 3 generic arguments but 5 generic arguments were supplied + --> $DIR/infers.rs:158:22 + | +LL | reuse Trait::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^^---------------------- help: remove the unnecessary generic arguments + | | + | expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- + +error[E0107]: method takes 1 lifetime argument but 3 lifetime arguments were supplied + --> $DIR/infers.rs:163:22 + | +LL | reuse Trait::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^^---------------------- help: remove the lifetime arguments + | | + | expected 1 lifetime argument + | +note: method defined here, with 1 lifetime parameter: `'bb` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ --- + +error[E0107]: method takes 3 generic arguments but 6 generic arguments were supplied + --> $DIR/infers.rs:163:22 + | +LL | reuse Trait::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^^------------------------------- help: remove the unnecessary generic arguments + | | + | expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- + +error[E0107]: method takes 3 generic arguments but 0 generic arguments were supplied + --> $DIR/infers.rs:178:22 + | +LL | reuse Trait::foo::<_> as foo9; + | ^^^ expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- +help: add missing generic arguments + | +LL | reuse Trait::fooXX, M, YY::<_> as foo9; + | +++++++++ + +error[E0107]: method takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/infers.rs:182:22 + | +LL | reuse Trait::foo::, _, _, ()> as foo10; + | ^^^ expected 1 lifetime argument + | +note: method defined here, with 1 lifetime parameter: `'bb` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ --- +help: add missing lifetime argument + | +LL | reuse Trait::foo::<'a, Vec<'_>, _, _, ()> as foo10; + | +++ + +error[E0107]: method takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:182:22 + | +LL | reuse Trait::foo::, _, _, ()> as foo10; + | ^^^-------------------- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:182:22 + | +LL | reuse Trait::foo::, _, _, ()> as foo10; + | ^^^ + +error[E0107]: struct takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/infers.rs:182:28 + | +LL | reuse Trait::foo::, _, _, ()> as foo10; + | ^^^---- help: remove the unnecessary generics + | | + | expected 0 lifetime arguments + +error[E0107]: struct takes at least 1 generic argument but 0 generic arguments were supplied + --> $DIR/infers.rs:182:28 + | +LL | reuse Trait::foo::, _, _, ()> as foo10; + | ^^^ expected at least 1 generic argument + | +help: add missing generic argument + | +LL | reuse Trait::foo::, _, _, ()> as foo10; + | +++ + +error[E0107]: method takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/infers.rs:189:22 + | +LL | reuse Trait::foo::, _, _, ()> as foo11; + | ^^^ expected 1 lifetime argument + | +note: method defined here, with 1 lifetime parameter: `'bb` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ --- +help: add missing lifetime argument + | +LL | reuse Trait::foo::<'a, Vec<_>, _, _, ()> as foo11; + | +++ + +error[E0107]: method takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:189:22 + | +LL | reuse Trait::foo::, _, _, ()> as foo11; + | ^^^------------------- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:189:22 + | +LL | reuse Trait::foo::, _, _, ()> as foo11; + | ^^^ + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:189:32 + | +LL | reuse Trait::foo::, _, _, ()> as foo11; + | ^ not allowed in type signatures + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:200:36 + | +LL | reuse Trait::foo::<'_, Vec<_>, Vec>, _> as foo13; + | ^ not allowed in type signatures + +error[E0747]: type provided when a constant was expected + --> $DIR/infers.rs:200:40 + | +LL | reuse Trait::foo::<'_, Vec<_>, Vec>, _> as foo13; + | ^^^^^^^^^^^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | reuse Trait::foo::<'_, Vec<_>, { Vec> }, _> as foo13; + | + + + +error[E0107]: method takes 3 generic arguments but 2 generic arguments were supplied + --> $DIR/infers.rs:207:22 + | +LL | reuse Trait::foo::<_, _, _> as foo15; + | ^^^ + | | + | expected 3 generic arguments + | supplied 2 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- +help: add missing generic argument + | +LL | reuse Trait::fooYY::<_, _, _> as foo15; + | ++ + +error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/infers.rs:218:15 + | +LL | reuse Trait::<'_, (), _, '_, _>::foo as foo3; + | ^^^^^ --- supplied 1 lifetime argument + | | + | expected 2 lifetime arguments + | +note: trait defined here, with 2 lifetime parameters: `'a`, `'b` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ -- -- +help: add missing lifetime argument + | +LL | reuse Trait::<'_, (), _, '_, _>::foo, 'a as foo3; + | ++++ + +error[E0107]: trait takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:218:15 + | +LL | reuse Trait::<'_, (), _, '_, _>::foo as foo3; + | ^^^^^ --- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: trait defined here, with 3 generic parameters: `X`, `C`, `Y` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ - -------------- - + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:218:15 + | +LL | reuse Trait::<'_, (), _, '_, _>::foo as foo3; + | ^^^^^ + +error[E0107]: trait takes 3 generic arguments but 0 generic arguments were supplied + --> $DIR/infers.rs:226:15 + | +LL | reuse Trait::<_, _>::foo as foo5; + | ^^^^^ expected 3 generic arguments + | +note: trait defined here, with 3 generic parameters: `X`, `C`, `Y` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ - -------------- - +help: add missing generic arguments + | +LL | reuse Trait::<_, _>::foo, X, C, Y as foo5; + | +++++++++ + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:231:15 + | +LL | reuse Trait::<'_, '_>::foo as foo6; + | ^^^^^ not allowed in type signatures + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:234:35 + | +LL | reuse Trait::<'_, '_, Vec<_>, 123, Vec>>::foo as foo7; + | ^ not allowed in type signatures + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:234:52 + | +LL | reuse Trait::<'_, '_, Vec<_>, 123, Vec>>::foo as foo7; + | ^ not allowed in type signatures + +error[E0107]: trait takes 3 generic arguments but 7 generic arguments were supplied + --> $DIR/infers.rs:241:15 + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo as foo10; + | ^^^^^ expected 3 generic arguments ------- help: remove the unnecessary generic arguments + | +note: trait defined here, with 3 generic parameters: `X`, `C`, `Y` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ - -------------- - + +error[E0107]: trait takes 2 lifetime arguments but 6 lifetime arguments were supplied + --> $DIR/infers.rs:244:15 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_, '_>::foo as foo11; + | ^^^^^ --------------------------- help: remove the lifetime arguments + | | + | expected 2 lifetime arguments + | +note: trait defined here, with 2 lifetime parameters: `'a`, `'b` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ -- -- + +error[E0107]: trait takes 3 generic arguments but 1 generic argument was supplied + --> $DIR/infers.rs:250:15 + | +LL | reuse Trait::<'static, 'static, _>::foo as foo12; + | ^^^^^ --- supplied 1 generic argument + | | + | expected 3 generic arguments + | +note: trait defined here, with 3 generic parameters: `X`, `C`, `Y` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ - -------------- - +help: add missing generic arguments + | +LL | reuse Trait::<'static, 'static, _>::foo, C, Y as foo12; + | ++++++ + +error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/infers.rs:260:15 + | +LL | reuse Trait::<'_, (), _, '_, _>::foo::<'static, String, _, _> as foo3; + | ^^^^^ --- supplied 1 lifetime argument + | | + | expected 2 lifetime arguments + | +note: trait defined here, with 2 lifetime parameters: `'a`, `'b` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ -- -- +help: add missing lifetime argument + | +LL | reuse Trait::<'_, (), _, '_, _>::foo, 'a::<'static, String, _, _> as foo3; + | ++++ + +error[E0107]: trait takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:260:15 + | +LL | reuse Trait::<'_, (), _, '_, _>::foo::<'static, String, _, _> as foo3; + | ^^^^^ --- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: trait defined here, with 3 generic parameters: `X`, `C`, `Y` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ - -------------- - + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:260:15 + | +LL | reuse Trait::<'_, (), _, '_, _>::foo::<'static, String, _, _> as foo3; + | ^^^^^ + +error[E0107]: trait takes 3 generic arguments but 0 generic arguments were supplied + --> $DIR/infers.rs:268:15 + | +LL | reuse Trait::<_, _>::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^^^^ expected 3 generic arguments + | +note: trait defined here, with 3 generic parameters: `X`, `C`, `Y` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ - -------------- - +help: add missing generic arguments + | +LL | reuse Trait::<_, _>::foo, X, C, Y::<'_, '_, '_, _, _, _,> as foo5; + | +++++++++ + +error[E0107]: method takes 3 generic arguments but 5 generic arguments were supplied + --> $DIR/infers.rs:268:30 + | +LL | reuse Trait::<_, _>::foo::<'_, '_, '_, _, _, _,> as foo5; + | ^^^---------------------- help: remove the unnecessary generic arguments + | | + | expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:276:15 + | +LL | reuse Trait::<'_, '_>::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^^^^ not allowed in type signatures + +error[E0107]: method takes 1 lifetime argument but 3 lifetime arguments were supplied + --> $DIR/infers.rs:276:32 + | +LL | reuse Trait::<'_, '_>::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^^---------------------- help: remove the lifetime arguments + | | + | expected 1 lifetime argument + | +note: method defined here, with 1 lifetime parameter: `'bb` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ --- + +error[E0107]: method takes 3 generic arguments but 6 generic arguments were supplied + --> $DIR/infers.rs:276:32 + | +LL | reuse Trait::<'_, '_>::foo::<_, _, _, '_, '_, '_, _, _, _,> as foo6; + | ^^^------------------------------- help: remove the unnecessary generic arguments + | | + | expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:283:35 + | +LL | reuse Trait::<'_, '_, Vec<_>, 123, Vec>>::foo::<_, '_, _, _> as foo7; + | ^ not allowed in type signatures + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:283:52 + | +LL | reuse Trait::<'_, '_, Vec<_>, 123, Vec>>::foo::<_, '_, _, _> as foo7; + | ^ not allowed in type signatures + +error[E0107]: method takes 3 generic arguments but 0 generic arguments were supplied + --> $DIR/infers.rs:294:51 + | +LL | reuse Trait::<'static, 'static, _, _, _>::foo::<_> as foo9; + | ^^^ expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- +help: add missing generic arguments + | +LL | reuse Trait::<'static, 'static, _, _, _>::fooXX, M, YY::<_> as foo9; + | +++++++++ + +error[E0107]: trait takes 3 generic arguments but 7 generic arguments were supplied + --> $DIR/infers.rs:298:15 + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::, _, _, ()> as foo10; + | ^^^^^ expected 3 generic arguments ------- help: remove the unnecessary generic arguments + | +note: trait defined here, with 3 generic parameters: `X`, `C`, `Y` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ - -------------- - + +error[E0107]: method takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/infers.rs:298:63 + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::, _, _, ()> as foo10; + | ^^^ expected 1 lifetime argument + | +note: method defined here, with 1 lifetime parameter: `'bb` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ --- +help: add missing lifetime argument + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::<'bb, Vec<'_>, _, _, ()> as foo10; + | ++++ + +error[E0107]: method takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:298:63 + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::, _, _, ()> as foo10; + | ^^^-------------------- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:298:63 + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::, _, _, ()> as foo10; + | ^^^ + +error[E0107]: struct takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/infers.rs:298:69 + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::, _, _, ()> as foo10; + | ^^^---- help: remove the unnecessary generics + | | + | expected 0 lifetime arguments + +error[E0107]: struct takes at least 1 generic argument but 0 generic arguments were supplied + --> $DIR/infers.rs:298:69 + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::, _, _, ()> as foo10; + | ^^^ expected at least 1 generic argument + | +help: add missing generic argument + | +LL | reuse Trait::<'static, 'static, _, _, _, _, _, _, _>::foo::, _, _, ()> as foo10; + | +++ + +error[E0107]: trait takes 2 lifetime arguments but 5 lifetime arguments were supplied + --> $DIR/infers.rs:306:15 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + | ^^^^^ ----------------------- help: remove the lifetime arguments + | | + | expected 2 lifetime arguments + | +note: trait defined here, with 2 lifetime parameters: `'a`, `'b` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ -- -- + +error[E0107]: method takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/infers.rs:306:65 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + | ^^^ expected 1 lifetime argument + | +note: method defined here, with 1 lifetime parameter: `'bb` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ --- +help: add missing lifetime argument + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::<'bb, Vec<_>, _, _, ()> as foo11; + | ++++ + +error[E0107]: method takes 3 generic arguments but 4 generic arguments were supplied + --> $DIR/infers.rs:306:65 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + | ^^^------------------- help: remove the unnecessary generic argument + | | + | expected 3 generic arguments + | +note: method defined here, with 3 generic parameters: `XX`, `M`, `YY` + --> $DIR/infers.rs:142:12 + | +LL | fn foo<'aa, 'bb: 'bb, 'cc, XX, const M: usize, YY>(&self, _: &'aa &'b &'cc ()) {} + | ^^^ -- -------------- -- + +error: inferred lifetimes are not allowed in delegations as we need to inherit signature + --> $DIR/infers.rs:306:65 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + | ^^^ + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/infers.rs:306:75 + | +LL | reuse Trait::<'static, 'static, '_,'_, '_, '_, '_, '_>::foo::, _, _, ()> as foo11; + | ^ not allowed in type signatures + +error[E0107]: trait takes 3 generic arguments but 1 generic argument was supplied + --> $DIR/infers.rs:316:15 + | +LL | reuse Trait::<'static, 'static, _>::foo::<'____, ___, _, ___> as foo12; + | ^^^^^ --- supplied 1 generic argument + | | + | expected 3 generic arguments + | +note: trait defined here, with 3 generic parameters: `X`, `C`, `Y` + --> $DIR/infers.rs:141:15 + | +LL | pub trait Trait<'a, 'b, X, const C: usize, Y> { + | ^^^^^ - -------------- - +help: add missing generic arguments + | +LL | reuse Trait::<'static, 'static, _>::foo, C, Y::<'____, ___, _, ___> as foo12; + | ++++++ + +error[E0308]: mismatched types + --> $DIR/infers.rs:338:42 + | +LL | reuse to_reuse::foo::<_, _, _> { self.0 } + | --- ^^^^^^ expected `()`, found `S` + | | + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/infers.rs:333:16 + | +LL | pub fn foo(_: ()) {} + | ^^^ ----- + +error[E0107]: function takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/infers.rs:344:25 + | +LL | reuse to_reuse::foo { self.0 } + | ^^^ + | | + | expected 0 lifetime arguments + | help: remove the lifetime argument + | +note: function defined here, with 0 lifetime parameters + --> $DIR/infers.rs:333:16 + | +LL | pub fn foo(_: ()) {} + | ^^^ + +error[E0308]: mismatched types + --> $DIR/infers.rs:344:31 + | +LL | reuse to_reuse::foo { self.0 } + | --- ^^^^^^ expected `()`, found `S` + | | + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/infers.rs:333:16 + | +LL | pub fn foo(_: ()) {} + | ^^^ ----- + +error[E0308]: mismatched types + --> $DIR/infers.rs:351:46 + | +LL | reuse to_reuse::foo::<(), 123, ()> { self.0 } + | --- ^^^^^^ expected `()`, found `S` + | | + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/infers.rs:333:16 + | +LL | pub fn foo(_: ()) {} + | ^^^ ----- + +error: aborting due to 138 previous errors + +Some errors have detailed explanations: E0107, E0121, E0207, E0214, E0224, E0261, E0308, E0425, E0747. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/delegation/generics/trait-impl-wrong-args-count.rs b/tests/ui/delegation/generics/trait-impl-wrong-args-count.rs index 4073e5ce88607..7b33e1d16eadf 100644 --- a/tests/ui/delegation/generics/trait-impl-wrong-args-count.rs +++ b/tests/ui/delegation/generics/trait-impl-wrong-args-count.rs @@ -26,6 +26,7 @@ mod test_1 { reuse to_reuse::bar1; //~^ ERROR: function takes 0 generic arguments but 3 generic arguments were supplied + //~| ERROR: function takes 0 lifetime arguments but 2 lifetime arguments were supplied reuse to_reuse::bar2; //~^ ERROR: type annotations needed @@ -59,8 +60,10 @@ mod test_2 { impl Trait for X { reuse ::bar; //~^ ERROR: missing generics for trait + //~| ERROR: associated function takes 1 lifetime argument but 2 lifetime arguments were supplied reuse >::bar as bar1; + //~^ ERROR: associated function takes 1 lifetime argument but 2 lifetime arguments were supplied reuse >::bar::<'static, u32, u32, 1> as bar2; @@ -94,9 +97,11 @@ mod test_3 { impl Trait for X { reuse >::bar; //~^ ERROR: associated function takes 0 generic arguments but 3 generic arguments were supplied + //~| ERROR: associated function takes 0 lifetime arguments but 2 lifetime arguments were supplied reuse >::bar as bar1; //~^ ERROR: associated function takes 0 generic arguments but 3 generic arguments were supplied + //~| ERROR: associated function takes 0 lifetime arguments but 2 lifetime arguments were supplied reuse >::foo as bar2; //~^ ERROR: type annotations needed diff --git a/tests/ui/delegation/generics/trait-impl-wrong-args-count.stderr b/tests/ui/delegation/generics/trait-impl-wrong-args-count.stderr index 09281768bbf73..931b61ef0ca85 100644 --- a/tests/ui/delegation/generics/trait-impl-wrong-args-count.stderr +++ b/tests/ui/delegation/generics/trait-impl-wrong-args-count.stderr @@ -13,11 +13,29 @@ note: function defined here, with at most 2 generic parameters: `A`, `B` LL | pub fn bar<'a: 'a, 'b: 'b, A, B>(x: &super::XX) {} | ^^^ - - +error[E0107]: function takes 0 lifetime arguments but 2 lifetime arguments were supplied + --> $DIR/trait-impl-wrong-args-count.rs:27:25 + | +LL | reuse to_reuse::bar1; + | ^^^^ + | | + | expected 0 lifetime arguments + | help: remove the lifetime arguments + | +note: function defined here, with 0 lifetime parameters + --> $DIR/trait-impl-wrong-args-count.rs:7:16 + | +LL | pub fn bar1(x: &super::XX) {} + | ^^^^ + error[E0107]: function takes 0 generic arguments but 3 generic arguments were supplied --> $DIR/trait-impl-wrong-args-count.rs:27:25 | LL | reuse to_reuse::bar1; - | ^^^^ expected 0 generic arguments + | ^^^^ + | | + | expected 0 generic arguments + | help: remove the unnecessary generic arguments | note: function defined here, with 0 generic parameters --> $DIR/trait-impl-wrong-args-count.rs:7:16 @@ -26,7 +44,7 @@ LL | pub fn bar1(x: &super::XX) {} | ^^^^ error[E0284]: type annotations needed - --> $DIR/trait-impl-wrong-args-count.rs:30:25 + --> $DIR/trait-impl-wrong-args-count.rs:31:25 | LL | reuse to_reuse::bar2; | ^^^^ cannot infer the value of the const parameter `X` declared on the function `bar2` @@ -42,7 +60,7 @@ LL | reuse to_reuse::bar2::; | ++++++++++++++++++++++++++ error[E0284]: type annotations needed - --> $DIR/trait-impl-wrong-args-count.rs:30:25 + --> $DIR/trait-impl-wrong-args-count.rs:31:25 | LL | reuse to_reuse::bar2; | ^^^^ cannot infer the value of the const parameter `Y` declared on the function `bar2` @@ -58,13 +76,13 @@ LL | reuse to_reuse::bar2::; | ++++++++++++++++++++++++++ error[E0107]: missing generics for trait `test_2::Trait1` - --> $DIR/trait-impl-wrong-args-count.rs:60:21 + --> $DIR/trait-impl-wrong-args-count.rs:61:21 | LL | reuse ::bar; | ^^^^^^ expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `A`, `B` - --> $DIR/trait-impl-wrong-args-count.rs:51:11 + --> $DIR/trait-impl-wrong-args-count.rs:52:11 | LL | trait Trait1 { | ^^^^^^ - - @@ -73,14 +91,44 @@ help: add missing generic arguments LL | reuse >::bar; | ++++++ +error[E0107]: associated function takes 1 lifetime argument but 2 lifetime arguments were supplied + --> $DIR/trait-impl-wrong-args-count.rs:61:30 + | +LL | reuse ::bar; + | ^^^ + | | + | expected 1 lifetime argument + | help: remove the lifetime argument + | +note: associated function defined here, with 1 lifetime parameter: `'x` + --> $DIR/trait-impl-wrong-args-count.rs:53:12 + | +LL | fn bar<'x: 'x, AA, BB, const NN: usize>() {} + | ^^^ -- + +error[E0107]: associated function takes 1 lifetime argument but 2 lifetime arguments were supplied + --> $DIR/trait-impl-wrong-args-count.rs:65:44 + | +LL | reuse >::bar as bar1; + | ^^^ + | | + | expected 1 lifetime argument + | help: remove the lifetime argument + | +note: associated function defined here, with 1 lifetime parameter: `'x` + --> $DIR/trait-impl-wrong-args-count.rs:53:12 + | +LL | fn bar<'x: 'x, AA, BB, const NN: usize>() {} + | ^^^ -- + error[E0107]: missing generics for trait `test_2::Trait1` - --> $DIR/trait-impl-wrong-args-count.rs:67:21 + --> $DIR/trait-impl-wrong-args-count.rs:70:21 | LL | reuse ::bar::<'static, u32, u32, 1> as bar3; | ^^^^^^ expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `A`, `B` - --> $DIR/trait-impl-wrong-args-count.rs:51:11 + --> $DIR/trait-impl-wrong-args-count.rs:52:11 | LL | trait Trait1 { | ^^^^^^ - - @@ -90,13 +138,13 @@ LL | reuse >::bar::<'static, u32, u32, 1> as bar3; | ++++++ error[E0107]: missing generics for trait `test_2::Trait1` - --> $DIR/trait-impl-wrong-args-count.rs:70:21 + --> $DIR/trait-impl-wrong-args-count.rs:73:21 | LL | reuse ::bar as bar4; | ^^^^^^ expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `A`, `B` - --> $DIR/trait-impl-wrong-args-count.rs:51:11 + --> $DIR/trait-impl-wrong-args-count.rs:52:11 | LL | trait Trait1 { | ^^^^^^ - - @@ -105,38 +153,74 @@ help: add missing generic arguments LL | reuse >::bar as bar4; | ++++++ +error[E0107]: associated function takes 0 lifetime arguments but 2 lifetime arguments were supplied + --> $DIR/trait-impl-wrong-args-count.rs:98:40 + | +LL | reuse >::bar; + | ^^^ + | | + | expected 0 lifetime arguments + | help: remove the lifetime arguments + | +note: associated function defined here, with 0 lifetime parameters + --> $DIR/trait-impl-wrong-args-count.rs:89:12 + | +LL | fn bar() {} + | ^^^ + error[E0107]: associated function takes 0 generic arguments but 3 generic arguments were supplied - --> $DIR/trait-impl-wrong-args-count.rs:95:40 + --> $DIR/trait-impl-wrong-args-count.rs:98:40 | LL | reuse >::bar; - | ^^^ expected 0 generic arguments + | ^^^ + | | + | expected 0 generic arguments + | help: remove the unnecessary generic arguments | note: associated function defined here, with 0 generic parameters - --> $DIR/trait-impl-wrong-args-count.rs:86:12 + --> $DIR/trait-impl-wrong-args-count.rs:89:12 + | +LL | fn bar() {} + | ^^^ + +error[E0107]: associated function takes 0 lifetime arguments but 2 lifetime arguments were supplied + --> $DIR/trait-impl-wrong-args-count.rs:102:40 + | +LL | reuse >::bar as bar1; + | ^^^ + | | + | expected 0 lifetime arguments + | help: remove the lifetime arguments + | +note: associated function defined here, with 0 lifetime parameters + --> $DIR/trait-impl-wrong-args-count.rs:89:12 | LL | fn bar() {} | ^^^ error[E0107]: associated function takes 0 generic arguments but 3 generic arguments were supplied - --> $DIR/trait-impl-wrong-args-count.rs:98:40 + --> $DIR/trait-impl-wrong-args-count.rs:102:40 | LL | reuse >::bar as bar1; - | ^^^ expected 0 generic arguments + | ^^^ + | | + | expected 0 generic arguments + | help: remove the unnecessary generic arguments | note: associated function defined here, with 0 generic parameters - --> $DIR/trait-impl-wrong-args-count.rs:86:12 + --> $DIR/trait-impl-wrong-args-count.rs:89:12 | LL | fn bar() {} | ^^^ error[E0282]: type annotations needed - --> $DIR/trait-impl-wrong-args-count.rs:101:40 + --> $DIR/trait-impl-wrong-args-count.rs:106:40 | LL | reuse >::foo as bar2; | ^^^ cannot infer type of the type parameter `X` declared on the associated function `foo` error[E0107]: associated function takes at most 2 generic arguments but 3 generic arguments were supplied - --> $DIR/trait-impl-wrong-args-count.rs:104:40 + --> $DIR/trait-impl-wrong-args-count.rs:109:40 | LL | reuse >::foo as bar3; | ^^^ @@ -145,12 +229,12 @@ LL | reuse >::foo as bar3; | help: remove the unnecessary generic argument | note: associated function defined here, with at most 2 generic parameters: `X`, `Y` - --> $DIR/trait-impl-wrong-args-count.rs:87:12 + --> $DIR/trait-impl-wrong-args-count.rs:90:12 | LL | fn foo() {} | ^^^ - - -error: aborting due to 11 previous errors +error: aborting due to 16 previous errors Some errors have detailed explanations: E0107, E0282, E0284. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/delegation/generics/unelided-lifetime-in-sig-ice-156848.rs b/tests/ui/delegation/generics/unelided-lifetime-in-sig-ice-156848.rs index fad06b37ae416..1f97f7c652c35 100644 --- a/tests/ui/delegation/generics/unelided-lifetime-in-sig-ice-156848.rs +++ b/tests/ui/delegation/generics/unelided-lifetime-in-sig-ice-156848.rs @@ -1,3 +1,4 @@ +//@ check-pass //@ compile-flags: -Z deduplicate-diagnostics=yes #![feature(fn_delegation)] @@ -12,11 +13,8 @@ impl Trait for F {} struct S(F); impl S { reuse Trait::foo::<> { self.0 } - //~^ ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature + reuse Trait::foo::<'_> as bar { self.0 } - //~^ ERROR: inferred lifetimes are not allowed in delegations as we need to inherit signature - //~| WARN: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - //~| WARN: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } fn main() {} diff --git a/tests/ui/delegation/generics/unelided-lifetime-in-sig-ice-156848.stderr b/tests/ui/delegation/generics/unelided-lifetime-in-sig-ice-156848.stderr deleted file mode 100644 index 464b6a3a76819..0000000000000 --- a/tests/ui/delegation/generics/unelided-lifetime-in-sig-ice-156848.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/unelided-lifetime-in-sig-ice-156848.rs:14:18 - | -LL | reuse Trait::foo::<> { self.0 } - | ^^^ - -warning: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/unelided-lifetime-in-sig-ice-156848.rs:16:24 - | -LL | fn foo<'a: 'a>(&self) {} - | - the late bound lifetime parameter is introduced here -... -LL | reuse Trait::foo::<'_> as bar { self.0 } - | ^^ - | - = 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 #42868 - = note: `#[warn(late_bound_lifetime_arguments)]` (part of `#[warn(future_incompatible)]`) on by default - -error: inferred lifetimes are not allowed in delegations as we need to inherit signature - --> $DIR/unelided-lifetime-in-sig-ice-156848.rs:16:24 - | -LL | reuse Trait::foo::<'_> as bar { self.0 } - | ^^ - -error: aborting due to 2 previous errors; 1 warning emitted - diff --git a/tests/ui/delegation/self-mapping-output-privacy.rs b/tests/ui/delegation/self-mapping-output-privacy.rs new file mode 100644 index 0000000000000..f072d240608f8 --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-privacy.rs @@ -0,0 +1,28 @@ +#![feature(fn_delegation)] + +trait Trait { + fn method(&self) -> Self; +} + +pub struct S; +impl Trait for S { + fn method(&self) -> S { + S + } +} + +mod private { + pub struct W(super::S); +} + +impl Trait for private::W { + reuse Trait::method { S } + //~^ ERROR: field `0` of struct `W` is private +} + +impl private::W { + reuse Trait::method { S } + //~^ ERROR: field `0` of struct `W` is private +} + +fn main() {} diff --git a/tests/ui/delegation/self-mapping-output-privacy.stderr b/tests/ui/delegation/self-mapping-output-privacy.stderr new file mode 100644 index 0000000000000..38f6b1a4a9ddf --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-privacy.stderr @@ -0,0 +1,15 @@ +error[E0451]: field `0` of struct `W` is private + --> $DIR/self-mapping-output-privacy.rs:19:18 + | +LL | reuse Trait::method { S } + | ^^^^^^ private field + +error[E0451]: field `0` of struct `W` is private + --> $DIR/self-mapping-output-privacy.rs:24:18 + | +LL | reuse Trait::method { S } + | ^^^^^^ private field + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0451`. diff --git a/tests/ui/delegation/self-mapping-output.rs b/tests/ui/delegation/self-mapping-output.rs new file mode 100644 index 0000000000000..47b122b0accda --- /dev/null +++ b/tests/ui/delegation/self-mapping-output.rs @@ -0,0 +1,357 @@ +#![feature(fn_delegation)] + +mod success { + trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } + } + + struct S; + impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } + } + + struct W(S); + impl Trait for W { + reuse Trait::method { self.0 } + reuse Trait::r#static; + //~^ WARN: function cannot return without recursing [unconditional_recursion] + reuse Trait::raw_S { self.0 } + } + + impl W { + reuse Trait::method { self.0 } + reuse Trait::r#static; + reuse Trait::raw_S { self.0 } + } +} + +mod success_non_field { + trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } + } + + struct S; + impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } + } + + struct W(S); + impl Trait for W { + reuse Trait::method { S } + reuse Trait::r#static; + //~^ WARN: function cannot return without recursing [unconditional_recursion] + reuse Trait::raw_S { S } + } + + impl W { + reuse Trait::method { S } + reuse Trait::r#static; + reuse Trait::raw_S { S } + } +} + +mod success_generics { + trait Trait<'a, T, const N: usize> { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S<'a, 'static, T, (), N, 123> { + S::<'a, 'static, T, (), N, 123>(std::marker::PhantomData) + } + } + + struct S<'a, 'b, A, B, const N: usize, const M: usize>( + std::marker::PhantomData<&'a &'b (A, B, &'a [(); N], &'b [(); M])> + ); + + impl<'a, T, const N: usize> Trait<'a, T, N> for S<'a, 'static, T, (), N, 123> { + fn method(&self) -> S<'a, 'static, T, (), N, 123> { + S(std::marker::PhantomData) + } + + fn r#static() -> S<'a, 'static, T, (), N, 123> { S(std::marker::PhantomData) } + } + + struct W<'a, 'b, A, B, const N: usize, const M: usize>(S<'a, 'b, A, B, N, M>); + impl<'a, T, const N: usize> Trait<'a, T, N> for W<'a, 'static, T, (), N, 123> { + reuse Trait::<'a, T, N>::method { self.0 } + reuse Trait::<'a, T, N>::r#static; + //~^ WARN: function cannot return without recursing [unconditional_recursion] + reuse Trait::<'a, T, N>::raw_S { self.0 } + } + + impl<'a, T, const N: usize> W<'a, 'static, T, (), N, 123> { + reuse Trait::<'a, T, N>::method { self.0 } + reuse Trait::<'a, T, N>::r#static; + reuse Trait::<'a, T, N>::raw_S { self.0 } + } +} + +mod no_constructor { + trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } + } + + struct S; + impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } + } + + struct W { s: S } + impl Trait for W { + reuse Trait::method { self.0 } + //~^ ERROR: no field `0` on type `&no_constructor::W` + //~| ERROR: struct `no_constructor::W` has no field named `0` + reuse Trait::r#static; + //~^ WARN: function cannot return without recursing [unconditional_recursion] + reuse Trait::raw_S { self.0 } + //~^ ERROR: no field `0` on type `&no_constructor::W` + } + + impl W { + reuse Trait::method { self.0 } + //~^ ERROR: no field `0` on type `&no_constructor::W` + //~| ERROR: struct `no_constructor::W` has no field named `0` + reuse Trait::r#static; + reuse Trait::raw_S { self.0 } + //~^ ERROR: no field `0` on type `&no_constructor::W` + } +} + +mod more_than_one_field { + trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } + } + + struct S; + impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } + } + + struct W(S, S, S); + impl Trait for W { + reuse Trait::method { self.0 } + //~^ ERROR: missing fields `1` and `2` in initializer of `more_than_one_field::W` + reuse Trait::r#static; + //~^ WARN: function cannot return without recursing [unconditional_recursion] + reuse Trait::raw_S { self.0 } + } + + impl W { + reuse Trait::method { self.0 } + //~^ ERROR: missing fields `1` and `2` in initializer of `more_than_one_field::W` + reuse Trait::r#static; + reuse Trait::raw_S { self.0 } + } +} + +mod non_trait_path_reuse { + trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } + } + + mod to_reuse { + pub fn method(_: impl super::Trait) -> impl super::Trait { + super::S + } + + pub fn r#static() -> impl super::Trait { + super::S + } + } + + pub struct S; + impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } + } + + struct W(S); + impl Trait for W { + reuse to_reuse::method { self.0 } + //~^ ERROR: mismatched types + reuse to_reuse::r#static; + //~^ ERROR: mismatched types + } + + impl W { + reuse to_reuse::method { self.0 } + //~^ ERROR: no field `0` on type `impl super::Trait` + reuse to_reuse::r#static; + } +} + +mod non_Self_return_type { + trait Trait { + fn method(&self) -> (); + fn r#static() -> (); + fn raw_S(&self) -> S { S } + } + + struct S; + impl Trait for S { + fn method(&self) -> () { () } + fn r#static() -> () { () } + fn raw_S(&self) -> S { S } + } + + struct W(()); + impl Trait for W { + reuse Trait::method { self.0 } + //~^ ERROR: mismatched types + + reuse Trait::r#static; + //~^ ERROR: type annotations needed + + reuse Trait::raw_S { self.0 } + //~^ ERROR: mismatched types + } + + impl W { + reuse Trait::method { self.0 } + //~^ ERROR: mismatched types + + reuse Trait::r#static; + //~^ ERROR: type annotations needed + + reuse Trait::raw_S { self.0 } + //~^ ERROR: mismatched types + } +} + +mod wrong_return_type { + trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } + } + + struct F; + impl Trait for F { + fn method(&self) -> F { F } + fn r#static() -> F { F } + } + + struct S; + impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } + } + + struct W(S); + impl Trait for W { + reuse ::method { self.0 } + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + + reuse ::r#static; + //~^ ERROR: mismatched types + + reuse ::raw_S { self.0 } + //~^ ERROR: mismatched types + } + + impl W { + reuse ::method { self.0 } + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + + reuse ::r#static; + //~^ ERROR: mismatched types + + reuse ::raw_S { self.0 } + //~^ ERROR: mismatched types + } +} + +mod wrong_target_expression { + trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } + } + + struct S; + impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } + } + + struct F; + impl Trait for F { + fn method(&self) -> F { F } + fn r#static() -> F { F } + } + + struct W(S); + impl Trait for W { + reuse Trait::method { F } + //~^ ERROR: mismatched types + + reuse Trait::r#static; + //~^ WARN: function cannot return without recursing [unconditional_recursion] + reuse Trait::raw_S { F } + } + + impl W { + reuse Trait::method { F } + //~^ ERROR: mismatched types + + reuse Trait::r#static; + reuse Trait::raw_S { F } + } +} + +mod privacy { + trait Trait { + fn method(&self) -> Self; + fn r#static() -> Self; + fn raw_S(&self) -> S { S } + } + + pub struct S; + impl Trait for S { + fn method(&self) -> S { S } + fn r#static() -> S { S } + } + + mod private { + pub struct W(super::S); + } + + impl Trait for private::W { + reuse Trait::method { self.0 } + //~^ ERROR: field `0` of struct `private::W` is private + + reuse Trait::r#static; + //~^ WARN: function cannot return without recursing [unconditional_recursion] + + reuse Trait::raw_S { self.0 } + //~^ ERROR: field `0` of struct `private::W` is private + } + + impl private::W { + reuse Trait::method { self.0 } + //~^ ERROR: field `0` of struct `private::W` is private + + reuse Trait::r#static; + + reuse Trait::raw_S { self.0 } + //~^ ERROR: field `0` of struct `private::W` is private + } +} + +fn main() {} diff --git a/tests/ui/delegation/self-mapping-output.stderr b/tests/ui/delegation/self-mapping-output.stderr new file mode 100644 index 0000000000000..86ce822a1821c --- /dev/null +++ b/tests/ui/delegation/self-mapping-output.stderr @@ -0,0 +1,468 @@ +error[E0560]: struct `no_constructor::W` has no field named `0` + --> $DIR/self-mapping-output.rs:110:22 + | +LL | reuse Trait::method { self.0 } + | ^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL - reuse Trait::method { self.0 } +LL + reuse Trait::s { self.0 } + | + +error[E0609]: no field `0` on type `&no_constructor::W` + --> $DIR/self-mapping-output.rs:110:36 + | +LL | reuse Trait::method { self.0 } + | ^ unknown field + | +help: a field with a similar name exists + | +LL - reuse Trait::method { self.0 } +LL + reuse Trait::method { self.s } + | + +error[E0609]: no field `0` on type `&no_constructor::W` + --> $DIR/self-mapping-output.rs:115:35 + | +LL | reuse Trait::raw_S { self.0 } + | ^ unknown field + | +help: a field with a similar name exists + | +LL - reuse Trait::raw_S { self.0 } +LL + reuse Trait::raw_S { self.s } + | + +error[E0560]: struct `no_constructor::W` has no field named `0` + --> $DIR/self-mapping-output.rs:120:22 + | +LL | reuse Trait::method { self.0 } + | ^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL - reuse Trait::method { self.0 } +LL + reuse Trait::s { self.0 } + | + +error[E0609]: no field `0` on type `&no_constructor::W` + --> $DIR/self-mapping-output.rs:120:36 + | +LL | reuse Trait::method { self.0 } + | ^ unknown field + | +help: a field with a similar name exists + | +LL - reuse Trait::method { self.0 } +LL + reuse Trait::method { self.s } + | + +error[E0609]: no field `0` on type `&no_constructor::W` + --> $DIR/self-mapping-output.rs:124:35 + | +LL | reuse Trait::raw_S { self.0 } + | ^ unknown field + | +help: a field with a similar name exists + | +LL - reuse Trait::raw_S { self.0 } +LL + reuse Trait::raw_S { self.s } + | + +error[E0063]: missing fields `1` and `2` in initializer of `more_than_one_field::W` + --> $DIR/self-mapping-output.rs:144:22 + | +LL | reuse Trait::method { self.0 } + | ^^^^^^ missing `1` and `2` + +error[E0063]: missing fields `1` and `2` in initializer of `more_than_one_field::W` + --> $DIR/self-mapping-output.rs:152:22 + | +LL | reuse Trait::method { self.0 } + | ^^^^^^ missing `1` and `2` + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:184:25 + | +LL | pub fn method(_: impl super::Trait) -> impl super::Trait { + | ----------------- the found opaque type +... +LL | reuse to_reuse::method { self.0 } + | ^^^^^^ + | | + | expected `W`, found opaque type + | expected `non_trait_path_reuse::W` because of return type + | + = note: expected struct `non_trait_path_reuse::W` + found opaque type `impl non_trait_path_reuse::Trait` + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:186:25 + | +LL | pub fn r#static() -> impl super::Trait { + | ----------------- the found opaque type +... +LL | reuse to_reuse::r#static; + | ^^^^^^^^ + | | + | expected `W`, found opaque type + | expected `non_trait_path_reuse::W` because of return type + | + = note: expected struct `non_trait_path_reuse::W` + found opaque type `impl non_trait_path_reuse::Trait` + +error[E0609]: no field `0` on type `impl super::Trait` + --> $DIR/self-mapping-output.rs:191:39 + | +LL | reuse to_reuse::method { self.0 } + | ^ unknown field + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:213:31 + | +LL | reuse Trait::method { self.0 } + | ------ ^^^^^^ expected `&_`, found `()` + | | + | arguments to this function are incorrect + | + = note: expected reference `&_` + found unit type `()` +note: method defined here + --> $DIR/self-mapping-output.rs:199:12 + | +LL | fn method(&self) -> (); + | ^^^^^^ ---- +help: consider borrowing here + | +LL | reuse Trait::method { &self.0 } + | + + +error[E0283]: type annotations needed + --> $DIR/self-mapping-output.rs:216:22 + | +LL | reuse Trait::r#static; + | ^^^^^^^^ cannot infer type + | + = note: the type must implement `non_Self_return_type::Trait` +help: the following types implement trait `non_Self_return_type::Trait` + --> $DIR/self-mapping-output.rs:205:5 + | +LL | impl Trait for S { + | ^^^^^^^^^^^^^^^^ `non_Self_return_type::S` +... +LL | impl Trait for W { + | ^^^^^^^^^^^^^^^^ `non_Self_return_type::W` + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:219:30 + | +LL | reuse Trait::raw_S { self.0 } + | ----- ^^^^^^ expected `&_`, found `()` + | | + | arguments to this function are incorrect + | + = note: expected reference `&_` + found unit type `()` +note: method defined here + --> $DIR/self-mapping-output.rs:201:12 + | +LL | fn raw_S(&self) -> S { S } + | ^^^^^ ----- +help: consider borrowing here + | +LL | reuse Trait::raw_S { &self.0 } + | + + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:224:31 + | +LL | reuse Trait::method { self.0 } + | ------ ^^^^^^ expected `&_`, found `()` + | | + | arguments to this function are incorrect + | + = note: expected reference `&_` + found unit type `()` +note: method defined here + --> $DIR/self-mapping-output.rs:199:12 + | +LL | fn method(&self) -> (); + | ^^^^^^ ---- +help: consider borrowing here + | +LL | reuse Trait::method { &self.0 } + | + + +error[E0283]: type annotations needed + --> $DIR/self-mapping-output.rs:227:22 + | +LL | reuse Trait::r#static; + | ^^^^^^^^ cannot infer type + | + = note: the type must implement `non_Self_return_type::Trait` +help: the following types implement trait `non_Self_return_type::Trait` + --> $DIR/self-mapping-output.rs:205:5 + | +LL | impl Trait for S { + | ^^^^^^^^^^^^^^^^ `non_Self_return_type::S` +... +LL | impl Trait for W { + | ^^^^^^^^^^^^^^^^ `non_Self_return_type::W` + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:230:30 + | +LL | reuse Trait::raw_S { self.0 } + | ----- ^^^^^^ expected `&_`, found `()` + | | + | arguments to this function are incorrect + | + = note: expected reference `&_` + found unit type `()` +note: method defined here + --> $DIR/self-mapping-output.rs:201:12 + | +LL | fn raw_S(&self) -> S { S } + | ^^^^^ ----- +help: consider borrowing here + | +LL | reuse Trait::raw_S { &self.0 } + | + + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:256:38 + | +LL | reuse ::method { self.0 } + | ------ ^^^^^^ expected `&F`, found `&S` + | | + | arguments to this function are incorrect + | this return type influences the call expression's return type + | + = note: expected reference `&wrong_return_type::F` + found reference `&wrong_return_type::S` +note: method defined here + --> $DIR/self-mapping-output.rs:237:12 + | +LL | fn method(&self) -> Self; + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:256:29 + | +LL | reuse ::method { self.0 } + | ^^^^^^ expected `S`, found `F` + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:260:29 + | +LL | reuse ::r#static; + | ^^^^^^^^ + | | + | expected `W`, found `F` + | expected `wrong_return_type::W` because of return type + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:263:37 + | +LL | reuse ::raw_S { self.0 } + | ----- ^^^^^^ expected `&F`, found `&S` + | | + | arguments to this function are incorrect + | + = note: expected reference `&wrong_return_type::F` + found reference `&wrong_return_type::S` +note: method defined here + --> $DIR/self-mapping-output.rs:239:12 + | +LL | fn raw_S(&self) -> S { S } + | ^^^^^ ----- + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:268:38 + | +LL | reuse ::method { self.0 } + | ------ ^^^^^^ expected `&F`, found `&S` + | | + | arguments to this function are incorrect + | this return type influences the call expression's return type + | + = note: expected reference `&wrong_return_type::F` + found reference `&wrong_return_type::S` +note: method defined here + --> $DIR/self-mapping-output.rs:237:12 + | +LL | fn method(&self) -> Self; + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:268:29 + | +LL | reuse ::method { self.0 } + | ^^^^^^ expected `S`, found `F` + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:272:29 + | +LL | reuse ::r#static; + | ^^^^^^^^ + | | + | expected `W`, found `F` + | expected `wrong_return_type::W` because of return type + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:275:37 + | +LL | reuse ::raw_S { self.0 } + | ----- ^^^^^^ expected `&F`, found `&S` + | | + | arguments to this function are incorrect + | + = note: expected reference `&wrong_return_type::F` + found reference `&wrong_return_type::S` +note: method defined here + --> $DIR/self-mapping-output.rs:239:12 + | +LL | fn raw_S(&self) -> S { S } + | ^^^^^ ----- + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:301:31 + | +LL | reuse Trait::method { F } + | ------ ^ expected `&S`, found `&F` + | | + | arguments to this function are incorrect + | this return type influences the call expression's return type + | + = note: expected reference `&wrong_target_expression::S` + found reference `&wrong_target_expression::F` +note: method defined here + --> $DIR/self-mapping-output.rs:282:12 + | +LL | fn method(&self) -> Self; + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-mapping-output.rs:310:31 + | +LL | reuse Trait::method { F } + | ------ ^ expected `&S`, found `&F` + | | + | arguments to this function are incorrect + | this return type influences the call expression's return type + | + = note: expected reference `&wrong_target_expression::S` + found reference `&wrong_target_expression::F` +note: method defined here + --> $DIR/self-mapping-output.rs:282:12 + | +LL | fn method(&self) -> Self; + | ^^^^^^ ---- + +error[E0616]: field `0` of struct `private::W` is private + --> $DIR/self-mapping-output.rs:336:36 + | +LL | reuse Trait::method { self.0 } + | ^ private field + +error[E0616]: field `0` of struct `private::W` is private + --> $DIR/self-mapping-output.rs:342:35 + | +LL | reuse Trait::raw_S { self.0 } + | ^ private field + +error[E0616]: field `0` of struct `private::W` is private + --> $DIR/self-mapping-output.rs:347:36 + | +LL | reuse Trait::method { self.0 } + | ^ private field + +error[E0616]: field `0` of struct `private::W` is private + --> $DIR/self-mapping-output.rs:352:35 + | +LL | reuse Trait::raw_S { self.0 } + | ^ private field + +warning: function cannot return without recursing + --> $DIR/self-mapping-output.rs:19:22 + | +LL | reuse Trait::r#static; + | ^^^^^^^^ + | | + | cannot return without recursing + | recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +warning: function cannot return without recursing + --> $DIR/self-mapping-output.rs:47:22 + | +LL | reuse Trait::r#static; + | ^^^^^^^^ + | | + | cannot return without recursing + | recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +warning: function cannot return without recursing + --> $DIR/self-mapping-output.rs:83:34 + | +LL | reuse Trait::<'a, T, N>::r#static; + | ^^^^^^^^ + | | + | cannot return without recursing + | recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +warning: function cannot return without recursing + --> $DIR/self-mapping-output.rs:113:22 + | +LL | reuse Trait::r#static; + | ^^^^^^^^ + | | + | cannot return without recursing + | recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +warning: function cannot return without recursing + --> $DIR/self-mapping-output.rs:146:22 + | +LL | reuse Trait::r#static; + | ^^^^^^^^ + | | + | cannot return without recursing + | recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +warning: function cannot return without recursing + --> $DIR/self-mapping-output.rs:304:22 + | +LL | reuse Trait::r#static; + | ^^^^^^^^ + | | + | cannot return without recursing + | recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +warning: function cannot return without recursing + --> $DIR/self-mapping-output.rs:339:22 + | +LL | reuse Trait::r#static; + | ^^^^^^^^ + | | + | cannot return without recursing + | recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +error: aborting due to 31 previous errors; 7 warnings emitted + +Some errors have detailed explanations: E0063, E0283, E0308, E0560, E0609, E0616. +For more information about an error, try `rustc --explain E0063`. diff --git a/tests/ui/delegation/self-ty-ice-156388.rs b/tests/ui/delegation/self-ty-ice-156388.rs index 542f56867b2b2..74cad01236375 100644 --- a/tests/ui/delegation/self-ty-ice-156388.rs +++ b/tests/ui/delegation/self-ty-ice-156388.rs @@ -1,8 +1,9 @@ //@ compile-flags: -Z deduplicate-diagnostics=yes +#![feature(const_trait_impl)] #![feature(fn_delegation)] reuse Default::default; -//~^ ERROR: delegation self type is not specified +//~^ ERROR: the trait bound `Self: [const] Default` is not satisfied fn main() {} diff --git a/tests/ui/delegation/self-ty-ice-156388.stderr b/tests/ui/delegation/self-ty-ice-156388.stderr index c2323cf1c6082..a311635db608a 100644 --- a/tests/ui/delegation/self-ty-ice-156388.stderr +++ b/tests/ui/delegation/self-ty-ice-156388.stderr @@ -1,10 +1,14 @@ -error: delegation self type is not specified - --> $DIR/self-ty-ice-156388.rs:5:16 +error[E0277]: the trait bound `Self: [const] Default` is not satisfied + --> $DIR/self-ty-ice-156388.rs:6:16 | LL | reuse Default::default; | ^^^^^^^ | - = help: consider explicitly specifying self type: `reuse ::function` +help: consider restricting type parameter `Self` with trait `Default` + | +LL | reuse Default::default [const] std::default::Default; + | +++++++++++++++++++++++++++++ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/target-expr.rs b/tests/ui/delegation/target-expr.rs index 14232d194a740..b4b3a31f437fc 100644 --- a/tests/ui/delegation/target-expr.rs +++ b/tests/ui/delegation/target-expr.rs @@ -15,7 +15,6 @@ fn foo(x: i32) -> i32 { x } fn bar(_: T) { reuse Trait::static_method { - //~^ ERROR: delegation self type is not specified let _ = T::Default(); //~^ ERROR can't use generic parameters from outer item } diff --git a/tests/ui/delegation/target-expr.stderr b/tests/ui/delegation/target-expr.stderr index 9126234a2c3cd..2ceefbf929b9f 100644 --- a/tests/ui/delegation/target-expr.stderr +++ b/tests/ui/delegation/target-expr.stderr @@ -1,18 +1,17 @@ error[E0401]: can't use generic parameters from outer item - --> $DIR/target-expr.rs:19:17 + --> $DIR/target-expr.rs:18:17 | LL | fn bar(_: T) { | - type parameter from outer item LL | reuse Trait::static_method { | ------------- generic parameter used in this inner delegated function -LL | LL | let _ = T::Default(); | ^ use of generic parameter from outer item | = note: nested items are independent from their parent item for everything except for privacy and name resolution error[E0434]: can't capture dynamic environment in a fn item - --> $DIR/target-expr.rs:27:17 + --> $DIR/target-expr.rs:26:17 | LL | let x = y; | ^ @@ -20,7 +19,7 @@ LL | let x = y; = help: use the `|| { ... }` closure form instead error[E0424]: expected value, found module `self` - --> $DIR/target-expr.rs:34:5 + --> $DIR/target-expr.rs:33:5 | LL | fn main() { | ---- this function can't have a `self` parameter @@ -29,26 +28,18 @@ LL | self.0; | ^^^^ `self` value is a keyword only available in methods with a `self` parameter error[E0425]: cannot find value `x` in this scope - --> $DIR/target-expr.rs:36:13 + --> $DIR/target-expr.rs:35:13 | LL | let z = x; | ^ | help: the binding `x` is available in a different scope in the same function - --> $DIR/target-expr.rs:27:13 + --> $DIR/target-expr.rs:26:13 | LL | let x = y; | ^ -error: delegation self type is not specified - --> $DIR/target-expr.rs:17:18 - | -LL | reuse Trait::static_method { - | ^^^^^^^^^^^^^ - | - = help: consider explicitly specifying self type: `reuse ::function` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0401, E0424, E0425, E0434. For more information about an error, try `rustc --explain E0401`. diff --git a/tests/ui/delegation/unsupported.current.stderr b/tests/ui/delegation/unsupported.current.stderr index fdfd9251b4650..6cdd54e2e27fc 100644 --- a/tests/ui/delegation/unsupported.current.stderr +++ b/tests/ui/delegation/unsupported.current.stderr @@ -20,14 +20,6 @@ LL | reuse ToReuse::opaque_ret; = note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition = note: for more information, see and -error: delegation self type is not specified - --> $DIR/unsupported.rs:54:18 - | -LL | reuse Trait::foo; - | ^^^ - | - = help: consider explicitly specifying self type: `reuse ::function` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/delegation/unsupported.next.stderr b/tests/ui/delegation/unsupported.next.stderr index fdfd9251b4650..6cdd54e2e27fc 100644 --- a/tests/ui/delegation/unsupported.next.stderr +++ b/tests/ui/delegation/unsupported.next.stderr @@ -20,14 +20,6 @@ LL | reuse ToReuse::opaque_ret; = note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition = note: for more information, see and -error: delegation self type is not specified - --> $DIR/unsupported.rs:54:18 - | -LL | reuse Trait::foo; - | ^^^ - | - = help: consider explicitly specifying self type: `reuse ::function` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/delegation/unsupported.rs b/tests/ui/delegation/unsupported.rs index 3349c4ec27ab1..3da1206b5b2df 100644 --- a/tests/ui/delegation/unsupported.rs +++ b/tests/ui/delegation/unsupported.rs @@ -52,7 +52,6 @@ mod effects { } reuse Trait::foo; - //~^ ERROR: delegation self type is not specified } fn main() {} diff --git a/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs new file mode 100644 index 0000000000000..6053b603cc8fa --- /dev/null +++ b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs @@ -0,0 +1,11 @@ +//! Regression test for . +//! Test anon fields in tuple-syntax structs which don't implement trait +//! get nice error message mentioning the type of field and its span. + +struct Foo; +#[derive(Copy, Clone)] +struct Bar(Foo); +//~^ ERROR: the trait `Copy` cannot be implemented for this type +//~| ERROR: `Foo: Clone` is not satisfied + +fn main() {} diff --git a/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr new file mode 100644 index 0000000000000..a997fc0aa0d98 --- /dev/null +++ b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr @@ -0,0 +1,28 @@ +error[E0204]: the trait `Copy` cannot be implemented for this type + --> $DIR/derive-copy-clone-non-copy-field-diagnostic.rs:7:8 + | +LL | #[derive(Copy, Clone)] + | ---- in this derive macro expansion +LL | struct Bar(Foo); + | ^^^ --- this field does not implement `Copy` + +error[E0277]: the trait bound `Foo: Clone` is not satisfied + --> $DIR/derive-copy-clone-non-copy-field-diagnostic.rs:7:12 + | +LL | #[derive(Copy, Clone)] + | ----- in this derive macro expansion +LL | struct Bar(Foo); + | ^^^ the trait `Clone` is not implemented for `Foo` + | +note: required by a bound in `std::clone::AssertParamIsClone` + --> $SRC_DIR/core/src/clone.rs:LL:COL +help: consider annotating `Foo` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct Foo; + | + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0204, E0277. +For more information about an error, try `rustc --explain E0204`. diff --git a/tests/ui/issues/issue-28561.rs b/tests/ui/derives/derive-compound-arities.rs similarity index 96% rename from tests/ui/issues/issue-28561.rs rename to tests/ui/derives/derive-compound-arities.rs index 642b2193a4f9b..704d6747f7e07 100644 --- a/tests/ui/issues/issue-28561.rs +++ b/tests/ui/derives/derive-compound-arities.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/28561 //@ check-pass #[derive(Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Clone, Copy)] struct Array { diff --git a/tests/ui/issues/issue-33571.rs b/tests/ui/derives/derive-error-identifies-unsafe-trait-name.rs similarity index 73% rename from tests/ui/issues/issue-33571.rs rename to tests/ui/derives/derive-error-identifies-unsafe-trait-name.rs index 2713f47ad2ff6..586c5acf21611 100644 --- a/tests/ui/issues/issue-33571.rs +++ b/tests/ui/derives/derive-error-identifies-unsafe-trait-name.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/33571 #[derive(Clone, Sync, //~ ERROR cannot find derive macro `Sync` in this scope //~| ERROR cannot find derive macro `Sync` in this scope diff --git a/tests/ui/issues/issue-33571.stderr b/tests/ui/derives/derive-error-identifies-unsafe-trait-name.stderr similarity index 69% rename from tests/ui/issues/issue-33571.stderr rename to tests/ui/derives/derive-error-identifies-unsafe-trait-name.stderr index 819a533ddbe2f..862571cb2aecd 100644 --- a/tests/ui/issues/issue-33571.stderr +++ b/tests/ui/derives/derive-error-identifies-unsafe-trait-name.stderr @@ -1,23 +1,23 @@ error: cannot find derive macro `Sync` in this scope - --> $DIR/issue-33571.rs:2:10 + --> $DIR/derive-error-identifies-unsafe-trait-name.rs:3:10 | LL | Sync, | ^^^^ | note: unsafe traits like `Sync` should be implemented explicitly - --> $DIR/issue-33571.rs:2:10 + --> $DIR/derive-error-identifies-unsafe-trait-name.rs:3:10 | LL | Sync, | ^^^^ error: cannot find derive macro `Sync` in this scope - --> $DIR/issue-33571.rs:2:10 + --> $DIR/derive-error-identifies-unsafe-trait-name.rs:3:10 | LL | Sync, | ^^^^ | note: unsafe traits like `Sync` should be implemented explicitly - --> $DIR/issue-33571.rs:2:10 + --> $DIR/derive-error-identifies-unsafe-trait-name.rs:3:10 | LL | Sync, | ^^^^ diff --git a/tests/ui/issues/issue-27340.rs b/tests/ui/derives/derived-trait-requires-field-impl.rs similarity index 70% rename from tests/ui/issues/issue-27340.rs rename to tests/ui/derives/derived-trait-requires-field-impl.rs index 9966c24a7441e..fb1a98c10d843 100644 --- a/tests/ui/issues/issue-27340.rs +++ b/tests/ui/derives/derived-trait-requires-field-impl.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/27340 struct Foo; #[derive(Copy, Clone)] struct Bar(Foo); diff --git a/tests/ui/issues/issue-27340.stderr b/tests/ui/derives/derived-trait-requires-field-impl.stderr similarity index 88% rename from tests/ui/issues/issue-27340.stderr rename to tests/ui/derives/derived-trait-requires-field-impl.stderr index 3b4ad58b1f080..6ca75fac56937 100644 --- a/tests/ui/issues/issue-27340.stderr +++ b/tests/ui/derives/derived-trait-requires-field-impl.stderr @@ -1,5 +1,5 @@ error[E0204]: the trait `Copy` cannot be implemented for this type - --> $DIR/issue-27340.rs:3:8 + --> $DIR/derived-trait-requires-field-impl.rs:4:8 | LL | #[derive(Copy, Clone)] | ---- in this derive macro expansion @@ -7,7 +7,7 @@ LL | struct Bar(Foo); | ^^^ --- this field does not implement `Copy` error[E0277]: the trait bound `Foo: Clone` is not satisfied - --> $DIR/issue-27340.rs:3:12 + --> $DIR/derived-trait-requires-field-impl.rs:4:12 | LL | #[derive(Copy, Clone)] | ----- in this derive macro expansion diff --git a/tests/ui/derives/hash-on-compound-types.rs b/tests/ui/derives/hash-on-compound-types.rs index 6d950df99d65c..be7094bccc4c3 100644 --- a/tests/ui/derives/hash-on-compound-types.rs +++ b/tests/ui/derives/hash-on-compound-types.rs @@ -1,5 +1,4 @@ -//! Regression test for . - +//! Regression test for https://github.com/rust-lang/rust/issues/21402 //@ check-pass #![allow(dead_code)] diff --git a/tests/ui/issues/issue-32324.rs b/tests/ui/derives/no-ice-on-derived-copy-with-associated-type-field.rs similarity index 83% rename from tests/ui/issues/issue-32324.rs rename to tests/ui/derives/no-ice-on-derived-copy-with-associated-type-field.rs index 50ecfe993d438..755ec1f9d5229 100644 --- a/tests/ui/issues/issue-32324.rs +++ b/tests/ui/derives/no-ice-on-derived-copy-with-associated-type-field.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/32324 //@ check-pass #![allow(dead_code)] diff --git a/tests/ui/issues/issue-46101.rs b/tests/ui/derives/no-ice-on-path-style-derive-macro.rs similarity index 65% rename from tests/ui/issues/issue-46101.rs rename to tests/ui/derives/no-ice-on-path-style-derive-macro.rs index 86b06f7c61d0a..664fe3f241115 100644 --- a/tests/ui/issues/issue-46101.rs +++ b/tests/ui/derives/no-ice-on-path-style-derive-macro.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/46101 trait Foo {} #[derive(Foo::Anything)] //~ ERROR cannot find //~| ERROR cannot find diff --git a/tests/ui/issues/issue-46101.stderr b/tests/ui/derives/no-ice-on-path-style-derive-macro.stderr similarity index 83% rename from tests/ui/issues/issue-46101.stderr rename to tests/ui/derives/no-ice-on-path-style-derive-macro.stderr index 1dada87d72d64..3e05d7b384704 100644 --- a/tests/ui/issues/issue-46101.stderr +++ b/tests/ui/derives/no-ice-on-path-style-derive-macro.stderr @@ -1,11 +1,11 @@ error[E0433]: cannot find derive macro `Anything` in trait `Foo` - --> $DIR/issue-46101.rs:2:10 + --> $DIR/no-ice-on-path-style-derive-macro.rs:3:10 | LL | #[derive(Foo::Anything)] | ^^^^^^^^^^^^^ a derive macro can't exist within a trait error[E0433]: cannot find derive macro `Anything` in trait `Foo` - --> $DIR/issue-46101.rs:2:10 + --> $DIR/no-ice-on-path-style-derive-macro.rs:3:10 | LL | #[derive(Foo::Anything)] | ^^^^^^^^^^^^^ a derive macro can't exist within a trait diff --git a/tests/ui/issues/issue-32292.rs b/tests/ui/derives/no-spurious-unused-variable-warning-on-derive-hash.rs similarity index 67% rename from tests/ui/issues/issue-32292.rs rename to tests/ui/derives/no-spurious-unused-variable-warning-on-derive-hash.rs index 2181dff505ace..4609730dff385 100644 --- a/tests/ui/issues/issue-32292.rs +++ b/tests/ui/derives/no-spurious-unused-variable-warning-on-derive-hash.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/32292 //@ run-pass #![deny(warnings)] diff --git a/tests/ui/issues/issue-30530.rs b/tests/ui/drop/drop-on-untaken-arm-raw-ptr.rs similarity index 56% rename from tests/ui/issues/issue-30530.rs rename to tests/ui/drop/drop-on-untaken-arm-raw-ptr.rs index 2c00e6d676884..b6dba0c5b26ff 100644 --- a/tests/ui/issues/issue-30530.rs +++ b/tests/ui/drop/drop-on-untaken-arm-raw-ptr.rs @@ -1,9 +1,10 @@ +//! Regression test for . +//! +//! alloca's created for storing intermediate scratch values during +//! brace-less match arms need to be initialized with their drop-flag +//! set to "dropped" (or else we end up running the destructors on garbage +//! data at the end of the function). //@ run-pass -// Regression test for Issue #30530: alloca's created for storing -// intermediate scratch values during brace-less match arms need to be -// initialized with their drop-flag set to "dropped" (or else we end -// up running the destructors on garbage data at the end of the -// function). pub enum Handler { Default, diff --git a/tests/ui/issues/issue-29092.rs b/tests/ui/drop/drop-on-untaken-arm.rs similarity index 62% rename from tests/ui/issues/issue-29092.rs rename to tests/ui/drop/drop-on-untaken-arm.rs index a5b2f4884f6e0..0195112e83c86 100644 --- a/tests/ui/issues/issue-29092.rs +++ b/tests/ui/drop/drop-on-untaken-arm.rs @@ -1,7 +1,7 @@ +//! Regression test for . +//! Drop glue for match expression `*t.clone()` ran when arm was never taken, +//! causing segfault. //@ run-pass -// Regression test for Issue #29092. -// -// (Possibly redundant with regression test run-pass/issue-30530.rs) use self::Term::*; @@ -12,7 +12,6 @@ pub enum Term { B(Box), } -// a small-step evaluator pub fn small_eval(v: Term) -> Term { match v { A(t) => *t.clone(), diff --git a/tests/ui/issues/issue-4252.rs b/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs similarity index 90% rename from tests/ui/issues/issue-4252.rs rename to tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs index 1939ad15f143a..7032a6bc5e759 100644 --- a/tests/ui/issues/issue-4252.rs +++ b/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/4252 //@ run-pass trait X { fn call(&self, x: &T); diff --git a/tests/ui/issues/issue-27401-dropflag-reinit.rs b/tests/ui/drop/dropflag-reinit-in-loop.rs similarity index 63% rename from tests/ui/issues/issue-27401-dropflag-reinit.rs rename to tests/ui/drop/dropflag-reinit-in-loop.rs index b89497241e096..04fb8eaee965e 100644 --- a/tests/ui/issues/issue-27401-dropflag-reinit.rs +++ b/tests/ui/drop/dropflag-reinit-in-loop.rs @@ -1,9 +1,9 @@ +//! Regression test for . +//! Check that when a `let`-binding occurs in a loop, its associated +//! drop-flag is reinitialized (to indicate "needs-drop" at the end of +//! the owning variable's scope). //@ run-pass -// Check that when a `let`-binding occurs in a loop, its associated -// drop-flag is reinitialized (to indicate "needs-drop" at the end of -// the owning variable's scope). - struct A<'a>(&'a mut i32); impl<'a> Drop for A<'a> { diff --git a/tests/ui/issues/issue-26709.rs b/tests/ui/drop/dst-coerce-unsized-drop.rs similarity index 58% rename from tests/ui/issues/issue-26709.rs rename to tests/ui/drop/dst-coerce-unsized-drop.rs index 5c31bd6562887..88dbd77c3d33d 100644 --- a/tests/ui/issues/issue-26709.rs +++ b/tests/ui/drop/dst-coerce-unsized-drop.rs @@ -1,4 +1,10 @@ +//! Regression test for . +//! Test value is still getting dropped after unsized coerce. +//! +//! Fat pointer was passed in two immediate args, but the drop invocation +//! used to accept one, which led to ICE. //@ run-pass + struct Wrapper<'a, T: ?Sized>(&'a mut i32, #[allow(dead_code)] T); impl<'a, T: ?Sized> Drop for Wrapper<'a, T> { diff --git a/tests/ui/issues/issue-26655.rs b/tests/ui/drop/enum-destructor-on-unwind.rs similarity index 78% rename from tests/ui/issues/issue-26655.rs rename to tests/ui/drop/enum-destructor-on-unwind.rs index 32c4b33a8c967..9d2655564ca8c 100644 --- a/tests/ui/issues/issue-26655.rs +++ b/tests/ui/drop/enum-destructor-on-unwind.rs @@ -1,10 +1,10 @@ +//! Regression test for . +//! Check that the destructors of simple enums are run on unwinding. //@ run-pass //@ needs-unwind //@ needs-threads //@ ignore-backends: gcc -// Check that the destructors of simple enums are run on unwinding - use std::sync::atomic::{Ordering, AtomicUsize}; use std::thread; diff --git a/tests/ui/drop/rc-trait-object-double-drop.rs b/tests/ui/drop/rc-trait-object-double-drop.rs new file mode 100644 index 0000000000000..cbed5b659c583 --- /dev/null +++ b/tests/ui/drop/rc-trait-object-double-drop.rs @@ -0,0 +1,30 @@ +//! Regression test for . +//! Test we don't drop twice when we take a reference through other +//! object like `Rc`. +//! +//! This used to drop Foo twice while coerced to a trait object. +//! `&T`, `Rc` and `&mut` dropped any unsized item each time +//! reference was taken. +//! +//! Value was being dropped when taking the address of an unsized field. +//@ run-pass + +use std::rc::Rc; + +struct Foo<'r>(&'r mut i32); + +impl<'r> Drop for Foo<'r> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +fn main() { + let mut drops = 0; + + { + let _: Rc = Rc::new(Foo(&mut drops)); + } + + assert_eq!(1, drops); +} diff --git a/tests/ui/issues/issue-41974.rs b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs similarity index 70% rename from tests/ui/issues/issue-41974.rs rename to tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs index 10c363479a374..9e046dee42cf7 100644 --- a/tests/ui/issues/issue-41974.rs +++ b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/41974 #[derive(Copy, Clone)] struct Flags; diff --git a/tests/ui/issues/issue-41974.stderr b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr similarity index 86% rename from tests/ui/issues/issue-41974.stderr rename to tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr index 2ae073dd1ba82..cc3488aaca0cf 100644 --- a/tests/ui/issues/issue-41974.stderr +++ b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr @@ -1,5 +1,5 @@ error[E0210]: type parameter `T` must be used as an argument to some local type (e.g., `MyStruct`) - --> $DIR/issue-41974.rs:7:6 + --> $DIR/drop-impl-for-type-param-with-trait-bound.rs:8:6 | LL | impl Drop for T where T: A { | ^ uncovered type parameter @@ -8,7 +8,7 @@ LL | impl Drop for T where T: A { = note: only traits defined in the current crate can be implemented for a type parameter error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions - --> $DIR/issue-41974.rs:7:18 + --> $DIR/drop-impl-for-type-param-with-trait-bound.rs:8:18 | LL | impl Drop for T where T: A { | ^ must be a struct, enum, or union in the current crate diff --git a/tests/ui/issues/issue-48132.rs b/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs similarity index 88% rename from tests/ui/issues/issue-48132.rs rename to tests/ui/dropck/dropck-resolves-associated-type-in-field.rs index d8d7167a4ce82..16a44cd0d6de4 100644 --- a/tests/ui/issues/issue-48132.rs +++ b/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/48132 // Regression test for #48132. This was failing due to problems around // the projection caching and dropck type enumeration. diff --git a/tests/ui/issues/issue-22777.rs b/tests/ui/dropck/no-dropck-overflow-on-deep-nesting.rs similarity index 82% rename from tests/ui/issues/issue-22777.rs rename to tests/ui/dropck/no-dropck-overflow-on-deep-nesting.rs index c95bb9cc3bb99..6553374189a32 100644 --- a/tests/ui/issues/issue-22777.rs +++ b/tests/ui/dropck/no-dropck-overflow-on-deep-nesting.rs @@ -1,7 +1,10 @@ +//! Regression test for . +//! +//! This test is reduced from librustc_ast. It is just checking that we +//! can successfully deal with a "deep" structure, which the drop-check +//! was hitting a recursion limit on at one point. + //@ check-pass -// This test is reduced from librustc_ast. It is just checking that we -// can successfully deal with a "deep" structure, which the drop-check -// was hitting a recursion limit on at one point. #![allow(non_camel_case_types)] diff --git a/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs b/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs new file mode 100644 index 0000000000000..f78f3907de9a2 --- /dev/null +++ b/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs @@ -0,0 +1,7 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/26641 +//@ run-pass +struct Parser<'a>(#[allow(dead_code)] Box); + +fn main() { + let _x = Parser(Box::new(|_|{})); +} diff --git a/tests/ui/issues/issue-23261.rs b/tests/ui/dst/match-dst-struct.rs similarity index 87% rename from tests/ui/issues/issue-23261.rs rename to tests/ui/dst/match-dst-struct.rs index 69f141e8bf870..1c3fca77c19b7 100644 --- a/tests/ui/issues/issue-23261.rs +++ b/tests/ui/dst/match-dst-struct.rs @@ -1,5 +1,6 @@ +//! Regression test for . +//! Matching on a DST struct should not trigger an LLVM assertion. //@ run-pass -// Matching on a DST struct should not trigger an LLVM assertion. struct Foo { a: i32, diff --git a/tests/ui/issues/issue-50781.rs b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs similarity index 81% rename from tests/ui/issues/issue-50781.rs rename to tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs index d837b848591f2..39eee9129e1dd 100644 --- a/tests/ui/issues/issue-50781.rs +++ b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/50781 trait Trait {} trait X { diff --git a/tests/ui/issues/issue-50781.stderr b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr similarity index 85% rename from tests/ui/issues/issue-50781.stderr rename to tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr index 4ba3166b6c581..81f031a2123fd 100644 --- a/tests/ui/issues/issue-50781.stderr +++ b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr @@ -1,12 +1,12 @@ error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-50781.rs:11:16 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:12:16 | LL | impl Trait for dyn X {} | ^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-50781.rs:4:8 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:5:8 | LL | trait X { | - this trait is not dyn compatible... @@ -16,14 +16,14 @@ LL | fn foo(&self) where Self: Trait; = help: only type `()` implements `X`; consider using it directly instead. error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-50781.rs:16:10 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:17:10 | LL | ::foo(&()); | ^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-50781.rs:4:8 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:5:8 | LL | trait X { | - this trait is not dyn compatible... diff --git a/tests/ui/issues/issue-26056.rs b/tests/ui/dyn-compatibility/supertrait-with-self-in-type-arg.rs similarity index 79% rename from tests/ui/issues/issue-26056.rs rename to tests/ui/dyn-compatibility/supertrait-with-self-in-type-arg.rs index 99d43ec792b3c..c4969b7f331bd 100644 --- a/tests/ui/issues/issue-26056.rs +++ b/tests/ui/dyn-compatibility/supertrait-with-self-in-type-arg.rs @@ -1,3 +1,5 @@ +//! Regression test for . + trait MapLookup { type MapValue; } diff --git a/tests/ui/issues/issue-26056.stderr b/tests/ui/dyn-compatibility/supertrait-with-self-in-type-arg.stderr similarity index 86% rename from tests/ui/issues/issue-26056.stderr rename to tests/ui/dyn-compatibility/supertrait-with-self-in-type-arg.stderr index c2168af94969b..1eff8855f760f 100644 --- a/tests/ui/issues/issue-26056.stderr +++ b/tests/ui/dyn-compatibility/supertrait-with-self-in-type-arg.stderr @@ -1,12 +1,12 @@ error[E0038]: the trait `Map` is not dyn compatible - --> $DIR/issue-26056.rs:20:17 + --> $DIR/supertrait-with-self-in-type-arg.rs:22:17 | LL | as &dyn Map; | ^^^^^^^^^^^^^^^^^^^^^^^^^ `Map` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-26056.rs:9:12 + --> $DIR/supertrait-with-self-in-type-arg.rs:11:12 | LL | trait Map: MapLookup<::Key> { | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter diff --git a/tests/ui/error-codes/E0264.rs b/tests/ui/error-codes/E0264.rs index 855644796ed45..ef3c8a1c3acd1 100644 --- a/tests/ui/error-codes/E0264.rs +++ b/tests/ui/error-codes/E0264.rs @@ -1,8 +1,8 @@ #![feature(lang_items)] extern "C" { - #[lang = "copy"] - fn copy(); //~ ERROR E0264 + #[lang = "copy"] //~ ERROR E0264 + fn copy(); } fn main() {} diff --git a/tests/ui/error-codes/E0264.stderr b/tests/ui/error-codes/E0264.stderr index 6442f42e689d6..b15ba468aa1a7 100644 --- a/tests/ui/error-codes/E0264.stderr +++ b/tests/ui/error-codes/E0264.stderr @@ -1,8 +1,8 @@ error[E0264]: unknown external lang item: `copy` - --> $DIR/E0264.rs:5:5 + --> $DIR/E0264.rs:4:5 | -LL | fn copy(); - | ^^^^^^^^^^ +LL | #[lang = "copy"] + | ^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0718.rs b/tests/ui/error-codes/E0718.rs index 358bc348ec76d..87e366c91a609 100644 --- a/tests/ui/error-codes/E0718.rs +++ b/tests/ui/error-codes/E0718.rs @@ -1,7 +1,7 @@ #![feature(lang_items)] // Box is expected to be a struct, so this will error. -#[lang = "owned_box"] //~ ERROR lang item must be applied to a struct +#[lang = "owned_box"] //~ ERROR `#[lang = "owned_box"]` attribute cannot be used on statics static X: u32 = 42; fn main() {} diff --git a/tests/ui/error-codes/E0718.stderr b/tests/ui/error-codes/E0718.stderr index e7784d193ba7b..873c714df8c2f 100644 --- a/tests/ui/error-codes/E0718.stderr +++ b/tests/ui/error-codes/E0718.stderr @@ -1,9 +1,10 @@ -error[E0718]: `owned_box` lang item must be applied to a struct +error: `#[lang = "owned_box"]` attribute cannot be used on statics --> $DIR/E0718.rs:4:1 | LL | #[lang = "owned_box"] - | ^^^^^^^^^^^^^^^^^^^^^ attribute should be applied to a struct, not a static + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[lang = "owned_box"]` can only be applied to structs error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0718`. diff --git a/tests/ui/explicit-tail-calls/no-unsized-arguments.aarch64.stderr b/tests/ui/explicit-tail-calls/no-unsized-arguments.aarch64.stderr new file mode 100644 index 0000000000000..33e190e1a4a7f --- /dev/null +++ b/tests/ui/explicit-tail-calls/no-unsized-arguments.aarch64.stderr @@ -0,0 +1,31 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/no-unsized-arguments.rs:40:42 + | +LL | extern "tail" fn unsized_argument(x: [u8]) -> u8 { + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +help: function arguments must have a statically known size, borrowed slices always have a known size + | +LL | extern "tail" fn unsized_argument(x: &[u8]) -> u8 { + | + + +error: unsized arguments cannot be used in a tail call + --> $DIR/no-unsized-arguments.rs:33:5 + | +LL | become unsized_argument(b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unsized argument of type `[u8]` + +error: unsized arguments cannot be used in a tail call + --> $DIR/no-unsized-arguments.rs:48:5 + | +LL | become unsized_argument(*b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unsized argument of type `[u8]` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/explicit-tail-calls/no-unsized-arguments.rs b/tests/ui/explicit-tail-calls/no-unsized-arguments.rs new file mode 100644 index 0000000000000..a0c5b53d8df0c --- /dev/null +++ b/tests/ui/explicit-tail-calls/no-unsized-arguments.rs @@ -0,0 +1,50 @@ +//@ add-minicore +//@ ignore-backends: gcc +//@ min-llvm-version: 22 +// +//@ revisions: x86 x86_64 aarch64 +// +//@ [x86] compile-flags: --target=i686-unknown-linux-gnu +//@ [x86] needs-llvm-components: x86 +//@ [x86_64] compile-flags: --target=x86_64-unknown-linux-gnu +//@ [x86_64] needs-llvm-components: x86 +//@ [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu +//@ [aarch64] needs-llvm-components: aarch64 +#![feature(explicit_tail_calls, rust_tail_cc, unsized_fn_params, no_core)] +#![allow(incomplete_features, internal_features)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +extern "C" { + fn extract(_: [u8]) -> u8; +} + +fn vanilla(b: [u8]) -> u8 { + fn unsized_argument(x: [u8]) -> u8 { + unsafe { extract(x) } + } + + // Non-tail call. + let _ = unsized_argument(b); + + become unsized_argument(b); + //~^ ERROR unsized arguments cannot be used in a tail call +} + +extern "tail" fn tailcc(b: &[u8]) -> u8 { + // `extern "tail"` is special because we also can't unsized parameters in standard definitions + // and calls. + extern "tail" fn unsized_argument(x: [u8]) -> u8 { + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time + unsafe { extract(x) } + } + + // Vanilla call. + let _ = unsized_argument(*b); + + become unsized_argument(*b); + //~^ ERROR unsized arguments cannot be used in a tail call +} diff --git a/tests/ui/explicit-tail-calls/no-unsized-arguments.x86.stderr b/tests/ui/explicit-tail-calls/no-unsized-arguments.x86.stderr new file mode 100644 index 0000000000000..33e190e1a4a7f --- /dev/null +++ b/tests/ui/explicit-tail-calls/no-unsized-arguments.x86.stderr @@ -0,0 +1,31 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/no-unsized-arguments.rs:40:42 + | +LL | extern "tail" fn unsized_argument(x: [u8]) -> u8 { + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +help: function arguments must have a statically known size, borrowed slices always have a known size + | +LL | extern "tail" fn unsized_argument(x: &[u8]) -> u8 { + | + + +error: unsized arguments cannot be used in a tail call + --> $DIR/no-unsized-arguments.rs:33:5 + | +LL | become unsized_argument(b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unsized argument of type `[u8]` + +error: unsized arguments cannot be used in a tail call + --> $DIR/no-unsized-arguments.rs:48:5 + | +LL | become unsized_argument(*b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unsized argument of type `[u8]` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/explicit-tail-calls/no-unsized-arguments.x86_64.stderr b/tests/ui/explicit-tail-calls/no-unsized-arguments.x86_64.stderr new file mode 100644 index 0000000000000..33e190e1a4a7f --- /dev/null +++ b/tests/ui/explicit-tail-calls/no-unsized-arguments.x86_64.stderr @@ -0,0 +1,31 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/no-unsized-arguments.rs:40:42 + | +LL | extern "tail" fn unsized_argument(x: [u8]) -> u8 { + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +help: function arguments must have a statically known size, borrowed slices always have a known size + | +LL | extern "tail" fn unsized_argument(x: &[u8]) -> u8 { + | + + +error: unsized arguments cannot be used in a tail call + --> $DIR/no-unsized-arguments.rs:33:5 + | +LL | become unsized_argument(b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unsized argument of type `[u8]` + +error: unsized arguments cannot be used in a tail call + --> $DIR/no-unsized-arguments.rs:48:5 + | +LL | become unsized_argument(*b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unsized argument of type `[u8]` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/explicit-tail-calls/signature-mismatch.rs b/tests/ui/explicit-tail-calls/signature-mismatch.rs index a32ac9d8bfee3..bed480f60f63b 100644 --- a/tests/ui/explicit-tail-calls/signature-mismatch.rs +++ b/tests/ui/explicit-tail-calls/signature-mismatch.rs @@ -1,5 +1,5 @@ #![expect(incomplete_features)] -#![feature(explicit_tail_calls)] +#![feature(explicit_tail_calls, rust_tail_cc)] #![feature(c_variadic)] fn _f0((): ()) { @@ -8,26 +8,30 @@ fn _f0((): ()) { fn _g0() {} - fn _f1() { become _g1(()); //~ error: mismatched signatures } fn _g1((): ()) {} - extern "C" fn _f2() { become _g2(); //~ error: mismatched function ABIs } fn _g2() {} - fn _f3() { become _g3(); //~ error: mismatched function ABIs } extern "C" fn _g3() {} +extern "tail" fn _tailcc() {} + +fn _f4() { + // tailcc does not need the signatures to match, + // but only tailcc can tail call tailcc. + become _tailcc(); //~ error: mismatched function ABIs +} fn main() {} diff --git a/tests/ui/explicit-tail-calls/signature-mismatch.stderr b/tests/ui/explicit-tail-calls/signature-mismatch.stderr index ba9e9dcb98483..6e26f1c075539 100644 --- a/tests/ui/explicit-tail-calls/signature-mismatch.stderr +++ b/tests/ui/explicit-tail-calls/signature-mismatch.stderr @@ -9,7 +9,7 @@ LL | become _g0(); = note: callee signature: `fn()` error: mismatched signatures - --> $DIR/signature-mismatch.rs:13:5 + --> $DIR/signature-mismatch.rs:12:5 | LL | become _g1(()); | ^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | become _g1(()); = note: callee signature: `fn(())` error: mismatched function ABIs - --> $DIR/signature-mismatch.rs:20:5 + --> $DIR/signature-mismatch.rs:18:5 | LL | become _g2(); | ^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | become _g2(); = note: caller ABI is `"C"`, while callee ABI is `"Rust"` error: mismatched function ABIs - --> $DIR/signature-mismatch.rs:27:5 + --> $DIR/signature-mismatch.rs:24:5 | LL | become _g3(); | ^^^^^^^^^^^^ @@ -36,5 +36,14 @@ LL | become _g3(); = note: `become` requires caller and callee to have the same ABI = note: caller ABI is `"Rust"`, while callee ABI is `"C"` -error: aborting due to 4 previous errors +error: mismatched function ABIs + --> $DIR/signature-mismatch.rs:34:5 + | +LL | become _tailcc(); + | ^^^^^^^^^^^^^^^^ + | + = note: `become` requires caller and callee to have the same ABI + = note: caller ABI is `"Rust"`, while callee ABI is `"tail"` + +error: aborting due to 5 previous errors diff --git a/tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs b/tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs new file mode 100644 index 0000000000000..9c9085ca1daca --- /dev/null +++ b/tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs @@ -0,0 +1,49 @@ +//@ run-pass +//@ ignore-backends: gcc +//@ min-llvm-version: 22 +//@ revisions: x86_64 aarch64 +// +// FIXME: enable x86 on LLVM 23. +//@ [x86_64] only-x86_64 +//@ [aarch64] only-aarch64 +#![feature(explicit_tail_calls, rust_tail_cc)] + +#[inline(never)] +pub extern "tail" fn add() -> u64 { + #[inline(never)] + extern "tail" fn add(a: u64, b: u64) -> u64 { + a.wrapping_add(b) + } + + become add(1, 2); +} + +#[inline(never)] +pub extern "tail" fn pass_struct(a: u64, d: u64) -> u64 { + #[derive(Clone, Copy)] + pub struct Large { + pub a: u64, + pub b: u64, + pub c: u64, + pub d: u64, + } + + #[inline(never)] + extern "tail" fn add(large: Large) -> u64 { + let _ = large.b; + let _ = large.c; + large.a.wrapping_add(large.d) + } + + let large = Large { a, b: 0xBBBB_BBBB_BBBB_BBBB, c: 0xCCCC_CCCC_CCCC_CCCC, d }; + become add(large); +} + +fn main() { + assert_eq!(add(), 3); + + // FIXME: LLVM 22 has a bug which makes this miscompile. + if false { + assert_eq!(pass_struct(5, 6), 5 + 6); + } +} diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs new file mode 100644 index 0000000000000..6f3ec783a4bfe --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.rs @@ -0,0 +1,16 @@ +// extern "rust-call" untuples the first (and only, except self) argument, this requires additional +// support for tail calls to work correctly. Since there doesn't seem to be a usecase for tail +// calling extern "rust-call" functions (extern "rust-call" only shows up in closures, which we +// disallow tail-calling anyway), we just disallow that. +// +// @ ignore-backends: gcc +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] +#![feature(unboxed_closures)] + +extern "rust-call" fn a(_: ()) { + become a(()); + //~^ error: ABI does not support guaranteed tail calls +} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr new file mode 100644 index 0000000000000..6662836672097 --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsupported-abi/rust-call.stderr @@ -0,0 +1,10 @@ +error: ABI does not support guaranteed tail calls + --> $DIR/rust-call.rs:12:5 + | +LL | become a(()); + | ^^^^^^^^^^^^ + | + = note: `become` is not supported for `extern "rust-call"` functions + +error: aborting due to 1 previous error + diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs index 5e64fd5d2af4a..94c6c85d28d27 100644 --- a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs @@ -1,6 +1,6 @@ fn main() { let number = 2; - Some(true).filter({ //~ ERROR expected a `FnOnce(&bool)` closure, found `bool` + Some(true).filter({ //~ ERROR expected an `FnOnce(&bool)` closure, found `bool` if number % 2 == 0 { number == 0 } else { diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr index c4c1c830afa6e..b8b4a0a17bcf6 100644 --- a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&bool)` closure, found `bool` +error[E0277]: expected an `FnOnce(&bool)` closure, found `bool` --> $DIR/block_instead_of_closure_in_arg.rs:3:23 | LL | Some(true).filter({ diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs index bb2e9c0a63c75..faad88764bf98 100644 --- a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs @@ -1,6 +1,6 @@ const x: usize =42; fn main() { - let p = Some(45).and_then({|x| //~ ERROR expected a `FnOnce({integer})` closure, found `Option` + let p = Some(45).and_then({|x| //~ ERROR expected an `FnOnce({integer})` closure, found `Option` 1 + 1; Some(x * 2) }); diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr index 54d3c6727f77f..ea41e77952ff5 100644 --- a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce({integer})` closure, found `Option` +error[E0277]: expected an `FnOnce({integer})` closure, found `Option` --> $DIR/ruby_style_closure_successful_parse.rs:3:31 | LL | let p = Some(45).and_then({|x| diff --git a/tests/ui/issues/issue-28600.rs b/tests/ui/extern/extern-c-method-with-str-param.rs similarity index 57% rename from tests/ui/issues/issue-28600.rs rename to tests/ui/extern/extern-c-method-with-str-param.rs index a5427b94a57c5..d0fe55bd1d0f1 100644 --- a/tests/ui/issues/issue-28600.rs +++ b/tests/ui/extern/extern-c-method-with-str-param.rs @@ -1,5 +1,6 @@ +//! Regression test for . +//! pub extern fn with parameter type &str inside struct impl caused ICE. //@ build-pass -// #28600 ICE: pub extern fn with parameter type &str inside struct impl struct Test; diff --git a/tests/ui/extern/extern-wrong-value-type.rs b/tests/ui/extern/extern-wrong-value-type.rs index 56c6cf1dfaba7..ae07a721e83ee 100644 --- a/tests/ui/extern/extern-wrong-value-type.rs +++ b/tests/ui/extern/extern-wrong-value-type.rs @@ -7,5 +7,5 @@ fn main() { // extern functions are extern "C" fn let _x: extern "C" fn() = f; // OK is_fn(f); - //~^ ERROR expected a `Fn()` closure, found `extern "C" fn() {f}` + //~^ ERROR expected an `Fn()` closure, found `extern "C" fn() {f}` } diff --git a/tests/ui/extern/extern-wrong-value-type.stderr b/tests/ui/extern/extern-wrong-value-type.stderr index 692a660117112..e1686f8a3ab9b 100644 --- a/tests/ui/extern/extern-wrong-value-type.stderr +++ b/tests/ui/extern/extern-wrong-value-type.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() {f}` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() {f}` --> $DIR/extern-wrong-value-type.rs:9:11 | LL | is_fn(f); diff --git a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs index 738c8daa1687f..a6cbbcc9347ce 100644 --- a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs +++ b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs @@ -1,9 +1,15 @@ //@ check-pass +#![deny(implicit_provenance_casts)] +//~^ WARNING unknown lint: `implicit_provenance_casts` + +// feature-gating also applies when the old names are helpfully replaced with the new one: #![deny(fuzzy_provenance_casts)] -//~^ WARNING unknown lint: `fuzzy_provenance_casts` +//~^ WARNING lint `fuzzy_provenance_casts` has been renamed to `implicit_provenance_casts` +//~| WARNING unknown lint: `implicit_provenance_casts` #![deny(lossy_provenance_casts)] -//~^ WARNING unknown lint: `lossy_provenance_casts` +//~^ WARNING lint `lossy_provenance_casts` has been renamed to `implicit_provenance_casts` +//~| WARNING unknown lint: `implicit_provenance_casts` fn main() { // no warnings emitted since the lints are not activated diff --git a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr index 3f3b49bc60671..4e7c835b57912 100644 --- a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr +++ b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr @@ -1,25 +1,50 @@ -warning: unknown lint: `fuzzy_provenance_casts` +warning: unknown lint: `implicit_provenance_casts` --> $DIR/feature-gate-strict_provenance_lints.rs:3:9 | +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `implicit_provenance_casts` lint is unstable + = note: see issue #130351 for more information + = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(unknown_lints)]` on by default + +warning: lint `fuzzy_provenance_casts` has been renamed to `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:7:9 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `implicit_provenance_casts` + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +warning: unknown lint: `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:7:9 + | LL | #![deny(fuzzy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: the `fuzzy_provenance_casts` lint is unstable + = note: the `implicit_provenance_casts` lint is unstable = note: see issue #130351 for more information = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `#[warn(unknown_lints)]` on by default -warning: unknown lint: `lossy_provenance_casts` - --> $DIR/feature-gate-strict_provenance_lints.rs:5:9 +warning: lint `lossy_provenance_casts` has been renamed to `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:10:9 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `implicit_provenance_casts` + +warning: unknown lint: `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:10:9 | LL | #![deny(lossy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: the `lossy_provenance_casts` lint is unstable + = note: the `implicit_provenance_casts` lint is unstable = note: see issue #130351 for more information = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 2 warnings emitted +warning: 5 warnings emitted diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs index 7b40c8760e368..d33625f06872e 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs @@ -9,7 +9,7 @@ struct Foo; impl Fn<()> for Foo { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `Fn` are experimental - //~| ERROR expected a `FnMut()` closure, found `Foo` + //~| ERROR expected an `FnMut()` closure, found `Foo` extern "rust-call" fn call(self, args: ()) -> () {} //~^ ERROR "rust-call" ABI is experimental and subject to change //~| ERROR `call` has an incompatible type for trait @@ -26,7 +26,7 @@ struct Bar; impl FnMut<()> for Bar { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `FnMut` are experimental - //~| ERROR expected a `FnOnce()` closure, found `Bar` + //~| ERROR expected an `FnOnce()` closure, found `Bar` extern "rust-call" fn call_mut(&self, args: ()) -> () {} //~^ ERROR "rust-call" ABI is experimental and subject to change //~| ERROR incompatible type for trait diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index b2a2eba9ad1da..56cfd6d0c52b9 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -87,7 +87,7 @@ LL | impl FnMut<()> for Bar { | = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error[E0277]: expected a `FnMut()` closure, found `Foo` +error[E0277]: expected an `FnMut()` closure, found `Foo` --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:9:17 | LL | impl Fn<()> for Foo { @@ -161,7 +161,7 @@ help: change the self-receiver type to match the trait LL | extern "rust-call" fn call_mut(&mut self, args: ()) -> () {} | +++ -error[E0277]: expected a `FnOnce()` closure, found `Bar` +error[E0277]: expected an `FnOnce()` closure, found `Bar` --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:26:20 | LL | impl FnMut<()> for Bar { diff --git a/tests/ui/fn/fn-trait-formatting.rs b/tests/ui/fn/fn-trait-formatting.rs index be74d1ca9be3c..a7b00491ade01 100644 --- a/tests/ui/fn/fn-trait-formatting.rs +++ b/tests/ui/fn/fn-trait-formatting.rs @@ -17,5 +17,5 @@ fn main() { //~| NOTE found struct `Box isize>` needs_fn(1); - //~^ ERROR expected a `Fn(isize)` closure, found `{integer}` + //~^ ERROR expected an `Fn(isize)` closure, found `{integer}` } diff --git a/tests/ui/fn/fn-trait-formatting.stderr b/tests/ui/fn/fn-trait-formatting.stderr index bfd86106bf923..0b86634a52e56 100644 --- a/tests/ui/fn/fn-trait-formatting.stderr +++ b/tests/ui/fn/fn-trait-formatting.stderr @@ -39,7 +39,7 @@ LL | let _: () = Box::new(|| -> isize { unimplemented!() }) as Box isize>` -error[E0277]: expected a `Fn(isize)` closure, found `{integer}` +error[E0277]: expected an `Fn(isize)` closure, found `{integer}` --> $DIR/fn-trait-formatting.rs:19:14 | LL | needs_fn(1); diff --git a/tests/ui/fn/issue-39259.rs b/tests/ui/fn/issue-39259.rs index d4569a9094b2f..d2b545e000b97 100644 --- a/tests/ui/fn/issue-39259.rs +++ b/tests/ui/fn/issue-39259.rs @@ -5,7 +5,7 @@ struct S; impl Fn(u32) -> u32 for S { //~^ ERROR associated item constraints are not allowed here [E0229] - //~| ERROR expected a `FnMut(u32)` closure, found `S` + //~| ERROR expected an `FnMut(u32)` closure, found `S` fn call(&self) -> u32 { //~^ ERROR method `call` has 1 parameter but the declaration in trait `call` has 2 5 diff --git a/tests/ui/fn/issue-39259.stderr b/tests/ui/fn/issue-39259.stderr index b932731b5275e..7f2f4ecdd8d9b 100644 --- a/tests/ui/fn/issue-39259.stderr +++ b/tests/ui/fn/issue-39259.stderr @@ -22,7 +22,7 @@ help: add the missing parameter from the trait LL | fn call(&self, args: Args) -> u32 { | ++++++++++++ -error[E0277]: expected a `FnMut(u32)` closure, found `S` +error[E0277]: expected an `FnMut(u32)` closure, found `S` --> $DIR/issue-39259.rs:6:25 | LL | impl Fn(u32) -> u32 for S { diff --git a/tests/ui/foreign/foreign-int-types.rs b/tests/ui/foreign/foreign-int-types.rs index d20a4c96ea0e2..bac9d8c603918 100644 --- a/tests/ui/foreign/foreign-int-types.rs +++ b/tests/ui/foreign/foreign-int-types.rs @@ -4,7 +4,7 @@ mod xx { extern "C" { - pub fn strlen(str: *const u8) -> usize; + pub fn strlen2(str: *const u8) -> usize; pub fn foo(x: isize, y: usize); } } diff --git a/tests/ui/issues/issue-26948.rs b/tests/ui/functional-struct-update/fru-on-enum-variant.rs similarity index 69% rename from tests/ui/issues/issue-26948.rs rename to tests/ui/functional-struct-update/fru-on-enum-variant.rs index 832c9641daa0d..db87ad43b38db 100644 --- a/tests/ui/issues/issue-26948.rs +++ b/tests/ui/functional-struct-update/fru-on-enum-variant.rs @@ -1,3 +1,5 @@ +//! Regression test for . + fn main() { enum Foo { A { x: u32 } } let orig = Foo::A { x: 5 }; diff --git a/tests/ui/issues/issue-26948.stderr b/tests/ui/functional-struct-update/fru-on-enum-variant.stderr similarity index 86% rename from tests/ui/issues/issue-26948.stderr rename to tests/ui/functional-struct-update/fru-on-enum-variant.stderr index 982c9758a3bc1..88a9a1c3d8ffd 100644 --- a/tests/ui/issues/issue-26948.stderr +++ b/tests/ui/functional-struct-update/fru-on-enum-variant.stderr @@ -1,5 +1,5 @@ error[E0436]: functional record update syntax requires a struct - --> $DIR/issue-26948.rs:4:22 + --> $DIR/fru-on-enum-variant.rs:6:22 | LL | Foo::A { x: 6, ..orig }; | ^^^^ diff --git a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs index c444ee9e1cdb4..edd9ad11ae778 100644 --- a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs +++ b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr index d98071efe8311..7821560d96090 100644 --- a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr +++ b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68642-broken-llvm-ir.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68643-broken-mir.rs b/tests/ui/generic-associated-types/issue-68643-broken-mir.rs index 39db51c0e2a21..23586f6af98bd 100644 --- a/tests/ui/generic-associated-types/issue-68643-broken-mir.rs +++ b/tests/ui/generic-associated-types/issue-68643-broken-mir.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } pub fn main() { diff --git a/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr b/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr index cd4c06a8660a8..99b56dd061933 100644 --- a/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr +++ b/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68643-broken-mir.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs b/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs index e379bce07d313..85b932a063c14 100644 --- a/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs +++ b/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr b/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr index 12f9949a0d304..4537091a60915 100644 --- a/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr +++ b/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68644-codegen-selection.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs index e69a08b0a3991..b8133148b1029 100644 --- a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs +++ b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr index 8b23f60953061..cc324f91d75eb 100644 --- a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr +++ b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68645-codegen-fulfillment.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/issues/issue-26217.rs b/tests/ui/higher-ranked/forall-implies-static.rs similarity index 66% rename from tests/ui/issues/issue-26217.rs rename to tests/ui/higher-ranked/forall-implies-static.rs index 422625e73c161..ef3e7a8303f4a 100644 --- a/tests/ui/issues/issue-26217.rs +++ b/tests/ui/higher-ranked/forall-implies-static.rs @@ -1,3 +1,5 @@ +//! Regression test for . + fn foo() where for<'a> T: 'a {} fn bar<'a>() { diff --git a/tests/ui/issues/issue-26217.stderr b/tests/ui/higher-ranked/forall-implies-static.stderr similarity index 83% rename from tests/ui/issues/issue-26217.stderr rename to tests/ui/higher-ranked/forall-implies-static.stderr index a875056781955..c74b6a12e7800 100644 --- a/tests/ui/issues/issue-26217.stderr +++ b/tests/ui/higher-ranked/forall-implies-static.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/issue-26217.rs:4:5 + --> $DIR/forall-implies-static.rs:6:5 | LL | fn bar<'a>() { | -- lifetime `'a` defined here @@ -7,7 +7,7 @@ LL | foo::<&'a i32>(); | ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` | note: due to a current limitation of the type system, this implies a `'static` lifetime - --> $DIR/issue-26217.rs:1:19 + --> $DIR/forall-implies-static.rs:3:19 | LL | fn foo() where for<'a> T: 'a {} | ^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-22872.rs b/tests/ui/higher-ranked/leak-check/trait-object-assoc-type-bound-unsatisfied.rs similarity index 85% rename from tests/ui/issues/issue-22872.rs rename to tests/ui/higher-ranked/leak-check/trait-object-assoc-type-bound-unsatisfied.rs index 5db2891e65e75..a171e6cdee453 100644 --- a/tests/ui/issues/issue-22872.rs +++ b/tests/ui/higher-ranked/leak-check/trait-object-assoc-type-bound-unsatisfied.rs @@ -1,3 +1,5 @@ +//! Regression test for . + trait Wrap<'b> { fn foo(&'b mut self); } diff --git a/tests/ui/issues/issue-22872.stderr b/tests/ui/higher-ranked/leak-check/trait-object-assoc-type-bound-unsatisfied.stderr similarity index 90% rename from tests/ui/issues/issue-22872.stderr rename to tests/ui/higher-ranked/leak-check/trait-object-assoc-type-bound-unsatisfied.stderr index 6ff710b113325..79ee006579e0b 100644 --- a/tests/ui/issues/issue-22872.stderr +++ b/tests/ui/higher-ranked/leak-check/trait-object-assoc-type-bound-unsatisfied.stderr @@ -1,12 +1,12 @@ error[E0277]: `

>::Item` is not an iterator - --> $DIR/issue-22872.rs:20:40 + --> $DIR/trait-object-assoc-type-bound-unsatisfied.rs:22:40 | LL | let _: Box Wrap<'b>> = Box::new(Wrapper(process)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `

>::Item` is not an iterator | = help: the trait `Iterator` is not implemented for `

>::Item` note: required for `Wrapper

` to implement `for<'b> Wrap<'b>` - --> $DIR/issue-22872.rs:7:13 + --> $DIR/trait-object-assoc-type-bound-unsatisfied.rs:9:13 | LL | impl<'b, P> Wrap<'b> for Wrapper

| ^^^^^^^^ ^^^^^^^^^^ diff --git a/tests/ui/issues/issue-28279.rs b/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs similarity index 64% rename from tests/ui/issues/issue-28279.rs rename to tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs index 23814b284e905..754e3ce2167a7 100644 --- a/tests/ui/issues/issue-28279.rs +++ b/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs @@ -1,4 +1,8 @@ +//! Regression test for . +//! Region variables escaped comparison for common supertype, which led +//! `Rc` and `*mut Fn(&T)` to break. //@ check-pass + #![allow(dead_code)] use std::rc::Rc; diff --git a/tests/ui/issues/issue-25810.rs b/tests/ui/higher-ranked/trait-bounds/infer-assoc-type-projection-type.rs similarity index 84% rename from tests/ui/issues/issue-25810.rs rename to tests/ui/higher-ranked/trait-bounds/infer-assoc-type-projection-type.rs index aa60e8afbef57..0e788a2a746bb 100644 --- a/tests/ui/issues/issue-25810.rs +++ b/tests/ui/higher-ranked/trait-bounds/infer-assoc-type-projection-type.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + fn main() { let x = X(15); let y = x.foo(); diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr index 36264b0d997b2..96cef0a6a5c9e 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(<_ as ATC<'a>>::Type)` closure, found `F` +error[E0277]: expected an `Fn(<_ as ATC<'a>>::Type)` closure, found `F` --> $DIR/issue-62529-3.rs:25:14 | LL | call(f, ()); diff --git a/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr index 1f14d2a80d0f3..a917c34b52ae5 100644 --- a/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr +++ b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr @@ -1,10 +1,10 @@ error[E0284]: type annotations needed - --> $DIR/rigid-equate-projections-in-higher-ranked-fn-signature.rs:27:50 + --> $DIR/rigid-equate-projections-in-higher-ranked-fn-signature.rs:27:12 | LL | let _: for<'a> fn(<_ as Trait<'a>>::Assoc) = foo::(); - | ^^^^^^^^^^ cannot infer type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type | - = note: cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc == >::Assoc` + = note: cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc == _` error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs index c87c8e90d741b..84d1d06234111 100644 --- a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs +++ b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs @@ -15,4 +15,5 @@ struct W(T); fn hello(_: W>) {} //~^ ERROR: the trait bound `usize: Foo` is not satisfied //~| ERROR: the trait bound `usize: Foo` is not satisfied +//~| ERROR: the type `W>` is not well-formed fn main() {} diff --git a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr index dec94730df615..09418b656f8ac 100644 --- a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr +++ b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr @@ -15,6 +15,12 @@ note: required by a bound in `A` LL | type A = T; | ^^^ required by this bound in `A` +error: the type `W>` is not well-formed + --> $DIR/alias-bounds-when-not-wf.rs:15:13 + | +LL | fn hello(_: W>) {} + | ^^^^^^^^^^^ + error[E0277]: the trait bound `usize: Foo` is not satisfied --> $DIR/alias-bounds-when-not-wf.rs:15:13 | @@ -27,6 +33,6 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/implied-bounds/issue-100690.rs b/tests/ui/implied-bounds/issue-100690.rs index b0dbf749c4670..4f8a2df9834f5 100644 --- a/tests/ui/implied-bounds/issue-100690.rs +++ b/tests/ui/implied-bounds/issue-100690.rs @@ -32,7 +32,7 @@ impl<'a, T: 'a> Handle<'a, T, UIView<'a, T>, Result<(), io::Error>> for TUIHandl F: FnOnce(&mut UIView<'a, T>) -> Result<(), io::Error> + Send + 'static, { real_dispatch(f) - //~^ ERROR expected a `FnOnce(&mut UIView<'_, T>)` closure, found `F` + //~^ ERROR expected an `FnOnce(&mut UIView<'_, T>)` closure, found `F` } } diff --git a/tests/ui/implied-bounds/issue-100690.stderr b/tests/ui/implied-bounds/issue-100690.stderr index d00895d8fc9cc..b5465e2f3ce08 100644 --- a/tests/ui/implied-bounds/issue-100690.stderr +++ b/tests/ui/implied-bounds/issue-100690.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&mut UIView<'_, T>)` closure, found `F` +error[E0277]: expected an `FnOnce(&mut UIView<'_, T>)` closure, found `F` --> $DIR/issue-100690.rs:34:23 | LL | real_dispatch(f) diff --git a/tests/ui/issues/issue-28936.rs b/tests/ui/inference/closure-arg-lifetime-by-ref.rs similarity index 90% rename from tests/ui/issues/issue-28936.rs rename to tests/ui/inference/closure-arg-lifetime-by-ref.rs index 96503f0711d27..f00a38ea061c8 100644 --- a/tests/ui/issues/issue-28936.rs +++ b/tests/ui/inference/closure-arg-lifetime-by-ref.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ check-pass + pub type Session = i32; pub struct StreamParser<'a, T> { _tokens: T, diff --git a/tests/ui/issues/issue-23041.rs b/tests/ui/inference/downcast-fn-ptr-placeholder.rs similarity index 54% rename from tests/ui/issues/issue-23041.rs rename to tests/ui/inference/downcast-fn-ptr-placeholder.rs index a1371521a0aa0..3b2d2c2857f9d 100644 --- a/tests/ui/issues/issue-23041.rs +++ b/tests/ui/inference/downcast-fn-ptr-placeholder.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! Previously ICEd with cat_expr error, fixed by delaying bug. + use std::any::Any; fn main() { diff --git a/tests/ui/issues/issue-23041.stderr b/tests/ui/inference/downcast-fn-ptr-placeholder.stderr similarity index 86% rename from tests/ui/issues/issue-23041.stderr rename to tests/ui/inference/downcast-fn-ptr-placeholder.stderr index b348a8695f6f2..0d1801ac644df 100644 --- a/tests/ui/issues/issue-23041.stderr +++ b/tests/ui/inference/downcast-fn-ptr-placeholder.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/issue-23041.rs:6:22 + --> $DIR/downcast-fn-ptr-placeholder.rs:9:22 | LL | b.downcast_ref::_>(); | ^^^^^^^^ cannot infer type of the type parameter `T` declared on the method `downcast_ref` diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr index f831df8fbd92e..8375055929ce8 100644 --- a/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.feature_new.stderr @@ -1,21 +1,21 @@ -error[E0271]: type mismatch resolving `_ == X1` +error[E0275]: overflow evaluating the requirement `X2 == _` --> $DIR/infinite-type-alias-mutual-recursion.rs:12:1 | LL | type X1 = X2; - | ^^^^^^^ types differ + | ^^^^^^^ -error[E0271]: type mismatch resolving `_ == X2` +error[E0275]: overflow evaluating the requirement `X3 == _` --> $DIR/infinite-type-alias-mutual-recursion.rs:16:1 | LL | type X2 = X3; - | ^^^^^^^ types differ + | ^^^^^^^ -error[E0271]: type mismatch resolving `_ == X3` +error[E0275]: overflow evaluating the requirement `X1 == _` --> $DIR/infinite-type-alias-mutual-recursion.rs:19:1 | LL | type X3 = X1; - | ^^^^^^^ types differ + | ^^^^^^^ error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs b/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs index 553d196891959..f876253b42b5f 100644 --- a/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs +++ b/tests/ui/infinite/infinite-type-alias-mutual-recursion.rs @@ -12,12 +12,12 @@ type X1 = X2; //[gated_old,gated_new]~^ ERROR cycle detected when expanding type alias `X1` //[feature_old]~^^ ERROR: overflow normalizing the type alias `X2` -//[feature_new]~^^^ ERROR: type mismatch resolving `_ == X1` +//[feature_new]~^^^ ERROR: overflow evaluating the requirement `X2 == _` type X2 = X3; //[feature_old]~^ ERROR: overflow normalizing the type alias `X3` -//[feature_new]~^^ ERROR: type mismatch resolving `_ == X2` +//[feature_new]~^^ ERROR: overflow evaluating the requirement `X3 == _` type X3 = X1; //[feature_old]~^ ERROR: overflow normalizing the type alias `X1` -//[feature_new]~^^ ERROR: type mismatch resolving `_ == X3` +//[feature_new]~^^ ERROR: overflow evaluating the requirement `X1 == _` fn main() {} diff --git a/tests/ui/intrinsics/const-eval-select-bad.rs b/tests/ui/intrinsics/const-eval-select-bad.rs index f407125129921..23a7a4be7ff10 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.rs +++ b/tests/ui/intrinsics/const-eval-select-bad.rs @@ -7,8 +7,8 @@ const fn not_fn_items() { const_eval_select((), || {}, || {}); //~^ ERROR const FnOnce()` is not satisfied const_eval_select((), 42, 0xDEADBEEF); - //~^ ERROR expected a `FnOnce()` closure - //~| ERROR expected a `FnOnce()` closure + //~^ ERROR expected an `FnOnce()` closure + //~| ERROR expected an `FnOnce()` closure } const fn foo(n: i32) -> i32 { diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr index d701f5ea90973..7880dfa36a8f4 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.stderr +++ b/tests/ui/intrinsics/const-eval-select-bad.stderr @@ -9,7 +9,7 @@ LL | const_eval_select((), || {}, || {}); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/const-eval-select-bad.rs:9:27 | LL | const_eval_select((), 42, 0xDEADBEEF); @@ -22,7 +22,7 @@ LL | const_eval_select((), 42, 0xDEADBEEF); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/const-eval-select-bad.rs:9:31 | LL | const_eval_select((), 42, 0xDEADBEEF); diff --git a/tests/ui/intrinsics/intrinsic-alignment.rs b/tests/ui/intrinsics/intrinsic-alignment.rs index 242eea1f8407d..d14bdf35e81f3 100644 --- a/tests/ui/intrinsics/intrinsic-alignment.rs +++ b/tests/ui/intrinsics/intrinsic-alignment.rs @@ -16,6 +16,7 @@ target_os = "solaris", target_os = "vxworks", target_os = "nto", + target_os = "qnx", target_vendor = "apple", ))] mod m { diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs index d2e5e158b7226..ed15f5bba9622 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs @@ -1,6 +1,6 @@ //@ normalize-stderr: "[[:xdigit:]]{2} __ ([[:xdigit:]]{2}\s){2}" -> "HEX_DUMP" #![feature(core_intrinsics)] -//@ ignore-parallel-frontend different alloc ids + const RAW_EQ_PADDING: bool = unsafe { std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) //~^ ERROR requires initialized memory diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr index 329da35297e1c..268527c0e5bf8 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr @@ -1,4 +1,4 @@ -error[E0080]: reading memory at ALLOC0[0x0..0x4], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x4], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory --> $DIR/intrinsic-raw_eq-const-bad.rs:5:5 | LL | std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) diff --git a/tests/ui/issues/issue-29663.rs b/tests/ui/intrinsics/volatile-composite-types.rs similarity index 90% rename from tests/ui/issues/issue-29663.rs rename to tests/ui/intrinsics/volatile-composite-types.rs index 9f7de6b72e117..edc71fc5dd386 100644 --- a/tests/ui/issues/issue-29663.rs +++ b/tests/ui/intrinsics/volatile-composite-types.rs @@ -1,5 +1,6 @@ +//! Regression test for . +//! `write_volatile` caused an LLVM assert with composite types. //@ run-pass -// write_volatile causes an LLVM assert with composite types use std::ptr::{read_volatile, write_volatile}; diff --git a/tests/ui/issues/issue-21974.rs b/tests/ui/issues/issue-21974.rs deleted file mode 100644 index 24e92ce7b9177..0000000000000 --- a/tests/ui/issues/issue-21974.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Test that (for now) we report an ambiguity error here, because -// specific trait relationships are ignored for the purposes of trait -// matching. This behavior should likely be improved such that this -// test passes. See #21974 for more details. - -trait Foo { - fn foo(self); -} - -fn foo<'a,'b,T>(x: &'a T, y: &'b T) - where &'a T : Foo, //~ ERROR type annotations needed - &'b T : Foo -{ - x.foo(); //~ ERROR type annotations needed - y.foo(); -} - -fn main() { } diff --git a/tests/ui/issues/issue-22008.rs b/tests/ui/issues/issue-22008.rs deleted file mode 100644 index b537ce8c2721c..0000000000000 --- a/tests/ui/issues/issue-22008.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass -pub fn main() { - let command = "a"; - - match command { - "foo" => println!("foo"), - _ => println!("{}", command), - } -} diff --git a/tests/ui/issues/issue-22370.rs b/tests/ui/issues/issue-22370.rs deleted file mode 100644 index bab0469c01193..0000000000000 --- a/tests/ui/issues/issue-22370.rs +++ /dev/null @@ -1,6 +0,0 @@ -trait A {} - -fn f(a: &dyn A) {} -//~^ ERROR E0393 - -fn main() {} diff --git a/tests/ui/issues/issue-22370.stderr b/tests/ui/issues/issue-22370.stderr deleted file mode 100644 index cd1580e844cad..0000000000000 --- a/tests/ui/issues/issue-22370.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0393]: the type parameter `T` must be explicitly specified - --> $DIR/issue-22370.rs:3:14 - | -LL | trait A {} - | --------------- type parameter `T` must be specified for this -LL | -LL | fn f(a: &dyn A) {} - | ^ - | - = note: because the parameter default references `Self`, the parameter must be specified on the trait object type -help: explicitly specify the type parameter - | -LL | fn f(a: &dyn A) {} - | +++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0393`. diff --git a/tests/ui/issues/issue-22629.rs b/tests/ui/issues/issue-22629.rs deleted file mode 100644 index 22da414650f85..0000000000000 --- a/tests/ui/issues/issue-22629.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass -#![allow(unused_imports)] -// Test transitive analysis for associated types. Collected types -// should be normalized and new obligations generated. - - -use std::borrow::{ToOwned, Cow}; - -fn assert_send(_: T) {} - -fn main() { - assert_send(Cow::Borrowed("foo")); -} diff --git a/tests/ui/issues/issue-22886.rs b/tests/ui/issues/issue-22886.rs deleted file mode 100644 index f8b576e094d8c..0000000000000 --- a/tests/ui/issues/issue-22886.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Regression test for #22886. - -fn crash_please() { - let mut iter = Newtype(Some(Box::new(0))); - let saved = iter.next().unwrap(); - println!("{}", saved); - iter.0 = None; - println!("{}", saved); -} - -struct Newtype(Option>); - -impl<'a> Iterator for Newtype { //~ ERROR E0207 - type Item = &'a Box; - - fn next(&mut self) -> Option<&Box> { - self.0.as_ref() - } -} - -fn main() { } diff --git a/tests/ui/issues/issue-23311.rs b/tests/ui/issues/issue-23311.rs deleted file mode 100644 index e827fa1670fb3..0000000000000 --- a/tests/ui/issues/issue-23311.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ run-pass - -// Test that we do not ICE when pattern matching an array against a slice. - -fn main() { - match "foo".as_bytes() { - b"food" => (), - &[b'f', ..] => (), - _ => () - } -} diff --git a/tests/ui/issues/issue-23354-2.rs b/tests/ui/issues/issue-23354-2.rs deleted file mode 100644 index a296477215ed1..0000000000000 --- a/tests/ui/issues/issue-23354-2.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-fail -//@ error-pattern:panic evaluated -//@ needs-subprocess - -#[allow(unused_variables)] -fn main() { - // This used to trigger an LLVM assertion during compilation - let x = [panic!("panic evaluated"); 2]; -} diff --git a/tests/ui/issues/issue-25515.rs b/tests/ui/issues/issue-25515.rs deleted file mode 100644 index 5eaea683e2128..0000000000000 --- a/tests/ui/issues/issue-25515.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ run-pass -use std::rc::Rc; - -struct Foo<'r>(&'r mut i32); - -impl<'r> Drop for Foo<'r> { - fn drop(&mut self) { - *self.0 += 1; - } -} - -fn main() { - let mut drops = 0; - - { - let _: Rc = Rc::new(Foo(&mut drops)); - } - - assert_eq!(1, drops); -} diff --git a/tests/ui/issues/issue-28776.rs b/tests/ui/issues/issue-28776.rs deleted file mode 100644 index e564ebcd110cb..0000000000000 --- a/tests/ui/issues/issue-28776.rs +++ /dev/null @@ -1,6 +0,0 @@ -use std::ptr; - -fn main() { - (&ptr::write)(1 as *mut _, 42); - //~^ ERROR E0133 -} diff --git a/tests/ui/issues/issue-29071.rs b/tests/ui/issues/issue-29071.rs deleted file mode 100644 index ab83914d5e80a..0000000000000 --- a/tests/ui/issues/issue-29071.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ check-pass -#![allow(dead_code)] -#![allow(non_upper_case_globals)] - -fn ret() -> u32 { - static x: u32 = 10; - x & if true { 10u32 } else { 20u32 } & x -} - -fn ret2() -> &'static u32 { - static x: u32 = 10; - if true { 10u32; } else { 20u32; } - &x -} - -fn main() {} diff --git a/tests/ui/issues/issue-29276.rs b/tests/ui/issues/issue-29276.rs deleted file mode 100644 index 9b05e6de671a5..0000000000000 --- a/tests/ui/issues/issue-29276.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ check-pass -#![allow(dead_code)] -struct S([u8; { struct Z; 0 }]); - -fn main() {} diff --git a/tests/ui/iterators/fold-iterator-error-23966.stderr b/tests/ui/iterators/fold-iterator-error-23966.stderr index 15249a935972a..2e27108e6a483 100644 --- a/tests/ui/iterators/fold-iterator-error-23966.stderr +++ b/tests/ui/iterators/fold-iterator-error-23966.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(_, char)` closure, found `()` +error[E0277]: expected an `FnMut(_, char)` closure, found `()` --> $DIR/fold-iterator-error-23966.rs:3:32 | LL | "".chars().fold(|_, _| (), ()); diff --git a/tests/ui/issues/issue-25343.rs b/tests/ui/label/label-in-closure-no-self-shadow.rs similarity index 52% rename from tests/ui/issues/issue-25343.rs rename to tests/ui/label/label-in-closure-no-self-shadow.rs index 878f248867766..ba573a598a945 100644 --- a/tests/ui/issues/issue-25343.rs +++ b/tests/ui/label/label-in-closure-no-self-shadow.rs @@ -1,4 +1,8 @@ +//! Regression test for . +//! Ensure we're not wrongly producing shadowing label warning. +//! More cases added from issue . //@ run-pass + #[allow(unused)] fn main() { || { @@ -6,8 +10,6 @@ fn main() { } }; - // More cases added from issue 31754 - 'label2: loop { break; } diff --git a/tests/ui/issues/issue-27042.rs b/tests/ui/label/loop-label-included-in-span.rs similarity index 80% rename from tests/ui/issues/issue-27042.rs rename to tests/ui/label/loop-label-included-in-span.rs index 517c1f2e6c7cc..d6eee79cfa1b9 100644 --- a/tests/ui/issues/issue-27042.rs +++ b/tests/ui/label/loop-label-included-in-span.rs @@ -1,4 +1,5 @@ -// Regression test for #27042. Test that a loop's label is included in its span. +//! Regression test for . +//! Test that a loop's label is included in its span. fn main() { let _: i32 = diff --git a/tests/ui/issues/issue-27042.stderr b/tests/ui/label/loop-label-included-in-span.stderr similarity index 87% rename from tests/ui/issues/issue-27042.stderr rename to tests/ui/label/loop-label-included-in-span.stderr index 6586e61f2f644..803d5a7747eaa 100644 --- a/tests/ui/issues/issue-27042.stderr +++ b/tests/ui/label/loop-label-included-in-span.stderr @@ -1,5 +1,5 @@ warning: denote infinite loops with `loop { ... }` - --> $DIR/issue-27042.rs:8:9 + --> $DIR/loop-label-included-in-span.rs:9:9 | LL | / 'b: LL | | @@ -9,7 +9,7 @@ LL | | while true { break }; // but here we cite the whole loop = note: `#[warn(while_true)]` on by default error[E0308]: mismatched types - --> $DIR/issue-27042.rs:6:16 + --> $DIR/loop-label-included-in-span.rs:7:16 | LL | let _: i32 = | - expected because of this assignment @@ -25,7 +25,7 @@ LL | loop { break 42 }; | ++ error[E0308]: mismatched types - --> $DIR/issue-27042.rs:8:9 + --> $DIR/loop-label-included-in-span.rs:9:9 | LL | let _: i32 = | --- expected due to this @@ -35,7 +35,7 @@ LL | | while true { break }; // but here we cite the whole loop | |____________________________^ expected `i32`, found `()` error[E0308]: mismatched types - --> $DIR/issue-27042.rs:12:9 + --> $DIR/loop-label-included-in-span.rs:13:9 | LL | / 'c: LL | | for _ in None { break }; // but here we cite the whole loop @@ -44,7 +44,7 @@ LL | | for _ in None { break }; // but here we cite the whole loop = note: `for` loops evaluate to unit type `()` error[E0308]: mismatched types - --> $DIR/issue-27042.rs:15:9 + --> $DIR/loop-label-included-in-span.rs:16:9 | LL | let _: i32 = | --- expected due to this diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.rs b/tests/ui/lang-items/start_lang_item_with_target_feature.rs index 19036819d3d80..61f34d0321202 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.rs +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.rs @@ -18,7 +18,7 @@ pub trait Sized: MetaSized {} #[lang = "start"] #[target_feature(enable = "avx2")] -//~^ ERROR `start` lang item function is not allowed to have `#[target_feature]` +//~^ ERROR #[target_feature]` cannot be applied to a lang item function fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { 0 } diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.stderr b/tests/ui/lang-items/start_lang_item_with_target_feature.stderr index ce0b1d7557445..8611aa7c7628d 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.stderr +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.stderr @@ -1,11 +1,13 @@ -error: `start` lang item function is not allowed to have `#[target_feature]` +error: `#[target_feature]` cannot be applied to a lang item function --> $DIR/start_lang_item_with_target_feature.rs:20:1 | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { - | ------------------------------------------------------------------------------------------- `start` lang item function is not allowed to have `#[target_feature]` +LL | / fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { +LL | | 0 +LL | | } + | |_- lang item function is not allowed to have `#[target_feature]` error: aborting due to 1 previous error diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr index 159bfb8916224..dbd982ea94c36 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr @@ -1,20 +1,20 @@ -error[E0271]: type mismatch resolving `_ == Loop` +error[E0275]: overflow evaluating the requirement `Loop == _` --> $DIR/inherent-impls-overflow.rs:8:1 | LL | type Loop = Loop; - | ^^^^^^^^^ types differ + | ^^^^^^^^^ -error[E0271]: type mismatch resolving `_ == Loop` +error[E0275]: overflow evaluating the requirement `Loop == _` --> $DIR/inherent-impls-overflow.rs:12:1 | LL | impl Loop {} - | ^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^ -error[E0271]: type mismatch resolving `_ == Loop` +error[E0275]: overflow evaluating the requirement `Loop == _` --> $DIR/inherent-impls-overflow.rs:12:6 | LL | impl Loop {} - | ^^^^ types differ + | ^^^^ error[E0275]: overflow evaluating the requirement `Poly1<(T,)> == _` --> $DIR/inherent-impls-overflow.rs:17:1 @@ -50,5 +50,4 @@ LL | impl Poly0<()> {} error: aborting due to 7 previous errors -Some errors have detailed explanations: E0271, E0275. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs index ac2c29d621159..66a81321624b0 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs @@ -7,12 +7,12 @@ type Loop = Loop; //[current]~^ ERROR overflow normalizing the type alias `Loop` -//[next]~^^ ERROR type mismatch resolving `_ == Loop` +//[next]~^^ ERROR overflow evaluating the requirement `Loop == _` impl Loop {} //[current]~^ ERROR overflow normalizing the type alias `Loop` -//[next]~^^ ERROR type mismatch resolving `_ == Loop` -//[next]~| ERROR type mismatch resolving `_ == Loop` +//[next]~^^ ERROR overflow evaluating the requirement `Loop == _` +//[next]~| ERROR overflow evaluating the requirement `Loop == _` type Poly0 = Poly1<(T,)>; //[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` diff --git a/tests/ui/issues/issue-28999.rs b/tests/ui/lifetimes/field-borrow-lifetime-inference.rs similarity index 62% rename from tests/ui/issues/issue-28999.rs rename to tests/ui/lifetimes/field-borrow-lifetime-inference.rs index 572a0beff61f6..a8582dbd29b62 100644 --- a/tests/ui/issues/issue-28999.rs +++ b/tests/ui/lifetimes/field-borrow-lifetime-inference.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! `this.v` was not constrained and inferred `'a`. //@ check-pass + pub struct Xyz<'a, V> { pub v: (V, &'a u32), } diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs index 85eeb5d4c901e..ab3bf619f83b2 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs @@ -4,14 +4,13 @@ use std::future::Future; async fn wrapper(f: F) -//~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` -//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` -//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` +//~^ ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` +//~| ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` +//~| ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` where -F:, -for<'a> >::Output: Future + 'a, + F:, + for<'a> >::Output: Future + 'a, { - //~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` let mut i = 41; &mut i; } diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr index 6da9f7380d597..7a825b93461c0 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr @@ -1,15 +1,15 @@ -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:1 | LL | / async fn wrapper(f: F) ... | -LL | | F:, -LL | | for<'a> >::Output: Future + 'a, - | |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` +LL | | F:, +LL | | for<'a> >::Output: Future + 'a, + | |______________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` | = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:26 | LL | async fn wrapper(f: F) @@ -17,7 +17,7 @@ LL | async fn wrapper(f: F) | = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:26 | LL | async fn wrapper(f: F) @@ -26,18 +26,6 @@ LL | async fn wrapper(f: F) = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` - --> $DIR/issue-76168-hr-outlives-3.rs:13:1 - | -LL | / { -LL | | -LL | | let mut i = 41; -LL | | &mut i; -LL | | } - | |_^ expected an `FnOnce(&'a mut i32)` closure, found `i32` - | - = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/lifetimes/issue-95023.rs b/tests/ui/lifetimes/issue-95023.rs index 1faae50d9f158..d26317d2b9058 100644 --- a/tests/ui/lifetimes/issue-95023.rs +++ b/tests/ui/lifetimes/issue-95023.rs @@ -4,7 +4,7 @@ impl Fn(&isize) for Error { //~^ ERROR manual implementations of `Fn` are experimental [E0183] //~| ERROR associated item constraints are not allowed here [E0229] //~| ERROR not all trait items implemented - //~| ERROR expected a `FnMut(&isize)` closure, found `Error` + //~| ERROR expected an `FnMut(&isize)` closure, found `Error` fn foo(&self) -> Self::B<{ N }>; //~^ ERROR associated function in `impl` without body //~| ERROR method `foo` is not a member of trait `Fn` [E0407] diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index 013bc51ff78a4..afb627de1c1bb 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -40,7 +40,7 @@ LL | impl Fn(&isize) for Error { | = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut(&isize)` closure, found `Error` +error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-95023.rs:3:21 | LL | impl Fn(&isize) for Error { diff --git a/tests/ui/issues/issue-27942.rs b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs similarity index 71% rename from tests/ui/issues/issue-27942.rs rename to tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs index 1c2e6456937bb..91a18bfbf4390 100644 --- a/tests/ui/issues/issue-27942.rs +++ b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! Test internal compiler structs do not leak into error message. //@ dont-require-annotations: NOTE pub trait Resources<'a> {} diff --git a/tests/ui/issues/issue-27942.stderr b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr similarity index 76% rename from tests/ui/issues/issue-27942.stderr rename to tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr index 671875aa7e2ac..bab0f4c180958 100644 --- a/tests/ui/issues/issue-27942.stderr +++ b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-27942.rs:7:25 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:25 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -7,18 +7,18 @@ LL | fn select(&self) -> BufferViewHandle; = note: expected trait `Resources<'_>` found trait `Resources<'a>` note: the lifetime `'a` as defined here... - --> $DIR/issue-27942.rs:5:18 + --> $DIR/trait-method-return-lifetime-mismatch.rs:7:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ note: ...does not necessarily outlive the anonymous lifetime defined here - --> $DIR/issue-27942.rs:7:15 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:15 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^ error[E0308]: mismatched types - --> $DIR/issue-27942.rs:7:25 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:25 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -26,12 +26,12 @@ LL | fn select(&self) -> BufferViewHandle; = note: expected trait `Resources<'_>` found trait `Resources<'a>` note: the anonymous lifetime defined here... - --> $DIR/issue-27942.rs:7:15 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:15 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined here - --> $DIR/issue-27942.rs:5:18 + --> $DIR/trait-method-return-lifetime-mismatch.rs:7:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ diff --git a/tests/ui/issues/issue-29048.rs b/tests/ui/lifetimes/no-spurious-ref-outlives-data.rs similarity index 69% rename from tests/ui/issues/issue-29048.rs rename to tests/ui/lifetimes/no-spurious-ref-outlives-data.rs index d379b67251c39..bf9cbe7ecfdf7 100644 --- a/tests/ui/issues/issue-29048.rs +++ b/tests/ui/lifetimes/no-spurious-ref-outlives-data.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! This produced incorrect warning. //@ check-pass + pub struct Chan; pub struct ChanSelect<'c, T> { chans: Vec<(&'c Chan, T)>, diff --git a/tests/ui/lint/auxiliary/lint_stability.rs b/tests/ui/lint/auxiliary/lint_stability.rs index 99c29dcdda677..bc2d47aebbc29 100644 --- a/tests/ui/lint/auxiliary/lint_stability.rs +++ b/tests/ui/lint/auxiliary/lint_stability.rs @@ -1,5 +1,6 @@ #![crate_name="lint_stability"] #![crate_type = "lib"] +#![feature(extern_types)] #![feature(staged_api)] #![feature(associated_type_defaults)] #![stable(feature = "lint_stability", since = "1.0.0")] @@ -186,3 +187,8 @@ macro_rules! macro_test_arg { macro_rules! macro_test_arg_nested { ($func:ident) => (macro_test_arg!($func())); } + +extern "C" { + #[unstable(feature = "unstable_test_feature", issue = "none")] + pub type UnstableForeignType; +} diff --git a/tests/ui/lint/c-void-returns.rs b/tests/ui/lint/c-void-returns.rs new file mode 100644 index 0000000000000..7531da3d72f41 --- /dev/null +++ b/tests/ui/lint/c-void-returns.rs @@ -0,0 +1,22 @@ +#![allow(unused)] +#![deny(c_void_returns)] + +use std::ffi::c_void; +use std::ptr; + +fn foo() -> c_void { //~ ERROR c_void + unreachable!() +} + +fn bar() -> *mut c_void { + ptr::null_mut() +} + +unsafe extern "C" { + fn baz() -> c_void; //~ ERROR c_void + fn quux() -> *const c_void; +} + +type Xyzzy = fn() -> c_void; //~ ERROR c_void + +fn main() {} diff --git a/tests/ui/lint/c-void-returns.stderr b/tests/ui/lint/c-void-returns.stderr new file mode 100644 index 0000000000000..7dc724955ecb4 --- /dev/null +++ b/tests/ui/lint/c-void-returns.stderr @@ -0,0 +1,38 @@ +error: `c_void` should not be used as a return type + --> $DIR/c-void-returns.rs:7:13 + | +LL | fn foo() -> c_void { + | ----^^^^^^ + | | + | help: remove the return type to implicitly return `()` + | + = help: returning `()` in Rust is equivalent to returning `void` in C +note: the lint level is defined here + --> $DIR/c-void-returns.rs:2:9 + | +LL | #![deny(c_void_returns)] + | ^^^^^^^^^^^^^^ + +error: declarations returning `c_void` are not compatible with C functions returning `void` + --> $DIR/c-void-returns.rs:16:17 + | +LL | fn baz() -> c_void; + | ----^^^^^^ + | | + | help: remove the return type to implicitly return `()` + | + = help: returning `()` in Rust is equivalent to returning `void` in C + = note: `c_void` is only used through raw pointers for compatibility with `void` pointers + +error: `c_void` should not be used as a return type + --> $DIR/c-void-returns.rs:20:22 + | +LL | type Xyzzy = fn() -> c_void; + | ----^^^^^^ + | | + | help: remove the return type to implicitly return `()` + | + = help: returning `()` in Rust is equivalent to returning `void` in C + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs b/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs new file mode 100644 index 0000000000000..22975b45a3b4d --- /dev/null +++ b/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs @@ -0,0 +1,21 @@ +//@ check-pass + +//! this test checks that irregular recursive types do not cause stack overflow in ImproperCTypes +//! Issue: https://github.com/rust-lang/rust/issues/94223 + +#![deny(improper_ctypes, improper_ctypes_definitions)] + +use std::marker::PhantomData; + +#[repr(C)] +struct A { + a: *const A>, // without a recursion limit, checking this ends up creating checks for + // infinitely deep types the likes of `A>>>>>` + p: PhantomData, +} + +extern "C" { + fn f(a: *const A<()>); +} + +fn main() {} diff --git a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs index 61e95dc5a464c..9aa9be052d761 100644 --- a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs +++ b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs @@ -1,8 +1,10 @@ -//@ check-pass +//! This test checks that the depth limit of the ImproperCTypes lints counts the depth +//! of a type properly. +//! Issue: https://github.com/rust-lang/rust/issues/130757 #![recursion_limit = "5"] #![allow(unused)] -#![deny(improper_ctypes)] +#![deny(improper_ctypes_definitions)] #[repr(C)] struct F1(*const ()); @@ -15,7 +17,7 @@ struct F4(*const ()); #[repr(C)] struct F5(*const ()); #[repr(C)] -struct F6(*const ()); +struct F6([char;8]); //oops! #[repr(C)] struct B { @@ -24,9 +26,13 @@ struct B { f3: F3, f4: F4, f5: F5, + // If the depth counter misbehaves, it will believe `f6` is "too deep" without good reason. + // And in response, it will assume `B` is safe. + // so, the error linked to F6 means the test succeeds. f6: F6, } extern "C" fn foo(_: B) {} +//~^ ERROR: uses type `char` fn main() {} diff --git a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr new file mode 100644 index 0000000000000..7a47da8a788f6 --- /dev/null +++ b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr @@ -0,0 +1,16 @@ +error: `extern` fn uses type `char`, which is not FFI-safe + --> $DIR/lint-non-recursion-limit.rs:35:22 + | +LL | extern "C" fn foo(_: B) {} + | ^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent +note: the lint level is defined here + --> $DIR/lint-non-recursion-limit.rs:7:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/lint-stability.rs b/tests/ui/lint/lint-stability.rs index f080b5e4bbeb1..c8712397189c7 100644 --- a/tests/ui/lint/lint-stability.rs +++ b/tests/ui/lint/lint-stability.rs @@ -6,7 +6,7 @@ #![allow(deprecated)] #![allow(dead_code)] #![feature(staged_api)] - +#![feature(extern_types)] #![stable(feature = "rust1", since = "1.0.0")] #[macro_use] @@ -18,6 +18,9 @@ mod cross_crate { use lint_stability::*; + fn test_foreign_type(_: &mut UnstableForeignType) { //~ ERROR use of unstable library feature + } + fn test() { type Foo = MethodTester; let foo = MethodTester; diff --git a/tests/ui/lint/lint-stability.stderr b/tests/ui/lint/lint-stability.stderr index 53d31b16811ab..6316f470349ad 100644 --- a/tests/ui/lint/lint-stability.stderr +++ b/tests/ui/lint/lint-stability.stderr @@ -8,7 +8,16 @@ LL | extern crate stability_cfg2; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:45:9 + --> $DIR/lint-stability.rs:21:34 + | +LL | fn test_foreign_type(_: &mut UnstableForeignType) { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` 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[E0658]: use of unstable library feature `unstable_test_feature` + --> $DIR/lint-stability.rs:48:9 | LL | deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^ @@ -17,7 +26,7 @@ LL | deprecated_unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:47:9 + --> $DIR/lint-stability.rs:50:9 | LL | Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +35,7 @@ LL | Trait::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:49:9 + --> $DIR/lint-stability.rs:52:9 | LL | ::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +44,7 @@ LL | ::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:52:9 + --> $DIR/lint-stability.rs:55:9 | LL | deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +53,7 @@ LL | deprecated_unstable_text(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:54:9 + --> $DIR/lint-stability.rs:57:9 | LL | Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +62,7 @@ LL | Trait::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:56:9 + --> $DIR/lint-stability.rs:59:9 | LL | ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +71,7 @@ LL | ::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:59:9 + --> $DIR/lint-stability.rs:62:9 | LL | unstable(); | ^^^^^^^^ @@ -71,7 +80,7 @@ LL | unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:60:9 + --> $DIR/lint-stability.rs:63:9 | LL | Trait::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +89,7 @@ LL | Trait::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:61:9 + --> $DIR/lint-stability.rs:64:9 | LL | ::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +98,7 @@ LL | ::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:63:9 + --> $DIR/lint-stability.rs:66:9 | LL | unstable_text(); | ^^^^^^^^^^^^^ @@ -98,7 +107,7 @@ LL | unstable_text(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:65:9 + --> $DIR/lint-stability.rs:68:9 | LL | Trait::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +116,7 @@ LL | Trait::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:67:9 + --> $DIR/lint-stability.rs:70:9 | LL | ::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -116,7 +125,7 @@ LL | ::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:99:17 + --> $DIR/lint-stability.rs:102:17 | LL | let _ = DeprecatedUnstableStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -125,7 +134,7 @@ LL | let _ = DeprecatedUnstableStruct { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:103:17 + --> $DIR/lint-stability.rs:106:17 | LL | let _ = UnstableStruct { i: 0 }; | ^^^^^^^^^^^^^^ @@ -134,7 +143,7 @@ LL | let _ = UnstableStruct { i: 0 }; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:107:17 + --> $DIR/lint-stability.rs:110:17 | LL | let _ = DeprecatedUnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -143,7 +152,7 @@ LL | let _ = DeprecatedUnstableUnitStruct; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:109:17 + --> $DIR/lint-stability.rs:112:17 | LL | let _ = UnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^ @@ -152,7 +161,7 @@ LL | let _ = UnstableUnitStruct; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:113:17 + --> $DIR/lint-stability.rs:116:17 | LL | let _ = Enum::DeprecatedUnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -161,7 +170,7 @@ LL | let _ = Enum::DeprecatedUnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:115:17 + --> $DIR/lint-stability.rs:118:17 | LL | let _ = Enum::UnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^ @@ -170,7 +179,7 @@ LL | let _ = Enum::UnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:119:17 + --> $DIR/lint-stability.rs:122:17 | LL | let _ = DeprecatedUnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,7 +188,7 @@ LL | let _ = DeprecatedUnstableTupleStruct (1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:121:17 + --> $DIR/lint-stability.rs:124:17 | LL | let _ = UnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^ @@ -188,7 +197,7 @@ LL | let _ = UnstableTupleStruct (1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:130:25 + --> $DIR/lint-stability.rs:133:25 | LL | macro_test_arg!(deprecated_unstable_text()); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +206,7 @@ LL | macro_test_arg!(deprecated_unstable_text()); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:144:9 + --> $DIR/lint-stability.rs:147:9 | LL | Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +215,7 @@ LL | Trait::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:146:9 + --> $DIR/lint-stability.rs:149:9 | LL | ::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -215,7 +224,7 @@ LL | ::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:148:9 + --> $DIR/lint-stability.rs:151:9 | LL | Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -224,7 +233,7 @@ LL | Trait::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:150:9 + --> $DIR/lint-stability.rs:153:9 | LL | ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +242,7 @@ LL | ::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:152:9 + --> $DIR/lint-stability.rs:155:9 | LL | Trait::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^ @@ -242,7 +251,7 @@ LL | Trait::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:153:9 + --> $DIR/lint-stability.rs:156:9 | LL | ::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -251,7 +260,7 @@ LL | ::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:154:9 + --> $DIR/lint-stability.rs:157:9 | LL | Trait::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -260,7 +269,7 @@ LL | Trait::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:156:9 + --> $DIR/lint-stability.rs:159:9 | LL | ::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -269,7 +278,7 @@ LL | ::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:172:10 + --> $DIR/lint-stability.rs:175:10 | LL | impl UnstableTrait for S { } | ^^^^^^^^^^^^^ @@ -278,7 +287,7 @@ LL | impl UnstableTrait for S { } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:174:24 + --> $DIR/lint-stability.rs:177:24 | LL | trait LocalTrait : UnstableTrait { } | ^^^^^^^^^^^^^ @@ -287,7 +296,7 @@ LL | trait LocalTrait : UnstableTrait { } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:179:9 + --> $DIR/lint-stability.rs:182:9 | LL | fn trait_unstable(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +305,7 @@ LL | fn trait_unstable(&self) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:184:5 + --> $DIR/lint-stability.rs:187:5 | LL | extern crate inherited_stability; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -305,7 +314,7 @@ LL | extern crate inherited_stability; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:185:9 + --> $DIR/lint-stability.rs:188:9 | LL | use self::inherited_stability::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -314,7 +323,7 @@ LL | use self::inherited_stability::*; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:188:9 + --> $DIR/lint-stability.rs:191:9 | LL | unstable(); | ^^^^^^^^ @@ -323,7 +332,7 @@ LL | unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:191:9 + --> $DIR/lint-stability.rs:194:9 | LL | stable_mod::unstable(); | ^^^^^^^^^^^^^^^^^^^^ @@ -332,7 +341,7 @@ LL | stable_mod::unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:194:9 + --> $DIR/lint-stability.rs:197:9 | LL | unstable_mod::deprecated(); | ^^^^^^^^^^^^ @@ -341,7 +350,7 @@ LL | unstable_mod::deprecated(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:195:9 + --> $DIR/lint-stability.rs:198:9 | LL | unstable_mod::unstable(); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +359,7 @@ LL | unstable_mod::unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:197:17 + --> $DIR/lint-stability.rs:200:17 | LL | let _ = Unstable::UnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -359,7 +368,7 @@ LL | let _ = Unstable::UnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:198:17 + --> $DIR/lint-stability.rs:201:17 | LL | let _ = Unstable::StableVariant; | ^^^^^^^^ @@ -368,7 +377,7 @@ LL | let _ = Unstable::StableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:88:48 + --> $DIR/lint-stability.rs:91:48 | LL | struct S1(T::TypeUnstable); | ^^^^^^^^^^^^^^^ @@ -377,7 +386,7 @@ LL | struct S1(T::TypeUnstable); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:92:13 + --> $DIR/lint-stability.rs:95:13 | LL | TypeUnstable = u8, | ^^^^^^^^^^^^^^^^^ @@ -385,6 +394,6 @@ LL | TypeUnstable = u8, = help: add `#![feature(unstable_test_feature)]` 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: aborting due to 43 previous errors +error: aborting due to 44 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs index 187209d4e207e..c9f494200b30a 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs @@ -1,7 +1,7 @@ #![feature(strict_provenance_lints)] -#![deny(fuzzy_provenance_casts)] +#![deny(implicit_provenance_casts)] fn main() { let dangling = 16_usize as *const u8; - //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` + //~^ ERROR cast from `usize` to `*const u8` implicitly relies on exposed provenance } diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr index f5eec6fc65667..b88e3d4a9c738 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr @@ -1,16 +1,17 @@ -error: strict provenance disallows casting integer `usize` to pointer `*const u8` +error: cast from `usize` to `*const u8` implicitly relies on exposed provenance --> $DIR/lint-strict-provenance-fuzzy-casts.rs:5:20 | LL | let dangling = 16_usize as *const u8; | ^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9 | -LL | #![deny(fuzzy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ -help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use `.with_addr()` to adjust the address of a valid pointer in the same allocation | LL - let dangling = 16_usize as *const u8; LL + let dangling = (...).with_addr(16_usize); diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.rs b/tests/ui/lint/lint-strict-provenance-lossy-casts.rs index 395dc75f82555..8f27d170ddec4 100644 --- a/tests/ui/lint/lint-strict-provenance-lossy-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.rs @@ -1,18 +1,18 @@ #![feature(strict_provenance_lints)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] fn main() { let x: u8 = 37; let addr: usize = &x as *const u8 as usize; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` implicitly exposes pointer provenance let addr_32bit = &x as *const u8 as u32; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` + //~^ ERROR cast from `*const u8` to `u32` implicitly exposes pointer provenance // don't add unnecessary parens in the suggestion let ptr = &x as *const u8; let ptr_addr = ptr as usize; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` implicitly exposes pointer provenance let ptr_addr_32bit = ptr as u32; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` + //~^ ERROR cast from `*const u8` to `u32` implicitly exposes pointer provenance } diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr index bcef0ae424e68..22c45d7f6e52a 100644 --- a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr @@ -1,34 +1,36 @@ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:6:23 | LL | let addr: usize = &x as *const u8 as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-lossy-casts.rs:2:9 | -LL | #![deny(lossy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer | LL - let addr: usize = &x as *const u8 as usize; LL + let addr: usize = (&x as *const u8).addr(); | -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` +error: cast from `*const u8` to `u32` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:9:22 | LL | let addr_32bit = &x as *const u8 as u32; | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit help: use `.addr()` to obtain the address of a pointer | LL | let addr_32bit = (&x as *const u8).addr() as u32; | + ++++++++ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:14:20 | LL | let ptr_addr = ptr as usize; @@ -36,9 +38,10 @@ LL | let ptr_addr = ptr as usize; | | | help: use `.addr()` to obtain the address of a pointer: `.addr()` | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` +error: cast from `*const u8` to `u32` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:16:26 | LL | let ptr_addr_32bit = ptr as u32; @@ -46,7 +49,8 @@ LL | let ptr_addr_32bit = ptr as u32; | | | help: use `.addr()` to obtain the address of a pointer: `.addr() as u32` | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit error: aborting due to 4 previous errors diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.rs b/tests/ui/lint/lint-strict-provenance-macro-casts.rs index 6d1c02c91fdbd..59a844b0fa134 100644 --- a/tests/ui/lint/lint-strict-provenance-macro-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.rs @@ -1,23 +1,22 @@ #![feature(strict_provenance_lints)] -#![deny(lossy_provenance_casts)] -#![deny(fuzzy_provenance_casts)] +#![deny(implicit_provenance_casts)] macro_rules! cast { ($e:expr, $t:ty) => { $e as $t - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - //~| ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` + //~^ ERROR cast from `*const u8` to `usize` + //~| ERROR cast from `usize` to `*const u8` }; } macro_rules! p2i { ($e:expr) => { $e as usize }; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` } macro_rules! i2p { ($e:expr) => { $e as *const () }; - //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const ()` + //~^ ERROR cast from `usize` to `*const ()` } fn main() { diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.stderr b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr index a7ec3d56965f3..08a1707490623 100644 --- a/tests/ui/lint/lint-strict-provenance-macro-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr @@ -1,5 +1,5 @@ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:6:9 | LL | $e as $t | ^^^^^^^^ @@ -7,16 +7,17 @@ LL | $e as $t LL | let _addr = cast!(ptr, usize); | ----------------- in this macro invocation | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-macro-casts.rs:2:9 | -LL | #![deny(lossy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) -error: strict provenance disallows casting integer `usize` to pointer `*const u8` - --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 +error: cast from `usize` to `*const u8` implicitly relies on exposed provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:6:9 | LL | $e as $t | ^^^^^^^^ @@ -24,16 +25,12 @@ LL | $e as $t LL | let _ptr = cast!(0usize, *const u8); | ------------------------ in this macro invocation | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead -note: the lint level is defined here - --> $DIR/lint-strict-provenance-macro-casts.rs:3:9 - | -LL | #![deny(fuzzy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - --> $DIR/lint-strict-provenance-macro-casts.rs:14:20 +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:13:20 | LL | ($e:expr) => { $e as usize }; | ^^^^^^^^^^^ @@ -41,11 +38,12 @@ LL | ($e:expr) => { $e as usize }; LL | p2i!(&raw const x); | ------------------ in this macro invocation | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit = note: this error originates in the macro `p2i` (in Nightly builds, run with -Z macro-backtrace for more info) -error: strict provenance disallows casting integer `usize` to pointer `*const ()` - --> $DIR/lint-strict-provenance-macro-casts.rs:19:20 +error: cast from `usize` to `*const ()` implicitly relies on exposed provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:18:20 | LL | ($e:expr) => { $e as *const () }; | ^^^^^^^^^^^^^^^ @@ -53,7 +51,8 @@ LL | ($e:expr) => { $e as *const () }; LL | i2p!(0x42); | ---------- in this macro invocation | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit = note: this error originates in the macro `i2p` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/lint/runtime-symbols.rs b/tests/ui/lint/runtime-symbols.rs new file mode 100644 index 0000000000000..9b03f43c8cfaf --- /dev/null +++ b/tests/ui/lint/runtime-symbols.rs @@ -0,0 +1,95 @@ +// This test checks the runtime symbols lint. + +//@ edition: 2021 +//@ normalize-stderr: "\*const [iu]8" -> "*const U8" + +#![feature(c_variadic)] +#![allow(clashing_extern_declarations)] // we are voluntarily testing different definitions + +use core::ffi::{c_char, c_int, c_void}; + +fn invalid() { + #[no_mangle] + pub fn memmove() {} + //~^ ERROR invalid definition of the runtime `memmove` symbol + + extern "C" { + pub fn memset(); + //~^ ERROR invalid definition of the runtime `memset` symbol + + pub fn memcmp(); + //~^ ERROR invalid definition of the runtime `memcmp` symbol + } + + #[no_mangle] + pub static strlen: () = (); + //~^ ERROR invalid definition of the runtime `strlen` symbol + + // ABI mismatch: Rust ABI instead of C ABI + #[no_mangle] + pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void { + dest + } + //~^^^ ERROR invalid definition of the runtime `memcpy` symbol + + // C-Variadic mismatch + #[no_mangle] + pub unsafe extern "C" fn bcmp(s1: *const c_void, s2: *const c_void, n: usize, _: ...) -> c_int { + 0 + } + //~^^^ ERROR invalid definition of the runtime `bcmp` symbol + + // Return type is missing + #[export_name = "bcmp"] + pub extern "C" fn bcmp_(s1: *const c_void, s2: *const c_void, n: usize) {} + //~^ ERROR invalid definition of the runtime `bcmp` symbol +} + +fn suspicious() { + #[no_mangle] + pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + std::ptr::null_mut() + } + //~^^^ WARN suspicious definition of the runtime `memcpy` symbol + + #[no_mangle] + pub extern "C" fn memmove(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + std::ptr::null_mut() + } + //~^^^ WARN suspicious definition of the runtime `memmove` symbol + + extern "C" { + fn memset(s: *mut c_void, c: c_int, n: usize) -> f64; + //~^ WARN suspicious definition of the runtime `memset` symbol + } + + #[export_name = "bcmp"] + pub extern "C" fn bcmp_(s1: *const u8, s2: *const u8, n: usize) -> c_int { + 0 + } + //~^^^ WARN suspicious definition of the runtime `bcmp` symbol + + #[no_mangle] + pub extern "C" fn strlen(s: *const u64) -> usize { + 0 + } + //~^^^ WARN suspicious definition of the runtime `strlen` symbol +} + +fn valid() { + extern "C" { + fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + + fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + static strlen: Option usize>; + } +} + +fn main() {} diff --git a/tests/ui/lint/runtime-symbols.stderr b/tests/ui/lint/runtime-symbols.stderr new file mode 100644 index 0000000000000..712f6532c1a59 --- /dev/null +++ b/tests/ui/lint/runtime-symbols.stderr @@ -0,0 +1,129 @@ +error: invalid definition of the runtime `memmove` symbol used by the standard library + --> $DIR/runtime-symbols.rs:13:5 + | +LL | pub fn memmove() {} + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memmove")]`, or `#[link_name = "memmove"]` + = note: `#[deny(invalid_runtime_symbol_definitions)]` on by default + +error: invalid definition of the runtime `memset` symbol used by the standard library + --> $DIR/runtime-symbols.rs:17:9 + | +LL | pub fn memset(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, i32, usize) -> *mut c_void` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memset")]`, or `#[link_name = "memset"]` + +error: invalid definition of the runtime `memcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:20:9 + | +LL | pub fn memcmp(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcmp")]`, or `#[link_name = "memcmp"]` + +error: invalid definition of the runtime `strlen` symbol used by the standard library + --> $DIR/runtime-symbols.rs:25:5 + | +LL | pub static strlen: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8) -> usize` + found `static strlen: ()` + = help: either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "strlen")]` + +error: invalid definition of the runtime `memcpy` symbol used by the standard library + --> $DIR/runtime-symbols.rs:30:5 + | +LL | pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `fn(*mut c_void, *const c_void, usize) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` + +error: invalid definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:37:5 + | +LL | pub unsafe extern "C" fn bcmp(s1: *const c_void, s2: *const c_void, n: usize, _: ...) -> c_int { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `unsafe extern "C" fn(*const c_void, *const c_void, usize, ...) -> i32` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + +error: invalid definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:44:5 + | +LL | pub extern "C" fn bcmp_(s1: *const c_void, s2: *const c_void, n: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `extern "C" fn(*const c_void, *const c_void, usize)` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + +warning: suspicious definition of the runtime `memcpy` symbol used by the standard library + --> $DIR/runtime-symbols.rs:50:5 + | +LL | pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` + = help: allow this lint if the signature is compatible + = note: `#[warn(suspicious_runtime_symbol_definitions)]` on by default + +warning: suspicious definition of the runtime `memmove` symbol used by the standard library + --> $DIR/runtime-symbols.rs:56:5 + | +LL | pub extern "C" fn memmove(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memmove")]`, or `#[link_name = "memmove"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `memset` symbol used by the standard library + --> $DIR/runtime-symbols.rs:62:9 + | +LL | fn memset(s: *mut c_void, c: c_int, n: usize) -> f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, i32, usize) -> *mut c_void` + found `unsafe extern "C" fn(*mut c_void, i32, usize) -> f64` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memset")]`, or `#[link_name = "memset"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:67:5 + | +LL | pub extern "C" fn bcmp_(s1: *const U8, s2: *const U8, n: usize) -> c_int { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `extern "C" fn(*const U8, *const U8, usize) -> i32` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `strlen` symbol used by the standard library + --> $DIR/runtime-symbols.rs:73:5 + | +LL | pub extern "C" fn strlen(s: *const u64) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8) -> usize` + found `extern "C" fn(*const u64) -> usize` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "strlen")]`, or `#[link_name = "strlen"]` + = help: allow this lint if the signature is compatible + +error: aborting due to 7 previous errors; 5 warnings emitted + diff --git a/tests/ui/lint/unused-parens-trailing-space-issue-158583.rs b/tests/ui/lint/unused-parens-trailing-space-issue-158583.rs new file mode 100644 index 0000000000000..d3c70f8769a9c --- /dev/null +++ b/tests/ui/lint/unused-parens-trailing-space-issue-158583.rs @@ -0,0 +1,5 @@ +fn main() { + #[deny(unused_parens)] + let _x = (3 + 6); + //~^ ERROR unnecessary parentheses around assigned value +} diff --git a/tests/ui/lint/unused-parens-trailing-space-issue-158583.stderr b/tests/ui/lint/unused-parens-trailing-space-issue-158583.stderr new file mode 100644 index 0000000000000..200379a5151eb --- /dev/null +++ b/tests/ui/lint/unused-parens-trailing-space-issue-158583.stderr @@ -0,0 +1,19 @@ +error: unnecessary parentheses around assigned value + --> $DIR/unused-parens-trailing-space-issue-158583.rs:3:14 + | +LL | let _x = (3 + 6); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/unused-parens-trailing-space-issue-158583.rs:2:12 + | +LL | #[deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - let _x = (3 + 6); +LL + let _x = 3 + 6; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/liveness/auxiliary/aux_issue_147648.rs b/tests/ui/liveness/auxiliary/aux_issue_147648.rs new file mode 100644 index 0000000000000..db188b50e285d --- /dev/null +++ b/tests/ui/liveness/auxiliary/aux_issue_147648.rs @@ -0,0 +1,23 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_derive(UnusedAssign)] +pub fn generate(ts: TokenStream) -> TokenStream { + let mut ts = ts.into_iter(); + let _pub = ts.next(); + let _struct = ts.next(); + let name = ts.next().unwrap(); + let TokenTree::Group(fields) = ts.next().unwrap() else { panic!() }; + let mut fields = fields.stream().into_iter(); + let field = fields.next().unwrap(); + + quote! { + impl Drop for $name { + fn drop(&mut self) { + let Self { $field } = self; + } + } + } +} diff --git a/tests/ui/liveness/unused-assignments-from-macro-147648.rs b/tests/ui/liveness/unused-assignments-from-macro-147648.rs new file mode 100644 index 0000000000000..92165c76d1d82 --- /dev/null +++ b/tests/ui/liveness/unused-assignments-from-macro-147648.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ proc-macro: aux_issue_147648.rs + +#![deny(unused_assignments)] +#![allow(dead_code)] +#![allow(unused_variables)] + +extern crate aux_issue_147648; +use aux_issue_147648::UnusedAssign; + +#[derive(UnusedAssign)] +pub struct MyError { + source_code: (), +} + +fn main() { + let _error = MyError { source_code: () }; +} diff --git a/tests/ui/issues/issue-2761.rs b/tests/ui/macros/assert-with-custom-message.rs similarity index 62% rename from tests/ui/issues/issue-2761.rs rename to tests/ui/macros/assert-with-custom-message.rs index 6df7a5118b820..78a4a2d61cb9b 100644 --- a/tests/ui/issues/issue-2761.rs +++ b/tests/ui/macros/assert-with-custom-message.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-fail //@ error-pattern:custom message //@ needs-subprocess diff --git a/tests/ui/macros/deref-raw-pointer-issue-158158.rs b/tests/ui/macros/deref-raw-pointer-issue-158158.rs new file mode 100644 index 0000000000000..6d45a1b78a63f --- /dev/null +++ b/tests/ui/macros/deref-raw-pointer-issue-158158.rs @@ -0,0 +1,21 @@ +struct Demo { + val: u32, +} + +macro_rules! as_ptr { + ($d:expr) => { + &mut $d as *mut Demo + }; +} + +macro_rules! get_value { + ($d:expr) => { + as_ptr!($d).val + //~^ ERROR no field `val` on type `*mut Demo` + } +} + +fn main() { + let mut d = Demo { val: 123 }; + let _ = get_value!(d); +} diff --git a/tests/ui/macros/deref-raw-pointer-issue-158158.stderr b/tests/ui/macros/deref-raw-pointer-issue-158158.stderr new file mode 100644 index 0000000000000..d93ac0eac1638 --- /dev/null +++ b/tests/ui/macros/deref-raw-pointer-issue-158158.stderr @@ -0,0 +1,14 @@ +error[E0609]: no field `val` on type `*mut Demo` + --> $DIR/deref-raw-pointer-issue-158158.rs:13:21 + | +LL | as_ptr!($d).val + | ^^^ unknown field +... +LL | let _ = get_value!(d); + | ------------- in this macro invocation + | + = note: this error originates in the macro `get_value` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/macros/format-foreign-dollar-without-spec.stderr b/tests/ui/macros/format-foreign-dollar-without-spec.stderr index d5a07c50f00d0..c4c241048abb0 100644 --- a/tests/ui/macros/format-foreign-dollar-without-spec.stderr +++ b/tests/ui/macros/format-foreign-dollar-without-spec.stderr @@ -2,15 +2,14 @@ error: argument never used --> $DIR/format-foreign-dollar-without-spec.rs:3:25 | LL | println!("%65536$", 1); - | ^ argument never used + | --------- ^ argument never used + | | + | formatting specifier missing | -note: format specifiers use curly braces, and the conversion specifier ` - ` is unknown or unsupported - --> $DIR/format-foreign-dollar-without-spec.rs:3:15 +help: format specifiers use curly braces, consider adding a format specifier | -LL | println!("%65536$", 1); - | ^^^^^^^^ - = note: printf formatting is not supported; see the documentation for `std::fmt` +LL | println!("%65536${}", 1); + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs index 68346a00ae1a7..2b1bacf7e0c31 100644 --- a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs @@ -4,7 +4,7 @@ struct T; impl T { - #[core::contracts::ensures] //~ ERROR expected a `Fn(&_)` closure, found `()` + #[core::contracts::ensures] //~ ERROR expected an `Fn(&_)` closure, found `()` fn b() {(loop)} //~^ ERROR expected `{`, found `)` //~| ERROR expected `{`, found `)` diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr index f1ffda2a9bee6..56dbdae14189b 100644 --- a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr @@ -16,7 +16,7 @@ LL | fn b() {(loop)} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: expected a `Fn(&_)` closure, found `()` +error[E0277]: expected an `Fn(&_)` closure, found `()` --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:7:5 | LL | #[core::contracts::ensures] diff --git a/tests/ui/macros/issue-92267.stderr b/tests/ui/macros/issue-92267.stderr index 4259815328b30..2318d5d565a1d 100644 --- a/tests/ui/macros/issue-92267.stderr +++ b/tests/ui/macros/issue-92267.stderr @@ -2,15 +2,14 @@ error: argument never used --> $DIR/issue-92267.rs:3:34 | LL | pub fn main() { println!("🦀%%%", 0) } - | ^ argument never used + | ------- ^ argument never used + | | + | formatting specifier missing | -note: format specifiers use curly braces, and the conversion specifier ` - ` is unknown or unsupported - --> $DIR/issue-92267.rs:3:30 +help: format specifiers use curly braces, consider adding a format specifier | -LL | pub fn main() { println!("🦀%%%", 0) } - | ^^ - = note: printf formatting is not supported; see the documentation for `std::fmt` +LL | pub fn main() { println!("🦀%%%{}", 0) } + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-26237.rs b/tests/ui/macros/non-function-call-in-macro.rs similarity index 78% rename from tests/ui/issues/issue-26237.rs rename to tests/ui/macros/non-function-call-in-macro.rs index 71e96677d4bbc..b23fd74c4fd16 100644 --- a/tests/ui/issues/issue-26237.rs +++ b/tests/ui/macros/non-function-call-in-macro.rs @@ -1,3 +1,5 @@ +//! Regression test for . + macro_rules! macro_panic { ($not_a_function:expr, $some_argument:ident) => { $not_a_function($some_argument) diff --git a/tests/ui/issues/issue-26237.stderr b/tests/ui/macros/non-function-call-in-macro.stderr similarity index 91% rename from tests/ui/issues/issue-26237.stderr rename to tests/ui/macros/non-function-call-in-macro.stderr index d15e5753a25e7..6e38c5e8e3493 100644 --- a/tests/ui/issues/issue-26237.stderr +++ b/tests/ui/macros/non-function-call-in-macro.stderr @@ -1,5 +1,5 @@ error[E0618]: expected function, found `{integer}` - --> $DIR/issue-26237.rs:10:18 + --> $DIR/non-function-call-in-macro.rs:12:18 | LL | $not_a_function($some_argument) | ------------------------------- call expression requires function diff --git a/tests/ui/macros/trailing-percent-format-hint-issue-158216.rs b/tests/ui/macros/trailing-percent-format-hint-issue-158216.rs new file mode 100644 index 0000000000000..bebea2dfe48c6 --- /dev/null +++ b/tests/ui/macros/trailing-percent-format-hint-issue-158216.rs @@ -0,0 +1,7 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib + +pub fn f(x: f64) { + println!("{x:>8.2}%", "foo"); + //~^ ERROR argument never used +} diff --git a/tests/ui/macros/trailing-percent-format-hint-issue-158216.stderr b/tests/ui/macros/trailing-percent-format-hint-issue-158216.stderr new file mode 100644 index 0000000000000..b5c120f939b40 --- /dev/null +++ b/tests/ui/macros/trailing-percent-format-hint-issue-158216.stderr @@ -0,0 +1,15 @@ +error: argument never used + --> $DIR/trailing-percent-format-hint-issue-158216.rs:5:27 + | +LL | println!("{x:>8.2}%", "foo"); + | ----------- ^^^^^ argument never used + | | + | formatting specifier missing + | +help: format specifiers use curly braces, consider adding a format specifier + | +LL | println!("{x:>8.2}%{}", "foo"); + | ++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/issues/issue-22814.rs b/tests/ui/macros/trait-bound-not-parsed-as-matcher.rs similarity index 68% rename from tests/ui/issues/issue-22814.rs rename to tests/ui/macros/trait-bound-not-parsed-as-matcher.rs index 95ddeb15b57dd..0230df4aeb30c 100644 --- a/tests/ui/issues/issue-22814.rs +++ b/tests/ui/macros/trait-bound-not-parsed-as-matcher.rs @@ -1,3 +1,5 @@ +//! Regression test for . + //@ check-pass trait Test {} diff --git a/tests/ui/match/match-str-proper-optimization.rs b/tests/ui/match/match-str-proper-optimization.rs new file mode 100644 index 0000000000000..79c56b22d0569 --- /dev/null +++ b/tests/ui/match/match-str-proper-optimization.rs @@ -0,0 +1,14 @@ +//! Regression test for . +//! Ensure matching against `str` doesn't cause segfault when compiled +//! with `opt-level` >= 2. +// +//@ compile-flags: -C opt-level=2 +//@ run-pass +pub fn main() { + let command = "a"; + + match command { + "foo" => println!("foo"), + _ => println!("{}", command), + } +} diff --git a/tests/ui/methods/filter-relevant-fn-bounds.rs b/tests/ui/methods/filter-relevant-fn-bounds.rs index 7945eaa0daa27..a23f4600d6d1b 100644 --- a/tests/ui/methods/filter-relevant-fn-bounds.rs +++ b/tests/ui/methods/filter-relevant-fn-bounds.rs @@ -16,5 +16,5 @@ impl Wrapper { fn main() { let mut wrapper = Wrapper; wrapper.do_something_wrapper(|value| ()); - //~^ ERROR expected a `FnOnce + //~^ ERROR expected an `FnOnce } diff --git a/tests/ui/methods/filter-relevant-fn-bounds.stderr b/tests/ui/methods/filter-relevant-fn-bounds.stderr index 4efe40ae0903b..9ad0319440e9a 100644 --- a/tests/ui/methods/filter-relevant-fn-bounds.stderr +++ b/tests/ui/methods/filter-relevant-fn-bounds.stderr @@ -12,7 +12,7 @@ help: consider further restricting type parameter `F` with trait `Output` LL | F: for<'a> FnOnce(>::Type) + for<'a> Output<'a>, | ++++++++++++++++++++ -error[E0277]: expected a `FnOnce(<{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41} as Output<'a>>::Type)` closure, found `{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41}` +error[E0277]: expected an `FnOnce(<{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41} as Output<'a>>::Type)` closure, found `{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41}` --> $DIR/filter-relevant-fn-bounds.rs:18:34 | LL | wrapper.do_something_wrapper(|value| ()); diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs index 0a98cdef5c923..f204e55f5a578 100644 --- a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs @@ -16,7 +16,7 @@ struct Bar; fn main() { let foo: Box usize> = Box::new(Bar); - //~^ ERROR expected a `Fn(bool)` closure, found `Bar` + //~^ ERROR expected an `Fn(bool)` closure, found `Bar` foo.borrow(); //~^ ERROR no method named `borrow` found foo.take() diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr index aa46a41fa3378..73f5f441b3fef 100644 --- a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr @@ -22,7 +22,7 @@ help: there is a method `borrow_mut` with a similar name LL | foo.borrow_mut(); | ++++ -error[E0277]: expected a `Fn(bool)` closure, found `Bar` +error[E0277]: expected an `Fn(bool)` closure, found `Bar` --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:18:43 | LL | let foo: Box usize> = Box::new(Bar); diff --git a/tests/ui/issues/issue-21622.rs b/tests/ui/methods/nested-fn-call-shadowed-assoc-fn-variable.rs similarity index 77% rename from tests/ui/issues/issue-21622.rs rename to tests/ui/methods/nested-fn-call-shadowed-assoc-fn-variable.rs index 8dbc2c21ba951..65059cf38cd3c 100644 --- a/tests/ui/issues/issue-21622.rs +++ b/tests/ui/methods/nested-fn-call-shadowed-assoc-fn-variable.rs @@ -1,3 +1,5 @@ +//! Regression test for . + //@ check-pass #![allow(dead_code)] #![allow(unused_variables)] diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr index afacb3a7d5213..4652bf5e3c586 100644 --- a/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr @@ -9,7 +9,7 @@ note: candidate #1 is defined in the trait `Trait1` | LL | fn method(&self) { | ^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in the trait `Trait2` +note: candidate #2 is defined in an impl of the trait `Trait2` for the type `T` --> $DIR/rigid-alias-bound-is-not-inherent.rs:27:5 | LL | fn method(&self) { diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const.rs b/tests/ui/methods/supertrait-shadowing/assoc-const.rs deleted file mode 100644 index 01d045b9fb74f..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/assoc-const.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![allow(dead_code)] - -trait A { - const CONST: i32; -} -impl A for T { - const CONST: i32 = 1; -} - -trait B: A { - const CONST: i32; -} -impl B for T { - const CONST: i32 = 2; -} - -fn main() { - assert_eq!(i32::CONST, 2) -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs deleted file mode 100644 index ce56c25df19ea..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs +++ /dev/null @@ -1,33 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `C` shadows identically named item - "C" - } -} -impl C for T {} - -fn main() { - assert_eq!(().hello(), "C"); - //~^ WARN trait item `hello` from `C` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs deleted file mode 100644 index b29f3c8d014ec..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs +++ /dev/null @@ -1,43 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `C` shadows identically named item - "C" - } -} -impl C for T {} - -// `D` extends `C` which extends `B` and `A` - -trait D: C { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `D` shadows identically named item - "D" - } -} -impl D for T {} - -fn main() { - assert_eq!(().hello(), "D"); - //~^ WARN trait item `hello` from `D` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr deleted file mode 100644 index 28fe7f72f94c3..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr +++ /dev/null @@ -1,68 +0,0 @@ -warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:23:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `B` and `A` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/common-ancestor-3.rs:5:9 - | -LL | #![warn(shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: trait item `hello` from `D` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:33:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `C`, `B`, and `A` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: trait item `hello` from `D` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:41:19 - | -LL | assert_eq!(().hello(), "D"); - | ^^^^^ - | -note: item from `D` shadows a supertrait item - --> $DIR/common-ancestor-3.rs:33:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: items from several supertraits are shadowed: `A`, `B`, and `C` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/common-ancestor-3.rs:4:9 - | -LL | #![warn(resolving_to_items_shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: 3 warnings emitted - diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor.rs deleted file mode 100644 index b288d6e22b8c3..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B: A { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `B` shadows identically named item - "B" - } -} -impl B for T {} - -fn main() { - assert_eq!(().hello(), "B"); - //~^ WARN trait item `hello` from `B` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.rs b/tests/ui/methods/supertrait-shadowing/definition-site.rs deleted file mode 100644 index 248df032736f6..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/definition-site.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![feature(supertrait_item_shadowing)] -#![deny(shadowing_supertrait_items)] - -trait SuperSuper { - fn method(); -} - -trait Super: SuperSuper { - fn method(); - //~^ ERROR trait item `method` from `Super` shadows identically named item -} - -trait Sub: Super { - fn method(); - //~^ ERROR trait item `method` from `Sub` shadows identically named item -} - -fn main() {} diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.stderr b/tests/ui/methods/supertrait-shadowing/definition-site.stderr deleted file mode 100644 index 1e35a753a9ebc..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/definition-site.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: trait item `method` from `Super` shadows identically named item from supertrait - --> $DIR/definition-site.rs:9:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ - | -note: item from `SuperSuper` is shadowed by a subtrait item - --> $DIR/definition-site.rs:5:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/definition-site.rs:2:9 - | -LL | #![deny(shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: trait item `method` from `Sub` shadows identically named item from supertrait - --> $DIR/definition-site.rs:14:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `Super` and `SuperSuper` - --> $DIR/definition-site.rs:5:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ -... -LL | fn method(); - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs deleted file mode 100644 index b49476c7a4f68..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![feature(supertrait_item_shadowing)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - "C" - } -} -impl C for T {} - -// Since `D` is not a subtrait of `C`, -// we have no obvious lower bound. - -trait D: B { - fn hello(&self) -> &'static str { - "D" - } -} -impl D for T {} - -fn main() { - ().hello(); - //~^ ERROR multiple applicable items in scope -} diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr deleted file mode 100644 index 745f61d00d660..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error[E0034]: multiple applicable items in scope - --> $DIR/no-common-ancestor-2.rs:35:8 - | -LL | ().hello(); - | ^^^^^ multiple `hello` found - | -note: candidate #1 is defined in an impl of the trait `A` for the type `T` - --> $DIR/no-common-ancestor-2.rs:4:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `B` for the type `T` - --> $DIR/no-common-ancestor-2.rs:11:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #3 is defined in an impl of the trait `C` for the type `T` - --> $DIR/no-common-ancestor-2.rs:18:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #4 is defined in an impl of the trait `D` for the type `T` - --> $DIR/no-common-ancestor-2.rs:28:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method for candidate #1 - | -LL - ().hello(); -LL + A::hello(&()); - | -help: disambiguate the method for candidate #2 - | -LL - ().hello(); -LL + B::hello(&()); - | -help: disambiguate the method for candidate #3 - | -LL - ().hello(); -LL + C::hello(&()); - | -help: disambiguate the method for candidate #4 - | -LL - ().hello(); -LL + D::hello(&()); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs deleted file mode 100644 index 03c822eb7375e..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![feature(supertrait_item_shadowing)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -fn main() { - ().hello(); - //~^ ERROR multiple applicable items in scope -} diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr deleted file mode 100644 index 29cf7ff1dbb11..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0034]: multiple applicable items in scope - --> $DIR/no-common-ancestor.rs:18:8 - | -LL | ().hello(); - | ^^^^^ multiple `hello` found - | -note: candidate #1 is defined in an impl of the trait `A` for the type `T` - --> $DIR/no-common-ancestor.rs:4:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `B` for the type `T` - --> $DIR/no-common-ancestor.rs:11:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method for candidate #1 - | -LL - ().hello(); -LL + A::hello(&()); - | -help: disambiguate the method for candidate #2 - | -LL - ().hello(); -LL + B::hello(&()); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs b/tests/ui/methods/supertrait-shadowing/out-of-scope.rs deleted file mode 100644 index 4112634399b78..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ run-pass - -#![allow(dead_code)] - -mod out_of_scope { - pub trait Subtrait: super::Supertrait { - fn hello(&self) -> &'static str { - "subtrait" - } - } - impl Subtrait for T {} -} - -trait Supertrait { - fn hello(&self) -> &'static str { - "supertrait" - } -} -impl Supertrait for T {} - -fn main() { - assert_eq!(().hello(), "supertrait"); -} diff --git a/tests/ui/methods/supertrait-shadowing/type-dependent.rs b/tests/ui/methods/supertrait-shadowing/type-dependent.rs deleted file mode 100644 index 4a5af1d598812..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/type-dependent.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ run-pass - -// Makes sure we can shadow with type-dependent method syntax. - -#![feature(supertrait_item_shadowing)] -#![allow(dead_code)] - -trait A { - fn hello() -> &'static str { - "A" - } -} -impl A for T {} - -trait B: A { - fn hello() -> &'static str { - "B" - } -} -impl B for T {} - -fn foo() -> &'static str { - T::hello() -} - -fn main() { - assert_eq!(foo::<()>(), "B"); -} diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed index c489b6e445dda..b1158e1cbffa7 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed @@ -5,10 +5,10 @@ fn main() { let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:6:29} as FnOnce<(&{integer},)>>::Output == bool` let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:10:29} as FnOnce<(&{integer},)>>::Output == bool` } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index 412fd53baeb37..eab9bf1ae34a8 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` +error[E0277]: expected an `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` --> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:29 | LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); @@ -23,7 +23,7 @@ LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0277]: expected a `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:10:29: 10:40}` +error[E0277]: expected an `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:10:29: 10:40}` --> $DIR/closure-arg-type-mismatch-issue-45727.rs:10:29 | LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs index 434b5d3d23110..09f7af7c0253e 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs @@ -5,10 +5,10 @@ fn main() { let _ = (-10..=10).find(|x: i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:6:29} as FnOnce<(&{integer},)>>::Output == bool` let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:10:29} as FnOnce<(&{integer},)>>::Output == bool` } diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs index 42adff43a58f2..445a98cd49630 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs @@ -24,9 +24,9 @@ fn main() { let _ = produces_string().and_then(takes_str_but_too_many_refs); //~^ ERROR type mismatch in function arguments let _ = produces_string().and_then(takes_str_but_wrong_abi); - //~^ ERROR expected a `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + //~^ ERROR expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` let _ = produces_string().and_then(takes_str_but_unsafe); - //~^ ERROR expected a `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + //~^ ERROR expected an `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` let _ = produces_string().and_then(no_args); //~^ ERROR function is expected to take 1 argument, but it takes 0 arguments let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index a657a65c426d4..14c821b260299 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -18,7 +18,7 @@ help: consider wrapping the function in a closure LL | let _ = produces_string().and_then(|arg0: String| takes_str_but_too_many_refs(/* &&str */)); | ++++++++++++++ +++++++++++++ -error[E0277]: expected a `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` +error[E0277]: expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` --> $DIR/suggest-option-asderef-unfixable.rs:26:40 | LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); @@ -30,7 +30,7 @@ LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL -error[E0277]: expected a `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` +error[E0277]: expected an `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` --> $DIR/suggest-option-asderef-unfixable.rs:28:40 | LL | let _ = produces_string().and_then(takes_str_but_unsafe); diff --git a/tests/ui/issues/issue-2590.rs b/tests/ui/moves/move-field-out-of-shared-self.rs similarity index 75% rename from tests/ui/issues/issue-2590.rs rename to tests/ui/moves/move-field-out-of-shared-self.rs index a9a0e5ca4ec16..05c19a8a7a8fb 100644 --- a/tests/ui/issues/issue-2590.rs +++ b/tests/ui/moves/move-field-out-of-shared-self.rs @@ -1,3 +1,5 @@ +//! Regression test for . + struct Parser { tokens: Vec , } diff --git a/tests/ui/issues/issue-2590.stderr b/tests/ui/moves/move-field-out-of-shared-self.stderr similarity index 90% rename from tests/ui/issues/issue-2590.stderr rename to tests/ui/moves/move-field-out-of-shared-self.stderr index 822856652e9df..3720b2bd963f2 100644 --- a/tests/ui/issues/issue-2590.stderr +++ b/tests/ui/moves/move-field-out-of-shared-self.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `self.tokens` which is behind a shared reference - --> $DIR/issue-2590.rs:11:9 + --> $DIR/move-field-out-of-shared-self.rs:13:9 | LL | self.tokens | ^^^^^^^^^^^ move occurs because `self.tokens` has type `Vec`, which does not implement the `Copy` trait diff --git a/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs new file mode 100644 index 0000000000000..694c95da7b482 --- /dev/null +++ b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs @@ -0,0 +1,20 @@ +// Regression test for https://github.com/rust-lang/rust/issues/148631 + +struct C; + +struct S(T); + +trait Tr {} + +impl Clone for S +//~^ ERROR the type parameter `T` is not constrained +where + S: Tr, +{ + fn clone(&self) -> Self { + *self + //~^ ERROR cannot move out of `*self` + } +} + +fn main() {} diff --git a/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr new file mode 100644 index 0000000000000..9cf17045b0296 --- /dev/null +++ b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr @@ -0,0 +1,19 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-impl-param-clone-suggestion.rs:9:6 + | +LL | impl Clone for S + | -^- + | || + | |unconstrained type parameter + | help: remove the unused type parameter `T` + +error[E0507]: cannot move out of `*self` which is behind a shared reference + --> $DIR/unconstrained-impl-param-clone-suggestion.rs:15:9 + | +LL | *self + | ^^^^^ move occurs because `*self` has type `S`, which does not implement the `Copy` trait + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0207, E0507. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/offset-of/inside-array-length.stderr b/tests/ui/offset-of/inside-array-length.stderr index 4b2a50acf82cb..0c430f0424ced 100644 --- a/tests/ui/offset-of/inside-array-length.stderr +++ b/tests/ui/offset-of/inside-array-length.stderr @@ -37,7 +37,7 @@ LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {} = note: ...which requires caching mir of `foo::{constant#0}` for CTFE... = note: ...which requires elaborating drops for `foo::{constant#0}`... = note: ...which requires borrow-checking `foo::{constant#0}`... - = note: ...which requires normalizing `Binder { value: ConstEvaluatable(Alias { kind: Anon { def_id: DefId(0:7 ~ inside_array_length[07d6]::foo::{constant#0}) }, args: ['^c_1, T/#1], .. }), bound_vars: [] }`... + = note: ...which requires normalizing `Binder { value: ConstEvaluatable(AliasConst(No, Alias { kind: Anon { def_id: DefId(0:7 ~ inside_array_length[07d6]::foo::{constant#0}) }, args: ['^c_1, T/#1], .. })), bound_vars: [] }`... = note: ...which again requires evaluating type-level constant, completing the cycle = note: cycle used when normalizing `inside_array_length::::foo::{constant#0}` = note: for more information, see and diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.rs b/tests/ui/panic-handler/panic-handler-with-target-feature.rs index aec00c637bedb..6e81516a91628 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.rs +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.rs @@ -8,7 +8,7 @@ use core::panic::PanicInfo; #[panic_handler] #[target_feature(enable = "avx2")] -//~^ ERROR `#[panic_handler]` function is not allowed to have `#[target_feature]` +//~^ ERROR `#[target_feature]` cannot be applied to a `#[panic_handler]` function fn panic(info: &PanicInfo) -> ! { unimplemented!(); } diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr index ddf0ae77a0a1a..93cf164618ed9 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr @@ -1,11 +1,13 @@ -error: `#[panic_handler]` function is not allowed to have `#[target_feature]` +error: `#[target_feature]` cannot be applied to a `#[panic_handler]` function --> $DIR/panic-handler-with-target-feature.rs:10:1 | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | fn panic(info: &PanicInfo) -> ! { - | ------------------------------- `#[panic_handler]` function is not allowed to have `#[target_feature]` +LL | / fn panic(info: &PanicInfo) -> ! { +LL | | unimplemented!(); +LL | | } + | |_- `#[panic_handler]` function is not allowed to have `#[target_feature]` error: aborting due to 1 previous error diff --git a/tests/ui/panic-handler/panic-handler-wrong-location.rs b/tests/ui/panic-handler/panic-handler-wrong-location.rs index 8fff7067136e2..22f6b6e724890 100644 --- a/tests/ui/panic-handler/panic-handler-wrong-location.rs +++ b/tests/ui/panic-handler/panic-handler-wrong-location.rs @@ -3,7 +3,7 @@ #![no_std] #![no_main] -#[panic_handler] //~ ERROR `panic_impl` lang item must be applied to a function +#[panic_handler] //~ ERROR attribute cannot be used on statics static X: u32 = 42; //~? ERROR `#[panic_handler]` function required, but not found diff --git a/tests/ui/panic-handler/panic-handler-wrong-location.stderr b/tests/ui/panic-handler/panic-handler-wrong-location.stderr index 9b361bf8d603f..7af0a4326d9a0 100644 --- a/tests/ui/panic-handler/panic-handler-wrong-location.stderr +++ b/tests/ui/panic-handler/panic-handler-wrong-location.stderr @@ -1,11 +1,12 @@ -error[E0718]: `panic_impl` lang item must be applied to a function +error: `#[panic_handler]` function required, but not found + +error: `#[panic_handler]` attribute cannot be used on statics --> $DIR/panic-handler-wrong-location.rs:6:1 | LL | #[panic_handler] - | ^^^^^^^^^^^^^^^^ attribute should be applied to a function, not a static - -error: `#[panic_handler]` function required, but not found + | ^^^^^^^^^^^^^^^^ + | + = help: `#[panic_handler]` can only be applied to functions error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0718`. diff --git a/tests/ui/panics/abort-on-panic.rs b/tests/ui/panics/abort-on-panic.rs index d3bf087bd3ef8..e8f491d5926f2 100644 --- a/tests/ui/panics/abort-on-panic.rs +++ b/tests/ui/panics/abort-on-panic.rs @@ -61,11 +61,20 @@ fn test_always_abort_thread() { bomb_out_but_not_abort("joined - but we were supposed to panic!"); } +fn test_always_abort_resume_unwind() { + panic::always_abort(); + let _ = panic::catch_unwind(|| { + panic::resume_unwind(Box::new(())); + }); + should_have_aborted(); +} + fn main() { let tests: &[(_, fn())] = &[ ("test", test), ("test_always_abort", test_always_abort), ("test_always_abort_thread", test_always_abort_thread), + ("test_always_abort_resume_unwind", test_always_abort_resume_unwind), ]; let args: Vec = env::args().collect(); diff --git a/tests/ui/panics/panic-in-hook.rs b/tests/ui/panics/panic-in-hook.rs new file mode 100644 index 0000000000000..db4d8a4dd1e0f --- /dev/null +++ b/tests/ui/panics/panic-in-hook.rs @@ -0,0 +1,14 @@ +// Checks what happens when panicking inside the panic hook. + +//@ run-crash +//@ exec-env:RUST_BACKTRACE=0 +//@ check-run-results +//@ error-pattern: panicked while processing panic +//@ ignore-emscripten "RuntimeError" junk in output + +use std::panic; + +fn main() { + panic::set_hook(Box::new(|_| panic!("panic in hook"))); + panic!(); +} diff --git a/tests/ui/panics/panic-in-hook.run.stderr b/tests/ui/panics/panic-in-hook.run.stderr new file mode 100644 index 0000000000000..9ce7e2f019105 --- /dev/null +++ b/tests/ui/panics/panic-in-hook.run.stderr @@ -0,0 +1,3 @@ +panicked at $DIR/panic-in-hook.rs:12:34: + +thread panicked while processing panic. aborting. diff --git a/tests/ui/panics/panic-resume_unwind-in-hook.rs b/tests/ui/panics/panic-resume_unwind-in-hook.rs new file mode 100644 index 0000000000000..a4c735312ebba --- /dev/null +++ b/tests/ui/panics/panic-resume_unwind-in-hook.rs @@ -0,0 +1,14 @@ +// Checks what happens when panicking inside the panic hook. + +//@ run-crash +//@ exec-env:RUST_BACKTRACE=0 +//@ check-run-results +//@ error-pattern: panicked while processing panic +//@ ignore-emscripten "RuntimeError" junk in output + +use std::panic; + +fn main() { + panic::set_hook(Box::new(|_| panic::resume_unwind(Box::new(())))); + panic!(); +} diff --git a/tests/ui/panics/panic-resume_unwind-in-hook.run.stderr b/tests/ui/panics/panic-resume_unwind-in-hook.run.stderr new file mode 100644 index 0000000000000..22e94e755a26a --- /dev/null +++ b/tests/ui/panics/panic-resume_unwind-in-hook.run.stderr @@ -0,0 +1 @@ +thread panicked while processing panic. aborting. diff --git a/tests/ui/issues/issue-29071-2.rs b/tests/ui/parser/block-expr-statement-vs-expr.rs similarity index 59% rename from tests/ui/issues/issue-29071-2.rs rename to tests/ui/parser/block-expr-statement-vs-expr.rs index cad776b984227..1c6b7ed3691ac 100644 --- a/tests/ui/issues/issue-29071-2.rs +++ b/tests/ui/parser/block-expr-statement-vs-expr.rs @@ -1,5 +1,19 @@ +//! Regression test for . //@ run-pass -#![allow(dead_code)] + +#![allow(non_upper_case_globals, dead_code)] + +fn ret() -> u32 { + static x: u32 = 10; + x & if true { 10u32 } else { 20u32 } & x +} + +fn ret2() -> &'static u32 { + static x: u32 = 10; + if true { 10u32; } else { 20u32; } + &x +} + fn t1() -> u32 { let x; x = if true { [1, 2, 3] } else { [2, 3, 4] }[0]; diff --git a/tests/ui/parser/eq-gt-to-gt-eq.fixed b/tests/ui/parser/eq-gt-to-gt-eq.fixed index abb328399be2a..93940a5971f08 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.fixed +++ b/tests/ui/parser/eq-gt-to-gt-eq.fixed @@ -43,3 +43,9 @@ fn b() { _ => todo!(), } } + +fn closure() { + let a = 0; + let b = 1; + let _ = [a].iter().any(|x| x >= &b); //~ERROR +} diff --git a/tests/ui/parser/eq-gt-to-gt-eq.rs b/tests/ui/parser/eq-gt-to-gt-eq.rs index 1f57fa8328198..92ca43ba3155b 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.rs +++ b/tests/ui/parser/eq-gt-to-gt-eq.rs @@ -43,3 +43,9 @@ fn b() { _ => todo!(), } } + +fn closure() { + let a = 0; + let b = 1; + let _ = [a].iter().any(|x| x => &b); //~ERROR +} diff --git a/tests/ui/parser/eq-gt-to-gt-eq.stderr b/tests/ui/parser/eq-gt-to-gt-eq.stderr index aa47ddecce9ef..d5cf71db2df38 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.stderr +++ b/tests/ui/parser/eq-gt-to-gt-eq.stderr @@ -109,5 +109,17 @@ LL - match a => b { LL + match a >= b { | -error: aborting due to 7 previous errors +error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` + --> $DIR/eq-gt-to-gt-eq.rs:50:34 + | +LL | let _ = [a].iter().any(|x| x => &b); + | ^^ expected one of 8 possible tokens + | +help: you might have meant to write a "greater than or equal to" comparison + | +LL - let _ = [a].iter().any(|x| x => &b); +LL + let _ = [a].iter().any(|x| x >= &b); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs index 5cbeccf1b0e4f..1223c0c5b05f9 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs @@ -24,3 +24,19 @@ pub fn no_parameters_given() {} #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] //~^ ERROR malformed pub fn duplicate_parameter() {} + +#[patchable_function_entry(section = 255)] +//~^ ERROR malformed +pub fn invalid_section_parameter() {} + +#[patchable_function_entry(section = "foo", section = "bar")] +//~^ ERROR malformed +pub fn duplicate_section_parameter() {} + +#[patchable_function_entry(section = "fo\0o")] +//~^ ERROR null characters +pub fn nul_in_section_parameter() {} + +#[patchable_function_entry(section = "")] +//~^ ERROR empty +pub fn empty_section_parameter() {} diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr index 6c32a76834c69..c3f09ff12384a 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr @@ -9,7 +9,7 @@ LL | #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -23,7 +23,7 @@ LL | #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -34,8 +34,8 @@ LL | #[patchable_function_entry] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0539]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:16:1 @@ -48,7 +48,7 @@ LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 10, something = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -61,8 +61,8 @@ LL | #[patchable_function_entry()] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0538]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:24:1 @@ -75,10 +75,50 @@ LL | #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | -error: aborting due to 6 previous errors +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:28:1 + | +LL | #[patchable_function_entry(section = 255)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^ + | | + | expected a string literal here + | +help: must be of the form + | +LL - #[patchable_function_entry(section = 255)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | + +error[E0538]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:32:1 + | +LL | #[patchable_function_entry(section = "foo", section = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^^^^^^^^^ + | | + | found `section` used as a key more than once + | +help: must be of the form + | +LL - #[patchable_function_entry(section = "foo", section = "bar")] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | + +error[E0648]: `section` may not contain null characters + --> $DIR/patchable-function-entry-attribute.rs:36:38 + | +LL | #[patchable_function_entry(section = "fo\0o")] + | ^^^^^^^ + +error: `section` may not be empty + --> $DIR/patchable-function-entry-attribute.rs:40:38 + | +LL | #[patchable_function_entry(section = "")] + | ^^ + +error: aborting due to 10 previous errors -Some errors have detailed explanations: E0538, E0539. +Some errors have detailed explanations: E0538, E0539, E0648. For more information about an error, try `rustc --explain E0538`. diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr index b09af94a61541..bde7f4aa9dff6 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr @@ -1,2 +1,2 @@ -error: incorrect value `1,2` for unstable option `patchable-function-entry` - either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops) was expected +error: incorrect value `1,2` for unstable option `patchable-function-entry` - a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops was expected diff --git a/tests/ui/pattern/match-array-against-slice.rs b/tests/ui/pattern/match-array-against-slice.rs new file mode 100644 index 0000000000000..a396e2adab822 --- /dev/null +++ b/tests/ui/pattern/match-array-against-slice.rs @@ -0,0 +1,11 @@ +//! Regression test for . +//! Test that we do not ICE when pattern matching an array against a slice. +//@ run-pass + +fn main() { + match "foo".as_bytes() { + b"food" => (), + &[b'f', ..] => (), + _ => () + } +} diff --git a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs index f58aa1b228e31..b29c40770b0e7 100644 --- a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs +++ b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs @@ -7,12 +7,28 @@ use proc_macro::*; use self::Mode::*; // FIXME: all cases should become `NormalOk` or `NormalErr` +// +// And .stderr should be empty (no diagnostics should get emitted from fallible parsing in the proc +// macro). #[derive(PartialEq, Clone, Copy)] enum Mode { NormalOk, NormalErr, OtherError, - OtherWithPanic, +} + +fn print_unspanned(s: &str) -> Result +where + T: FromStr + Debug, +{ + let t = T::from_str(s); + let mut s = format!("{t:?}"); + while let Some((l, r)) = s.split_once("span: #") { + let (_, r) = r.split_once(")").unwrap(); + s = format!("{l}span: Span{r}"); + } + println!("{s}"); + t } fn parse(s: &str, mode: Mode) @@ -21,22 +37,19 @@ where { match mode { NormalOk => { - let t = T::from_str(s); - println!("{:?}", t); + let t = print_unspanned::(s); assert!(t.is_ok()); } NormalErr => { - let t = T::from_str(s); - println!("{:?}", t); + let t = print_unspanned::(s); assert!(t.is_err()); } OtherError => { - println!("{:?}", T::from_str(s)); - } - OtherWithPanic => { - if catch_unwind(|| println!("{:?}", T::from_str(s))).is_ok() { - eprintln!("{s} did not panic"); - } + let t = print_unspanned::(s); + // For now we assert OK here, but in the future this should all move to NormalErr. + // Any case that's failing this should go to NormalErr, probably after verifying that a + // diagnostic did get emitted. + assert!(t.is_ok()); } } } @@ -64,7 +77,10 @@ fn lit(s: &str, mode: Mode) { } pub fn run() { + assert_eq!("\'", "'"); // returns Ok(valid instance) + lit("r\"g\"", NormalOk); + lit("r#\"g\"#", NormalOk); lit("123", NormalOk); lit("\"ab\"", NormalOk); lit("\'b\'", NormalOk); @@ -72,6 +88,7 @@ pub fn run() { lit("b\"b\"", NormalOk); lit("c\"b\"", NormalOk); lit("cr\"b\"", NormalOk); + lit("'\\''", NormalOk); lit("b'b'", NormalOk); lit("256u8", NormalOk); lit("-256u8", NormalOk); @@ -99,10 +116,13 @@ pub fn run() { NormalOk, ); stream("/*a*/ //", NormalOk); + lit("\"\"", NormalOk); + stream("", NormalOk); println!("### ERRORS"); // returns Err(LexError) + lit("", NormalErr); lit("\'c\'/**/", NormalErr); lit(" 0", NormalErr); lit("0 ", NormalErr); @@ -117,16 +137,18 @@ pub fn run() { // FIXME: all of the cases below should return an Err and emit no diagnostics, but don't yet. // emits diagnostics and returns LexError - lit("r'r'", OtherError); - lit("c'r'", OtherError); + lit("r'r'", NormalErr); + lit("c'r'", NormalErr); + lit("\u{2000}", NormalErr); // emits diagnostic and returns a seemingly valid tokenstream stream("r'r'", OtherError); stream("c'r'", OtherError); + stream("\u{2000}", OtherError); for parse in [stream as fn(&str, Mode), lit] { - // emits diagnostic(s), then panics - parse("r#", OtherWithPanic); + // emits diagnostic(s), then returns LexError + parse("r#", NormalErr); // emits diagnostic(s), then returns Ok(Literal { kind: ErrWithGuar, .. }) parse("0b2", OtherError); @@ -137,9 +159,10 @@ pub fn run() { "' '", OtherError, ); - parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), OtherWithPanic); - - // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar - parse("/*a*/ 0b2 //", OtherError); + parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), NormalErr); } + + // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar + lit("/*a*/ 0b2 //", NormalErr); + stream("/*a*/ 0b2 //", OtherError); } diff --git a/tests/ui/proc-macro/nonfatal-parsing.stderr b/tests/ui/proc-macro/nonfatal-parsing.stderr index 874a7a2f410d9..f015794e2ce67 100644 --- a/tests/ui/proc-macro/nonfatal-parsing.stderr +++ b/tests/ui/proc-macro/nonfatal-parsing.stderr @@ -22,6 +22,17 @@ help: consider inserting whitespace here LL | c 'r' | + +error: unknown start of token: \u{2000} + --> :1:1 + | +LL |   + | ^ + | +help: Unicode character ' ' (En Quad) looks like ' ' (Space), but it is not + | +LL | + | + error: prefix `r` is unknown --> $DIR/nonfatal-parsing.rs:15:5 | @@ -40,6 +51,19 @@ LL | nonfatal_parsing::run!(); = note: prefixed identifiers and literals are reserved since Rust 2021 = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) +error: unknown start of token: \u{2000} + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) +help: Unicode character ' ' (En Quad) looks like ' ' (Space), but it is not + --> :1:1 + | +LL | + | + error: found invalid character; only `#` is allowed in raw string delimitation: \u{0} --> $DIR/nonfatal-parsing.rs:15:5 | @@ -106,15 +130,6 @@ LL | nonfatal_parsing::run!(); | = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid digit for a base 2 literal - --> $DIR/nonfatal-parsing.rs:15:5 - | -LL | nonfatal_parsing::run!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) - error: found invalid character; only `#` is allowed in raw string delimitation: \u{0} --> :1:1 | @@ -175,6 +190,15 @@ error: invalid digit for a base 2 literal LL | /*a*/ 0b2 // | ^ -error: aborting due to 20 previous errors +error: invalid digit for a base 2 literal + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 22 previous errors For more information about this error, try `rustc --explain E0768`. diff --git a/tests/ui/proc-macro/nonfatal-parsing.stdout b/tests/ui/proc-macro/nonfatal-parsing.stdout index 2b92474fb8b4c..fcefde22541ba 100644 --- a/tests/ui/proc-macro/nonfatal-parsing.stdout +++ b/tests/ui/proc-macro/nonfatal-parsing.stdout @@ -1,34 +1,40 @@ -Ok(Literal { kind: Integer, symbol: "123", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Str, symbol: "ab", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Char, symbol: "b", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Char, symbol: "b", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: ByteStr, symbol: "b", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: CStr, symbol: "b", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: CStrRaw(0), symbol: "b", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Byte, symbol: "b", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Integer, symbol: "256", suffix: Some("u8"), span: #44 bytes(361..385) }) -Ok(Literal { kind: Integer, symbol: "-256", suffix: Some("u8"), span: #44 bytes(361..385) }) -Ok(TokenStream [Punct { ch: '-', spacing: Alone, span: #44 bytes(361..385) }, Literal { kind: Integer, symbol: "256", suffix: Some("u8"), span: #44 bytes(361..385) }]) -Ok(Literal { kind: Integer, symbol: "0b11111000000001111", suffix: Some("i16"), span: #44 bytes(361..385) }) -Ok(Literal { kind: Integer, symbol: "0xf32", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Integer, symbol: "0b0", suffix: Some("f32"), span: #44 bytes(361..385) }) -Ok(Literal { kind: Float, symbol: "2E4", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Float, symbol: "2.2E-4", suffix: Some("f64"), span: #44 bytes(361..385) }) -Ok(Literal { kind: Integer, symbol: "18", suffix: Some("u8E"), span: #44 bytes(361..385) }) -Ok(Literal { kind: Float, symbol: "18.0", suffix: Some("u8E"), span: #44 bytes(361..385) }) -Ok(Literal { kind: CStrRaw(1), symbol: "// /* // \n */", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Char, symbol: "\'", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: Char, symbol: "\'", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: StrRaw(255), symbol: "a", suffix: None, span: #44 bytes(361..385) }) -Ok(TokenStream [Ident { ident: "fn", span: #44 bytes(361..385) }, Ident { ident: "main", span: #44 bytes(361..385) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #44 bytes(361..385) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "println", span: #44 bytes(361..385) }, Punct { ch: '!', spacing: Alone, span: #44 bytes(361..385) }, Group { delimiter: Parenthesis, stream: TokenStream [Literal { kind: Str, symbol: "Hello, world!", suffix: None, span: #44 bytes(361..385) }], span: #44 bytes(361..385) }], span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: Integer, symbol: "18", suffix: None, span: #44 bytes(361..385) }, Punct { ch: '.', spacing: Alone, span: #44 bytes(361..385) }, Ident { ident: "u8E", span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: Float, symbol: "18.0", suffix: Some("f32"), span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: Float, symbol: "18.0", suffix: Some("f34"), span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: Integer, symbol: "18", suffix: None, span: #44 bytes(361..385) }, Punct { ch: '.', spacing: Alone, span: #44 bytes(361..385) }, Ident { ident: "bu8", span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: Integer, symbol: "3", suffix: None, span: #44 bytes(361..385) }, Literal { kind: Integer, symbol: "4", suffix: None, span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: Char, symbol: "c", suffix: None, span: #44 bytes(361..385) }]) +Ok(Literal { kind: StrRaw(0), symbol: "g", suffix: None, span: Span }) +Ok(Literal { kind: StrRaw(1), symbol: "g", suffix: None, span: Span }) +Ok(Literal { kind: Integer, symbol: "123", suffix: None, span: Span }) +Ok(Literal { kind: Str, symbol: "ab", suffix: None, span: Span }) +Ok(Literal { kind: Char, symbol: "b", suffix: None, span: Span }) +Ok(Literal { kind: Char, symbol: "b", suffix: None, span: Span }) +Ok(Literal { kind: ByteStr, symbol: "b", suffix: None, span: Span }) +Ok(Literal { kind: CStr, symbol: "b", suffix: None, span: Span }) +Ok(Literal { kind: CStrRaw(0), symbol: "b", suffix: None, span: Span }) +Ok(Literal { kind: Char, symbol: "\'", suffix: None, span: Span }) +Ok(Literal { kind: Byte, symbol: "b", suffix: None, span: Span }) +Ok(Literal { kind: Integer, symbol: "256", suffix: Some("u8"), span: Span }) +Ok(Literal { kind: Integer, symbol: "-256", suffix: Some("u8"), span: Span }) +Ok(TokenStream [Punct { ch: '-', spacing: Alone, span: Span }, Literal { kind: Integer, symbol: "256", suffix: Some("u8"), span: Span }]) +Ok(Literal { kind: Integer, symbol: "0b11111000000001111", suffix: Some("i16"), span: Span }) +Ok(Literal { kind: Integer, symbol: "0xf32", suffix: None, span: Span }) +Ok(Literal { kind: Integer, symbol: "0b0", suffix: Some("f32"), span: Span }) +Ok(Literal { kind: Float, symbol: "2E4", suffix: None, span: Span }) +Ok(Literal { kind: Float, symbol: "2.2E-4", suffix: Some("f64"), span: Span }) +Ok(Literal { kind: Integer, symbol: "18", suffix: Some("u8E"), span: Span }) +Ok(Literal { kind: Float, symbol: "18.0", suffix: Some("u8E"), span: Span }) +Ok(Literal { kind: CStrRaw(1), symbol: "// /* // \n */", suffix: None, span: Span }) +Ok(Literal { kind: Char, symbol: "\'", suffix: None, span: Span }) +Ok(Literal { kind: Char, symbol: "\'", suffix: None, span: Span }) +Ok(Literal { kind: StrRaw(255), symbol: "a", suffix: None, span: Span }) +Ok(TokenStream [Ident { ident: "fn", span: Span }, Ident { ident: "main", span: Span }, Group { delimiter: Parenthesis, stream: TokenStream [], span: Span }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "println", span: Span }, Punct { ch: '!', spacing: Alone, span: Span }, Group { delimiter: Parenthesis, stream: TokenStream [Literal { kind: Str, symbol: "Hello, world!", suffix: None, span: Span }], span: Span }], span: Span }]) +Ok(TokenStream [Literal { kind: Integer, symbol: "18", suffix: None, span: Span }, Punct { ch: '.', spacing: Alone, span: Span }, Ident { ident: "u8E", span: Span }]) +Ok(TokenStream [Literal { kind: Float, symbol: "18.0", suffix: Some("f32"), span: Span }]) +Ok(TokenStream [Literal { kind: Float, symbol: "18.0", suffix: Some("f34"), span: Span }]) +Ok(TokenStream [Literal { kind: Integer, symbol: "18", suffix: None, span: Span }, Punct { ch: '.', spacing: Alone, span: Span }, Ident { ident: "bu8", span: Span }]) +Ok(TokenStream [Literal { kind: Integer, symbol: "3", suffix: None, span: Span }, Literal { kind: Integer, symbol: "4", suffix: None, span: Span }]) +Ok(TokenStream [Literal { kind: Char, symbol: "c", suffix: None, span: Span }]) +Ok(TokenStream []) +Ok(Literal { kind: Str, symbol: "", suffix: None, span: Span }) Ok(TokenStream []) ### ERRORS +Err(LexError("not a literal")) Err(LexError("comment or whitespace around literal")) Err(LexError("comment or whitespace around literal")) Err(LexError("comment or whitespace around literal")) @@ -42,17 +48,23 @@ Err(LexError("unexpected closing delimiter: `)`")) Err(LexError("unexpected closing delimiter: `]`")) Err(LexError("not a literal")) Err(LexError("not a literal")) -Ok(TokenStream [Ident { ident: "r", span: #44 bytes(361..385) }, Literal { kind: Char, symbol: "r", suffix: None, span: #44 bytes(361..385) }]) -Ok(TokenStream [Ident { ident: "c", span: #44 bytes(361..385) }, Literal { kind: Char, symbol: "r", suffix: None, span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: #44 bytes(361..385) }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: #44 bytes(361..385) }]) -Ok(Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: #44 bytes(361..385) }) -Ok(Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: #44 bytes(361..385) }) -Ok(Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: #44 bytes(361..385) }) -Ok(Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: #44 bytes(361..385) }) +Err(LexError("not a literal")) +Ok(TokenStream [Ident { ident: "r", span: Span }, Literal { kind: Char, symbol: "r", suffix: None, span: Span }]) +Ok(TokenStream [Ident { ident: "c", span: Span }, Literal { kind: Char, symbol: "r", suffix: None, span: Span }]) +Ok(TokenStream []) +Err(LexError("failed to parse to tokenstream")) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: Span }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: Span }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: Span }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: Span }]) +Err(LexError("failed to parse to tokenstream")) +Err(LexError("failed to parse to literal")) +Ok(Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }) +Ok(Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: Span }) +Ok(Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: Span }) +Ok(Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: Span }) +Ok(Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: Span }) +Err(LexError("failed to parse to literal")) Err(LexError("comment or whitespace around literal")) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) diff --git a/tests/ui/process/nofile-limit.rs b/tests/ui/process/nofile-limit.rs index f5246856b80f2..96b298cc78105 100644 --- a/tests/ui/process/nofile-limit.rs +++ b/tests/ui/process/nofile-limit.rs @@ -8,6 +8,9 @@ //@ no-prefer-dynamic //@ compile-flags: -Ctarget-feature=+crt-static -Crpath=no -Crelocation-model=static //@ ignore-backends: gcc +// aarch64-unknown-linux-pauthtest requires dynamic linking, which makes use of file descriptors. +// Setting RLIMIT_NOFILE would result in the binary failing even before main is reached. +//@ ignore-pauthtest #![feature(exit_status_error)] #![feature(rustc_private)] diff --git a/tests/ui/process/process-sigpipe.rs b/tests/ui/process/process-sigpipe.rs index 574d79ee1ddaf..b8eb2a9688b4d 100644 --- a/tests/ui/process/process-sigpipe.rs +++ b/tests/ui/process/process-sigpipe.rs @@ -31,9 +31,9 @@ fn main() { thread::sleep_ms(5000); process::exit(1); }); - // QNX Neutrino does not have `yes`. Therefore, use `while-echo` for `nto` + // QNX does not have `yes`. Therefore, use `while-echo` for `nto` // and `yes` for other platforms. - let command = if cfg!(target_os = "nto") { + let command = if cfg!(any(target_os = "nto", target_os = "qnx")) { "while echo y ; do : ; done | head" } else { "yes | head" diff --git a/tests/ui/issues/issue-2150.rs b/tests/ui/reachable/unreachable-ret-after-panic.rs similarity index 76% rename from tests/ui/issues/issue-2150.rs rename to tests/ui/reachable/unreachable-ret-after-panic.rs index baa1dde0fc2e8..db8673e5fd59b 100644 --- a/tests/ui/issues/issue-2150.rs +++ b/tests/ui/reachable/unreachable-ret-after-panic.rs @@ -1,3 +1,5 @@ +//! Regression test for . + #![deny(unreachable_code)] #![allow(unused_variables)] #![allow(dead_code)] diff --git a/tests/ui/issues/issue-2150.stderr b/tests/ui/reachable/unreachable-ret-after-panic.stderr similarity index 79% rename from tests/ui/issues/issue-2150.stderr rename to tests/ui/reachable/unreachable-ret-after-panic.stderr index a9268646edfcb..7b73ddc723042 100644 --- a/tests/ui/issues/issue-2150.stderr +++ b/tests/ui/reachable/unreachable-ret-after-panic.stderr @@ -1,5 +1,5 @@ error: unreachable statement - --> $DIR/issue-2150.rs:8:5 + --> $DIR/unreachable-ret-after-panic.rs:10:5 | LL | panic!(); | -------- any code following this expression is unreachable @@ -7,7 +7,7 @@ LL | for x in &v { i += 1; } | ^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement | note: the lint level is defined here - --> $DIR/issue-2150.rs:1:9 + --> $DIR/unreachable-ret-after-panic.rs:3:9 | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-22638.rs b/tests/ui/recursion/infinite-instantiation-via-nested-closures.rs similarity index 93% rename from tests/ui/issues/issue-22638.rs rename to tests/ui/recursion/infinite-instantiation-via-nested-closures.rs index be9304e5829f0..6961d9a4dfde8 100644 --- a/tests/ui/issues/issue-22638.rs +++ b/tests/ui/recursion/infinite-instantiation-via-nested-closures.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ build-fail #![allow(unused)] diff --git a/tests/ui/issues/issue-22638.stderr b/tests/ui/recursion/infinite-instantiation-via-nested-closures.stderr similarity index 59% rename from tests/ui/issues/issue-22638.stderr rename to tests/ui/recursion/infinite-instantiation-via-nested-closures.stderr index 96f1794486838..c4171e23b5917 100644 --- a/tests/ui/issues/issue-22638.stderr +++ b/tests/ui/recursion/infinite-instantiation-via-nested-closures.stderr @@ -1,11 +1,11 @@ -error: reached the recursion limit while instantiating `A::matches::<{closure@$DIR/issue-22638.rs:42:23: 42:25}>` - --> $DIR/issue-22638.rs:54:9 +error: reached the recursion limit while instantiating `A::matches::<{closure@$DIR/infinite-instantiation-via-nested-closures.rs:43:23: 43:25}>` + --> $DIR/infinite-instantiation-via-nested-closures.rs:55:9 | LL | a.matches(f) | ^^^^^^^^^^^^ | note: `A::matches` defined here - --> $DIR/issue-22638.rs:13:5 + --> $DIR/infinite-instantiation-via-nested-closures.rs:14:5 | LL | pub fn matches(&self, f: &F) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-29466.rs b/tests/ui/recursion/many-statements-no-overflow.rs similarity index 99% rename from tests/ui/issues/issue-29466.rs rename to tests/ui/recursion/many-statements-no-overflow.rs index 3962940e848eb..79431f2b39636 100644 --- a/tests/ui/issues/issue-29466.rs +++ b/tests/ui/recursion/many-statements-no-overflow.rs @@ -1,7 +1,7 @@ +//! Regression test for . +//! This used to overflow stack. // ignore-tidy-filelength -// //@ run-pass - // FIXME(#61117): Respect debuginfo-level-tests, do not force debuginfo-level=0 //@ compile-flags: -Cdebuginfo=0 diff --git a/tests/ui/issues/issue-21546.rs b/tests/ui/resolve/module-type-name-conflict.rs similarity index 77% rename from tests/ui/issues/issue-21546.rs rename to tests/ui/resolve/module-type-name-conflict.rs index 9f3974b431470..b054fead6bc1f 100644 --- a/tests/ui/issues/issue-21546.rs +++ b/tests/ui/resolve/module-type-name-conflict.rs @@ -1,4 +1,7 @@ -// Also works as a test for #14564 +//! Regression test for . +//! Ensure structs, enums and modules cannot share name in namespace. +//! +//! Also works as a test for . #[allow(non_snake_case)] mod Foo { } diff --git a/tests/ui/issues/issue-21546.stderr b/tests/ui/resolve/module-type-name-conflict.stderr similarity index 87% rename from tests/ui/issues/issue-21546.stderr rename to tests/ui/resolve/module-type-name-conflict.stderr index 5fd06fc673361..fd5649cfc4512 100644 --- a/tests/ui/issues/issue-21546.stderr +++ b/tests/ui/resolve/module-type-name-conflict.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `Foo` is defined multiple times - --> $DIR/issue-21546.rs:7:1 + --> $DIR/module-type-name-conflict.rs:10:1 | LL | mod Foo { } | ------- previous definition of the module `Foo` here @@ -10,7 +10,7 @@ LL | struct Foo; = note: `Foo` must be defined only once in the type namespace of this module error[E0428]: the name `Bar` is defined multiple times - --> $DIR/issue-21546.rs:14:1 + --> $DIR/module-type-name-conflict.rs:17:1 | LL | mod Bar { } | ------- previous definition of the module `Bar` here @@ -21,7 +21,7 @@ LL | struct Bar(i32); = note: `Bar` must be defined only once in the type namespace of this module error[E0428]: the name `Baz` is defined multiple times - --> $DIR/issue-21546.rs:22:1 + --> $DIR/module-type-name-conflict.rs:25:1 | LL | struct Baz(i32); | ---------------- previous definition of the type `Baz` here @@ -32,7 +32,7 @@ LL | mod Baz { } = note: `Baz` must be defined only once in the type namespace of this module error[E0428]: the name `Qux` is defined multiple times - --> $DIR/issue-21546.rs:30:1 + --> $DIR/module-type-name-conflict.rs:33:1 | LL | struct Qux { x: bool } | ---------- previous definition of the type `Qux` here @@ -43,7 +43,7 @@ LL | mod Qux { } = note: `Qux` must be defined only once in the type namespace of this module error[E0428]: the name `Quux` is defined multiple times - --> $DIR/issue-21546.rs:38:1 + --> $DIR/module-type-name-conflict.rs:41:1 | LL | struct Quux; | ------------ previous definition of the type `Quux` here @@ -54,7 +54,7 @@ LL | mod Quux { } = note: `Quux` must be defined only once in the type namespace of this module error[E0428]: the name `Corge` is defined multiple times - --> $DIR/issue-21546.rs:46:1 + --> $DIR/module-type-name-conflict.rs:49:1 | LL | enum Corge { A, B } | ---------- previous definition of the type `Corge` here diff --git a/tests/ui/issues/issue-28498-must-work-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs similarity index 63% rename from tests/ui/issues/issue-28498-must-work-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs index 813cd8645f923..9afd7ddbd88a9 100644 --- a/tests/ui/issues/issue-28498-must-work-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs @@ -1,8 +1,7 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-must-continue-to-work use std::cell::Cell; diff --git a/tests/ui/issues/issue-28498-must-work-ex2.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs similarity index 67% rename from tests/ui/issues/issue-28498-must-work-ex2.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs index 98514b1163675..4e8d403672641 100644 --- a/tests/ui/issues/issue-28498-must-work-ex2.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs @@ -1,8 +1,7 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-must-continue-to-work use std::cell::Cell; diff --git a/tests/ui/span/issue28498-reject-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs similarity index 76% rename from tests/ui/span/issue28498-reject-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs index 4d1b4125503b6..3209a17041f67 100644 --- a/tests/ui/span/issue28498-reject-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs @@ -1,9 +1,7 @@ -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-will-start-to-be-rejected - -// Compare against test/run-pass/issue28498-must-work-ex2.rs +//! Test for . +//! Example taken from RFC 1238 text +//! . +//! Compare against tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs. use std::cell::Cell; diff --git a/tests/ui/span/issue28498-reject-ex1.stderr b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr similarity index 93% rename from tests/ui/span/issue28498-reject-ex1.stderr rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr index 7b8af23a6cd7b..6e20f06d7fe4c 100644 --- a/tests/ui/span/issue28498-reject-ex1.stderr +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr @@ -1,5 +1,5 @@ error[E0713]: borrow may still be in use when destructor runs - --> $DIR/issue28498-reject-ex1.rs:34:29 + --> $DIR/reject-ex1.rs:32:29 | LL | foo.data[0].1.set(Some(&foo.data[1])); | ^^^^^^^^ diff --git a/tests/ui/issues/issue-28498-ugeh-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs similarity index 73% rename from tests/ui/issues/issue-28498-ugeh-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs index f606d2489484a..722523c094329 100644 --- a/tests/ui/issues/issue-28498-ugeh-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs @@ -1,10 +1,8 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #example-of-the-unguarded-escape-hatch - #![feature(dropck_eyepatch)] use std::cell::Cell; diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs index 82053a12b133f..b7eaabdf6ad3f 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs @@ -26,15 +26,15 @@ fn call_once_i32(f: impl FnOnce(i32)) { } fn main() { - call(foo); //~ ERROR expected a `Fn()` closure, found `#[target_features] fn() {foo}` - call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `#[target_features] fn() {foo}` - call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` - call_once_i32(bar); //~ ERROR expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` + call(foo); //~ ERROR expected an `Fn()` closure, found `#[target_features] fn() {foo}` + call_mut(foo); //~ ERROR expected an `FnMut()` closure, found `#[target_features] fn() {foo}` + call_once(foo); //~ ERROR expected an `FnOnce()` closure, found `#[target_features] fn() {foo}` + call_once_i32(bar); //~ ERROR expected an `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` call(foo_unsafe); - //~^ ERROR expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `Fn()` closure, found `unsafe fn() {foo_unsafe}` call_mut(foo_unsafe); - //~^ ERROR expected a `FnMut()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `FnMut()` closure, found `unsafe fn() {foo_unsafe}` call_once(foo_unsafe); - //~^ ERROR expected a `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` } diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 52543caad37db..5333307a59280 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `Fn()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:29:10 | LL | call(foo); @@ -16,7 +16,7 @@ note: required by a bound in `call` LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` -error[E0277]: expected a `FnMut()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `FnMut()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:30:14 | LL | call_mut(foo); @@ -34,7 +34,7 @@ note: required by a bound in `call_mut` LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` -error[E0277]: expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `FnOnce()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:31:15 | LL | call_once(foo); @@ -52,7 +52,7 @@ note: required by a bound in `call_once` LL | fn call_once(f: impl FnOnce()) { | ^^^^^^^^ required by this bound in `call_once` -error[E0277]: expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` +error[E0277]: expected an `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` --> $DIR/fn-traits.rs:32:19 | LL | call_once_i32(bar); @@ -69,7 +69,7 @@ note: required by a bound in `call_once_i32` LL | fn call_once_i32(f: impl FnOnce(i32)) { | ^^^^^^^^^^^ required by this bound in `call_once_i32` -error[E0277]: expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:34:10 | LL | call(foo_unsafe); @@ -88,7 +88,7 @@ note: required by a bound in `call` LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` -error[E0277]: expected a `FnMut()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `FnMut()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:36:14 | LL | call_mut(foo_unsafe); @@ -107,7 +107,7 @@ note: required by a bound in `call_mut` LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` -error[E0277]: expected a `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:38:15 | LL | call_once(foo_unsafe); diff --git a/tests/ui/issues/issue-28472.rs b/tests/ui/span/foreign-item-vis-span.rs similarity index 55% rename from tests/ui/issues/issue-28472.rs rename to tests/ui/span/foreign-item-vis-span.rs index 6db1f9a5bddf9..839e07ea1b961 100644 --- a/tests/ui/issues/issue-28472.rs +++ b/tests/ui/span/foreign-item-vis-span.rs @@ -1,4 +1,5 @@ -// Check that the visibility modifier is included in the span of foreign items. +//! Regression test for . +//! Check that the visibility modifier is included in the span of foreign items. extern "C" { fn foo(); diff --git a/tests/ui/issues/issue-28472.stderr b/tests/ui/span/foreign-item-vis-span.stderr similarity index 90% rename from tests/ui/issues/issue-28472.stderr rename to tests/ui/span/foreign-item-vis-span.stderr index 051ed25b6c9d9..912a338e4616a 100644 --- a/tests/ui/issues/issue-28472.stderr +++ b/tests/ui/span/foreign-item-vis-span.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `foo` is defined multiple times - --> $DIR/issue-28472.rs:6:3 + --> $DIR/foreign-item-vis-span.rs:7:3 | LL | fn foo(); | --------- previous definition of the value `foo` here @@ -11,7 +11,7 @@ LL | | fn foo(); = note: `foo` must be defined only once in the value namespace of this module error[E0428]: the name `foo` is defined multiple times - --> $DIR/issue-28472.rs:9:3 + --> $DIR/foreign-item-vis-span.rs:10:3 | LL | fn foo(); | --------- previous definition of the value `foo` here diff --git a/tests/ui/specialization/specialization-default-projection.next.stderr b/tests/ui/specialization/specialization-default-projection.next.stderr index 1a5869f2d0a5f..859f83e2314ec 100644 --- a/tests/ui/specialization/specialization-default-projection.next.stderr +++ b/tests/ui/specialization/specialization-default-projection.next.stderr @@ -5,7 +5,7 @@ LL | fn generic() -> ::Assoc { | ----------------- expected `::Assoc` because of return type ... LL | () - | ^^ types differ + | ^^ expected associated type, found `()` | = note: expected associated type `::Assoc` found unit type `()` @@ -23,7 +23,7 @@ LL | fn monomorphic() -> () { LL | generic::<()>() | ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;` | | - | types differ + | expected `()`, found associated type | = note: expected unit type `()` found associated type `<() as Foo>::Assoc` diff --git a/tests/ui/specialization/specialization-default-types.next.stderr b/tests/ui/specialization/specialization-default-types.next.stderr index b6f61b40ccb24..2447669a83986 100644 --- a/tests/ui/specialization/specialization-default-types.next.stderr +++ b/tests/ui/specialization/specialization-default-types.next.stderr @@ -6,7 +6,7 @@ LL | default type Output = Box; LL | default fn generate(self) -> Self::Output { | ------------ expected `::Output` because of return type LL | Box::new(self) - | ^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^ expected associated type, found `Box` | = note: expected associated type `::Output` found struct `Box` @@ -19,7 +19,7 @@ error[E0308]: mismatched types LL | fn trouble(t: T) -> Box { | ------ expected `Box` because of return type LL | Example::generate(t) - | ^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^ expected `Box`, found associated type | = note: expected struct `Box` found associated type `::Output` diff --git a/tests/ui/splat/splat-255-limit-fail.rs b/tests/ui/splat/splat-255-limit-fail.rs new file mode 100644 index 0000000000000..31767f11d999b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-fail.rs @@ -0,0 +1,199 @@ +// ignore-tidy-linelength +//! Test `#[splat]` fails over the 255th argument index (or higher). +//! FIXME(splat): The 255 argument limit is a temporary performance hack. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(dead_code)] + +type A = (); + +// These types and functions are deliberately formatted with 17 arguments in 15 lines, to show they +// have ~255 arguments. +#[rustfmt::skip] +type Tuple256 = ( + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, +); + +#[rustfmt::skip] +fn s_255_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 +) {} + +#[rustfmt::skip] +fn s_256_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 +) {} + +#[rustfmt::skip] +fn s_255_non_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_256_non_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + _: A, +) {} + +// It's only the splatted index that's constrained to 255, not the argument count of the caller or callee. +fn more_than_255_splatted_args(#[splat] _t: Tuple256) {} + +fn main() { + let a = (); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 255 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + /* missing: a, */ + ); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 257 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, /* unexpected: */ a, + ); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 512 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + /* unexpected: */ + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + ); +} diff --git a/tests/ui/splat/splat-255-limit-fail.stderr b/tests/ui/splat/splat-255-limit-fail.stderr new file mode 100644 index 0000000000000..da62da8aa605b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-fail.stderr @@ -0,0 +1,71 @@ +error: `#[splat]` is not supported on argument index 255 + --> $DIR/splat-255-limit-fail.rs:50:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 256 + --> $DIR/splat-255-limit-fail.rs:71:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 255 + --> $DIR/splat-255-limit-fail.rs:91:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 256 + --> $DIR/splat-255-limit-fail.rs:113:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error[E0057]: this splatted function takes 256 arguments, but 255 were provided + --> $DIR/splat-255-limit-fail.rs:124:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | /* missing: a, */ +LL | | ); + | |_____^ + +error[E0057]: this splatted function takes 256 arguments, but 257 were provided + --> $DIR/splat-255-limit-fail.rs:144:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | a, /* unexpected: */ a, +LL | | ); + | |_____^ + +error[E0057]: this splatted function takes 256 arguments, but 512 were provided + --> $DIR/splat-255-limit-fail.rs:164:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | a, +LL | | ); + | |_____^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-255-limit-pass.rs b/tests/ui/splat/splat-255-limit-pass.rs new file mode 100644 index 0000000000000..9713616830f9b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-pass.rs @@ -0,0 +1,201 @@ +//@ run-pass +// ignore-tidy-linelength +//! Test `#[splat]` on the 255th argument index (or lower). +//! FIXME(splat): The 255 argument limit is a temporary performance hack. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(dead_code)] + +type A = (); + +// These types and functions are deliberately formatted with 17 arguments in 15 lines, to show they +// have ~255 arguments. +#[rustfmt::skip] +type Tuple256 = ( + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, +); + +#[rustfmt::skip] +fn s_253_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), +) {} + +#[rustfmt::skip] +fn s_254_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), +) {} + +#[rustfmt::skip] +fn s_254_non_terminal_272_args( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_253_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_254_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_255_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +// It's only the splatted index that's constrained to 255, not the argument count of the caller or callee. +#[rustfmt::skip] +fn s_0_initial_256_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, +) {} + +fn more_than_255_splatted_args(#[splat] _t: Tuple256) {} + +fn main() { + let a = (); + + #[rustfmt::skip] + more_than_255_splatted_args( + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + ); +} diff --git a/tests/ui/splat/splat-assoc-fn-tuple-simple.rs b/tests/ui/splat/splat-assoc-fn-tuple-simple.rs new file mode 100644 index 0000000000000..d2681c0d2574c --- /dev/null +++ b/tests/ui/splat/splat-assoc-fn-tuple-simple.rs @@ -0,0 +1,22 @@ +//@ run-pass +//! Test using `#[splat]` on associated function tuple arguments (no receivers). + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo; + +impl Foo { + fn tuple_1(#[splat] (_a,): (u32,)) {} + + fn tuple_3(#[splat] (_a, _b, _c): (u32, i32, i8)) {} +} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::tuple_1((1u32,)); + + Foo::tuple_1(1u32); + Foo::tuple_3(1u32, 2i32, 3i8); +} diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.rs b/tests/ui/splat/splat-async-fn-tuple-fail.rs new file mode 100644 index 0000000000000..a1f67dfe606d6 --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple-fail.rs @@ -0,0 +1,17 @@ +//@ edition:2024 +//! Test that using `#[splat]` incorrectly on async functions gives errors. + +#![allow(incomplete_features)] +#![feature(splat)] + +async fn async_wrong_type(#[splat] _x: u32) {} +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function + +fn main() { + async_wrong_type(1u32); + async_multi_splat(1u32, 2i8, 3u32, 4i8); + //~^ ERROR this splatted function takes 3 arguments, but 4 were provided +} diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.stderr b/tests/ui/splat/splat-async-fn-tuple-fail.stderr new file mode 100644 index 0000000000000..e643b29c88c8a --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple-fail.stderr @@ -0,0 +1,27 @@ +error: multiple `#[splat]`s are not allowed in the same function + --> $DIR/splat-async-fn-tuple-fail.rs:10:28 + | +LL | async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-async-fn-tuple-fail.rs:7:40 + | +LL | async fn async_wrong_type(#[splat] _x: u32) {} + | ^^^ +... +LL | async_wrong_type(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 3 arguments, but 4 were provided + --> $DIR/splat-async-fn-tuple-fail.rs:15:5 + | +LL | async_multi_splat(1u32, 2i8, 3u32, 4i8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277. +For more information about an error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-async-fn-tuple.rs b/tests/ui/splat/splat-async-fn-tuple.rs new file mode 100644 index 0000000000000..81e56193bd4ab --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple.rs @@ -0,0 +1,18 @@ +//@ run-pass +//@ edition:2024 +//! Test using `#[splat]` on tuple arguments of async functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +async fn async_tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +async fn async_splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + let _ = async_tuple_args(1u32, 2i8); + let _ = async_tuple_args(1, 2); + + let _ = async_splat_non_terminal_arg(1u32, 2i8, 3.5f64); + let _ = async_splat_non_terminal_arg(1, 2, 3.5); +} diff --git a/tests/ui/splat/splat-cannot-resolve.rs b/tests/ui/splat/splat-cannot-resolve.rs new file mode 100644 index 0000000000000..1c22a53f82916 --- /dev/null +++ b/tests/ui/splat/splat-cannot-resolve.rs @@ -0,0 +1,49 @@ +//! Test that using `#[splat]` on un-resolvable types is an error. + +#![allow(incomplete_features)] +#![allow(unconditional_recursion)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn tuple(#[splat] t: impl Sized) -> impl Sized { + //~^ ERROR cannot resolve opaque type + tuple(tuple((t, ()))) +} + +fn tuple_trait(#[splat] t: impl std::marker::Tuple) -> impl std::marker::Tuple { + //~^ ERROR cannot resolve opaque type + tuple_trait(tuple_trait((t, ()))) +} + +trait Trait { + type MaybeTup; + type Tup: std::marker::Tuple; +} + +fn ambig(#[splat] t: Trait::MaybeTup) {} +//~^ ERROR ambiguous associated type +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +fn ambig_tup(#[splat] t: Trait::Tup) {} +//~^ ERROR ambiguous associated type +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a + +fn main() { + tuple(); + tuple_trait(); + ambig(); + ambig_tup(); + + tuple(1); + tuple_trait(1); + ambig(1); + ambig_tup(1); + + tuple(1, 2.0); + tuple_trait(1, 2.0); + ambig(1, 2.0); + ambig_tup(1, 2.0); +} diff --git a/tests/ui/splat/splat-cannot-resolve.stderr b/tests/ui/splat/splat-cannot-resolve.stderr new file mode 100644 index 0000000000000..f91267d37dbc8 --- /dev/null +++ b/tests/ui/splat/splat-cannot-resolve.stderr @@ -0,0 +1,94 @@ +error[E0223]: ambiguous associated type + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - fn ambig(#[splat] t: Trait::MaybeTup) {} +LL + fn ambig(#[splat] t: ::MaybeTup) {} + | + +error[E0223]: ambiguous associated type + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - fn ambig_tup(#[splat] t: Trait::Tup) {} +LL + fn ambig_tup(#[splat] t: ::Tup) {} + | + +error[E0720]: cannot resolve opaque type + --> $DIR/splat-cannot-resolve.rs:8:37 + | +LL | fn tuple(#[splat] t: impl Sized) -> impl Sized { + | ^^^^^^^^^^ + +error[E0720]: cannot resolve opaque type + --> $DIR/splat-cannot-resolve.rs:13:56 + | +LL | fn tuple_trait(#[splat] t: impl std::marker::Tuple) -> impl std::marker::Tuple { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(); + | ^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(); + | ^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(1); + | ^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(1); + | ^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(1, 2.0); + | ^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(1, 2.0); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0223, E0277, E0720. +For more information about an error, try `rustc --explain E0223`. diff --git a/tests/ui/splat/splat-const-fn-tuple-generic.rs b/tests/ui/splat/splat-const-fn-tuple-generic.rs new file mode 100644 index 0000000000000..646bf7e9b9f19 --- /dev/null +++ b/tests/ui/splat/splat-const-fn-tuple-generic.rs @@ -0,0 +1,34 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of const functions with generics. + +#![allow(incomplete_features)] +#![feature(splat)] + +// Generic type in first position +const fn const_generic_first(#[splat] _: (T, u32)) {} + +// Generic type in second position +const fn const_generic_second(#[splat] _: (u32, T)) {} + +// Multiple generic types +const fn const_generic_both(#[splat] _: (T, U)) {} + +// Generic with extra non-splatted arg +const fn const_generic_extra(#[splat] _: (T, u32), _extra: i32) {} + +fn main() { + const_generic_first(1i8, 2u32); + const_generic_first(true, 2u32); + const_generic_first(1u64, 2u32); + + const_generic_second(1u32, 2i8); + const_generic_second(1u32, true); + const_generic_second(1u32, 2u64); + + const_generic_both(1u32, 2i8); + const_generic_both(true, 2u64); + const_generic_both(1i8, false); + + const_generic_extra(1i8, 2u32, 42i32); + const_generic_extra(true, 2u32, 42i32); +} diff --git a/tests/ui/splat/splat-const-fn-tuple.rs b/tests/ui/splat/splat-const-fn-tuple.rs new file mode 100644 index 0000000000000..f4ae2e124a72d --- /dev/null +++ b/tests/ui/splat/splat-const-fn-tuple.rs @@ -0,0 +1,17 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of const functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +const fn sum(#[splat] (a, b): (u32, u32)) -> u32 { + a + b +} + +const RESULT: u32 = sum(1, 2); + +fn main() { + assert_eq!(RESULT, 3); + assert_eq!(sum(12, 18) , 30); + assert_eq!(sum(1, 2), 3); +} diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.rs b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs new file mode 100644 index 0000000000000..40d702346b7a3 --- /dev/null +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs @@ -0,0 +1,14 @@ +//! Test that `#[splat]` on `&dyn AsRef` where `T: Tuple` is an error. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} +//~^ ERROR cannot use splat attribute + +fn main() { + let s: String = "hello".to_owned(); + dyn_asref_splat::(&s); + //~^ ERROR `String` is not a tuple +} diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr new file mode 100644 index 0000000000000..cba1ab28181a4 --- /dev/null +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr @@ -0,0 +1,24 @@ +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a &'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3 (&'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3) + --> $DIR/splat-dyn-asref-tuple-fail.rs:7:36 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} + | ^^^^^^^^^^^^^ +... +LL | dyn_asref_splat::(&s); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `String` is not a tuple + --> $DIR/splat-dyn-asref-tuple-fail.rs:12:23 + | +LL | dyn_asref_splat::(&s); + | ^^^^^^ the nightly-only, unstable trait `std::marker::Tuple` is not implemented for `String` + | +note: required by a bound in `dyn_asref_splat` + --> $DIR/splat-dyn-asref-tuple-fail.rs:7:60 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} + | ^^^^^^^^^^^^^^^^^^ required by this bound in `dyn_asref_splat` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs new file mode 100644 index 0000000000000..297dbc0457794 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -0,0 +1,53 @@ +//@ failure-status: 101 + +//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" +//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" +//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" +//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" +//@ normalize-stderr: " +\d{1,}: .*\n" -> "" +//@ normalize-stderr: " + at .*\n" -> "" +//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" +//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" +//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" + +//! Test using `#[splat]` on tuple arguments of simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in + // MIR lowering + // FIXME(rustfmt): the attribute gets deleted by rustfmt + // Functions + #[rustfmt::skip] + let fn_ptr: fn(#[splat] (u32, i8)) = tuple_args; + fn_ptr(1, 2); //~ ERROR no splatted def for function or method callee + fn_ptr(1u32, 2i8); + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //fn_ptr((1, 2)); // ERROR this splatted function takes 2 arguments, but 1 was provided + + #[rustfmt::skip] + let fn_ptr: fn(#[splat] (u32, i8), f64) = splat_non_terminal_arg; + fn_ptr(1, 2, 3.5); + fn_ptr(1u32, 2i8, 3.5f64); + + // Function pointers + #[rustfmt::skip] + let fn_ptr: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); + (*fn_ptr)(1, 2); + (*fn_ptr)(1u32, 2i8); + + #[rustfmt::skip] + let fn_ptr: *const fn(#[splat] (u32, i8), f64) = + splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); + (*fn_ptr)(1, 2, 3.5); + (*fn_ptr)(1u32, 2i8, 3.5f64); +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr new file mode 100644 index 0000000000000..a69175bd5e886 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple.stderr @@ -0,0 +1,23 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee + | +LL | fn_ptr(1, 2); + | ^^^^^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main` +#1 [check_unsafety] unsafety-checking `main` +#2 [analysis] running analysis passes on crate `splat_fn_ptr_tuple` +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.rs b/tests/ui/splat/splat-fn-tuple-generic-fail.rs new file mode 100644 index 0000000000000..8a8651e75c9ff --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.rs @@ -0,0 +1,27 @@ +//! Test failing use of `#[splat]` on tuple trait arguments of generic functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + + // Calling with un-splatted arguments might look like it works, but the actual generic type is + // a tuple inside another tuple. Aren't generics great? + splat_generic_tuple((1, 2)); + splat_generic_tuple((1u32, 2i8)); + + // FIXME(splat): Make the splat generic handling code handle tuples inside tuples + // (if we want to support tupled calls) + splat_generic_tuple::<(((u32, i8)))>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + splat_generic_tuple::<((u32, i8))>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + splat_generic_tuple::<(u32, i8)>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided +} diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.stderr b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr new file mode 100644 index 0000000000000..7fd4a0719b493 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr @@ -0,0 +1,39 @@ +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:19:5 + | +LL | splat_generic_tuple::<(((u32, i8)))>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:20:5 + | +LL | splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:22:5 + | +LL | splat_generic_tuple::<((u32, i8))>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:23:5 + | +LL | splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:25:5 + | +LL | splat_generic_tuple::<(u32, i8)>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:26:5 + | +LL | splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-fn-tuple-generic.rs b/tests/ui/splat/splat-fn-tuple-generic.rs new file mode 100644 index 0000000000000..b7e3615f62c45 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic.rs @@ -0,0 +1,23 @@ +//@ run-pass +//! Test using `#[splat]` on tuple trait arguments of generic functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn main() { + // Calling with un-splatted arguments might look like it works, but the actual generic type is + // a tuple inside another tuple. Aren't generics great? + splat_generic_tuple((1, 2)); + splat_generic_tuple((1u32, 2i8)); + + // Generic tuple trait implementers are resolved during caller typeck. + splat_generic_tuple::<(u32, i8)>(1u32, 2i8); + splat_generic_tuple(1u32, 2i8); + splat_generic_tuple(1, 2); + + splat_generic_tuple::<()>(); + splat_generic_tuple(); +} diff --git a/tests/ui/splat/splat-fn-tuple-simple.rs b/tests/ui/splat/splat-fn-tuple-simple.rs new file mode 100644 index 0000000000000..c7234a15b9d55 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-simple.rs @@ -0,0 +1,32 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of simple functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + tuple_args(1, 2); + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //tuple_args((1, 2)); + + tuple_args(1, 2); + tuple_args(1u32, 2i8); + + splat_non_terminal_arg(1, 2, 3.5); + splat_non_terminal_arg(1u32, 2i8, 3.5f64); + + #[expect(unused_variables, reason = "FIXME(splat or lint): this is obviously used")] + let fn_ptr = tuple_args; + fn_ptr(1, 2); + fn_ptr(1u32, 2i8); + + #[expect(unused_variables, reason = "FIXME(splat or lint): this is obviously used")] + let fn_ptr = splat_non_terminal_arg; + fn_ptr(1, 2, 3.5); + fn_ptr(1u32, 2i8, 3.5f64); +} diff --git a/tests/ui/splat/splat-generics-complex-types.rs b/tests/ui/splat/splat-generics-complex-types.rs new file mode 100644 index 0000000000000..7d4490c18da75 --- /dev/null +++ b/tests/ui/splat/splat-generics-complex-types.rs @@ -0,0 +1,26 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with complex generic types inside the splatted tuple. + +#![allow(incomplete_features)] +#![feature(splat)] + +// Vec and Option inside splatted tuple +fn nested_generic(#[splat] _: (Vec, Option)) {} + +// Box inside splatted tuple +fn box_generic(#[splat] _: (Box, u32)) {} + +// Multiple complex generics +fn multi_generic(#[splat] _: (Vec, Option, Box)) {} + +fn main() { + nested_generic(vec![1u32, 2u32], Some(2i8)); + nested_generic(vec![1, 2, 3], None::); + nested_generic::(vec![], Some("hello")); + + box_generic(Box::new(1u32), 42u32); + box_generic(Box::new("hello"), 1u32); + + multi_generic(vec![1u32], Some(2i8), Box::new(3.0f64)); + multi_generic::(vec![], None::, Box::new("hello")); +} diff --git a/tests/ui/splat/splat-generics-everywhere.rs b/tests/ui/splat/splat-generics-everywhere.rs new file mode 100644 index 0000000000000..be4f917ce21df --- /dev/null +++ b/tests/ui/splat/splat-generics-everywhere.rs @@ -0,0 +1,64 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with generics in various positions. + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo(T); + +// FIXME(splat): also add assoc/method with splatted generic tuple traits +// also add generics inside the splatted tuple +impl Foo { + fn new(t: T) -> Self { + Self(t) + } + + fn assoc(_u: U, #[splat] _s: ()) {} + + fn method(&self, _v: V, #[splat] _s: (u32, f64)) {} + + fn lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +// FIXME(splat): also add generics to the trait +// also add assoc/method with splatted generic tuple traits +// also add generics inside the splatted tuple +trait BarTrait { + fn trait_assoc(w: W, #[splat] _s: ()); + + fn trait_method(&self, x: X, #[splat] _s: (u32, f64)); + + fn trait_lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +impl BarTrait for Foo { + fn trait_assoc(_w: W, #[splat] _s: ()) {} + + fn trait_method(&self, _x: X, #[splat] _s: (u32, f64)) {} + + fn trait_lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::::assoc(("u",)); + + Foo::::assoc("u"); + Foo::::trait_assoc("w"); + + let foo = Foo::new("t"); + foo.method("v", 1u32, 2.3); + foo.lifetime(1u32, 2.3, "asdf"); + foo.const_generic(1u32, 2.3, [1, 2, 3]); + + foo.trait_method("x", 42u32, 9.8); + foo.trait_lifetime(1u32, 2.3, "asdf"); + foo.trait_const_generic(1u32, 2.3, [1, 2, 3]); +} diff --git a/tests/ui/splat/splat-generics-inside-tuple.rs b/tests/ui/splat/splat-generics-inside-tuple.rs new file mode 100644 index 0000000000000..dce26e55fa2d9 --- /dev/null +++ b/tests/ui/splat/splat-generics-inside-tuple.rs @@ -0,0 +1,29 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with generics inside the splatted tuple. +#![allow(incomplete_features)] +#![feature(splat)] + +fn generic_second(#[splat] _s: (u32, T)) {} + +fn generic_first(#[splat] _s: (T, u32)) {} + +fn generic_both(#[splat] _s: (T, U)) {} + +fn generic_triple(#[splat] _s: (T, U, V)) {} + +fn main() { + generic_second(1u32, 2i8); + generic_second(1u32, 2.0f64); + generic_second(1u32, "hello"); + + generic_first(1i8, 2u32); + generic_first(2.0f64, 2u32); + generic_first("hello", 2u32); + + generic_both(1u32, 2i8); + generic_both("hello", 2.0f64); + generic_both(true, "world"); + + generic_triple(1u32, 2.0f64, "hello"); + generic_triple(true, 42i32, 3.14f32); +} diff --git a/tests/ui/splat/splat-invalid-trait-impl.rs b/tests/ui/splat/splat-invalid-trait-impl.rs new file mode 100644 index 0000000000000..fe0d5ddc5a1c8 --- /dev/null +++ b/tests/ui/splat/splat-invalid-trait-impl.rs @@ -0,0 +1,27 @@ +//! Test that `#[splat]` trait impls with mismatched tuple element types are rejected. +#![allow(incomplete_features)] +#![feature(splat)] + +trait FooTrait { + fn method(#[splat] _: (u32, i8)); +} + +struct Foo; +struct Foo1; +struct Foo2; + +impl FooTrait for Foo { + fn method(#[splat] _: (u32, f32)) {} + //~^ ERROR method `method` has an incompatible type for trait +} + +impl FooTrait for Foo1 { + fn method(#[splat] _: (f32, i8)) {} + //~^ ERROR method `method` has an incompatible type for trait +} + +impl FooTrait for Foo2 { + fn method(#[splat] _: (f32, f64)) {} + //~^ ERROR method `method` has an incompatible type for trait +} +fn main() {} diff --git a/tests/ui/splat/splat-invalid-trait-impl.stderr b/tests/ui/splat/splat-invalid-trait-impl.stderr new file mode 100644 index 0000000000000..684cc65d7a326 --- /dev/null +++ b/tests/ui/splat/splat-invalid-trait-impl.stderr @@ -0,0 +1,60 @@ +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:14:27 + | +LL | fn method(#[splat] _: (u32, f32)) {} + | ^^^^^^^^^^ expected `i8`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (_, i8))` + found signature `fn(#[splat] (_, f32))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (u32, f32)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:19:27 + | +LL | fn method(#[splat] _: (f32, i8)) {} + | ^^^^^^^^^ expected `u32`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (u32, _))` + found signature `fn(#[splat] (f32, _))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (f32, i8)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:24:27 + | +LL | fn method(#[splat] _: (f32, f64)) {} + | ^^^^^^^^^^ expected `u32`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (u32, i8))` + found signature `fn(#[splat] (f32, f64))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (f32, f64)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-invalid.rs b/tests/ui/splat/splat-invalid.rs index 314003459ef72..2c4bf57ef972a 100644 --- a/tests/ui/splat/splat-invalid.rs +++ b/tests/ui/splat/splat-invalid.rs @@ -30,4 +30,18 @@ extern "C" { fn bar_2(#[splat] _: (u32, i8)); } +trait FooTrait { + fn has_splat(#[splat] _: ()); + + fn no_splat(_: (u32, f64)); +} + +struct Foo; + +impl FooTrait for Foo { + fn has_splat(_: ()) {} //~ ERROR method `has_splat` has an incompatible type for trait + + fn no_splat(#[splat] _: (u32, f64)) {} //~ ERROR method `no_splat` has an incompatible type for trait +} + fn main() {} diff --git a/tests/ui/splat/splat-invalid.stderr b/tests/ui/splat/splat-invalid.stderr index d88cbf4ff0b6d..74c54c00cd285 100644 --- a/tests/ui/splat/splat-invalid.stderr +++ b/tests/ui/splat/splat-invalid.stderr @@ -73,5 +73,34 @@ LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} | = help: remove `#[splat]` or remove `...` -error: aborting due to 9 previous errors +error[E0053]: method `has_splat` has an incompatible type for trait + --> $DIR/splat-invalid.rs:42:5 + | +LL | fn has_splat(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg + | +note: type in trait + --> $DIR/splat-invalid.rs:34:5 + | +LL | fn has_splat(#[splat] _: ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn(#[splat] ())` + found signature `fn(())` + +error[E0053]: method `no_splat` has an incompatible type for trait + --> $DIR/splat-invalid.rs:44:5 + | +LL | fn no_splat(#[splat] _: (u32, f64)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | +note: type in trait + --> $DIR/splat-invalid.rs:36:5 + | +LL | fn no_splat(_: (u32, f64)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn((_, _))` + found signature `fn(#[splat] (_, _))` + +error: aborting due to 11 previous errors +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-maybe-tuple.rs b/tests/ui/splat/splat-maybe-tuple.rs new file mode 100644 index 0000000000000..a74af66e9a874 --- /dev/null +++ b/tests/ui/splat/splat-maybe-tuple.rs @@ -0,0 +1,22 @@ +//! Test that using `#[splat]` on maybe-tuple generic function arguments is an error, +//! but only when the generics aren't tuples. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn unbound_generic_arg(#[splat] t: T) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +fn main() { + unbound_generic_arg(); + unbound_generic_arg::<()>(); + + unbound_generic_arg(1); + unbound_generic_arg::<(u32,)>(1); + + unbound_generic_arg(1, 2.0); + unbound_generic_arg::<(u32, f32)>(1, 2.0); + + // The error comes from this call + unbound_generic_arg::(1); +} diff --git a/tests/ui/splat/splat-maybe-tuple.stderr b/tests/ui/splat/splat-maybe-tuple.stderr new file mode 100644 index 0000000000000..9abacdd657b93 --- /dev/null +++ b/tests/ui/splat/splat-maybe-tuple.stderr @@ -0,0 +1,12 @@ +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-maybe-tuple.rs:8:39 + | +LL | fn unbound_generic_arg(#[splat] t: T) {} + | ^ +... +LL | unbound_generic_arg::(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-method-tuple-simple.rs b/tests/ui/splat/splat-method-tuple-simple.rs new file mode 100644 index 0000000000000..887c9515bcdae --- /dev/null +++ b/tests/ui/splat/splat-method-tuple-simple.rs @@ -0,0 +1,40 @@ +//@ run-pass +//! Test using `#[splat]` on method tuple arguments (with receivers). + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo; + +impl Foo { + fn tuple_2(&self, #[splat] (a, _b): (u32, i8)) -> u32 { + a + } + + fn tuple_4(&self, #[splat] (a, _b, _c, _d): (u32, i8, (), f32)) -> u32 { + a + } +} + +#[expect(dead_code)] +struct TupleStruct(u32, i8); + +impl TupleStruct { + fn tuple_2(&self, #[splat] (a, _b): (u32, i8)) -> u32 { + a + } +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //foo.tuple_2((1, 2)); + + foo.tuple_2(1u32, 2i8); + foo.tuple_4(1u32, 2i8, (), 3f32); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct.tuple_2(1u32, 2i8); +} diff --git a/tests/ui/splat/splat-non-tuple.rs b/tests/ui/splat/splat-non-tuple.rs new file mode 100644 index 0000000000000..35841a770b226 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.rs @@ -0,0 +1,90 @@ +//! Test that using `#[splat]` on non-tuple function arguments is an error. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn primitive_arg(#[splat] x: u32) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +enum NotATuple { + A(u32), + B(i8), +} + +fn enum_arg(#[splat] y: NotATuple) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a NotATuple + +trait FooTrait { + fn tuple_1(#[splat] _: (u32,)); //~ NOTE type in trait + + // Ambiguous case, self could be a tuple or a non-tuple + fn tuple_4(#[splat] self, _: (u32, i8, (), f32)); +} + +struct Foo; + +fn struct_arg(#[splat] z: Foo) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Foo + +impl Foo { + fn tuple_2_self( + // FIXME(splat): ERROR cannot use splat attribute; the splatted argument type must be a... + #[splat] self, + (a, b): (u32, i8), + ) -> u32 { + a + } +} + +impl FooTrait for Foo { + fn tuple_1(_: (u32,)) {} + //~^ ERROR method `tuple_1` has an incompatible type for trait + //~| NOTE expected fn with arg 0 splatted, found fn with no splatted arg + //~| NOTE expected signature `fn(#[splat] (_,))` + //~| NOTE found signature `fn((_,))` + + fn tuple_4( + // FIXME(splat): ERROR cannot use splat attribute; the splatted argument type must be a... + #[splat] self, + _: (u32, i8, (), f32), + ) { + } +} + +struct TupleStruct(u32, i8); + +fn tuple_struct_arg(#[splat] z: TupleStruct) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a TupleStruct + +impl TupleStruct { + fn tuple_2( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + (a, b): (f32, f64), + ) -> f32 { + a + } +} + +impl FooTrait for TupleStruct { + fn tuple_1(#[splat] _: (u32,)) {} + + fn tuple_4( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + _: (u32, i8, (), f32), + ) { + } +} + +fn main() { + // FIXME(splat): is it enough for just the definitions/callees to error, + // or should the callers also error? + primitive_arg(1u32); + enum_arg(NotATuple::A(1u32)); + + let foo = Foo; + struct_arg(foo); + foo.tuple_2_self((1u32, 2i8)); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct_arg(tuple_struct); + tuple_struct.tuple_2((1f32, 2f64)); + TupleStruct::tuple_1(1u32); + tuple_struct.tuple_4((1u32, 2i8, (), 3f32)); +} diff --git a/tests/ui/splat/splat-non-tuple.stderr b/tests/ui/splat/splat-non-tuple.stderr new file mode 100644 index 0000000000000..64f7148d8dbd2 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.stderr @@ -0,0 +1,54 @@ +error[E0053]: method `tuple_1` has an incompatible type for trait + --> $DIR/splat-non-tuple.rs:38:5 + | +LL | fn tuple_1(_: (u32,)) {} + | ^^^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg + | +note: type in trait + --> $DIR/splat-non-tuple.rs:17:5 + | +LL | fn tuple_1(#[splat] _: (u32,)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn(#[splat] (_,))` + found signature `fn((_,))` + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-non-tuple.rs:7:30 + | +LL | fn primitive_arg(#[splat] x: u32) {} + | ^^^ +... +LL | primitive_arg(1u32); + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a NotATuple (NotATuple) + --> $DIR/splat-non-tuple.rs:14:25 + | +LL | fn enum_arg(#[splat] y: NotATuple) {} + | ^^^^^^^^^ +... +LL | enum_arg(NotATuple::A(1u32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Foo (Foo) + --> $DIR/splat-non-tuple.rs:25:27 + | +LL | fn struct_arg(#[splat] z: Foo) {} + | ^^^ +... +LL | struct_arg(foo); + | ^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a TupleStruct (TupleStruct) + --> $DIR/splat-non-tuple.rs:54:33 + | +LL | fn tuple_struct_arg(#[splat] z: TupleStruct) {} + | ^^^^^^^^^^^ +... +LL | tuple_struct_arg(tuple_struct); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0053, E0277. +For more information about an error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-overload-at-home-fail.rs b/tests/ui/splat/splat-overload-at-home-fail.rs new file mode 100644 index 0000000000000..8e6a375e0eb96 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home-fail.rs @@ -0,0 +1,41 @@ +//! Test error cases for `#[splat]` "overloading at home" example code. +//! Splatted calls that don't match any registered MethodArgs impl should fail. +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +struct Foo; + +trait MethodArgs: std::marker::Tuple { + fn call_method(self, _this: &Foo); +} + +impl MethodArgs for () { + fn call_method(self, _this: &Foo) {} +} + +impl MethodArgs for (i32,) { + fn call_method(self, _this: &Foo) {} +} + +impl MethodArgs for (i32, String) { + fn call_method(self, _this: &Foo) {} +} + +impl Foo { + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // No impl for (f32,) — wrong type + foo.method(42f32); + //~^ ERROR mismatched types + + // No impl for (i32,i32) - wrong type + foo.method(42i32, 42i32); + //~^ ERROR mismatched types +} diff --git a/tests/ui/splat/splat-overload-at-home-fail.stderr b/tests/ui/splat/splat-overload-at-home-fail.stderr new file mode 100644 index 0000000000000..fcedccf28c359 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home-fail.stderr @@ -0,0 +1,40 @@ +error[E0308]: mismatched types + --> $DIR/splat-overload-at-home-fail.rs:35:16 + | +LL | foo.method(42f32); + | ------ ^^^^^ expected `i32`, found `f32` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/splat-overload-at-home-fail.rs:26:8 + | +LL | fn method(&self, #[splat] args: T) { + | ^^^^^^ ---------------- +help: change the type of the numeric literal from `f32` to `i32` + | +LL - foo.method(42f32); +LL + foo.method(42i32); + | + +error[E0308]: mismatched types + --> $DIR/splat-overload-at-home-fail.rs:39:23 + | +LL | foo.method(42i32, 42i32); + | ------ ^^^^^ expected `String`, found `i32` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/splat-overload-at-home-fail.rs:26:8 + | +LL | fn method(&self, #[splat] args: T) { + | ^^^^^^ +help: try using a conversion method + | +LL | foo.method(42i32, 42i32.to_string()); + | ++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/splat/splat-overload-at-home.rs b/tests/ui/splat/splat-overload-at-home.rs new file mode 100644 index 0000000000000..621f0e04f67e1 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home.rs @@ -0,0 +1,52 @@ +//@ run-pass +// ignore-tidy-linelength +//! Test using `#[splat]` on some "overloading at home" example code. +//! + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +struct Foo; + +trait MethodArgs: std::marker::Tuple { + fn call_method(self, _this: &Foo); +} +impl MethodArgs for () { + fn call_method(self, _this: &Foo) {} +} +impl MethodArgs for (i32,) { + fn call_method(self, _this: &Foo) {} +} +impl MethodArgs for (i32, String) { + fn call_method(self, _this: &Foo) {} +} + +impl Foo { + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //foo.method(()); + //foo.method((42i32,)); + + // Generic tuple trait implementers work without explicit tuple type parameters. + foo.method::<()>(); + foo.method(); + + foo.method::<(i32,)>(42i32); + foo.method::<(i32,)>(42); + foo.method(42i32); + foo.method(42); + + foo.method::<(i32, String)>(42i32, "asdf".to_owned()); + foo.method::<(i32, String)>(42, "asdf".to_owned()); + foo.method(42i32, "asdf".to_owned()); + foo.method(42, "asdf".to_owned()); +} diff --git a/tests/ui/splat/splat-trait-tuple.rs b/tests/ui/splat/splat-trait-tuple.rs new file mode 100644 index 0000000000000..a5b74a40cffd9 --- /dev/null +++ b/tests/ui/splat/splat-trait-tuple.rs @@ -0,0 +1,45 @@ +//@ run-pass +//! Test using `#[splat]` on trait assoc function/method tuple arguments. + +#![allow(incomplete_features)] +#![feature(splat)] + +trait FooTrait { + fn tuple_1_trait(#[splat] _: (u32,)); + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)); +} + +struct Foo; + +impl FooTrait for Foo { + // Currently, splat attributes on impls must match traits. This provides better UX. + fn tuple_1_trait(#[splat] _: (u32,)) {} + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)) {} +} + +#[expect(dead_code)] +struct TupleStruct(u32, i8); + +impl FooTrait for TupleStruct { + fn tuple_1_trait(#[splat] _: (u32,)) {} + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)) {} +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::tuple_1_trait((1u32,)); + //foo.tuple_2_trait((1, 3.5)); + + Foo::tuple_1_trait(1u32); + foo.tuple_2_trait(1, 3.5); + + let tuple_struct = TupleStruct(1u32, 2i8); + TupleStruct::tuple_1_trait(1u32); + tuple_struct.tuple_2_trait(1, 3.5) +} diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs new file mode 100644 index 0000000000000..3f29b9d292d22 --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs @@ -0,0 +1,16 @@ +//! Test that using `#[splat]` incorrectly on unsafe functions gives errors. + +#![allow(incomplete_features)] +#![feature(splat)] + +unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function + +fn main() { + unsafe { + unsafe_wrong_type(1u32); + } +} diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr new file mode 100644 index 0000000000000..67935e170671a --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr @@ -0,0 +1,20 @@ +error: multiple `#[splat]`s are not allowed in the same function + --> $DIR/splat-unsafe-fn-tuple-fail.rs:9:30 + | +LL | unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-unsafe-fn-tuple-fail.rs:6:42 + | +LL | unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} + | ^^^ +... +LL | unsafe_wrong_type(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-unsafe-fn-tuple.rs b/tests/ui/splat/splat-unsafe-fn-tuple.rs new file mode 100644 index 0000000000000..0b9b3510ad50b --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple.rs @@ -0,0 +1,19 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of unsafe functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +unsafe fn unsafe_tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +unsafe fn unsafe_splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + unsafe { + unsafe_tuple_args(1u32, 2i8); + unsafe_tuple_args(1, 2); + + unsafe_splat_non_terminal_arg(1u32, 2i8, 3.5f64); + unsafe_splat_non_terminal_arg(1, 2, 3.5); + } +} diff --git a/tests/ui/splat/splat-where-clause.rs b/tests/ui/splat/splat-where-clause.rs new file mode 100644 index 0000000000000..42a23fe67b216 --- /dev/null +++ b/tests/ui/splat/splat-where-clause.rs @@ -0,0 +1,44 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments with where clause bounds. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn where_splat(#[splat] _t: T) where T: std::marker::Tuple {} + +fn where_splat_with_extra(#[splat] _t: T, _extra: u32) where T: std::marker::Tuple {} + +fn impl_tuple_splat(#[splat] _t: impl std::marker::Tuple) {} + +fn impl_tuple_splat_with_extra(#[splat] _t: impl std::marker::Tuple, _extra: u32) {} + +fn main() { + // empty tuple + where_splat(); + + // single element + where_splat(1u32); + where_splat(1); + + // two elements + where_splat(1u32, 2i8); + where_splat(1, 2); + + // three elements + where_splat(1u32, 2i8, 3.0f64); + where_splat(1, 2, 3.0); + + // with extra non-splatted arg + where_splat_with_extra(1u32, 2i8, 42u32); + where_splat_with_extra(1, 2, 42); + + // impl Trait syntax variants + impl_tuple_splat(); + impl_tuple_splat(1u32); + impl_tuple_splat(1u32, 2i8); + impl_tuple_splat(1, 2, 3.0); + + impl_tuple_splat_with_extra(1u32, 2i8, 42u32); + impl_tuple_splat_with_extra(1, 2, 42); +} diff --git a/tests/ui/statics/crt-static-pauthtest.rs b/tests/ui/statics/crt-static-pauthtest.rs new file mode 100644 index 0000000000000..bc5badc600d88 --- /dev/null +++ b/tests/ui/statics/crt-static-pauthtest.rs @@ -0,0 +1,9 @@ +//@ compile-flags: -C target-feature=+crt-static --target aarch64-unknown-linux-pauthtest +//@ needs-llvm-components: aarch64 +//@ only-pauthtest + + +#![feature(no_core)] +#![no_main] + +//~? ERROR pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static` diff --git a/tests/ui/statics/crt-static-pauthtest.stderr b/tests/ui/statics/crt-static-pauthtest.stderr new file mode 100644 index 0000000000000..00ab7fb8079de --- /dev/null +++ b/tests/ui/statics/crt-static-pauthtest.stderr @@ -0,0 +1,4 @@ +error: pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/statics/mutable_memory_validation.rs b/tests/ui/statics/mutable_memory_validation.rs index bdef949a38bb7..bcc12d34d0994 100644 --- a/tests/ui/statics/mutable_memory_validation.rs +++ b/tests/ui/statics/mutable_memory_validation.rs @@ -3,6 +3,7 @@ // Strip out raw byte dumps to make comparison platform-independent: //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ normalize-stderr: "╾ALLOC\$ID╼\s+│.*╾.*╼" -> "╾ALLOC$$ID╼ │ ╾─╼" use std::cell::UnsafeCell; diff --git a/tests/ui/statics/mutable_memory_validation.stderr b/tests/ui/statics/mutable_memory_validation.stderr index 297a965420340..de596d04ee522 100644 --- a/tests/ui/statics/mutable_memory_validation.stderr +++ b/tests/ui/statics/mutable_memory_validation.stderr @@ -1,12 +1,12 @@ error[E0080]: constructing invalid value of type Meh: at .x., encountered `UnsafeCell` in read-only memory - --> $DIR/mutable_memory_validation.rs:13:1 + --> $DIR/mutable_memory_validation.rs:14:1 | LL | const MUH: Meh = Meh { x: unsafe { &mut *(&READONLY as *const _ as *mut _) } }; | ^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP + ╾ALLOC$ID╼ │ ╾─╼ } error: aborting due to 1 previous error diff --git a/tests/ui/std/linux-getrandom-fallback.rs b/tests/ui/std/linux-getrandom-fallback.rs index c50fbe8257af6..81c0fa623d835 100644 --- a/tests/ui/std/linux-getrandom-fallback.rs +++ b/tests/ui/std/linux-getrandom-fallback.rs @@ -1,5 +1,6 @@ //@ only-linux //@ run-pass +//@ needs-unwind #![feature(random)] #![feature(rustc_private)] diff --git a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr deleted file mode 100644 index 109985c005286..0000000000000 --- a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: use of unstable library feature `atomic_from_mut` - --> $DIR/atomic-from-mut-not-available.rs:24:5 - | -LL | core::sync::atomic::AtomicU64::from_mut(&mut 0u64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #76314 for more information - = help: add `#![feature(atomic_from_mut)]` 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: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr index 4df97a655d626..4d17a6d4b5e9e 100644 --- a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr +++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr @@ -1,5 +1,5 @@ error[E0599]: no associated function or constant named `from_mut` found for struct `Atomic` in the current scope - --> $DIR/atomic-from-mut-not-available.rs:24:36 + --> $DIR/atomic-from-mut-not-available.rs:25:36 | LL | core::sync::atomic::AtomicU64::from_mut(&mut 0u64); | ^^^^^^^^ associated function or constant not found in `Atomic` diff --git a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs index e8aad9cbeb9fa..0d5e09e6e2883 100644 --- a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs +++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs @@ -19,9 +19,9 @@ // ... but pass on 64-bit x86_64 linux. //@[alignment_matches] only-x86_64 //@[alignment_matches] only-linux +//@[alignment_matches] check-pass fn main() { core::sync::atomic::AtomicU64::from_mut(&mut 0u64); //[alignment_mismatch]~^ ERROR no associated function or constant named `from_mut` found for struct `Atomic` - //[alignment_matches]~^^ ERROR use of unstable library feature `atomic_from_mut` } diff --git a/tests/ui/structs/item-definition-in-field.rs b/tests/ui/structs/item-definition-in-field.rs new file mode 100644 index 0000000000000..98e3770581bb5 --- /dev/null +++ b/tests/ui/structs/item-definition-in-field.rs @@ -0,0 +1,8 @@ +//! Regression test for . +//! Item definitions in fields ICE'd. +//@ check-pass + +#![allow(dead_code)] +struct S([u8; { struct Z; 0 }]); + +fn main() {} diff --git a/tests/ui/issues/issue-27815.rs b/tests/ui/structs/non-struct-in-struct-position.rs similarity index 75% rename from tests/ui/issues/issue-27815.rs rename to tests/ui/structs/non-struct-in-struct-position.rs index 9e53014f45522..782808b8e8520 100644 --- a/tests/ui/issues/issue-27815.rs +++ b/tests/ui/structs/non-struct-in-struct-position.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! Test usage of struct literal syntax with non-structs doesn't ICE. + mod A {} fn main() { diff --git a/tests/ui/issues/issue-27815.stderr b/tests/ui/structs/non-struct-in-struct-position.stderr similarity index 79% rename from tests/ui/issues/issue-27815.stderr rename to tests/ui/structs/non-struct-in-struct-position.stderr index 43f78ccf6395a..667ebe7387d07 100644 --- a/tests/ui/issues/issue-27815.stderr +++ b/tests/ui/structs/non-struct-in-struct-position.stderr @@ -1,23 +1,23 @@ error[E0574]: expected struct, variant or union type, found module `A` - --> $DIR/issue-27815.rs:4:13 + --> $DIR/non-struct-in-struct-position.rs:7:13 | LL | let u = A { x: 1 }; | ^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found builtin type `u32` - --> $DIR/issue-27815.rs:5:13 + --> $DIR/non-struct-in-struct-position.rs:8:13 | LL | let v = u32 { x: 1 }; | ^^^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found module `A` - --> $DIR/issue-27815.rs:7:9 + --> $DIR/non-struct-in-struct-position.rs:10:9 | LL | A { x: 1 } => {} | ^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found builtin type `u32` - --> $DIR/issue-27815.rs:9:9 + --> $DIR/non-struct-in-struct-position.rs:12:9 | LL | u32 { x: 1 } => {} | ^^^ not a struct, variant or union type diff --git a/tests/ui/structs/rec-align-u64.rs b/tests/ui/structs/rec-align-u64.rs index e49726c7d438f..a3d835799ad62 100644 --- a/tests/ui/structs/rec-align-u64.rs +++ b/tests/ui/structs/rec-align-u64.rs @@ -36,6 +36,7 @@ struct Outer { target_os = "solaris", target_os = "vxworks", target_os = "nto", + target_os = "qnx", target_vendor = "apple", ))] mod m { diff --git a/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs b/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs new file mode 100644 index 0000000000000..0350ef50705d9 --- /dev/null +++ b/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs @@ -0,0 +1,27 @@ +// Regression test for https://github.com/rust-lang/rust/issues/142803. + +trait Pair { + type Left; + type Right; + + fn split(self) -> (Self::Left, Self::Right); +} + +impl Pair for (A, B) { + type Left = A; + type Right = B; + + fn split(self) -> (Self::Left, Self::Right) { + self + } +} + +fn frob(pair: impl Pair) -> impl Pair { + //~^ ERROR type mismatch + //~| HELP consider further restricting this bound + //~| SUGGESTION , Right = B + let (left, right) = pair.split(); + (left, right) +} + +fn main() {} diff --git a/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr b/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr new file mode 100644 index 0000000000000..a284f2adb366c --- /dev/null +++ b/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr @@ -0,0 +1,24 @@ +error[E0271]: type mismatch resolving `<(A, as Pair>::Right) as Pair>::Right == B` + --> $DIR/restrict-bound-already-has-generic-args.rs:19:45 + | +LL | fn frob(pair: impl Pair) -> impl Pair { + | - expected this type parameter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<(A, as Pair>::Right) as Pair>::Right == B` +... +LL | (left, right) + | ------------- return type was inferred to be `(A, as Pair>::Right)` here + | +note: expected this to be `B` + --> $DIR/restrict-bound-already-has-generic-args.rs:12:18 + | +LL | type Right = B; + | ^ + = note: expected type parameter `B` + found associated type ` as Pair>::Right` +help: consider further restricting this bound + | +LL | fn frob(pair: impl Pair) -> impl Pair { + | +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/supertrait-shadowing/assoc-const.rs b/tests/ui/supertrait-shadowing/assoc-const.rs new file mode 100644 index 0000000000000..be0d990284b0c --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-const.rs @@ -0,0 +1,31 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![allow(dead_code)] + +trait A { + const CONST: i32; +} +impl A for T { + const CONST: i32 = 1; +} + +trait B: A { + type const CONST: i32; +} +impl B for T { + type const CONST: i32 = 2; +} + +trait C: B {} +impl C for T {} + +fn main() { + assert_eq!(i32::CONST, 2); + generic::(); +} + +fn generic>() { + assert_eq!(T::CONST, 2); +} diff --git a/tests/ui/supertrait-shadowing/assoc-type-fail.rs b/tests/ui/supertrait-shadowing/assoc-type-fail.rs new file mode 100644 index 0000000000000..5df4edb7e96e2 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-fail.rs @@ -0,0 +1,47 @@ +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + type Assoc; +} +impl A for X { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for X { + type Assoc = i16; +} + +trait C: B {} +impl C for X {} + +fn main() { + b_unbound::(); + c_unbound::(); + + b_assoc_is_a::(); + //~^ ERROR type mismatch resolving `::Assoc == i8` + c_assoc_is_a::(); + //~^ ERROR type mismatch resolving `::Assoc == i8` +} + +fn b_unbound() { + let _ = size_of::(); +} + +fn c_unbound() { + let _ = size_of::(); +} + +fn b_assoc_is_a>() { + let _ = size_of::(); +} + +fn c_assoc_is_a>() { + let _ = size_of::(); +} diff --git a/tests/ui/supertrait-shadowing/assoc-type-fail.stderr b/tests/ui/supertrait-shadowing/assoc-type-fail.stderr new file mode 100644 index 0000000000000..36a9bd082aaf6 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-fail.stderr @@ -0,0 +1,37 @@ +error[E0271]: type mismatch resolving `::Assoc == i8` + --> $DIR/assoc-type-fail.rs:27:20 + | +LL | b_assoc_is_a::(); + | ^^^ type mismatch resolving `::Assoc == i8` + | +note: expected this to be `i8` + --> $DIR/assoc-type-fail.rs:17:18 + | +LL | type Assoc = i16; + | ^^^ +note: required by a bound in `b_assoc_is_a` + --> $DIR/assoc-type-fail.rs:41:22 + | +LL | fn b_assoc_is_a>() { + | ^^^^^^^^^^ required by this bound in `b_assoc_is_a` + +error[E0271]: type mismatch resolving `::Assoc == i8` + --> $DIR/assoc-type-fail.rs:29:20 + | +LL | c_assoc_is_a::(); + | ^^^ type mismatch resolving `::Assoc == i8` + | +note: expected this to be `i8` + --> $DIR/assoc-type-fail.rs:17:18 + | +LL | type Assoc = i16; + | ^^^ +note: required by a bound in `c_assoc_is_a` + --> $DIR/assoc-type-fail.rs:45:22 + | +LL | fn c_assoc_is_a>() { + | ^^^^^^^^^^ required by this bound in `c_assoc_is_a` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/supertrait-shadowing/assoc-type-predicates.rs b/tests/ui/supertrait-shadowing/assoc-type-predicates.rs new file mode 100644 index 0000000000000..55d2b84a50f9a --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-predicates.rs @@ -0,0 +1,45 @@ +//@ normalize-stderr: "assoc_type_predicates\[[^\]]+\]" -> "assoc_type_predicates[HASH]" + +#![feature(rustc_attrs)] +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +trait A { + type Assoc; +} +impl A for T { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for T { + type Assoc = i16; +} + +trait C: B {} +impl C for T {} + +#[rustc_dump_predicates] +fn a_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE A::Assoc + +#[rustc_dump_predicates] +fn b_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE B::Assoc + +#[rustc_dump_predicates] +fn c_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE B::Assoc + +fn main() {} diff --git a/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr b/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr new file mode 100644 index 0000000000000..be765e737a6e1 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr @@ -0,0 +1,32 @@ +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:25:1 + | +LL | fn a_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:4 ~ assoc_type_predicates[HASH]::A::Assoc) }, args: [T/#0], .. }, Term::Ty(i8)), bound_vars: [] } + +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:32:1 + | +LL | fn b_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:9 ~ assoc_type_predicates[HASH]::B::Assoc) }, args: [T/#0], .. }, Term::Ty(i16)), bound_vars: [] } + +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:39:1 + | +LL | fn c_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:9 ~ assoc_type_predicates[HASH]::B::Assoc) }, args: [T/#0], .. }, Term::Ty(i16)), bound_vars: [] } + +error: aborting due to 3 previous errors + diff --git a/tests/ui/supertrait-shadowing/assoc-type.rs b/tests/ui/supertrait-shadowing/assoc-type.rs new file mode 100644 index 0000000000000..9e9799397c571 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type.rs @@ -0,0 +1,53 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + type Assoc; +} +impl A for T { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for T { + type Assoc = i16; +} + +trait C: B {} +impl C for T {} + +fn main() { + generic::(); + generic2::(); + generic3::(); + generic4::(); + generic5::(); +} + +fn generic() { + assert_eq!(size_of::(), 2); +} + +fn generic2>() { + assert_eq!(size_of::(), 1); +} + +fn generic3>() { + assert_eq!(size_of::(), 2); +} + +fn generic4>() { + assert_eq!(size_of::(), 2); +} + +fn generic5() { + assert_eq!(size_of::<::Assoc>(), 1); + assert_eq!(size_of::<::Assoc>(), 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/auxiliary/shadowed_stability.rs b/tests/ui/supertrait-shadowing/auxiliary/shadowed_stability.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/auxiliary/shadowed_stability.rs rename to tests/ui/supertrait-shadowing/auxiliary/shadowed_stability.rs diff --git a/tests/ui/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/supertrait-shadowing/common-ancestor-2.rs new file mode 100644 index 0000000000000..908c5d2bf2199 --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-2.rs @@ -0,0 +1,59 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `C` shadows identically named item + "C" + } + type Assoc; + //~^ WARN trait item `Assoc` from `C` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `C` shadows identically named item +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +fn main() { + assert_eq!(().hello(), "C"); + //~^ WARN trait item `hello` from `C` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 4); + assert_eq!(T::CONST, 3); +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr similarity index 55% rename from tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr rename to tests/ui/supertrait-shadowing/common-ancestor-2.stderr index b0f61b46b6911..646726b835041 100644 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr @@ -1,11 +1,11 @@ warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-2.rs:23:5 + --> $DIR/common-ancestor-2.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: items from several supertraits are shadowed: `B` and `A` - --> $DIR/common-ancestor-2.rs:9:5 + --> $DIR/common-ancestor-2.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,24 +13,54 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-2.rs:5:9 + --> $DIR/common-ancestor-2.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:40:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:42:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-2.rs:31:19 + --> $DIR/common-ancestor-2.rs:51:19 | LL | assert_eq!(().hello(), "C"); | ^^^^^ | note: item from `C` shadows a supertrait item - --> $DIR/common-ancestor-2.rs:23:5 + --> $DIR/common-ancestor-2.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: items from several supertraits are shadowed: `A` and `B` - --> $DIR/common-ancestor-2.rs:9:5 + --> $DIR/common-ancestor-2.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,10 +68,10 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-2.rs:4:9 + --> $DIR/common-ancestor-2.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/supertrait-shadowing/common-ancestor-3.rs new file mode 100644 index 0000000000000..ade23ca88aec2 --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-3.rs @@ -0,0 +1,76 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `C` shadows identically named item + "C" + } + type Assoc; + //~^ WARN trait item `Assoc` from `C` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `C` shadows identically named item +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +// `D` extends `C` which extends `B` and `A` + +trait D: C { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `D` shadows identically named item + "D" + } + type Assoc; + //~^ WARN trait item `Assoc` from `D` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `D` shadows identically named item +} +impl D for T { + type Assoc = i64; + type const CONST: i32 = 4; +} + +fn main() { + assert_eq!(().hello(), "D"); + //~^ WARN trait item `hello` from `D` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 8); + assert_eq!(T::CONST, 4); +} diff --git a/tests/ui/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr new file mode 100644 index 0000000000000..62132832da4ba --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr @@ -0,0 +1,134 @@ +warning: trait item `hello` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:36:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-3.rs:6:9 + | +LL | #![warn(shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `Assoc` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:40:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:42:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:53:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `Assoc` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:57:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:59:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:68:19 + | +LL | assert_eq!(().hello(), "D"); + | ^^^^^ + | +note: item from `D` shadows a supertrait item + --> $DIR/common-ancestor-3.rs:53:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: items from several supertraits are shadowed: `A`, `B`, and `C` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-3.rs:5:9 + | +LL | #![warn(resolving_to_items_shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 7 warnings emitted + diff --git a/tests/ui/supertrait-shadowing/common-ancestor.rs b/tests/ui/supertrait-shadowing/common-ancestor.rs new file mode 100644 index 0000000000000..903f85957870b --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor.rs @@ -0,0 +1,47 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B: A { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `B` shadows identically named item + "B" + } + type Assoc; + //~^ WARN trait item `Assoc` from `B` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `B` shadows identically named item +} +impl B for T { + type Assoc = i16; + type const CONST: i32 = 2; +} + +fn main() { + assert_eq!(().hello(), "B"); + //~^ WARN trait item `hello` from `B` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 2); + assert_eq!(T::CONST, 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr b/tests/ui/supertrait-shadowing/common-ancestor.stderr similarity index 56% rename from tests/ui/methods/supertrait-shadowing/common-ancestor.stderr rename to tests/ui/supertrait-shadowing/common-ancestor.stderr index 9afedeab5e2c2..9b13537cf800c 100644 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor.stderr @@ -1,41 +1,65 @@ warning: trait item `hello` from `B` shadows identically named item from supertrait - --> $DIR/common-ancestor.rs:16:5 + --> $DIR/common-ancestor.rs:24:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: item from `A` is shadowed by a subtrait item - --> $DIR/common-ancestor.rs:9:5 + --> $DIR/common-ancestor.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor.rs:5:9 + --> $DIR/common-ancestor.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:28:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:30:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `B` shadows identically named item from supertrait - --> $DIR/common-ancestor.rs:24:19 + --> $DIR/common-ancestor.rs:39:19 | LL | assert_eq!(().hello(), "B"); | ^^^^^ | note: item from `B` shadows a supertrait item - --> $DIR/common-ancestor.rs:16:5 + --> $DIR/common-ancestor.rs:24:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: item from `A` is shadowed by a subtrait item - --> $DIR/common-ancestor.rs:9:5 + --> $DIR/common-ancestor.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor.rs:4:9 + --> $DIR/common-ancestor.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/supertrait-shadowing/definition-site.rs b/tests/ui/supertrait-shadowing/definition-site.rs new file mode 100644 index 0000000000000..3309ee97e9f00 --- /dev/null +++ b/tests/ui/supertrait-shadowing/definition-site.rs @@ -0,0 +1,28 @@ +#![feature(supertrait_item_shadowing)] +#![deny(shadowing_supertrait_items)] + +trait SuperSuper { + fn method(); + const CONST: i32; + type Assoc; +} + +trait Super: SuperSuper { + fn method(); + //~^ ERROR trait item `method` from `Super` shadows identically named item + const CONST: i32; + //~^ ERROR trait item `CONST` from `Super` shadows identically named item + type Assoc; + //~^ ERROR trait item `Assoc` from `Super` shadows identically named item +} + +trait Sub: Super { + fn method(); + //~^ ERROR trait item `method` from `Sub` shadows identically named item + const CONST: i32; + //~^ ERROR trait item `CONST` from `Sub` shadows identically named item + type Assoc; + //~^ ERROR trait item `Assoc` from `Sub` shadows identically named item +} + +fn main() {} diff --git a/tests/ui/supertrait-shadowing/definition-site.stderr b/tests/ui/supertrait-shadowing/definition-site.stderr new file mode 100644 index 0000000000000..faf01cc711c6a --- /dev/null +++ b/tests/ui/supertrait-shadowing/definition-site.stderr @@ -0,0 +1,88 @@ +error: trait item `method` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:11:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:5:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/definition-site.rs:2:9 + | +LL | #![deny(shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: trait item `CONST` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:13:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:6:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +error: trait item `Assoc` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:7:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + +error: trait item `method` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:20:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:5:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ +... +LL | fn method(); + | ^^^^^^^^^^^^ + +error: trait item `CONST` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:22:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:6:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +error: trait item `Assoc` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:24:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:7:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs b/tests/ui/supertrait-shadowing/false-subtrait-after-inference.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs rename to tests/ui/supertrait-shadowing/false-subtrait-after-inference.rs diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr b/tests/ui/supertrait-shadowing/false-subtrait-after-inference.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr rename to tests/ui/supertrait-shadowing/false-subtrait-after-inference.stderr diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs new file mode 100644 index 0000000000000..957aabf5a51f8 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs @@ -0,0 +1,68 @@ +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + "C" + } + type Assoc; + type const CONST: i32; +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +// Since `D` is not a subtrait of `C`, +// we have no obvious lower bound. + +trait D: B { + fn hello(&self) -> &'static str { + "D" + } + type Assoc; + type const CONST: i32; +} +impl D for T { + type Assoc = i64; + type const CONST: i32 = 4; +} + +fn main() { + ().hello(); + //~^ ERROR multiple applicable items in scope + check::<()>(); +} + +fn check() { + let _ = size_of::(); + //~^ ERROR ambiguous associated type `Assoc` in bounds of `T` + let _ = T::CONST; + //~^ ERROR multiple applicable items in scope +} diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr new file mode 100644 index 0000000000000..987adcfbbe959 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr @@ -0,0 +1,131 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor-2.rs:58:8 + | +LL | ().hello(); + | ^^^^^ multiple `hello` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `T` + --> $DIR/no-common-ancestor-2.rs:7:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `B` for the type `T` + --> $DIR/no-common-ancestor-2.rs:19:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #3 is defined in an impl of the trait `C` for the type `T` + --> $DIR/no-common-ancestor-2.rs:31:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #4 is defined in an impl of the trait `D` for the type `T` + --> $DIR/no-common-ancestor-2.rs:46:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - ().hello(); +LL + A::hello(&()); + | +help: disambiguate the method for candidate #2 + | +LL - ().hello(); +LL + B::hello(&()); + | +help: disambiguate the method for candidate #3 + | +LL - ().hello(); +LL + C::hello(&()); + | +help: disambiguate the method for candidate #4 + | +LL - ().hello(); +LL + D::hello(&()); + | + +error[E0221]: ambiguous associated type `Assoc` in bounds of `T` + --> $DIR/no-common-ancestor-2.rs:64:23 + | +LL | type Assoc; + | ---------- ambiguous `Assoc` from `A` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `B` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `C` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `D` +... +LL | let _ = size_of::(); + | ^^^^^^^^ ambiguous associated type `Assoc` + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor-2.rs:66:16 + | +LL | let _ = T::CONST; + | ^^^^^ multiple `CONST` found + | +note: candidate #1 is defined in the trait `A` + --> $DIR/no-common-ancestor-2.rs:11:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `B` + --> $DIR/no-common-ancestor-2.rs:23:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #3 is defined in the trait `C` + --> $DIR/no-common-ancestor-2.rs:35:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ +note: candidate #4 is defined in the trait `D` + --> $DIR/no-common-ancestor-2.rs:50:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0034, E0221. +For more information about an error, try `rustc --explain E0034`. diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/supertrait-shadowing/no-common-ancestor.rs new file mode 100644 index 0000000000000..73209c015f8f5 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor.rs @@ -0,0 +1,41 @@ +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +fn main() { + ().hello(); + //~^ ERROR multiple applicable items in scope + check::<()>(); +} + +fn check() { + let _ = size_of::(); + //~^ ERROR ambiguous associated type `Assoc` in bounds of `T` + let _ = T::CONST; + //~^ ERROR multiple applicable items in scope +} diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr new file mode 100644 index 0000000000000..78fd8f4670577 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr @@ -0,0 +1,79 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor.rs:31:8 + | +LL | ().hello(); + | ^^^^^ multiple `hello` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `T` + --> $DIR/no-common-ancestor.rs:7:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `B` for the type `T` + --> $DIR/no-common-ancestor.rs:19:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - ().hello(); +LL + A::hello(&()); + | +help: disambiguate the method for candidate #2 + | +LL - ().hello(); +LL + B::hello(&()); + | + +error[E0221]: ambiguous associated type `Assoc` in bounds of `T` + --> $DIR/no-common-ancestor.rs:37:23 + | +LL | type Assoc; + | ---------- ambiguous `Assoc` from `A` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `B` +... +LL | let _ = size_of::(); + | ^^^^^^^^ ambiguous associated type `Assoc` + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor.rs:39:16 + | +LL | let _ = T::CONST; + | ^^^^^ multiple `CONST` found + | +note: candidate #1 is defined in the trait `A` + --> $DIR/no-common-ancestor.rs:11:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `B` + --> $DIR/no-common-ancestor.rs:23:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0034, E0221. +For more information about an error, try `rustc --explain E0034`. diff --git a/tests/ui/supertrait-shadowing/out-of-scope.rs b/tests/ui/supertrait-shadowing/out-of-scope.rs new file mode 100644 index 0000000000000..7b2322843f1c3 --- /dev/null +++ b/tests/ui/supertrait-shadowing/out-of-scope.rs @@ -0,0 +1,42 @@ +//@ run-pass + +#![feature(min_generic_const_args)] +#![allow(dead_code)] + +use std::mem::size_of; + +mod out_of_scope { + pub trait Subtrait: super::Supertrait { + fn hello(&self) -> &'static str { + "subtrait" + } + type Assoc; + type const CONST: i32; + } + impl Subtrait for T { + type Assoc = i16; + type const CONST: i32 = 2; + } +} + +trait Supertrait { + fn hello(&self) -> &'static str { + "supertrait" + } + type Assoc; + const CONST: i32; +} +impl Supertrait for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +fn main() { + assert_eq!(().hello(), "supertrait"); + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 1); + assert_eq!(T::CONST, 1); +} diff --git a/tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs b/tests/ui/supertrait-shadowing/trivially-false-subtrait.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs rename to tests/ui/supertrait-shadowing/trivially-false-subtrait.rs diff --git a/tests/ui/supertrait-shadowing/type-dependent.rs b/tests/ui/supertrait-shadowing/type-dependent.rs new file mode 100644 index 0000000000000..75272d101dd29 --- /dev/null +++ b/tests/ui/supertrait-shadowing/type-dependent.rs @@ -0,0 +1,51 @@ +//@ run-pass + +// Makes sure we can shadow with type-dependent associated item syntax. + +#![feature(min_generic_const_args)] +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello() -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B: A { + fn hello() -> &'static str { + "B" + } + type Assoc; + type const CONST: i32; +} +impl B for T { + type Assoc = i16; + type const CONST: i32 = 2; +} + +fn foo() -> &'static str { + T::hello() +} + +fn assoc() -> usize { + size_of::() +} + +fn konst() -> i32 { + T::CONST +} + +fn main() { + assert_eq!(foo::<()>(), "B"); + assert_eq!(assoc::<()>(), 2); + assert_eq!(konst::<()>(), 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/unstable.off_normal.stderr b/tests/ui/supertrait-shadowing/unstable.off_normal.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.off_normal.stderr rename to tests/ui/supertrait-shadowing/unstable.off_normal.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.off_shadowing.stderr b/tests/ui/supertrait-shadowing/unstable.off_shadowing.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.off_shadowing.stderr rename to tests/ui/supertrait-shadowing/unstable.off_shadowing.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.on_normal.stderr b/tests/ui/supertrait-shadowing/unstable.on_normal.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.on_normal.stderr rename to tests/ui/supertrait-shadowing/unstable.on_normal.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.rs b/tests/ui/supertrait-shadowing/unstable.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.rs rename to tests/ui/supertrait-shadowing/unstable.rs diff --git a/tests/ui/symbol-names/basic.legacy.stderr b/tests/ui/symbol-names/basic.legacy.stderr index f88fdf7509c76..59085aeb3279b 100644 --- a/tests/ui/symbol-names/basic.legacy.stderr +++ b/tests/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h27f62b2d0b2beac6E) +error: symbol-name(_ZN5basic4main17h33f35fba43592008E) --> $DIR/basic.rs:8:1 | LL | #[rustc_dump_symbol_name] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h27f62b2d0b2beac6) +error: demangling(basic::main::h33f35fba43592008) --> $DIR/basic.rs:8:1 | LL | #[rustc_dump_symbol_name] diff --git a/tests/ui/symbol-names/issue-60925.legacy.stderr b/tests/ui/symbol-names/issue-60925.legacy.stderr index c1fc9f4b1cea1..367c2d14011ac 100644 --- a/tests/ui/symbol-names/issue-60925.legacy.stderr +++ b/tests/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h1eb769490ff06e77E) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17hc741419c44ba4d79E) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_dump_symbol_name] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo::foo::h1eb769490ff06e77) +error: demangling(issue_60925::foo::Foo::foo::hc741419c44ba4d79) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_dump_symbol_name] diff --git a/tests/ui/trait-bounds/mismatch-fn-trait.stderr b/tests/ui/trait-bounds/mismatch-fn-trait.stderr index 051e660c2d106..a7805e25f651e 100644 --- a/tests/ui/trait-bounds/mismatch-fn-trait.stderr +++ b/tests/ui/trait-bounds/mismatch-fn-trait.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut(u32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut(u32)` --> $DIR/mismatch-fn-trait.rs:4:10 | LL | take(f) @@ -14,7 +14,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut(i32, i32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut(i32, i32)` --> $DIR/mismatch-fn-trait.rs:9:10 | LL | take(f) @@ -29,7 +29,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut()` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut()` --> $DIR/mismatch-fn-trait.rs:14:10 | LL | take(f) @@ -44,7 +44,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnOnce(i32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnOnce(i32)` --> $DIR/mismatch-fn-trait.rs:19:10 | LL | take(f) @@ -59,7 +59,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnOnce(u32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnOnce(u32)` --> $DIR/mismatch-fn-trait.rs:24:10 | LL | take(f) diff --git a/tests/ui/traits/ambiguous-trait-matching-distinct-lifetimes.rs b/tests/ui/traits/ambiguous-trait-matching-distinct-lifetimes.rs new file mode 100644 index 0000000000000..d5cf73b42f561 --- /dev/null +++ b/tests/ui/traits/ambiguous-trait-matching-distinct-lifetimes.rs @@ -0,0 +1,20 @@ +//! . +//! +//! Test that (for now) we report an ambiguity error here, because +//! specific trait relationships are ignored for the purposes of trait +//! matching. This behavior should likely be improved such that this +//! test passes. + +trait Foo { + fn foo(self); +} + +fn foo<'a,'b,T>(x: &'a T, y: &'b T) + where &'a T : Foo, //~ ERROR type annotations needed + &'b T : Foo +{ + x.foo(); //~ ERROR type annotations needed + y.foo(); +} + +fn main() { } diff --git a/tests/ui/issues/issue-21974.stderr b/tests/ui/traits/ambiguous-trait-matching-distinct-lifetimes.stderr similarity index 73% rename from tests/ui/issues/issue-21974.stderr rename to tests/ui/traits/ambiguous-trait-matching-distinct-lifetimes.stderr index cc9164e5621b8..d2d7df466512d 100644 --- a/tests/ui/issues/issue-21974.stderr +++ b/tests/ui/traits/ambiguous-trait-matching-distinct-lifetimes.stderr @@ -1,11 +1,11 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo` - --> $DIR/issue-21974.rs:11:19 + --> $DIR/ambiguous-trait-matching-distinct-lifetimes.rs:13:19 | LL | where &'a T : Foo, | ^^^ | note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found - --> $DIR/issue-21974.rs:11:19 + --> $DIR/ambiguous-trait-matching-distinct-lifetimes.rs:13:19 | LL | where &'a T : Foo, | ^^^ @@ -13,13 +13,13 @@ LL | &'b T : Foo | ^^^ error[E0283]: type annotations needed: cannot satisfy `&T: Foo` - --> $DIR/issue-21974.rs:14:7 + --> $DIR/ambiguous-trait-matching-distinct-lifetimes.rs:16:7 | LL | x.foo(); | ^^^ | note: multiple `impl`s or `where` clauses satisfying `&T: Foo` found - --> $DIR/issue-21974.rs:11:19 + --> $DIR/ambiguous-trait-matching-distinct-lifetimes.rs:13:19 | LL | where &'a T : Foo, | ^^^ diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs index 891cc32e7b758..f19c9da7c5eb5 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs @@ -11,5 +11,5 @@ fn f X<'r> + ?Sized>() { fn main() { f:: X<'x, F = i32>>(); - //~^ ERROR expected a `FnOnce(&i32)` closure, found `i32` + //~^ ERROR expected an `FnOnce(&i32)` closure, found `i32` } diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr index ebd45b9dd9635..c4bc50746ebba 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&i32)` closure, found `i32` --> $DIR/check-trait-object-bounds-2.rs:13:9 | LL | f:: X<'x, F = i32>>(); diff --git a/tests/ui/traits/const-traits/associated-const-stability.rs b/tests/ui/traits/const-traits/associated-const-stability.rs new file mode 100644 index 0000000000000..837629d608a36 --- /dev/null +++ b/tests/ui/traits/const-traits/associated-const-stability.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ aux-build: associated-const-stability.rs + +extern crate associated_const_stability; + +use associated_const_stability::{Probe, TraitWithConstGate}; + +// Demonstrate that it's possible to use a `const` associated item in a stable `const` context +// even when the item is defined inside a const-unstable enclosing parent item. +// +// Here `TraitWithConstGate` is const-unstable, as is its `const impl` for `Probe`. +// This crate *does not* enable the `const_trait_gate` feature for those const impls. +// By showing that this crate compiles without that feature, while having const contexts +// that use both `::ASSOC` specifically +// and generic `::ASSOC`, we prove that the associated consts respect +// the item-level stability like `#[stable]`, not const-stability like `#[rustc_const_unstable]`. + +const VALUE: usize = ::ASSOC; + +const fn example(_: &T) -> usize { + ::ASSOC +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/auxiliary/associated-const-stability.rs b/tests/ui/traits/const-traits/auxiliary/associated-const-stability.rs new file mode 100644 index 0000000000000..77d94cd98ed43 --- /dev/null +++ b/tests/ui/traits/const-traits/auxiliary/associated-const-stability.rs @@ -0,0 +1,19 @@ +#![feature(const_trait_impl)] +#![feature(staged_api)] +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Probe; + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_trait_gate", issue = "none")] +pub const trait TraitWithConstGate { + #[stable(feature = "rust1", since = "1.0.0")] + const ASSOC: usize; +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_trait_gate", issue = "none")] +const impl TraitWithConstGate for Probe { + const ASSOC: usize = 1; +} diff --git a/tests/ui/issues/issue-26802.rs b/tests/ui/traits/default-method/trait-object-lifetime-bound.rs similarity index 78% rename from tests/ui/issues/issue-26802.rs rename to tests/ui/traits/default-method/trait-object-lifetime-bound.rs index 62fcc617b46a4..d6bea7f956a9f 100644 --- a/tests/ui/issues/issue-26802.rs +++ b/tests/ui/traits/default-method/trait-object-lifetime-bound.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + trait Foo<'a> { fn bar<'b>(&self, x: &'b u8) -> u8 where 'a: 'b { *x+7 } } diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index ca908a3062d33..b8b9ea57612b4 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -32,7 +32,7 @@ LL | impl Fn(&isize) for Error { | = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut(&isize)` closure, found `Error` +error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-87558.rs:3:21 | LL | impl Fn(&isize) for Error { diff --git a/tests/ui/issues/issue-29147.rs b/tests/ui/traits/method-on-deeply-nested-struct-perf-2.rs similarity index 85% rename from tests/ui/issues/issue-29147.rs rename to tests/ui/traits/method-on-deeply-nested-struct-perf-2.rs index 190ecf46009ff..69b1f79596d2b 100644 --- a/tests/ui/issues/issue-29147.rs +++ b/tests/ui/traits/method-on-deeply-nested-struct-perf-2.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! This used to compile for over 30 seconds. + #![recursion_limit="1024"] #![allow(dead_code)] diff --git a/tests/ui/issues/issue-29147.stderr b/tests/ui/traits/method-on-deeply-nested-struct-perf-2.stderr similarity index 79% rename from tests/ui/issues/issue-29147.stderr rename to tests/ui/traits/method-on-deeply-nested-struct-perf-2.stderr index a9cda55fd9acf..5bf058344c705 100644 --- a/tests/ui/issues/issue-29147.stderr +++ b/tests/ui/traits/method-on-deeply-nested-struct-perf-2.stderr @@ -1,11 +1,11 @@ error[E0283]: type annotations needed - --> $DIR/issue-29147.rs:22:14 + --> $DIR/method-on-deeply-nested-struct-perf-2.rs:25:14 | LL | let _ = >::xxx; | ^^^^^ cannot infer type for struct `S5<_>` | note: multiple `impl`s satisfying `S5<_>: Foo` found - --> $DIR/issue-29147.rs:18:1 + --> $DIR/method-on-deeply-nested-struct-perf-2.rs:21:1 | LL | impl Foo for S5 { fn xxx(&self) {} } | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-29147-rpass.rs b/tests/ui/traits/method-on-deeply-nested-struct-perf.rs similarity index 86% rename from tests/ui/issues/issue-29147-rpass.rs rename to tests/ui/traits/method-on-deeply-nested-struct-perf.rs index 7ac6a98e8e1f7..a53c4c367c272 100644 --- a/tests/ui/issues/issue-29147-rpass.rs +++ b/tests/ui/traits/method-on-deeply-nested-struct-perf.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! This used to compile for over 30 seconds. //@ run-pass + #![recursion_limit="1024"] #![allow(dead_code)] diff --git a/tests/ui/traits/next-solver-ice.stderr b/tests/ui/traits/next-solver-ice.stderr index d6b022d70175b..6c4cd0296ebaa 100644 --- a/tests/ui/traits/next-solver-ice.stderr +++ b/tests/ui/traits/next-solver-ice.stderr @@ -2,19 +2,12 @@ error[E0277]: the trait bound `f32: From<::Item>` is not satisfie --> $DIR/next-solver-ice.rs:5:6 | LL | ::Item>>::from; - | ^^^ the nightly-only, unstable trait `ZeroablePrimitive` is not implemented for `f32` + | ^^^ the trait `From<::Item>` is not implemented for `f32` | - = help: the following other types implement trait `ZeroablePrimitive`: - i128 - i16 - i32 - i64 - i8 - isize - u128 - u16 - and 4 others - = note: required for `f32` to implement `From<::Item>` +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn check() where f32: From<::Item> { + | ++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr index f21da6f473cda..effab17e8c3bc 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr @@ -1,11 +1,9 @@ -error[E0284]: type annotations needed - --> $DIR/ambiguity-due-to-uniquification-4.rs:17:44 +error[E0282]: type annotations needed + --> $DIR/ambiguity-due-to-uniquification-4.rs:17:47 | LL | pub fn f<'a, 'b, T: Trait<'a> + Trait<'b>>(v: >::Type) {} - | ^ cannot infer type - | - = note: cannot satisfy `>::Type == _` + | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for associated type `>::Type` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0284`. +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs index bd33e3c2f47ee..c9a3eaa0fea52 100644 --- a/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs +++ b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs @@ -7,6 +7,7 @@ trait Wf { struct S { f: &'static <() as Wf>::Assoc, //~^ ERROR the trait bound `(): Wf` is not satisfied + //~| ERROR: the type `&'static <() as Wf>::Assoc` is not well-formed } fn main() { diff --git a/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr index d484a0a1c4c28..2f7307eb96b77 100644 --- a/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr +++ b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr @@ -10,8 +10,14 @@ help: this trait has no implementations, consider adding one LL | trait Wf { | ^^^^^^^^ +error: the type `&'static <() as Wf>::Assoc` is not well-formed + --> $DIR/non-wf-in-coerce-pointers.rs:8:8 + | +LL | f: &'static <() as Wf>::Assoc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0277]: the trait bound `(): Wf` is not satisfied - --> $DIR/non-wf-in-coerce-pointers.rs:14:18 + --> $DIR/non-wf-in-coerce-pointers.rs:15:18 | LL | let y: &() = x.f; | ^^^ the trait `Wf` is not implemented for `()` @@ -22,6 +28,6 @@ help: this trait has no implementations, consider adding one LL | trait Wf { | ^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs new file mode 100644 index 0000000000000..d1e2a25b29521 --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -Znext-solver=globally +//@ check-fail + +fn main() { + let f = |f: dyn Fn()| f; + //~^ ERROR the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + //~| ERROR return type cannot be a trait object without pointer indirection + f(); + //~^ ERROR this function takes 1 argument but 0 arguments were supplied +} diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr new file mode 100644 index 0000000000000..a20e5f331d31a --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:17 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Fn() + 'static)` + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | let f = |f: &dyn Fn()| f; + | + + +error[E0746]: return type cannot be a trait object without pointer indirection + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:27 + | +LL | let f = |f: dyn Fn()| f; + | ^ doesn't have a size known at compile-time + +error[E0057]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:8:5 + | +LL | f(); + | ^-- argument #1 of type `(dyn Fn() + 'static)` is missing + | +note: closure defined here + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:13 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^^^^^^ +help: provide the argument + | +LL | f(/* (dyn Fn() + 'static) */); + | ++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277, E0746. +For more information about an error, try `rustc --explain E0057`. diff --git a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs index 8463cb3f6ea1b..a0d1db3ff02b3 100644 --- a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs +++ b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs @@ -7,7 +7,7 @@ const fn with_positive [const] Fn(&'a ())>() {} const _: () = { with_positive::<()>(); - //~^ ERROR expected a `Fn(&'a ())` closure, found `()` + //~^ ERROR expected an `Fn(&'a ())` closure, found `()` //~| ERROR type mismatch resolving `<() as FnOnce<(&(),)>>::Output == ()` }; diff --git a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr index 8ec772a3d7fa8..7b98ba618a727 100644 --- a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr +++ b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&'a ())` closure, found `()` +error[E0277]: expected an `Fn(&'a ())` closure, found `()` --> $DIR/const-host-effect-hrtb-no-ice.rs:9:21 | LL | with_positive::<()>(); diff --git a/tests/ui/traits/next-solver/fn-trait.rs b/tests/ui/traits/next-solver/fn-trait.rs index 7994dd3052c40..e31d4d2b580a5 100644 --- a/tests/ui/traits/next-solver/fn-trait.rs +++ b/tests/ui/traits/next-solver/fn-trait.rs @@ -18,11 +18,11 @@ fn main() { require_fn(f); require_fn(f as fn() -> i32); require_fn(f as unsafe fn() -> i32); - //~^ ERROR: expected a `Fn()` closure, found `unsafe fn() -> i32` + //~^ ERROR: expected an `Fn()` closure, found `unsafe fn() -> i32` require_fn(g); - //~^ ERROR: expected a `Fn()` closure, found `extern "C" fn() -> i32 {g}` + //~^ ERROR: expected an `Fn()` closure, found `extern "C" fn() -> i32 {g}` require_fn(g as extern "C" fn() -> i32); - //~^ ERROR: expected a `Fn()` closure, found `extern "C" fn() -> i32` + //~^ ERROR: expected an `Fn()` closure, found `extern "C" fn() -> i32` require_fn(h); - //~^ ERROR: expected a `Fn()` closure, found `unsafe fn() -> i32 {h}` + //~^ ERROR: expected an `Fn()` closure, found `unsafe fn() -> i32 {h}` } diff --git a/tests/ui/traits/next-solver/fn-trait.stderr b/tests/ui/traits/next-solver/fn-trait.stderr index 77c834f377852..6218928b309f7 100644 --- a/tests/ui/traits/next-solver/fn-trait.stderr +++ b/tests/ui/traits/next-solver/fn-trait.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `unsafe fn() -> i32` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() -> i32` --> $DIR/fn-trait.rs:20:16 | LL | require_fn(f as unsafe fn() -> i32); @@ -15,7 +15,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() -> i32 {g}` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() -> i32 {g}` --> $DIR/fn-trait.rs:22:16 | LL | require_fn(g); @@ -31,7 +31,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() -> i32` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() -> i32` --> $DIR/fn-trait.rs:24:16 | LL | require_fn(g as extern "C" fn() -> i32); @@ -47,7 +47,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `unsafe fn() -> i32 {h}` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() -> i32 {h}` --> $DIR/fn-trait.rs:26:16 | LL | require_fn(h); diff --git a/tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.next.stderr b/tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.next.stderr index f16a17833368d..0ad8e3444600b 100644 --- a/tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.next.stderr +++ b/tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.next.stderr @@ -5,7 +5,7 @@ LL | fn foo(x: <(*const T,) as Id>::This) -> (*const T,) | ----------- expected `(*const T,)` because of return type ... LL | x - | ^ types differ + | ^ expected `(*const T,)`, found associated type | = note: expected tuple `(*const T,)` found associated type `<(*const T,) as Id>::This` diff --git a/tests/ui/traits/next-solver/normalization-shadowing/param-candidate-shadows-project.stderr b/tests/ui/traits/next-solver/normalization-shadowing/param-candidate-shadows-project.stderr index 0c72fc25dca7a..24947912b3d0e 100644 --- a/tests/ui/traits/next-solver/normalization-shadowing/param-candidate-shadows-project.stderr +++ b/tests/ui/traits/next-solver/normalization-shadowing/param-candidate-shadows-project.stderr @@ -2,8 +2,10 @@ error[E0271]: type mismatch resolving `::Assoc == i32` --> $DIR/param-candidate-shadows-project.rs:27:19 | LL | require_bar::(); - | ^ types differ + | ^ expected `i32`, found associated type | + = note: expected type `i32` + found associated type `::Assoc` note: required for `T` to implement `Bar` --> $DIR/param-candidate-shadows-project.rs:13:9 | @@ -14,6 +16,10 @@ note: required by a bound in `require_bar` | LL | fn require_bar() {} | ^^^ required by this bound in `require_bar` +help: consider constraining the associated type `::Assoc` to `i32` + | +LL | fn foo>() { + | +++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/normalize/dont-ice-on-normalization-failure.rs b/tests/ui/traits/next-solver/normalize/dont-ice-on-normalization-failure.rs index 302c48d173566..25d11aec56ddb 100644 --- a/tests/ui/traits/next-solver/normalize/dont-ice-on-normalization-failure.rs +++ b/tests/ui/traits/next-solver/normalize/dont-ice-on-normalization-failure.rs @@ -13,7 +13,7 @@ type TraitObject = dyn Generic<::Associated>; //~^ ERROR: the trait bound `i32: Trait` is not satisfied struct Wrap(TraitObject); -//~^ ERROR: the trait bound `i32: Trait` is not satisfied +//~^ ERROR: type mismatch resolving `TraitObject == _` fn cast(x: *mut Wrap) { x as *mut Wrap; diff --git a/tests/ui/traits/next-solver/normalize/dont-ice-on-normalization-failure.stderr b/tests/ui/traits/next-solver/normalize/dont-ice-on-normalization-failure.stderr index 5e857df955adf..f019819ef66c8 100644 --- a/tests/ui/traits/next-solver/normalize/dont-ice-on-normalization-failure.stderr +++ b/tests/ui/traits/next-solver/normalize/dont-ice-on-normalization-failure.stderr @@ -10,18 +10,13 @@ help: this trait has no implementations, consider adding one LL | trait Trait { | ^^^^^^^^^^^ -error[E0277]: the trait bound `i32: Trait` is not satisfied +error[E0271]: type mismatch resolving `TraitObject == _` --> $DIR/dont-ice-on-normalization-failure.rs:15:13 | LL | struct Wrap(TraitObject); - | ^^^^^^^^^^^ the trait `Trait` is not implemented for `i32` - | -help: this trait has no implementations, consider adding one - --> $DIR/dont-ice-on-normalization-failure.rs:6:1 - | -LL | trait Trait { - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ types differ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0271, E0277. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/normalize/normalize-assoc-ty-expected-int-var-no-ice-2.next.stderr b/tests/ui/traits/next-solver/normalize/normalize-assoc-ty-expected-int-var-no-ice-2.next.stderr index f5d50e6d29ed4..70d35e5cbc5b2 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-assoc-ty-expected-int-var-no-ice-2.next.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-assoc-ty-expected-int-var-no-ice-2.next.stderr @@ -1,11 +1,16 @@ -error[E0271]: type mismatch resolving ` impl Sized {foo} as FnOnce<()>>::Output == {integer}` +error[E0271]: expected `foo` to return `{integer}`, but it returns `impl Sized` --> $DIR/normalize-assoc-ty-expected-int-var-no-ice-2.rs:13:10 | +LL | fn foo() -> impl Sized {} + | ---------- the found opaque type +... LL | proj(x, 1); - | ---- ^ types differ + | ---- ^ expected integer, found opaque type | | | required by a bound introduced by this call | + = note: expected type `{integer}` + found opaque type `impl Sized` note: required by a bound in `proj` --> $DIR/normalize-assoc-ty-expected-int-var-no-ice-2.rs:9:24 | diff --git a/tests/ui/traits/next-solver/normalize/normalize-assoc-ty-expected-int-var-no-ice-2.rs b/tests/ui/traits/next-solver/normalize/normalize-assoc-ty-expected-int-var-no-ice-2.rs index ca7870459b5c4..1bbde5f55eb90 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-assoc-ty-expected-int-var-no-ice-2.rs +++ b/tests/ui/traits/next-solver/normalize/normalize-assoc-ty-expected-int-var-no-ice-2.rs @@ -11,7 +11,6 @@ fn proj U, U>(x: Option, y: U) {} fn main() { let mut x = None; proj(x, 1); - //[current]~^ ERROR: expected `foo` to return `{integer}`, but it returns `impl Sized` - //[next]~^^ ERROR: type mismatch resolving ` impl Sized {foo} as FnOnce<()>>::Output == {integer}` + //~^ ERROR: expected `foo` to return `{integer}`, but it returns `impl Sized` x = Some(foo); } diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index bdc03e5e1bf9a..550256d40d935 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -1,22 +1,33 @@ error[E0308]: mismatched types --> $DIR/specialization-transmute.rs:13:9 | +LL | impl Default for T { + | - found this type parameter +LL | default type Id = T; LL | fn intu(&self) -> &Self::Id { | --------- expected `&::Id` because of return type LL | self - | ^^^^ types differ + | ^^^^ expected `&::Id`, found `&T` | = note: expected reference `&::Id` found reference `&T` +help: consider restricting type parameter `T` with `` + | +LL | impl> Default for T { + | ++++++++++ error[E0271]: type mismatch resolving `::Id == Option>` --> $DIR/specialization-transmute.rs:24:50 | LL | let s = transmute::>>(0); - | ------------------------------------ ^ types differ + | ------------------------------------ ^ expected `Option>`, found associated type | | | required by a bound introduced by this call | + = note: expected enum `Option>` + found associated type `::Id` + = help: consider constraining the associated type `::Id` to `Option>` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html note: required by a bound in `transmute` --> $DIR/specialization-transmute.rs:17:25 | diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.stderr b/tests/ui/traits/next-solver/specialization-unconstrained.stderr index 3e3638143873c..ca2dc56039273 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.stderr +++ b/tests/ui/traits/next-solver/specialization-unconstrained.stderr @@ -2,8 +2,12 @@ error[E0271]: type mismatch resolving `::Id == ()` --> $DIR/specialization-unconstrained.rs:19:12 | LL | test::(); - | ^^^ types differ + | ^^^ expected `()`, found associated type | + = note: expected unit type `()` + found associated type `::Id` + = help: consider constraining the associated type `::Id` to `()` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html note: required by a bound in `test` --> $DIR/specialization-unconstrained.rs:16:20 | diff --git a/tests/ui/traits/next-solver/stalled-coroutine-obligations.rs b/tests/ui/traits/next-solver/stalled-coroutine-obligations.rs index 5e5bc0a39f6a5..b69310edf3d19 100644 --- a/tests/ui/traits/next-solver/stalled-coroutine-obligations.rs +++ b/tests/ui/traits/next-solver/stalled-coroutine-obligations.rs @@ -16,11 +16,11 @@ fn stalled_copy_clone() { type T = impl Copy; let foo: T = async {}; - //~^ ERROR: the trait bound + //~^ ERROR: type mismatch resolving `T == {async block type U = impl Clone; let bar: U = async {}; - //~^ ERROR: the trait bound + //~^ ERROR: type mismatch resolving `U == {async block } auto trait Valid {} @@ -31,13 +31,13 @@ fn stalled_auto_traits() { type T = impl Valid; let a = False; let foo: T = async { a }; - //~^ ERROR: the trait bound `False: Valid` is not satisfied + //~^ ERROR: type mismatch resolving `T == {async block } trait Trait { fn stalled_send(&self, b: *mut ()) -> impl Future + Send { - //~^ ERROR: type mismatch resolving + //~^ ERROR: type mismatch resolving `impl Future + Send == {async block async move { b } diff --git a/tests/ui/traits/next-solver/stalled-coroutine-obligations.stderr b/tests/ui/traits/next-solver/stalled-coroutine-obligations.stderr index 0a01803e2168e..6fc64fe4768a5 100644 --- a/tests/ui/traits/next-solver/stalled-coroutine-obligations.stderr +++ b/tests/ui/traits/next-solver/stalled-coroutine-obligations.stderr @@ -1,41 +1,33 @@ -error[E0277]: the trait bound `{async block@$DIR/stalled-coroutine-obligations.rs:18:18: 18:23}: Copy` is not satisfied +error[E0271]: type mismatch resolving `T == {async block@$DIR/stalled-coroutine-obligations.rs:18:18: 18:23}` --> $DIR/stalled-coroutine-obligations.rs:18:14 | LL | let foo: T = async {}; - | ^ the trait `Copy` is not implemented for `{async block@$DIR/stalled-coroutine-obligations.rs:18:18: 18:23}` + | ^ types differ -error[E0277]: the trait bound `{async block@$DIR/stalled-coroutine-obligations.rs:22:18: 22:23}: Clone` is not satisfied +error[E0271]: type mismatch resolving `U == {async block@$DIR/stalled-coroutine-obligations.rs:22:18: 22:23}` --> $DIR/stalled-coroutine-obligations.rs:22:14 | LL | let bar: U = async {}; - | ^ the trait `Clone` is not implemented for `{async block@$DIR/stalled-coroutine-obligations.rs:22:18: 22:23}` + | ^ types differ -error[E0277]: the trait bound `False: Valid` is not satisfied in `{async block@$DIR/stalled-coroutine-obligations.rs:33:18: 33:23}` +error[E0271]: type mismatch resolving `T == {async block@$DIR/stalled-coroutine-obligations.rs:33:18: 33:23}` --> $DIR/stalled-coroutine-obligations.rs:33:14 | LL | let foo: T = async { a }; - | ^ ----- within this `{async block@$DIR/stalled-coroutine-obligations.rs:33:18: 33:23}` - | | - | unsatisfied trait bound - | -help: within `{async block@$DIR/stalled-coroutine-obligations.rs:33:18: 33:23}`, the trait `Valid` is not implemented for `False` - --> $DIR/stalled-coroutine-obligations.rs:27:1 - | -LL | struct False; - | ^^^^^^^^^^^^ -note: captured value does not implement `Valid` - --> $DIR/stalled-coroutine-obligations.rs:33:26 - | -LL | let foo: T = async { a }; - | ^ has type `False` which does not implement `Valid` + | ^ types differ error[E0271]: type mismatch resolving `impl Future + Send == {async block@$DIR/stalled-coroutine-obligations.rs:41:9: 41:19}` --> $DIR/stalled-coroutine-obligations.rs:39:43 | LL | fn stalled_send(&self, b: *mut ()) -> impl Future + Send { - | ^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^ expected `async` block, found associated type +LL | +LL | async move { + | ---------- the expected `async` block + | + = note: expected `async` block `{async block@$DIR/stalled-coroutine-obligations.rs:41:9: 41:19}` + found associated type `impl Future + Send` error: aborting due to 4 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs new file mode 100644 index 0000000000000..ca97b25fa64fa --- /dev/null +++ b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs @@ -0,0 +1,22 @@ +// Regression test for #154568 +//@ compile-flags: -Znext-solver=globally + +trait Role { + type Inner; +} + +struct HandshakeCallback(C); +struct Handshake(R::Inner); + +fn main() { + let callback = HandshakeCallback(()); + let handshake = Handshake(callback.0.clone()); + //~^ ERROR type annotations needed + match &handshake { + hs if (|| { + let borrowed_inner = &hs.0; + borrowed_inner == &callback.0 + })() => println!(), + _ => {} + } +} diff --git a/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr new file mode 100644 index 0000000000000..3c5a00744a636 --- /dev/null +++ b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr @@ -0,0 +1,20 @@ +error[E0283]: type annotations needed for `Handshake<_>` + --> $DIR/unexpected-pointer-deref-issue-154568.rs:13:9 + | +LL | let handshake = Handshake(callback.0.clone()); + | ^^^^^^^^^ ----------------------------- type must be known at this point + | + = note: the type must implement `Role` +note: required by a bound in `Handshake` + --> $DIR/unexpected-pointer-deref-issue-154568.rs:9:21 + | +LL | struct Handshake(R::Inner); + | ^^^^ required by this bound in `Handshake` +help: consider giving `handshake` an explicit type, where the type for type parameter `R` is specified + | +LL | let handshake: Handshake = Handshake(callback.0.clone()); + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr index 67334e12326f3..2826858f7048b 100644 --- a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr @@ -1,15 +1,15 @@ -error[E0271]: type mismatch resolving `::Diverges == _` +error[E0275]: overflow evaluating the requirement `::Diverges == _` --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 | LL | field: Box<::Diverges>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `::Diverges == _` - | -note: types differ - --> $DIR/normalize-diverging-alias-in-struct.rs:17:31 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating whether `Box<::Diverges>` is well-formed + --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 | -LL | type Diverges = D::Diverges; - | ^^^^^^^^^^^^^^ +LL | field: Box<::Diverges>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs index 0b31bf32aa285..17f57374a0da2 100644 --- a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs @@ -19,8 +19,8 @@ impl Trait for T { struct Foo { field: Box<::Diverges>, - //[current]~^ ERROR: overflow evaluating the requirement `::Diverges == _` - //[next]~^^ ERROR: type mismatch resolving `::Diverges == _` + //~^ ERROR: overflow evaluating the requirement `::Diverges == _` + //[next]~^^ ERROR: overflow evaluating whether `Box<::Diverges>` is well-formed } fn main() {} diff --git a/tests/ui/issues/issue-26805.rs b/tests/ui/traits/object/dyn-iterator-with-non-ord-item.rs similarity index 61% rename from tests/ui/issues/issue-26805.rs rename to tests/ui/traits/object/dyn-iterator-with-non-ord-item.rs index cf75908570acf..2e818884d0511 100644 --- a/tests/ui/issues/issue-26805.rs +++ b/tests/ui/traits/object/dyn-iterator-with-non-ord-item.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + struct NonOrd; fn main() { diff --git a/tests/ui/issues/issue-27842.rs b/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs similarity index 70% rename from tests/ui/issues/issue-27842.rs rename to tests/ui/tuple/tuple-bracket-index-suggest-dot.rs index 060d3b34e09d9..a72be060529c8 100644 --- a/tests/ui/issues/issue-27842.rs +++ b/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs @@ -1,3 +1,7 @@ +//! Regression test for . +//! Test we suggest the use of dot syntax when user is trying to index +//! tuple via square brackets. + fn main() { let tup = (0, 1, 2); // the case where we show a suggestion diff --git a/tests/ui/issues/issue-27842.stderr b/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr similarity index 84% rename from tests/ui/issues/issue-27842.stderr rename to tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr index f388fdf85cde5..f6e6528572661 100644 --- a/tests/ui/issues/issue-27842.stderr +++ b/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr @@ -1,5 +1,5 @@ error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` - --> $DIR/issue-27842.rs:4:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:8:16 | LL | let _ = tup[0]; | ^^^ help: to access tuple element `0`, use: `.0` @@ -7,7 +7,7 @@ LL | let _ = tup[0]; = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` - --> $DIR/issue-27842.rs:9:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:13:16 | LL | let _ = tup[i]; | ^^^ @@ -15,7 +15,7 @@ LL | let _ = tup[i]; = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error[E0608]: cannot index into a value of type `({integer},)` - --> $DIR/issue-27842.rs:14:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:18:16 | LL | let _ = tup[3]; | ^^^ diff --git a/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs index 22dbdb28d8ba5..66442ac874516 100644 --- a/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs +++ b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs @@ -9,6 +9,7 @@ // @lcnr: Because of this I decided to not bother and cause this to fail instead. // In the future we can definitely modify the compiler to accept this // again. + #![feature(type_alias_impl_trait)] trait Trait {} @@ -18,7 +19,7 @@ type TAIT = impl Sized; impl Trait for (TAIT, TAIT) {} impl Trait for (u32, i32) {} -//~^ ERROR: conflicting implementations of trait `Trait` for type +//~^ ERROR: conflicting implementations of trait `Trait` for type `(TAIT, TAIT)` #[define_opaque(TAIT)] fn define() -> TAIT {} diff --git a/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr index ef170101b4455..d7f6606d2099a 100644 --- a/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr +++ b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait` for type `(TAIT, TAIT)` - --> $DIR/coherence_different_hidden_ty.rs:20:1 + --> $DIR/coherence_different_hidden_ty.rs:21:1 | LL | impl Trait for (TAIT, TAIT) {} | --------------------------- first implementation here diff --git a/tests/ui/type-alias-impl-trait/const_eval_reveal_opaque_in_param_env.rs b/tests/ui/type-alias-impl-trait/const_eval_reveal_opaque_in_param_env.rs new file mode 100644 index 0000000000000..c2c04ce22f782 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/const_eval_reveal_opaque_in_param_env.rs @@ -0,0 +1,32 @@ +//@ edition: 2024 +//@ compile-flags: -Znext-solver -Zdisable-fast-paths +//@ check-pass + +// Test whether we mark the rigid alias in hir typeck non-rigid in const eval +// where we reveal all hidden types with `PostAnalysis` typing mode. +// +// This shouldn't compile as we shouldn't do const eval in non-empty param env +// in the future. + +#![feature(type_alias_impl_trait)] +type Tait = impl Sized; +#[define_opaque(Tait)] +fn foo(x: T) -> Tait { + x +} + +trait Trait { + type Assoc; +} + +fn bar() -> [u8; 4] +where + T: Trait, + Tait: Trait, +{ + [0; std::mem::size_of::<::Assoc>()] + //~^ WARN: cannot use constants which depend on generic parameters in types + //~| WARN: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/const_eval_reveal_opaque_in_param_env.stderr b/tests/ui/type-alias-impl-trait/const_eval_reveal_opaque_in_param_env.stderr new file mode 100644 index 0000000000000..e724327ea060d --- /dev/null +++ b/tests/ui/type-alias-impl-trait/const_eval_reveal_opaque_in_param_env.stderr @@ -0,0 +1,12 @@ +warning: cannot use constants which depend on generic parameters in types + --> $DIR/const_eval_reveal_opaque_in_param_env.rs:27:9 + | +LL | [0; std::mem::size_of::<::Assoc>()] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #76200 + = note: `#[warn(const_evaluatable_unchecked)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/type-alias-impl-trait/issue-63279.rs b/tests/ui/type-alias-impl-trait/issue-63279.rs index 10da40d2f90d2..50d4a42e1f355 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.rs +++ b/tests/ui/type-alias-impl-trait/issue-63279.rs @@ -4,11 +4,11 @@ type Closure = impl FnOnce(); #[define_opaque(Closure)] fn c() -> Closure { - //~^ ERROR: expected a `FnOnce()` closure, found `()` + //~^ ERROR: expected an `FnOnce()` closure, found `()` || -> Closure { || () } //~^ ERROR: mismatched types //~| ERROR: mismatched types - //~| ERROR: expected a `FnOnce()` closure, found `()` + //~| ERROR: expected an `FnOnce()` closure, found `()` } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-63279.stderr b/tests/ui/type-alias-impl-trait/issue-63279.stderr index 8f0e9c5fde04d..165c1c25280bd 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.stderr +++ b/tests/ui/type-alias-impl-trait/issue-63279.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/issue-63279.rs:6:11 | LL | fn c() -> Closure { @@ -7,7 +7,7 @@ LL | fn c() -> Closure { = help: the trait `FnOnce()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/issue-63279.rs:8:11 | LL | || -> Closure { || () } diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs index 075b0bd75fa53..aec311ede864f 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs @@ -8,10 +8,10 @@ type Tait = impl FnOnce() -> (); #[define_opaque(Tait)] fn reify_as_tait() -> Thunk { - //~^ ERROR: expected a `FnOnce()` closure, found `()` + //~^ ERROR: expected an `FnOnce()` closure, found `()` Thunk::new(|cont| cont) //~^ ERROR: mismatched types - //~| ERROR: expected a `FnOnce()` closure, found `()` + //~| ERROR: expected an `FnOnce()` closure, found `()` } struct Thunk(F); diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr index 0bee0dfa9c7a3..22d16e39fc404 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr @@ -10,7 +10,7 @@ LL | Thunk::new(|cont| cont) = note: expected struct `Thunk<_>` found unit type `()` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/lazy_subtyping_of_opaques.rs:12:23 | LL | Thunk::new(|cont| cont) @@ -19,7 +19,7 @@ LL | Thunk::new(|cont| cont) = help: the trait `FnOnce()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/lazy_subtyping_of_opaques.rs:10:23 | LL | fn reify_as_tait() -> Thunk { diff --git a/tests/ui/type-alias-impl-trait/method_resolution.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution.next.stderr index 7462d2b478bb6..660e4351d0cae 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution.next.stderr @@ -5,7 +5,9 @@ LL | struct Bar(T); | ------------- method `bar` not found for this struct ... LL | self.bar() - | ^^^ method cannot be called on `Bar` due to unsatisfied trait bounds + | ^^^ method not found in `Bar` + | + = note: the method was found for `Bar` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-22471.rs b/tests/ui/type-alias/where-clause-on-generic-type-alias.rs similarity index 55% rename from tests/ui/issues/issue-22471.rs rename to tests/ui/type-alias/where-clause-on-generic-type-alias.rs index a983fec05c865..9e306e1fdfac6 100644 --- a/tests/ui/issues/issue-22471.rs +++ b/tests/ui/type-alias/where-clause-on-generic-type-alias.rs @@ -1,5 +1,6 @@ +//! Regression test for . + //@ check-pass -#![allow(dead_code)] #![allow(type_alias_bounds)] type Foo where T: Copy = Box; diff --git a/tests/ui/type-inference/index-expr-ambiguous-type.rs b/tests/ui/type-inference/index-expr-ambiguous-type.rs new file mode 100644 index 0000000000000..061699c8a013f --- /dev/null +++ b/tests/ui/type-inference/index-expr-ambiguous-type.rs @@ -0,0 +1,54 @@ +// Regression test for #156738 +// +// When the index type in `arr[idx]` is ambiguous, the error should point +// at the index sub-expression, not the whole indexing expression or +// surrounding operators. + +fn with_cast() { + let bad_idx = 0u8; + let _foo = [1, 2, 3][bad_idx.into()] as i32; + //~^ ERROR type annotations needed +} + +fn with_binop() { + let bad_idx = 0u8; + let _foo = 0 + [1, 2, 3][bad_idx.into()]; + //~^ ERROR type annotations needed +} + +fn standalone() { + let bad_idx = 0u8; + let _foo = [1, 2, 3][bad_idx.into()]; + //~^ ERROR type annotations needed +} + +fn with_known_index_type() { + let bad_idx = 0u8; + let _foo = [1, 2, 3][Into::::into(bad_idx)] as i32; +} + +fn invalid_operator_with_ambiguous_index() { + let bad_idx = 0u8; + let _foo = true + [1, 2, 3][bad_idx.into()]; + //~^ ERROR cannot add +} + +fn mismatched_numeric_binop_with_ambiguous_index() { + let bad_idx = 0u8; + let _foo = 0u64 + [1i32, 2, 3][bad_idx.into()]; + //~^ ERROR type annotations needed +} + +fn shift_with_ambiguous_index() { + let bad_idx = 0u8; + let _foo = 1u32 << [0u8][bad_idx.into()]; + //~^ ERROR type annotations needed +} + +fn string_add_with_ambiguous_index() { + let bad_idx = 0u8; + let _foo = String::new() + [""][bad_idx.into()]; + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/type-inference/index-expr-ambiguous-type.stderr b/tests/ui/type-inference/index-expr-ambiguous-type.stderr new file mode 100644 index 0000000000000..83de98d80cae6 --- /dev/null +++ b/tests/ui/type-inference/index-expr-ambiguous-type.stderr @@ -0,0 +1,70 @@ +error[E0282]: type annotations needed + --> $DIR/index-expr-ambiguous-type.rs:9:34 + | +LL | let _foo = [1, 2, 3][bad_idx.into()] as i32; + | ^^^^ cannot infer type + +error[E0284]: type annotations needed + --> $DIR/index-expr-ambiguous-type.rs:15:38 + | +LL | let _foo = 0 + [1, 2, 3][bad_idx.into()]; + | ^^^^ cannot infer type + | + = note: cannot satisfy `>::Output == _` + +error[E0283]: type annotations needed + --> $DIR/index-expr-ambiguous-type.rs:21:34 + | +LL | let _foo = [1, 2, 3][bad_idx.into()]; + | ^^^^ + | + = note: the type must implement `From` + = note: required for `u8` to implement `Into<_>` +help: try using a fully qualified path to specify the expected types + | +LL - let _foo = [1, 2, 3][bad_idx.into()]; +LL + let _foo = [1, 2, 3][>::into(bad_idx)]; + | + +error[E0369]: cannot add `_` to `bool` + --> $DIR/index-expr-ambiguous-type.rs:32:21 + | +LL | let _foo = true + [1, 2, 3][bad_idx.into()]; + | ---- ^ ------------------------- _ + | | + | bool + +error[E0284]: type annotations needed + --> $DIR/index-expr-ambiguous-type.rs:38:21 + | +LL | let _foo = 0u64 + [1i32, 2, 3][bad_idx.into()]; + | ^ cannot infer type + | + = note: cannot satisfy `>::Output == _` + +error[E0284]: type annotations needed + --> $DIR/index-expr-ambiguous-type.rs:44:38 + | +LL | let _foo = 1u32 << [0u8][bad_idx.into()]; + | ^^^^ cannot infer type + | + = note: cannot satisfy `>::Output == _` + +error[E0283]: type annotations needed + --> $DIR/index-expr-ambiguous-type.rs:50:45 + | +LL | let _foo = String::new() + [""][bad_idx.into()]; + | ^^^^ + | + = note: the type must implement `From` + = note: required for `u8` to implement `Into<_>` +help: try using a fully qualified path to specify the expected types + | +LL - let _foo = String::new() + [""][bad_idx.into()]; +LL + let _foo = String::new() + [""][>::into(bad_idx)]; + | + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0282, E0283, E0284, E0369. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/type/pattern_types/validity.rs b/tests/ui/type/pattern_types/validity.rs index 6c630fc2633b6..f4c62dc6712e9 100644 --- a/tests/ui/type/pattern_types/validity.rs +++ b/tests/ui/type/pattern_types/validity.rs @@ -1,7 +1,7 @@ //! Check that pattern types have their validity checked // Strip out raw byte dumps to make tests platform-independent: //@ normalize-stderr: "([[:xdigit:]]{2}\s){4,8}\s+│\s.{4,8}" -> "HEX_DUMP" -//@ ignore-parallel-frontend different alloc ids + #![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] diff --git a/tests/ui/type/pattern_types/validity.stderr b/tests/ui/type/pattern_types/validity.stderr index a4fb46cf3ed05..a0941c9b718a6 100644 --- a/tests/ui/type/pattern_types/validity.stderr +++ b/tests/ui/type/pattern_types/validity.stderr @@ -9,7 +9,7 @@ LL | const BAD: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; HEX_DUMP } -error[E0080]: reading memory at ALLOC0[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory --> $DIR/validity.rs:13:1 | LL | const BAD_UNINIT: pattern_type!(u32 is 1..) = @@ -50,7 +50,7 @@ LL | const BAD_FOO: Foo = Foo(Bar(unsafe { std::mem::transmute(0) })); HEX_DUMP } -error[E0080]: reading memory at ALLOC1[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory +error[E0080]: reading memory at ALLOC$ID[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory --> $DIR/validity.rs:29:1 | LL | const CHAR_UNINIT: pattern_type!(char is 'A'..'Z') = diff --git a/tests/ui/issues/issue-21701.rs b/tests/ui/typeck/call-non-fn-type-param.rs similarity index 74% rename from tests/ui/issues/issue-21701.rs rename to tests/ui/typeck/call-non-fn-type-param.rs index bfa03c5e42f8d..993d585bd2185 100644 --- a/tests/ui/issues/issue-21701.rs +++ b/tests/ui/typeck/call-non-fn-type-param.rs @@ -1,3 +1,5 @@ +//! Regression test for . + fn foo(t: U) { let y = t(); //~^ ERROR: expected function, found `U` diff --git a/tests/ui/issues/issue-21701.stderr b/tests/ui/typeck/call-non-fn-type-param.stderr similarity index 89% rename from tests/ui/issues/issue-21701.stderr rename to tests/ui/typeck/call-non-fn-type-param.stderr index 9f1fe7dde735a..f62b571a5d9ad 100644 --- a/tests/ui/issues/issue-21701.stderr +++ b/tests/ui/typeck/call-non-fn-type-param.stderr @@ -1,5 +1,5 @@ error[E0618]: expected function, found `U` - --> $DIR/issue-21701.rs:2:13 + --> $DIR/call-non-fn-type-param.rs:4:13 | LL | fn foo(t: U) { | - `t` has type `U` @@ -9,7 +9,7 @@ LL | let y = t(); | call expression requires function error[E0618]: expected function, found struct `Bar` - --> $DIR/issue-21701.rs:9:13 + --> $DIR/call-non-fn-type-param.rs:11:13 | LL | struct Bar; | ---------- struct `Bar` defined here diff --git a/tests/ui/issues/issue-22781.rs b/tests/ui/typeck/match-boxed-fn-entry-type-inference.rs similarity index 82% rename from tests/ui/issues/issue-22781.rs rename to tests/ui/typeck/match-boxed-fn-entry-type-inference.rs index 36314afd6222d..8d2b1603a6fa6 100644 --- a/tests/ui/issues/issue-22781.rs +++ b/tests/ui/typeck/match-boxed-fn-entry-type-inference.rs @@ -1,3 +1,5 @@ +//! Regression test for . + //@ check-pass #![allow(unused_variables)] use std::collections::HashMap; diff --git a/tests/ui/issues/issue-22603.rs b/tests/ui/unboxed-closures/fnonce-impl-on-unit-struct.rs similarity index 76% rename from tests/ui/issues/issue-22603.rs rename to tests/ui/unboxed-closures/fnonce-impl-on-unit-struct.rs index 63d5e3b2ae6cb..bee2b91c6b430 100644 --- a/tests/ui/issues/issue-22603.rs +++ b/tests/ui/unboxed-closures/fnonce-impl-on-unit-struct.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ check-pass #![feature(unboxed_closures, fn_traits)] diff --git a/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr index 0dd6dcfe6a3f2..d579b5bd75002 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(isize)` closure, found `S` +error[E0277]: expected an `Fn(isize)` closure, found `S` --> $DIR/unboxed-closures-fnmut-as-fn.rs:27:21 | LL | let x = call_it(&S, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr index dfdb3ea19c3f2..a637b180e31e7 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:20:21 | LL | let x = call_it(&square, 22); @@ -14,7 +14,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:25:25 | LL | let y = call_it_mut(&mut square, 22); @@ -30,7 +30,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:30:26 | LL | let z = call_it_once(square, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr index b4521cc6890d8..25cf793ca00e8 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:20:21 | LL | let x = call_it(&square, 22); @@ -13,7 +13,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:25:25 | LL | let y = call_it_mut(&mut square, 22); @@ -28,7 +28,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:30:26 | LL | let z = call_it_once(square, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr index 5519bb33ebc27..e09eda3b99d29 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:21:21 | LL | let x = call_it(&square, 22); @@ -14,7 +14,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:26:25 | LL | let y = call_it_mut(&mut square, 22); @@ -30,7 +30,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:31:26 | LL | let z = call_it_once(square, 22); diff --git a/tests/ui/uninhabited/void-branch.rs b/tests/ui/uninhabited/void-branch.rs index b9e1d70382910..0f3e6987fead5 100644 --- a/tests/ui/uninhabited/void-branch.rs +++ b/tests/ui/uninhabited/void-branch.rs @@ -29,4 +29,13 @@ fn with_infallible() { println!() } +fn infallible_with_arg(x: T) -> (T, std::convert::Infallible) { + (x, loop {}) + //~^ ERROR unreachable expression +} + +fn in_if_else(x: String) -> Result { + if x.len() > 0 { Err(infallible_with_arg(x)) } else { Ok(x) } +} + fn main() {} diff --git a/tests/ui/uninhabited/void-branch.stderr b/tests/ui/uninhabited/void-branch.stderr index 15693fc85f4b6..7d886056098aa 100644 --- a/tests/ui/uninhabited/void-branch.stderr +++ b/tests/ui/uninhabited/void-branch.stderr @@ -1,3 +1,18 @@ +error: unreachable expression + --> $DIR/void-branch.rs:33:5 + | +LL | (x, loop {}) + | ^^^^-------^ + | | | + | | any code following this expression is unreachable + | unreachable expression + | +note: the lint level is defined here + --> $DIR/void-branch.rs:1:9 + | +LL | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + error: unreachable expression --> $DIR/void-branch.rs:10:13 | @@ -11,11 +26,6 @@ note: this expression has type `Void`, which is uninhabited | LL | std::mem::uninitialized::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/void-branch.rs:1:9 - | -LL | #![deny(unreachable_code)] - | ^^^^^^^^^^^^^^^^ error: unreachable expression --> $DIR/void-branch.rs:25:9 @@ -31,5 +41,5 @@ note: this expression has type `Infallible`, which is uninhabited LL | infallible(); | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index f555904e5dd7d..17995cabc75a7 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -449,7 +449,7 @@ mod items { impl () {} impl () {} impl Default for () {} - impl const Default for () {} + const impl Default for () {} } /// ItemKind::MacCall mod item_mac_call { } diff --git a/tests/ui/unsafe/unsafe-fn-called-through-ref.rs b/tests/ui/unsafe/unsafe-fn-called-through-ref.rs new file mode 100644 index 0000000000000..d5ac06fe32956 --- /dev/null +++ b/tests/ui/unsafe/unsafe-fn-called-through-ref.rs @@ -0,0 +1,9 @@ +//! Regression test for . +//! Unsafe fn could be called outside of unsafe block through autoderef. + +use std::ptr; + +fn main() { + (&ptr::write)(1 as *mut _, 42); + //~^ ERROR E0133 +} diff --git a/tests/ui/issues/issue-28776.stderr b/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr similarity index 90% rename from tests/ui/issues/issue-28776.stderr rename to tests/ui/unsafe/unsafe-fn-called-through-ref.stderr index 3db94ee181017..6978839ed1c8b 100644 --- a/tests/ui/issues/issue-28776.stderr +++ b/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr @@ -1,5 +1,5 @@ error[E0133]: call to unsafe function `std::ptr::write` is unsafe and requires unsafe function or block - --> $DIR/issue-28776.rs:4:5 + --> $DIR/unsafe-fn-called-through-ref.rs:7:5 | LL | (&ptr::write)(1 as *mut _, 42); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function diff --git a/tests/ui/issues/issue-27078.rs b/tests/ui/unsized/trait-method-unsized-self-param.rs similarity index 67% rename from tests/ui/issues/issue-27078.rs rename to tests/ui/unsized/trait-method-unsized-self-param.rs index 5f09b9587b15a..f3df9706503ca 100644 --- a/tests/ui/issues/issue-27078.rs +++ b/tests/ui/unsized/trait-method-unsized-self-param.rs @@ -1,3 +1,5 @@ +//! Regression test for . + trait Foo { const BAR: i32; fn foo(self) -> &'static i32 { diff --git a/tests/ui/issues/issue-27078.stderr b/tests/ui/unsized/trait-method-unsized-self-param.stderr similarity index 93% rename from tests/ui/issues/issue-27078.stderr rename to tests/ui/unsized/trait-method-unsized-self-param.stderr index d0ba9f6ae2b18..0f5409190d2f2 100644 --- a/tests/ui/issues/issue-27078.stderr +++ b/tests/ui/unsized/trait-method-unsized-self-param.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `Self` cannot be known at compilation time - --> $DIR/issue-27078.rs:3:12 + --> $DIR/trait-method-unsized-self-param.rs:5:12 | LL | fn foo(self) -> &'static i32 { | ^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/issues/issue-22874.rs b/tests/ui/unsized/unsized-slice-element.rs similarity index 72% rename from tests/ui/issues/issue-22874.rs rename to tests/ui/unsized/unsized-slice-element.rs index c4500aacb6184..8ac81463be7e4 100644 --- a/tests/ui/issues/issue-22874.rs +++ b/tests/ui/unsized/unsized-slice-element.rs @@ -1,3 +1,5 @@ +//! Regression test for . + struct Table { rows: [[String]], //~^ ERROR the size for values of type diff --git a/tests/ui/issues/issue-22874.stderr b/tests/ui/unsized/unsized-slice-element.stderr similarity index 90% rename from tests/ui/issues/issue-22874.stderr rename to tests/ui/unsized/unsized-slice-element.stderr index 7d5b601ed494f..1707e5f7a2aa5 100644 --- a/tests/ui/issues/issue-22874.stderr +++ b/tests/ui/unsized/unsized-slice-element.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `[String]` cannot be known at compilation time - --> $DIR/issue-22874.rs:2:11 + --> $DIR/unsized-slice-element.rs:4:11 | LL | rows: [[String]], | ^^^^^^^^^^ doesn't have a size known at compile-time @@ -8,7 +8,7 @@ LL | rows: [[String]], = note: slice and array elements must have `Sized` type error[E0277]: the size for values of type `[String]` cannot be known at compilation time - --> $DIR/issue-22874.rs:7:6 + --> $DIR/unsized-slice-element.rs:9:6 | LL | &table.rows[0] | ^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/issues/issue-29037.rs b/tests/ui/variance/smart-pointers-covariant.rs similarity index 68% rename from tests/ui/issues/issue-29037.rs rename to tests/ui/variance/smart-pointers-covariant.rs index 0e03d8e20ae71..91ab1aefbec2a 100644 --- a/tests/ui/issues/issue-29037.rs +++ b/tests/ui/variance/smart-pointers-covariant.rs @@ -1,6 +1,7 @@ +//! Regression test for . +//! This test ensures that each pointer type `P` is covariant in `X`. //@ check-pass #![allow(dead_code)] -// This test ensures that each pointer type `P` is covariant in `X`. use std::rc::Rc; use std::sync::Arc; diff --git a/tests/ui/wf/return-type-non-wf-no-ice.next.stderr b/tests/ui/wf/return-type-non-wf-no-ice.next.stderr index 888f0893ec7c4..0ee259a401407 100644 --- a/tests/ui/wf/return-type-non-wf-no-ice.next.stderr +++ b/tests/ui/wf/return-type-non-wf-no-ice.next.stderr @@ -1,22 +1,8 @@ -error[E0277]: `T` is not an iterator +error: the type `Foo` is not well-formed --> $DIR/return-type-non-wf-no-ice.rs:13:16 | LL | fn foo() -> Foo { - | ^^^^^^ `T` is not an iterator - | -note: required by a bound in `Foo` - --> $DIR/return-type-non-wf-no-ice.rs:10:8 - | -LL | pub struct Foo(T) - | --- required by a bound in this struct -LL | where -LL | T: Iterator, - | ^^^^^^^^ required by this bound in `Foo` -help: consider restricting type parameter `T` with trait `Iterator` - | -LL | fn foo() -> Foo { - | +++++++++++++++++++++ + | ^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/return-type-non-wf-no-ice.rs b/tests/ui/wf/return-type-non-wf-no-ice.rs index 915e33418d7c0..c597ccdab00df 100644 --- a/tests/ui/wf/return-type-non-wf-no-ice.rs +++ b/tests/ui/wf/return-type-non-wf-no-ice.rs @@ -11,7 +11,8 @@ where ::Item: Default; fn foo() -> Foo { - //~^ ERROR: `T` is not an iterator + //[current]~^ ERROR: `T` is not an iterator + //[next]~^^ ERROR: the type `Foo` is not well-formed loop {} } diff --git a/triagebot.toml b/triagebot.toml index d68e4a4882159..9fd7bad75160f 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1155,7 +1155,6 @@ otherwise, make sure you bump the `FORMAT_VERSION` constant. """ cc = [ "@CraftSpider", - "@aDotInTheVoid", "@Enselic", "@obi1kenobi", ] @@ -1313,7 +1312,7 @@ Please ensure that if you've changed the output: - It's intentional. - The `FORMAT_VERSION` in `src/librustdoc-json-types` is bumped if necessary. """ -cc = ["@aDotInTheVoid", "@obi1kenobi"] +cc = ["@obi1kenobi"] [mentions."tests/ui/derives/deriving-all-codegen.stdout"] message = "Changes to the code generated for builtin derived traits." @@ -1698,7 +1697,7 @@ dep-bumps = [ "/tests/rustdoc-gui" = ["rustdoc"] "/tests/rustdoc-js-std" = ["rustdoc"] "/tests/rustdoc-js/" = ["rustdoc"] -"/tests/rustdoc-json" = ["@aDotInTheVoid"] +"/tests/rustdoc-json" = ["rustdoc"] "/tests/rustdoc-ui" = ["rustdoc"] "/tests/ui" = ["compiler"] "/tests/ui-fulldeps" = ["compiler"] diff --git a/typos.toml b/typos.toml index f680f5b0e8abf..1e3d5c5e89d09 100644 --- a/typos.toml +++ b/typos.toml @@ -21,6 +21,7 @@ extend-exclude = [ # right now. Entries should look like `mipsel = "mipsel"`. # # tidy-alphabetical-start +EXPORTAS = "EXPORTAS" # MSVC linker keyword used with /EXPORT directives anser = "anser" # an ANSI parsing package used by rust-analyzer arange = "arange" # short for A-range childs = "childs"